FIX: FBF phonebook on older firmwares
[enigma2-plugins.git] / fritzcall / src / plugin.py
1 # -*- coding: utf-8 -*-
2 '''
3 $Author$
4 $Revision$
5 $Date$
6 $Id$
7 '''
8 from Screens.Screen import Screen
9 from Screens.MessageBox import MessageBox
10 from Screens.NumericalTextInputHelpDialog import NumericalTextInputHelpDialog
11 from Screens.InputBox import InputBox
12 from Screens import Standby
13 from Screens.HelpMenu import HelpableScreen
14
15 from enigma import eTimer, eSize, ePoint #@UnresolvedImport # pylint: disable-msg=E0611
16 from enigma import eListboxPythonMultiContent, gFont, RT_HALIGN_LEFT, RT_HALIGN_RIGHT, eDVBVolumecontrol
17
18 from Components.MenuList import MenuList
19 from Components.ActionMap import ActionMap
20 from Components.Label import Label
21 from Components.Button import Button
22 from Components.Pixmap import Pixmap
23 from Components.config import config, ConfigSubsection, ConfigSelection, ConfigEnableDisable, getConfigListEntry, ConfigText, ConfigInteger
24 from Components.ConfigList import ConfigListScreen
25 from Components.Harddisk import harddiskmanager
26 try:
27         from Components.config import ConfigPassword
28 except ImportError:
29         ConfigPassword = ConfigText
30
31 from Plugins.Plugin import PluginDescriptor
32 from Tools import Notifications
33 from Tools.NumericalTextInput import NumericalTextInput
34 from Tools.Directories import resolveFilename, SCOPE_PLUGINS, SCOPE_SKIN_IMAGE, SCOPE_CONFIG, SCOPE_MEDIA
35 from Tools.LoadPixmap import LoadPixmap
36 from GlobalActions import globalActionMap # for muting
37
38 from twisted.internet import reactor #@UnresolvedImport
39 from twisted.internet.protocol import ReconnectingClientFactory #@UnresolvedImport
40 from twisted.protocols.basic import LineReceiver #@UnresolvedImport
41 from twisted.web.client import getPage #@UnresolvedImport
42
43 from urllib import urlencode 
44 import re, time, os, hashlib, traceback
45
46 from nrzuname import ReverseLookupAndNotifier, html2unicode
47 import FritzOutlookCSV, FritzLDIF
48 from . import _, initDebug, debug #@UnresolvedImport # pylint: disable-msg=E0611,F0401
49
50 from enigma import getDesktop
51 DESKTOP_WIDTH = getDesktop(0).size().width()
52 DESKTOP_HEIGHT = getDesktop(0).size().height()
53 DESKTOP_SKIN = config.skin.primary_skin.value.replace("/skin.xml", "")
54 XXX = 0 # TODO: Platzhalter für fullscreen SD skin
55 #
56 # this is pure magic.
57 # It returns the first value, if HD (1280x720),
58 # the second if SD (720x576),
59 # else something scaled accordingly
60 # if one of the parameters is -1, scale proportionally
61 #
62 def scaleH(y2, y1):
63         if y2 == -1:
64                 y2 = y1*1280/720
65         elif y1 == -1:
66                 y1 = y2*720/1280
67         return scale(y2, y1, 1280, 720, DESKTOP_WIDTH)
68 def scaleV(y2, y1):
69         if y2 == -1:
70                 y2 = y1*720/576
71         elif y1 == -1:
72                 y1 = y2*576/720
73         return scale(y2, y1, 720, 576, DESKTOP_HEIGHT)
74 def scale(y2, y1, x2, x1, x):
75         return (y2 - y1) * (x - x1) / (x2 - x1) + y1
76
77 my_global_session = None
78
79 config.plugins.FritzCall = ConfigSubsection()
80 config.plugins.FritzCall.debug = ConfigEnableDisable(default=False)
81 #config.plugins.FritzCall.muteOnCall = ConfigSelection(choices=[(None, _("no")), ("ring", _("on ring")), ("connect", _("on connect"))])
82 config.plugins.FritzCall.muteOnCall = ConfigSelection(choices=[(None, _("no")), ("ring", _("on ring"))])
83 config.plugins.FritzCall.hostname = ConfigText(default="fritz.box", fixed_size=False)
84 config.plugins.FritzCall.afterStandby = ConfigSelection(choices=[("none", _("show nothing")), ("inList", _("show as list")), ("each", _("show each call"))])
85 config.plugins.FritzCall.filter = ConfigEnableDisable(default=False)
86 config.plugins.FritzCall.filtermsn = ConfigText(default="", fixed_size=False)
87 config.plugins.FritzCall.filtermsn.setUseableChars('0123456789,')
88 config.plugins.FritzCall.filterCallList = ConfigEnableDisable(default=True)
89 config.plugins.FritzCall.showOutgoing = ConfigEnableDisable(default=False)
90 config.plugins.FritzCall.timeout = ConfigInteger(default=15, limits=(0, 60))
91 config.plugins.FritzCall.lookup = ConfigEnableDisable(default=False)
92 config.plugins.FritzCall.internal = ConfigEnableDisable(default=False)
93 config.plugins.FritzCall.fritzphonebook = ConfigEnableDisable(default=False)
94 config.plugins.FritzCall.phonebook = ConfigEnableDisable(default=False)
95 config.plugins.FritzCall.addcallers = ConfigEnableDisable(default=False)
96 config.plugins.FritzCall.enable = ConfigEnableDisable(default=False)
97 config.plugins.FritzCall.password = ConfigPassword(default="", fixed_size=False)
98 config.plugins.FritzCall.extension = ConfigText(default='1', fixed_size=False)
99 config.plugins.FritzCall.extension.setUseableChars('0123456789')
100 config.plugins.FritzCall.showType = ConfigEnableDisable(default=True)
101 config.plugins.FritzCall.showShortcut = ConfigEnableDisable(default=False)
102 config.plugins.FritzCall.showVanity = ConfigEnableDisable(default=False)
103 config.plugins.FritzCall.prefix = ConfigText(default="", fixed_size=False)
104 config.plugins.FritzCall.prefix.setUseableChars('0123456789')
105 config.plugins.FritzCall.fullscreen = ConfigEnableDisable(default=False)
106 config.plugins.FritzCall.connectionVerbose = ConfigEnableDisable(default=True)
107
108 def handleMountpoint(loc):
109         # debug("[FritzCall] handleMountpoint: %s" %repr(loc))
110         mp = loc[0]
111         while mp[-1] == '/':
112                 mp = mp[:-1]
113         if os.path.exists(os.path.join(mp, "PhoneBook.txt")):
114                 desc = ' *'
115         else:
116                 desc = ''
117         desc = loc[1] + desc
118         return (mp, desc + " (" + mp + ")")
119
120 mountedDevs = [(resolveFilename(SCOPE_CONFIG), _("Flash"))]
121 if os.path.isdir(resolveFilename(SCOPE_MEDIA, "cf")):
122         mountedDevs.append((resolveFilename(SCOPE_MEDIA, "cf"), _("Compact Flash")))
123 if os.path.isdir(resolveFilename(SCOPE_MEDIA, "usb")):
124         mountedDevs.append((resolveFilename(SCOPE_MEDIA, "usb"), _("USB Device")))
125 for p in harddiskmanager.getMountedPartitions(True):
126         mountedDevs.append((p.mountpoint, (_(p.description) if p.description else "")))
127 mountedDevs = map(handleMountpoint, mountedDevs)
128 config.plugins.FritzCall.phonebookLocation = ConfigSelection(choices=mountedDevs)
129
130 countryCodes = [
131         ("0049", _("Germany")),
132         ("0031", _("The Netherlands")),
133         ("0033", _("France")),
134         ("0039", _("Italy")),
135         ("0041", _("Switzerland")),
136         ("0043", _("Austria"))
137         ]
138 config.plugins.FritzCall.country = ConfigSelection(choices=countryCodes)
139
140 FBF_ALL_CALLS = "."
141 FBF_IN_CALLS = "1"
142 FBF_MISSED_CALLS = "2"
143 FBF_OUT_CALLS = "3"
144 fbfCallsChoices = {FBF_ALL_CALLS: _("All calls"),
145                                    FBF_IN_CALLS: _("Incoming calls"),
146                                    FBF_MISSED_CALLS: _("Missed calls"),
147                                    FBF_OUT_CALLS: _("Outgoing calls")
148                                    }
149 config.plugins.FritzCall.fbfCalls = ConfigSelection(choices=fbfCallsChoices)
150
151 config.plugins.FritzCall.name = ConfigText(default="", fixed_size=False)
152 config.plugins.FritzCall.number = ConfigText(default="", fixed_size=False)
153 config.plugins.FritzCall.number.setUseableChars('0123456789')
154
155 phonebook = None
156 fritzbox = None
157
158 avon = {}
159
160 def initAvon():
161         avonFileName = resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/avon.dat")
162         if os.path.exists(avonFileName):
163                 for line in open(avonFileName):
164                         line = line.decode("iso-8859-1").encode('utf-8')
165                         if line[0] == '#':
166                                 continue
167                         parts = line.split(':')
168                         if len(parts) == 2:
169                                 avon[parts[0].replace('-','').replace('*','').replace('/','')] = parts[1]
170
171 def resolveNumberWithAvon(number, countrycode):
172         if not number or number[0] != '0':
173                 return ""
174                 
175         countrycode = countrycode.replace('00','+')
176         if number[:2] == '00':
177                 normNumber = '+' + number[2:]
178         elif number[:1] == '0':
179                 normNumber = countrycode + number[1:]
180         else: # this should can not happen, but safety first
181                 return ""
182         
183         # debug('normNumer: ' + normNumber)
184         for i in reversed(range(min(10, len(number)))):
185                 if avon.has_key(normNumber[:i]):
186                         return '[' + avon[normNumber[:i]].strip() + ']'
187         return ""
188
189 def handleReverseLookupResult(name):
190         found = re.match("NA: ([^;]*);VN: ([^;]*);STR: ([^;]*);HNR: ([^;]*);PLZ: ([^;]*);ORT: ([^;]*)", name)
191         if found:
192                 ( name, firstname, street, streetno, zipcode, city ) = (found.group(1),
193                                                                                                 found.group(2),
194                                                                                                 found.group(3),
195                                                                                                 found.group(4),
196                                                                                                 found.group(5),
197                                                                                                 found.group(6)
198                                                                                                 )
199                 if firstname:
200                         name += ' ' + firstname
201                 if street or streetno or zipcode or city:
202                         name += ', '
203                 if street:
204                         name += street
205                 if streetno:
206                         name += ' ' + streetno
207                 if (street or streetno) and (zipcode or city):
208                         name += ', '
209                 if zipcode and city:
210                         name += zipcode + ' ' + city
211                 elif zipcode:
212                         name += zipcode
213                 elif city:
214                         name += city
215         return name
216
217 from xml.dom.minidom import parse
218 cbcInfos = {}
219 def initCbC():
220         callbycallFileName = resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/callbycall_world.xml")
221         if os.path.exists(callbycallFileName):
222                 dom = parse(callbycallFileName)
223                 for top in dom.getElementsByTagName("callbycalls"):
224                         for cbc in top.getElementsByTagName("country"):
225                                 code = cbc.getAttribute("code").replace("+","00")
226                                 cbcInfos[code] = cbc.getElementsByTagName("callbycall")
227         else:
228                 debug("[FritzCall] initCbC: callbycallFileName does not exist?!?!")
229
230 def stripCbCPrefix(number, countrycode):
231         if number and number[:2] != "00" and cbcInfos.has_key(countrycode):
232                 for cbc in cbcInfos[countrycode]:
233                         if len(cbc.getElementsByTagName("length"))<1 or len(cbc.getElementsByTagName("prefix"))<1:
234                                 debug("[FritzCall] stripCbCPrefix: entries for " + countrycode + " %s invalid")
235                                 return number
236                         length = int(cbc.getElementsByTagName("length")[0].childNodes[0].data)
237                         prefix = cbc.getElementsByTagName("prefix")[0].childNodes[0].data
238                         # if re.match('^'+prefix, number):
239                         if number[:len(prefix)] == prefix:
240                                 return number[length:]
241         return number
242
243 class FritzAbout(Screen):
244
245         def __init__(self, session):
246                 textFieldWidth = scaleV(350, 250)
247                 width = 5 + 150 + 20 + textFieldWidth + 5 + 175 + 5
248                 height = 5 + 175 + 5 + 25 + 5
249                 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
250                 self.skin = """
251                         <screen name="FritzAbout" position="%d,%d" size="%d,%d" title="%s" >
252                                 <widget name="text" position="175,%d" size="%d,%d" font="Regular;%d" />
253                                 <ePixmap position="5,37" size="150,110" pixmap="%s" transparent="1" alphatest="blend" />
254                                 <ePixmap position="%d,5" size="175,175" pixmap="%s" transparent="1" alphatest="blend" />
255                                 <widget name="url" position="20,185" size="%d,25" font="Regular;%d" />
256                         </screen>""" % (
257                                                         (DESKTOP_WIDTH - width) / 2, (DESKTOP_HEIGHT - height) / 2, # position
258                                                         width, height, # size
259                                                         _("About FritzCall"), # title
260                                                         (height-scaleV(150,130)) / 2, # text vertical position
261                                                         textFieldWidth,
262                                                         scaleV(150,130), # text height
263                                                         scaleV(24,21), # text font size
264                                                         resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/fritz.png"), # 150x110
265                                                         5 + 150 + 5 + textFieldWidth + 5, # qr code horizontal offset
266                                                         resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/website.png"), # 175x175
267                                                         width-40, # url width
268                                                         scaleV(24,21) # url font size
269                                                         )
270                 Screen.__init__(self, session)
271                 self["aboutActions"] = ActionMap(["OkCancelActions"],
272                 {
273                 "cancel": self.exit,
274                 "ok": self.exit,
275                 }, -2)
276                 self["text"] = Label(
277                                                         "FritzCall Plugin" + "\n\n" +
278                                                         "$Author$"[1:-2] + "\n" +
279                                                         "$Revision$"[1:-2] + "\n" + 
280                                                         "$Date$"[1:23] + "\n"
281                                                         )
282                 self["url"] = Label("http://wiki.blue-panel.com/index.php/FritzCall")
283                 
284         def exit(self):
285                 self.close()
286
287 FBF_boxInfo = 0
288 FBF_upTime = 1
289 FBF_ipAddress = 2
290 FBF_wlanState = 3
291 FBF_dslState = 4
292 FBF_tamActive = 5
293 FBF_dectActive = 6
294 FBF_faxActive = 7
295 FBF_rufumlActive = 8
296
297 class FritzCallFBF:
298         def __init__(self):
299                 debug("[FritzCallFBF] __init__")
300                 self._callScreen = None
301                 self._md5LoginTimestamp = None
302                 self._md5Sid = '0000000000000000'
303                 self._callTimestamp = 0
304                 self._callList = []
305                 self._callType = config.plugins.FritzCall.fbfCalls.value
306                 self._phoneBookID = '0'
307                 self.info = None # (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive)
308                 self.getInfo(None)
309                 self.blacklist = ([], [])
310                 self.readBlacklist()
311
312         def _notify(self, text):
313                 debug("[FritzCallFBF] notify: " + text)
314                 self._md5LoginTimestamp = None
315                 if self._callScreen:
316                         debug("[FritzCallFBF] notify: try to close callScreen")
317                         self._callScreen.close()
318                         self._callScreen = None
319                 Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_ERROR, timeout=config.plugins.FritzCall.timeout.value)
320                         
321         def _login(self, callback=None):
322                 debug("[FritzCallFBF] _login")
323                 if self._callScreen:
324                         self._callScreen.updateStatus(_("login"))
325                 if self._md5LoginTimestamp and ((time.time() - self._md5LoginTimestamp) < float(9.5*60)) and self._md5Sid != '0000000000000000': # new login after 9.5 minutes inactivity 
326                         debug("[FritzCallFBF] _login: renew timestamp: " + time.ctime(self._md5LoginTimestamp) + " time: " + time.ctime())
327                         self._md5LoginTimestamp = time.time()
328                         callback(None)
329                 else:
330                         debug("[FritzCallFBF] _login: not logged in or outdated login")
331                         # http://fritz.box/cgi-bin/webcm?getpage=../html/login_sid.xml
332                         parms = urlencode({'getpage':'../html/login_sid.xml'})
333                         url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
334                         debug("[FritzCallFBF] _login: '" + url + "' parms: '" + parms + "'")
335                         getPage(url,
336                                 method="POST",
337                                 headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
338                                                 }, postdata=parms).addCallback(lambda x: self._md5Login(callback,x)).addErrback(lambda x:self._oldLogin(callback,x))
339
340         def _oldLogin(self, callback, error): 
341                 debug("[FritzCallFBF] _oldLogin: " + repr(error))
342                 self._md5LoginTimestamp = None
343                 if config.plugins.FritzCall.password.value != "":
344                         parms = "login:command/password=%s" % (config.plugins.FritzCall.password.value)
345                         url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
346                         debug("[FritzCallFBF] _oldLogin: '" + url + "' parms: '" + parms + "'")
347                         getPage(url,
348                                 method="POST",
349                                 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
350                                 headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
351                                                 }, postdata=parms).addCallback(self._gotPageLogin).addCallback(callback).addErrback(self._errorLogin)
352                 elif callback:
353                         debug("[FritzCallFBF] _oldLogin: no password, calling " + repr(callback))
354                         callback(None)
355
356         def _md5Login(self, callback, sidXml):
357                 def buildResponse(challenge, text):
358                         debug("[FritzCallFBF] _md5Login7buildResponse: challenge: " + challenge + ' text: ' + text)
359                         text = (challenge + '-' + text).decode('utf-8','ignore').encode('utf-16-le')
360                         for i in range(len(text)):
361                                 if ord(text[i]) > 255:
362                                         text[i] = '.'
363                         md5 = hashlib.md5()
364                         md5.update(text)
365                         debug("[FritzCallFBF] md5Login/buildResponse: " + md5.hexdigest())
366                         return challenge + '-' + md5.hexdigest()
367
368                 debug("[FritzCallFBF] _md5Login")
369                 found = re.match('.*<SID>([^<]*)</SID>', sidXml, re.S)
370                 if found:
371                         self._md5Sid = found.group(1)
372                         debug("[FritzCallFBF] _md5Login: SID "+ self._md5Sid)
373                 else:
374                         debug("[FritzCallFBF] _md5Login: no sid! That must be an old firmware.")
375                         self._oldLogin(callback, 'No error')
376                         return
377
378                 debug("[FritzCallFBF] _md5Login: renew timestamp: " + time.ctime(self._md5LoginTimestamp) + " time: " + time.ctime())
379                 self._md5LoginTimestamp = time.time()
380                 if sidXml.find('<iswriteaccess>0</iswriteaccess>') != -1:
381                         debug("[FritzCallFBF] _md5Login: logging in")
382                         found = re.match('.*<Challenge>([^<]*)</Challenge>', sidXml, re.S)
383                         if found:
384                                 challenge = found.group(1)
385                                 debug("[FritzCallFBF] _md5Login: challenge " + challenge)
386                         else:
387                                 challenge = None
388                                 debug("[FritzCallFBF] _md5Login: login necessary and no challenge! That is terribly wrong.")
389                         parms = urlencode({
390                                                         'getpage':'../html/de/menus/menu2.html', # 'var:pagename':'home', 'var:menu':'home', 
391                                                         'login:command/response': buildResponse(challenge, config.plugins.FritzCall.password.value),
392                                                         })
393                         url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
394                         debug("[FritzCallFBF] _md5Login: '" + url + "' parms: '" + parms + "'")
395                         getPage(url,
396                                 method="POST",
397                                 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
398                                 headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
399                                                 }, postdata=parms).addCallback(self._gotPageLogin).addCallback(callback).addErrback(self._errorLogin)
400                 elif callback: # we assume value 1 here, no login necessary
401                         debug("[FritzCallFBF] _md5Login: no login necessary")
402                         callback(None)
403
404         def _gotPageLogin(self, html):
405                 if self._callScreen:
406                         self._callScreen.updateStatus(_("login verification"))
407                 debug("[FritzCallFBF] _gotPageLogin: verify login")
408                 found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
409                 if found:
410                         text = _("FRITZ!Box - Error logging in: %s") + found.group(1)
411                         self._notify(text)
412                 else:
413                         if self._callScreen:
414                                 self._callScreen.updateStatus(_("login ok"))
415
416                 found = re.match('.*<input type="hidden" name="sid" value="([^\"]*)"', html, re.S)
417                 if found:
418                         self._md5Sid = found.group(1)
419                         debug("[FritzCallFBF] _gotPageLogin: found sid: " + self._md5Sid)
420
421         def _errorLogin(self, error):
422                 global fritzbox
423                 debug("[FritzCallFBF] _errorLogin: %s" % (error))
424                 text = _("FRITZ!Box - Error logging in: %s\nDisabling plugin.") % error.getErrorMessage()
425                 # config.plugins.FritzCall.enable.value = False
426                 fritzbox = None
427                 self._notify(text)
428
429         def _logout(self):
430                 if self._md5LoginTimestamp:
431                         self._md5LoginTimestamp = None
432                         parms = urlencode({
433                                                         'getpage':'../html/de/menus/menu2.html', # 'var:pagename':'home', 'var:menu':'home', 
434                                                         'login:command/logout':'bye bye Fritz'
435                                                         })
436                         url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
437                         debug("[FritzCallFBF] logout: '" + url + "' parms: '" + parms + "'")
438                         getPage(url,
439                                 method="POST",
440                                 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
441                                 headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
442                                                 }, postdata=parms).addErrback(self._errorLogout)
443
444         def _errorLogout(self, error):
445                 debug("[FritzCallFBF] _errorLogout: %s" % (error))
446                 text = _("FRITZ!Box - Error logging out: %s") % error.getErrorMessage()
447                 self._notify(text)
448
449         def loadFritzBoxPhonebook(self):
450                 debug("[FritzCallFBF] loadFritzBoxPhonebook")
451                 if config.plugins.FritzCall.fritzphonebook.value:
452                         self._phoneBookID = '0'
453                         debug("[FritzCallFBF] loadFritzBoxPhonebook: logging in")
454                         self._login(self._loadFritzBoxPhonebook)
455
456         def _loadFritzBoxPhonebook(self, html):
457                 if html:
458                         found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
459                         if found:
460                                 self._errorLoad('Login: ' + found.group(1))
461                                 return
462                 parms = urlencode({
463                                                 'getpage':'../html/de/menus/menu2.html',
464                                                 'var:lang':'de',
465                                                 'var:pagename':'fonbuch',
466                                                 'var:menu':'fon',
467                                                 'sid':self._md5Sid,
468                                                 'telcfg:settings/Phonebook/Books/Select':self._phoneBookID, # this selects always the first phonbook
469                                                 })
470                 url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
471                 debug("[FritzCallFBF] _loadFritzBoxPhonebook: '" + url + "' parms: '" + parms + "'")
472                 getPage(url,
473                         method="POST",
474                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
475                         headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
476                                         }, postdata=parms).addCallback(self._parseFritzBoxPhonebook).addErrback(self._errorLoad)
477
478         def _parseFritzBoxPhonebook(self, html):
479                 debug("[FritzCallFBF] _parseFritzBoxPhonebook")
480
481                 # first, let us get the charset
482                 found = re.match('.*<meta http-equiv=content-type content="text/html; charset=([^"]*)">', html, re.S)
483                 if found:
484                         charset = found.group(1)
485                         debug("[FritzCallFBF] _parseFritzBoxPhonebook: found charset: " + charset)
486                         html = html2unicode(html.decode(charset), charset).encode('utf-8') # this looks silly, but has to be
487                 else: # this is kind of emergency conversion...
488                         try:
489                                 debug("[FritzCallFBF] _parseFritzBoxPhonebook: try charset utf-8")
490                                 charset = 'utf-8'
491                                 html = html2unicode(html.decode('utf-8'), 'utf-8').encode('utf-8') # this looks silly, but has to be
492                         except UnicodeDecodeError:
493                                 debug("[FritzCallFBF] _parseFritzBoxPhonebook: try charset iso-8859-1")
494                                 charset = 'iso-8859-1'
495                                 html = html2unicode(html.decode('iso-8859-1'), 'iso-8859-1').encode('utf-8') # this looks silly, but has to be
496
497                 if re.search('document.write\(TrFon1\(\)', html):
498                         #===============================================================================
499                         #                                New Style: 7270 (FW 54.04.58, 54.04.63-11941, 54.04.70, 54.04.74-14371, 54.04.76)
500                         #                                                       7170 (FW 29.04.70) 22.03.2009
501                         #                                                       7141 (FW 40.04.68) 22.03.2009
502                         #       We expect one line with TrFonName followed by several lines with
503                         #       TrFonNr(Type,Number,Shortcut,Vanity), which all belong to the name in TrFonName.
504                         #===============================================================================
505                         found = re.match('.*<input type="hidden" name="telcfg:settings/Phonebook/Books/Name(\d+)" value="[Dd]reambox" id="uiPostPhonebookName\d+" disabled>', html, re.S)
506                         if found:
507                                 phoneBookID = found.group(1)
508                                 debug("[FritzCallFBF] _parseFritzBoxPhonebook: found dreambox phonebook with id: " + phoneBookID)
509                                 if self._phoneBookID != phoneBookID:
510                                         self._phoneBookID = phoneBookID
511                                         debug("[FritzCallFBF] _parseFritzBoxPhonebook: reload phonebook")
512                                         self._loadFritzBoxPhonebook(self._phoneBookID) # reload with dreambox phonebook
513                                         return
514
515                         entrymask = re.compile('(TrFonName\("[^"]+", "[^"]+", "[^"]*"\);.*?)document.write\(TrFon1\(\)', re.S)
516                         entries = entrymask.finditer(html)
517                         for entry in entries:
518                                 # debug(entry.group(1)
519                                 # TrFonName (id, name, category)
520                                 found = re.match('TrFonName\("[^"]*", "([^"]+)", "[^"]*"\);', entry.group(1))
521                                 if found:
522                                         name = found.group(1).replace(',','').strip()
523                                 else:
524                                         continue
525                                 # TrFonNr (type, rufnr, code, vanity)
526                                 detailmask = re.compile('TrFonNr\("([^"]*)", "([^"]*)", "([^"]*)", "([^"]*)"\);', re.S)
527                                 details = detailmask.finditer(entry.group(1))
528                                 for found in details:
529                                         thisnumber = found.group(2).strip()
530                                         if not thisnumber:
531                                                 debug("[FritzCallFBF] Ignoring entry with empty number for '''%s'''" % (name))
532                                                 continue
533                                         else:
534                                                 thisname = name
535                                                 callType = found.group(1)
536                                                 if config.plugins.FritzCall.showType.value:
537                                                         if callType == "mobile":
538                                                                 thisname = thisname + " (" + _("mobile") + ")"
539                                                         elif callType == "home":
540                                                                 thisname = thisname + " (" + _("home") + ")"
541                                                         elif callType == "work":
542                                                                 thisname = thisname + " (" + _("work") + ")"
543
544                                                 if config.plugins.FritzCall.showShortcut.value and found.group(3):
545                                                         thisname = thisname + ", " + _("Shortcut") + ": " + found.group(3)
546                                                 if config.plugins.FritzCall.showVanity.value and found.group(4):
547                                                         thisname = thisname + ", " + _("Vanity") + ": " + found.group(4)
548
549                                                 debug("[FritzCallFBF] Adding '''%s''' with '''%s''' from FRITZ!Box Phonebook!" % (thisname.strip(), thisnumber))
550                                                 # Beware: strings in phonebook.phonebook have to be in utf-8!
551                                                 phonebook.phonebook[thisnumber] = thisname
552
553                 elif re.search('document.write\(TrFon\(', html):
554                         #===============================================================================
555                         #                               Old Style: 7050 (FW 14.04.33)
556                         #       We expect one line with TrFon(No,Name,Number,Shortcut,Vanity)
557                         #   Encoding should be plain Ascii...
558                         #===============================================================================                                
559                         entrymask = re.compile('TrFon\("[^"]*", "([^"]*)", "([^"]*)", "([^"]*)", "([^"]*)"\)', re.S)
560                         entries = entrymask.finditer(html)
561                         for found in entries:
562                                 name = found.group(1).strip().replace(',','')
563                                 # debug("[FritzCallFBF] pos: %s name: %s" %(found.group(0),name))
564                                 thisnumber = found.group(2).strip()
565                                 if config.plugins.FritzCall.showShortcut.value and found.group(3):
566                                         name = name + ", " + _("Shortcut") + ": " + found.group(3)
567                                 if config.plugins.FritzCall.showVanity.value and found.group(4):
568                                         name = name + ", " + _("Vanity") + ": " + found.group(4)
569                                 if thisnumber:
570                                         # name = name.encode('utf-8')
571                                         debug("[FritzCallFBF] Adding '''%s''' with '''%s''' from FRITZ!Box Phonebook!" % (name, thisnumber))
572                                         # Beware: strings in phonebook.phonebook have to be in utf-8!
573                                         phonebook.phonebook[thisnumber] = name
574                                 else:
575                                         debug("[FritzCallFBF] ignoring empty number for %s" % name)
576                                 continue
577                 elif self._md5Sid == '0000000000000000': # retry, it could be a race condition
578                         debug("[FritzCallFBF] _parseFritzBoxPhonebook: retry loading phonebook")
579                         self.loadFritzBoxPhonebook()
580                 else:
581                         self._notify(_("Could not parse FRITZ!Box Phonebook entry"))
582
583         def _errorLoad(self, error):
584                 debug("[FritzCallFBF] _errorLoad: %s" % (error))
585                 text = _("FRITZ!Box - Could not load phonebook: %s") % error.getErrorMessage()
586                 self._notify(text)
587
588         def getCalls(self, callScreen, callback, callType):
589                 #
590                 # call sequence must be:
591                 # - login
592                 # - getPage -> _gotPageLogin
593                 # - loginCallback (_getCalls)
594                 # - getPage -> _getCalls1
595                 debug("[FritzCallFBF] getCalls")
596                 self._callScreen = callScreen
597                 self._callType = callType
598                 if (time.time() - self._callTimestamp) > 180: 
599                         debug("[FritzCallFBF] getCalls: outdated data, login and get new ones: " + time.ctime(self._callTimestamp) + " time: " + time.ctime())
600                         self._callTimestamp = time.time()
601                         self._login(lambda x:self._getCalls(callback, x))
602                 elif not self._callList:
603                         debug("[FritzCallFBF] getCalls: time is ok, but no callList")
604                         self._getCalls1(callback)
605                 else:
606                         debug("[FritzCallFBF] getCalls: time is ok, callList is ok")
607                         self._gotPageCalls(callback)
608
609         def _getCalls(self, callback, html):
610                 if html:
611                         found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
612                         if found:
613                                 self._errorCalls('Login: ' + found.group(1))
614                                 return
615                 #
616                 # we need this to fill Anrufliste.csv
617                 # http://repeater1/cgi-bin/webcm?getpage=../html/de/menus/menu2.html&var:lang=de&var:menu=fon&var:pagename=foncalls
618                 #
619                 debug("[FritzCallFBF] _getCalls")
620                 if html:
621                         found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
622                         if found:
623                                 text = _("FRITZ!Box - Error logging in: %s") + found.group(1)
624                                 self._notify(text)
625                                 return
626
627                 if self._callScreen:
628                         self._callScreen.updateStatus(_("preparing"))
629                 parms = urlencode({'getpage':'../html/de/menus/menu2.html', 'var:lang':'de', 'var:pagename':'foncalls', 'var:menu':'fon', 'sid':self._md5Sid})
630                 url = "http://%s/cgi-bin/webcm?%s" % (config.plugins.FritzCall.hostname.value, parms)
631                 getPage(url).addCallback(lambda x:self._getCalls1(callback)).addErrback(self._errorCalls) #@UnusedVariable # pylint: disable-msg=W0613
632
633         def _getCalls1(self, callback):
634                 #
635                 # finally we should have successfully lgged in and filled the csv
636                 #
637                 debug("[FritzCallFBF] _getCalls1")
638                 if self._callScreen:
639                         self._callScreen.updateStatus(_("finishing"))
640                 parms = urlencode({'getpage':'../html/de/FRITZ!Box_Anrufliste.csv', 'sid':self._md5Sid})
641                 url = "http://%s/cgi-bin/webcm?%s" % (config.plugins.FritzCall.hostname.value, parms)
642                 getPage(url).addCallback(lambda x:self._gotPageCalls(callback, x)).addErrback(self._errorCalls)
643
644         def _gotPageCalls(self, callback, csv=""):
645                 def resolveNumber(number):
646                         if number.isdigit():
647                                 if config.plugins.FritzCall.internal.value and len(number) > 3 and number[0] == "0":
648                                         number = number[1:]
649                                 # strip CbC prefix
650                                 number = stripCbCPrefix(number, config.plugins.FritzCall.country.value)
651                                 if config.plugins.FritzCall.prefix.value and number and number[0] != '0':               # should only happen for outgoing
652                                         number = config.plugins.FritzCall.prefix.value + number
653                                 name = phonebook.search(number)
654                                 if name:
655                                         found = re.match('(.*?)\n.*', name)
656                                         if found:
657                                                 name = found.group(1)
658                                         number = name
659                                 else:
660                                         name = resolveNumberWithAvon(number, config.plugins.FritzCall.country.value)
661                                         if name:
662                                                 number = number + ' ' + name
663                         elif number == "":
664                                 number = _("UNKNOWN")
665                         # if len(number) > 20: number = number[:20]
666                         return number
667
668                 if csv:
669                         debug("[FritzCallFBF] _gotPageCalls: got csv, setting callList")
670                         if self._callScreen:
671                                 self._callScreen.updateStatus(_("done"))
672                         # check for error: wrong password or password not set... TODO
673                         found = re.search('Melden Sie sich mit dem Kennwort der FRITZ!Box an', csv)
674                         if found:
675                                 text = _("You need to set the password of the FRITZ!Box\nin the configuration dialog to display calls\n\nIt could be a communication issue, just try again.")
676                                 # self.session.open(MessageBox, text, MessageBox.TYPE_ERROR, timeout=config.plugins.FritzCall.timeout.value)
677                                 self._notify(text)
678                                 return
679
680                         csv = csv.decode('iso-8859-1', 'replace').encode('utf-8', 'replace')
681                         lines = csv.splitlines()
682                         self._callList = lines
683                 elif self._callList:
684                         debug("[FritzCallFBF] _gotPageCalls: got no csv, but have callList")
685                         if self._callScreen:
686                                 self._callScreen.updateStatus(_("done, using last list"))
687                         lines = self._callList
688                 else:
689                         debug("[FritzCallFBF] _gotPageCalls: got no csv, no callList, laving")
690                         return
691                         
692                 callListL = []
693                 if config.plugins.FritzCall.filter.value and config.plugins.FritzCall.filterCallList.value:
694                         filtermsns = map(lambda x: x.strip(), config.plugins.FritzCall.filtermsn.value.split(","))
695                         debug("[FritzCallFBF] _gotPageCalls: filtermsns %s" % (repr(filtermsns)))
696                 for line in lines:
697                         # Typ;e;Rufnummer;Nebenstelle;Eigene Rufnummer;Dauer
698                         found = re.match("^(" + self._callType + ");([^;]*);([^;]*);([^;]*);([^;]*);([^;]*);([^;]*)", line)
699                         if found:
700                                 direct = found.group(1)
701                                 date = found.group(2)
702                                 length = found.group(7)
703                                 remote = resolveNumber(found.group(4))
704                                 if not remote and direct != FBF_OUT_CALLS and found.group(3):
705                                         remote = found.group(3)
706                                 found1 = re.match('Internet: (.*)', found.group(6))
707                                 if found1:
708                                         here = found1.group(1)
709                                 else:
710                                         here = found.group(6)
711                                 if config.plugins.FritzCall.filter.value and config.plugins.FritzCall.filterCallList.value:
712                                         debug("[FritzCallFBF] _gotPageCalls: check %s" % (here))
713                                         if here not in filtermsns:
714                                                 debug("[FritzCallFBF] _gotPageCalls: skip %s" % (here))
715                                                 continue
716                                 here = resolveNumber(here)
717
718                                 number = stripCbCPrefix(found.group(4), config.plugins.FritzCall.country.value)
719                                 if config.plugins.FritzCall.prefix.value and number and number[0] != '0':               # should only happen for outgoing
720                                         number = config.plugins.FritzCall.prefix.value + number
721                                 callListL.append((number, date, here, direct, remote, length))
722
723                 # debug("[FritzCallFBF] _gotPageCalls result:\n" + text
724
725                 if callback:
726                         # debug("[FritzCallFBF] _gotPageCalls call callback with\n" + text
727                         callback(callListL)
728                 self._callScreen = None
729
730         def _errorCalls(self, error):
731                 debug("[FritzCallFBF] _errorCalls: %s" % (error))
732                 text = _("FRITZ!Box - Could not load calls: %s") % error.getErrorMessage()
733                 self._notify(text)
734
735         def dial(self, number):
736                 ''' initiate a call to number '''
737                 self._login(lambda x: self._dial(number, x))
738                 
739         def _dial(self, number, html):
740                 if html:
741                         found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
742                         if found:
743                                 self._errorDial('Login: ' + found.group(1))
744                                 return
745                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
746                 parms = urlencode({
747                         'getpage':'../html/de/menus/menu2.html',
748                         'var:pagename':'fonbuch',
749                         'var:menu':'home',
750                         'telcfg:settings/UseClickToDial':'1',
751                         'telcfg:settings/DialPort':config.plugins.FritzCall.extension.value,
752                         'telcfg:command/Dial':number,
753                         'sid':self._md5Sid
754                         })
755                 debug("[FritzCallFBF] dial url: '" + url + "' parms: '" + parms + "'")
756                 getPage(url,
757                         method="POST",
758                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
759                         headers={
760                                         'Content-Type': "application/x-www-form-urlencoded",
761                                         'Content-Length': str(len(parms))},
762                         postdata=parms).addCallback(self._okDial).addErrback(self._errorDial)
763
764         def _okDial(self, html): #@UnusedVariable # pylint: disable-msg=W0613
765                 debug("[FritzCallFBF] okDial")
766
767         def _errorDial(self, error):
768                 debug("[FritzCallFBF] errorDial: $s" % error)
769                 text = _("FRITZ!Box - Dialling failed: %s") % error.getErrorMessage()
770                 self._notify(text)
771
772         def changeWLAN(self, statusWLAN):
773                 ''' get status info from FBF '''
774                 debug("[FritzCallFBF] changeWLAN start")
775                 if not statusWLAN or (statusWLAN != '1' and statusWLAN != '0'):
776                         return
777                 self._login(lambda x: self._changeWLAN(statusWLAN, x))
778                 
779         def _changeWLAN(self, statusWLAN, html):
780                 if html:
781                         found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
782                         if found:
783                                 self._errorChangeWLAN('Login: ' + found.group(1))
784                                 return
785                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
786                 parms = urlencode({
787                         'getpage':'../html/de/menus/menu2.html',
788                         'var:lang':'de',
789                         'var:pagename':'wlan',
790                         'var:menu':'wlan',
791                         'wlan:settings/ap_enabled':str(statusWLAN),
792                         'sid':self._md5Sid
793                         })
794                 debug("[FritzCallFBF] changeWLAN url: '" + url + "' parms: '" + parms + "'")
795                 getPage(url,
796                         method="POST",
797                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
798                         headers={
799                                         'Content-Type': "application/x-www-form-urlencoded",
800                                         'Content-Length': str(len(parms))},
801                         postdata=parms).addCallback(self._okChangeWLAN).addErrback(self._errorChangeWLAN)
802
803         def _okChangeWLAN(self, html): #@UnusedVariable # pylint: disable-msg=W0613
804                 debug("[FritzCallFBF] _okChangeWLAN")
805
806         def _errorChangeWLAN(self, error):
807                 debug("[FritzCallFBF] _errorChangeWLAN: $s" % error)
808                 text = _("FRITZ!Box - Failed changing WLAN: %s") % error.getErrorMessage()
809                 self._notify(text)
810
811         def changeMailbox(self, whichMailbox):
812                 ''' switch mailbox on/off '''
813                 debug("[FritzCallFBF] changeMailbox start: " + str(whichMailbox))
814                 self._login(lambda x: self._changeMailbox(whichMailbox, x))
815
816         def _changeMailbox(self, whichMailbox, html):
817                 if html:
818                         found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
819                         if found:
820                                 self._errorChangeMailbox('Login: ' + found.group(1))
821                                 return
822                 debug("[FritzCallFBF] _changeMailbox")
823                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
824                 if whichMailbox == -1:
825                         for i in range(5):
826                                 if self.info[FBF_tamActive][i+1]:
827                                         state = '0'
828                                 else:
829                                         state = '1'
830                                 parms = urlencode({
831                                         'tam:settings/TAM'+str(i)+'/Active':state,
832                                         'sid':self._md5Sid
833                                         })
834                                 debug("[FritzCallFBF] changeMailbox url: '" + url + "' parms: '" + parms + "'")
835                                 getPage(url,
836                                         method="POST",
837                                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
838                                         headers={
839                                                         'Content-Type': "application/x-www-form-urlencoded",
840                                                         'Content-Length': str(len(parms))},
841                                         postdata=parms).addCallback(self._okChangeMailbox).addErrback(self._errorChangeMailbox)
842                 elif whichMailbox > 4:
843                         debug("[FritzCallFBF] changeMailbox invalid mailbox number")
844                 else:
845                         if self.info[FBF_tamActive][whichMailbox+1]:
846                                 state = '0'
847                         else:
848                                 state = '1'
849                         parms = urlencode({
850                                 'tam:settings/TAM'+str(whichMailbox)+'/Active':state,
851                                 'sid':self._md5Sid
852                                 })
853                         debug("[FritzCallFBF] changeMailbox url: '" + url + "' parms: '" + parms + "'")
854                         getPage(url,
855                                 method="POST",
856                                 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
857                                 headers={
858                                                 'Content-Type': "application/x-www-form-urlencoded",
859                                                 'Content-Length': str(len(parms))},
860                                 postdata=parms).addCallback(self._okChangeMailbox).addErrback(self._errorChangeMailbox)
861
862         def _okChangeMailbox(self, html): #@UnusedVariable # pylint: disable-msg=W0613
863                 debug("[FritzCallFBF] _okChangeMailbox")
864
865         def _errorChangeMailbox(self, error):
866                 debug("[FritzCallFBF] _errorChangeMailbox: $s" % error)
867                 text = _("FRITZ!Box - Failed changing Mailbox: %s") % error.getErrorMessage()
868                 self._notify(text)
869
870         def getInfo(self, callback):
871                 ''' get status info from FBF '''
872                 debug("[FritzCallFBF] getInfo")
873                 self._login(lambda x:self._getInfo(callback, x))
874                 
875         def _getInfo(self, callback, html):
876                 # http://192.168.178.1/cgi-bin/webcm?getpage=../html/de/menus/menu2.html&var:lang=de&var:pagename=home&var:menu=home
877                 debug("[FritzCallFBF] _getInfo: verify login")
878                 if html:
879                         found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
880                         if found:
881                                 self._errorGetInfo('Login: ' + found.group(1))
882                                 return
883
884                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
885                 parms = urlencode({
886                         'getpage':'../html/de/menus/menu2.html',
887                         'var:lang':'de',
888                         'var:pagename':'home',
889                         'var:menu':'home',
890                         'sid':self._md5Sid
891                         })
892                 debug("[FritzCallFBF] _getInfo url: '" + url + "' parms: '" + parms + "'")
893                 getPage(url,
894                         method="POST",
895                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
896                         headers={
897                                         'Content-Type': "application/x-www-form-urlencoded",
898                                         'Content-Length': str(len(parms))},
899                         postdata=parms).addCallback(lambda x:self._okGetInfo(callback,x)).addErrback(self._errorGetInfo)
900
901         def _okGetInfo(self, callback, html):
902                 def readInfo(html):
903                         if self.info:
904                                 (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive) = self.info
905                         else:
906                                 (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive) = (None, None, None, None, None, None, None, None, None)
907
908                         debug("[FritzCallFBF] _okGetInfo/readinfo")
909                         found = re.match('.*<table class="tborder" id="tProdukt">\s*<tr>\s*<td style="padding-top:2px;">([^<]*)</td>\s*<td style="padding-top:2px;text-align:right;">\s*([^\s]*)\s*</td>', html, re.S)
910                         if found:
911                                 boxInfo = found.group(1)+ ', ' + found.group(2)
912                                 boxInfo = boxInfo.replace('&nbsp;',' ')
913                                 # debug("[FritzCallFBF] _okGetInfo Boxinfo: " + boxInfo)
914                         else:
915                                 found = re.match('.*<p class="ac">([^<]*)</p>', html, re.S)
916                                 if found:
917                                         # debug("[FritzCallFBF] _okGetInfo Boxinfo: " + found.group(1))
918                                         boxInfo = found.group(1)
919
920                         if html.find('home_coninf.txt') != -1:
921                                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
922                                 parms = urlencode({
923                                         'getpage':'../html/de/home/home_coninf.txt',
924                                         'sid':self._md5Sid
925                                         })
926                                 # debug("[FritzCallFBF] get coninfo: url: '" + url + "' parms: '" + parms + "'")
927                                 getPage(url,
928                                         method="POST",
929                                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
930                                         headers={
931                                                         'Content-Type': "application/x-www-form-urlencoded",
932                                                         'Content-Length': str(len(parms))},
933                                         postdata=parms).addCallback(lambda x:self._okSetConInfo(callback,x)).addErrback(self._errorGetInfo)
934                         else:
935                                 found = re.match('.*if \(isNaN\(jetzt\)\)\s*return "";\s*var str = "([^"]*)";', html, re.S)
936                                 if found:
937                                         # debug("[FritzCallFBF] _okGetInfo Uptime: " + found.group(1))
938                                         upTime = found.group(1)
939                                 else:
940                                         found = re.match('.*str = g_pppSeit \+"([^<]*)<br>"\+mldIpAdr;', html, re.S)
941                                         if found:
942                                                 # debug("[FritzCallFBF] _okGetInfo Uptime: " + found.group(1))
943                                                 upTime = found.group(1)
944         
945                                 found = re.match(".*IpAdrDisplay\('([.\d]+)'\)", html, re.S)
946                                 if found:
947                                         # debug("[FritzCallFBF] _okGetInfo IpAdrDisplay: " + found.group(1))
948                                         ipAddress = found.group(1)
949
950                         if html.find('g_tamActive') != -1:
951                                 entries = re.compile('if \("(\d)" == "1"\) {\s*g_tamActive \+= 1;\s*}', re.S).finditer(html)
952                                 tamActive = [0, False, False, False, False, False]
953                                 i = 1
954                                 for entry in entries:
955                                         state = entry.group(1)
956                                         if state == '1':
957                                                 tamActive[0] += 1
958                                                 tamActive[i] = True
959                                         i += 1
960                                 # debug("[FritzCallFBF] _okGetInfo tamActive: " + str(tamActive))
961                 
962                         if html.find('home_dect.txt') != -1:
963                                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
964                                 parms = urlencode({
965                                         'getpage':'../html/de/home/home_dect.txt',
966                                         'sid':self._md5Sid
967                                         })
968                                 # debug("[FritzCallFBF] get coninfo: url: '" + url + "' parms: '" + parms + "'")
969                                 getPage(url,
970                                         method="POST",
971                                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
972                                         headers={
973                                                         'Content-Type': "application/x-www-form-urlencoded",
974                                                         'Content-Length': str(len(parms))},
975                                         postdata=parms).addCallback(lambda x:self._okSetDect(callback,x)).addErrback(self._errorGetInfo)
976                         else:
977                                 if html.find('countDect2') != -1:
978                                         entries = re.compile('if \("1" == "1"\) countDect2\+\+;', re.S).findall(html)
979                                         dectActive = len(entries)
980                                         # debug("[FritzCallFBF] _okGetInfo dectActive: " + str(dectActive))
981
982                         found = re.match('.*var g_intFaxActive = "0";\s*if \("1" != ""\) {\s*g_intFaxActive = "1";\s*}\s*', html, re.S)
983                         if found:
984                                 faxActive = True
985                                 # debug("[FritzCallFBF] _okGetInfo faxActive")
986
987                         if html.find('cntRufumleitung') != -1:
988                                 entries = re.compile('mode = "1";\s*ziel = "[^"]+";\s*if \(mode == "1" \|\| ziel != ""\)\s*{\s*g_RufumleitungAktiv = true;', re.S).findall(html)
989                                 rufumlActive = len(entries)
990                                 entries = re.compile('if \("([^"]*)"=="([^"]*)"\) isAllIncoming\+\+;', re.S).finditer(html)
991                                 isAllIncoming = 0
992                                 for entry in entries:
993                                         # debug("[FritzCallFBF] _okGetInfo rufumlActive add isAllIncoming")
994                                         if entry.group(1) == entry.group(2):
995                                                 isAllIncoming += 1
996                                 if isAllIncoming == 2 and rufumlActive > 0:
997                                         rufumlActive -= 1
998                                 # debug("[FritzCallFBF] _okGetInfo rufumlActive: " + str(rufumlActive))
999
1000                         # /cgi-bin/webcm?getpage=../html/de/home/home_dsl.txt
1001                         # { "dsl_carrier_state": "5", "umts_enabled": "0", "ata_mode": "0", "isusbgsm": "", "dsl_ds_nrate": "3130", "dsl_us_nrate": "448", "hint_dsl_no_cable": "0", "wds_enabled": "0", "wds_hop": "0", "isata": "" } 
1002                         if html.find('home_dsl.txt') != -1:
1003                                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
1004                                 parms = urlencode({
1005                                         'getpage':'../html/de/home/home_dsl.txt',
1006                                         'sid':self._md5Sid
1007                                         })
1008                                 # debug("[FritzCallFBF] get dsl state: url: '" + url + "' parms: '" + parms + "'")
1009                                 getPage(url,
1010                                         method="POST",
1011                                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
1012                                         headers={
1013                                                         'Content-Type': "application/x-www-form-urlencoded",
1014                                                         'Content-Length': str(len(parms))},
1015                                         postdata=parms).addCallback(lambda x:self._okSetDslState(callback,x)).addErrback(self._errorGetInfo)
1016                         else:
1017                                 found = re.match('.*function DslStateDisplay \(state\){\s*var state = "(\d+)";', html, re.S)
1018                                 if found:
1019                                         # debug("[FritzCallFBF] _okGetInfo DslState: " + found.group(1))
1020                                         dslState = [ found.group(1), None ] # state, speed
1021                                         found = re.match('.*function DslStateDisplay \(state\){\s*var state = "\d+";.*?if \("3130" != "0"\) str = "([^"]*)";', html, re.S)
1022                                         if found:
1023                                                 # debug("[FritzCallFBF] _okGetInfo DslSpeed: " + found.group(1).strip())
1024                                                 dslState[1] = found.group(1).strip()
1025                 
1026                         # /cgi-bin/webcm?getpage=../html/de/home/home_wlan.txt
1027                         # { "ap_enabled": "1", "active_stations": "0", "encryption": "4", "wireless_stickandsurf_enabled": "0", "is_macfilter_active": "0", "wmm_enabled": "1", "wlan_state": [ "end" ] }
1028                         if html.find('home_wlan.txt') != -1:
1029                                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
1030                                 parms = urlencode({
1031                                         'getpage':'../html/de/home/home_wlan.txt',
1032                                         'sid':self._md5Sid
1033                                         })
1034                                 # debug("[FritzCallFBF] get wlan state: url: '" + url + "' parms: '" + parms + "'")
1035                                 getPage(url,
1036                                         method="POST",
1037                                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
1038                                         headers={
1039                                                         'Content-Type': "application/x-www-form-urlencoded",
1040                                                         'Content-Length': str(len(parms))},
1041                                         postdata=parms).addCallback(lambda x:self._okSetWlanState(callback,x)).addErrback(self._errorGetInfo)
1042                         else:
1043                                 found = re.match('.*function WlanStateLed \(state\){.*?return StateLed\("(\d+)"\);\s*}', html, re.S)
1044                                 if found:
1045                                         # debug("[FritzCallFBF] _okGetInfo WlanState: " + found.group(1))
1046                                         wlanState = [ found.group(1), 0, 0 ] # state, encryption, number of devices
1047                                         found = re.match('.*var (?:g_)?encryption = "(\d+)";', html, re.S)
1048                                         if found:
1049                                                 # debug("[FritzCallFBF] _okGetInfo WlanEncrypt: " + found.group(1))
1050                                                 wlanState[1] = found.group(1)
1051
1052                         return (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive)
1053
1054                 debug("[FritzCallFBF] _okGetInfo")
1055                 info = readInfo(html)
1056                 debug("[FritzCallFBF] _okGetInfo info: " + str(info))
1057                 self.info = info
1058                 if callback:
1059                         callback(info)
1060
1061         def _okSetDect(self, callback, html):
1062                 # debug("[FritzCallFBF] _okSetDect: " + html)
1063                 # found = re.match('.*"connection_status":"(\d+)".*"connection_ip":"([.\d]+)".*"connection_detail":"([^"]+)".*"connection_uptime":"([^"]+)"', html, re.S)
1064                 if html.find('"dect_enabled": "1"') != -1:
1065                         # debug("[FritzCallFBF] _okSetDect: dect_enabled")
1066                         found = re.match('.*"dect_device_list":.*\[([^\]]*)\]', html, re.S)
1067                         if found:
1068                                 # debug("[FritzCallFBF] _okSetDect: dect_device_list: %s" %(found.group(1)))
1069                                 entries = re.compile('"1"', re.S).findall(found.group(1))
1070                                 dectActive = len(entries)
1071                                 (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dummy, faxActive, rufumlActive) = self.info
1072                                 self.info = (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive)
1073                                 debug("[FritzCallFBF] _okSetDect info: " + str(self.info))
1074                 if callback:
1075                         callback(self.info)
1076
1077         def _okSetConInfo(self, callback, html):
1078                 # debug("[FritzCallFBF] _okSetConInfo: " + html)
1079                 # found = re.match('.*"connection_status":"(\d+)".*"connection_ip":"([.\d]+)".*"connection_detail":"([^"]+)".*"connection_uptime":"([^"]+)"', html, re.S)
1080                 found = re.match('.*"connection_ip": "([.\d]+)".*"connection_uptime": "([^"]+)"', html, re.S)
1081                 if found:
1082                         # debug("[FritzCallFBF] _okSetConInfo: connection_ip: %s upTime: %s" %( found.group(1), found.group(2)))
1083                         ipAddress = found.group(1)
1084                         upTime = found.group(2)
1085                         (boxInfo, dummy, dummy, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive) = self.info
1086                         self.info = (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive)
1087                         debug("[FritzCallFBF] _okSetWlanState info: " + str(self.info))
1088                 else:
1089                         found = re.match('.*_ip": "([.\d]+)".*"connection_uptime": "([^"]+)"', html, re.S)
1090                         if found:
1091                                 # debug("[FritzCallFBF] _okSetConInfo: _ip: %s upTime: %s" %( found.group(1), found.group(2)))
1092                                 ipAddress = found.group(1)
1093                                 upTime = found.group(2)
1094                                 (boxInfo, dummy, dummy, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive) = self.info
1095                                 self.info = (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive)
1096                                 debug("[FritzCallFBF] _okSetWlanState info: " + str(self.info))
1097                 if callback:
1098                         callback(self.info)
1099
1100         def _okSetWlanState(self, callback, html):
1101                 # debug("[FritzCallFBF] _okSetWlanState: " + html)
1102                 found = re.match('.*"ap_enabled": "(\d+)"', html, re.S)
1103                 if found:
1104                         # debug("[FritzCallFBF] _okSetWlanState: ap_enabled: " + found.group(1))
1105                         wlanState = [ found.group(1), None, None ]
1106                         found = re.match('.*"encryption": "(\d+)"', html, re.S)
1107                         if found:
1108                                 # debug("[FritzCallFBF] _okSetWlanState: encryption: " + found.group(1))
1109                                 wlanState[1] = found.group(1)
1110                         found = re.match('.*"active_stations": "(\d+)"', html, re.S)
1111                         if found:
1112                                 # debug("[FritzCallFBF] _okSetWlanState: active_stations: " + found.group(1))
1113                                 wlanState[2] = found.group(1)
1114                         (boxInfo, upTime, ipAddress, dummy, dslState, tamActive, dectActive, faxActive, rufumlActive) = self.info
1115                         self.info = (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive)
1116                         debug("[FritzCallFBF] _okSetWlanState info: " + str(self.info))
1117                 if callback:
1118                         callback(self.info)
1119
1120         def _okSetDslState(self, callback, html):
1121                 # debug("[FritzCallFBF] _okSetDslState: " + html)
1122                 found = re.match('.*"dsl_carrier_state": "(\d+)"', html, re.S)
1123                 if found:
1124                         # debug("[FritzCallFBF] _okSetDslState: dsl_carrier_state: " + found.group(1))
1125                         dslState = [ found.group(1), None ]
1126                         found = re.match('.*"dsl_ds_nrate": "(\d+)"', html, re.S)
1127                         if found:
1128                                 # debug("[FritzCallFBF] _okSetDslState: dsl_ds_nrate: " + found.group(1))
1129                                 dslState[1] = found.group(1)
1130                         found = re.match('.*"dsl_us_nrate": "(\d+)"', html, re.S)
1131                         if found:
1132                                 # debug("[FritzCallFBF] _okSetDslState: dsl_us_nrate: " + found.group(1))
1133                                 dslState[1] = dslState[1] + '/' + found.group(1)
1134                         (boxInfo, upTime, ipAddress, wlanState, dummy, tamActive, dectActive, faxActive, rufumlActive) = self.info
1135                         self.info = (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive)
1136                         debug("[FritzCallFBF] _okSetDslState info: " + str(self.info))
1137                 if callback:
1138                         callback(self.info)
1139
1140         def _errorGetInfo(self, error):
1141                 debug("[FritzCallFBF] _errorGetInfo: %s" % (error))
1142                 text = _("FRITZ!Box - Error getting status: %s") % error.getErrorMessage()
1143                 self._notify(text)
1144                 # linkP = open("/tmp/FritzCall_errorGetInfo.htm", "w")
1145                 # linkP.write(error)
1146                 # linkP.close()
1147
1148         def reset(self):
1149                 self._login(self._reset)
1150
1151         def _reset(self, html):
1152                 # POSTDATA=getpage=../html/reboot.html&errorpage=../html/de/menus/menu2.html&var:lang=de&var:pagename=home&var:errorpagename=home&var:menu=home&var:pagemaster=&time:settings/time=1242207340%2C-120&var:tabReset=0&logic:command/reboot=../gateway/commands/saveconfig.html
1153                 if html:
1154                         found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
1155                         if found:
1156                                 self._errorReset('Login: ' + found.group(1))
1157                                 return
1158                 if self._callScreen:
1159                         self._callScreen.close()
1160                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
1161                 parms = urlencode({
1162                         'getpage':'../html/reboot.html',
1163                         'var:lang':'de',
1164                         'var:pagename':'reset',
1165                         'var:menu':'system',
1166                         'logic:command/reboot':'../gateway/commands/saveconfig.html',
1167                         'sid':self._md5Sid
1168                         })
1169                 debug("[FritzCallFBF] _reset url: '" + url + "' parms: '" + parms + "'")
1170                 getPage(url,
1171                         method="POST",
1172                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
1173                         headers={
1174                                         'Content-Type': "application/x-www-form-urlencoded",
1175                                         'Content-Length': str(len(parms))},
1176                         postdata=parms)
1177
1178         def _okReset(self, html): #@UnusedVariable # pylint: disable-msg=W0613
1179                 debug("[FritzCallFBF] _okReset")
1180
1181         def _errorReset(self, error):
1182                 debug("[FritzCallFBF] _errorReset: %s" % (error))
1183                 text = _("FRITZ!Box - Error resetting: %s") % error.getErrorMessage()
1184                 self._notify(text)
1185
1186         def readBlacklist(self):
1187                 self._login(self._readBlacklist)
1188                 
1189         def _readBlacklist(self, html):
1190                 if html:
1191                         found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
1192                         if found:
1193                                 self._errorBlacklist('Login: ' + found.group(1))
1194                                 return
1195                 # http://fritz.box/cgi-bin/webcm?getpage=../html/de/menus/menu2.html&var:lang=de&var:menu=fon&var:pagename=sperre
1196                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
1197                 parms = urlencode({
1198                         'getpage':'../html/de/menus/menu2.html',
1199                         'var:lang':'de',
1200                         'var:pagename':'sperre',
1201                         'var:menu':'fon',
1202                         'sid':self._md5Sid
1203                         })
1204                 debug("[FritzCallFBF] _readBlacklist url: '" + url + "' parms: '" + parms + "'")
1205                 getPage(url,
1206                         method="POST",
1207                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
1208                         headers={
1209                                         'Content-Type': "application/x-www-form-urlencoded",
1210                                         'Content-Length': str(len(parms))},
1211                         postdata=parms).addCallback(self._okBlacklist).addErrback(self._errorBlacklist)
1212
1213         def _okBlacklist(self, html):
1214                 debug("[FritzCallFBF] _okBlacklist")
1215                 entries = re.compile('<script type="text/javascript">document.write\(Tr(Out|In)\("\d+", "(\d+)", "\w*"\)\);</script>', re.S).finditer(html)
1216                 self.blacklist = ([], [])
1217                 for entry in entries:
1218                         if entry.group(1) == "In":
1219                                 self.blacklist[0].append(entry.group(2))
1220                         else:
1221                                 self.blacklist[1].append(entry.group(2))
1222                 debug("[FritzCallFBF] _okBlacklist: %s" % repr(self.blacklist))
1223
1224         def _errorBlacklist(self, error):
1225                 debug("[FritzCallFBF] _errorBlacklist: %s" % (error))
1226                 text = _("FRITZ!Box - Error getting blacklist: %s") % error.getErrorMessage()
1227                 self._notify(text)
1228
1229 #===============================================================================
1230 #       def hangup(self):
1231 #               ''' hangup call on port; not used for now '''
1232 #               url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
1233 #               parms = urlencode({
1234 #                       'id':'uiPostForm',
1235 #                       'name':'uiPostForm',
1236 #                       'login:command/password': config.plugins.FritzCall.password.value,
1237 #                       'telcfg:settings/UseClickToDial':'1',
1238 #                       'telcfg:settings/DialPort':config.plugins.FritzCall.extension.value,
1239 #                       'telcfg:command/Hangup':'',
1240 #                       'sid':self._md5Sid
1241 #                       })
1242 #               debug("[FritzCallFBF] hangup url: '" + url + "' parms: '" + parms + "'")
1243 #               getPage(url,
1244 #                       method="POST",
1245 #                       headers={
1246 #                                       'Content-Type': "application/x-www-form-urlencoded",
1247 #                                       'Content-Length': str(len(parms))},
1248 #                       postdata=parms)
1249 #===============================================================================
1250
1251 fritzbox = None
1252
1253 class FritzMenu(Screen, HelpableScreen):
1254         def __init__(self, session):
1255                 fontSize = scaleV(24, 21) # indeed this is font size +2
1256                 noButtons = 2 # reset, wlan
1257
1258                 if not fritzbox or not fritzbox.info:
1259                         return
1260
1261                 if fritzbox.info[FBF_tamActive]:
1262                         noButtons += 1 # toggle mailboxes
1263                 width = max(DESKTOP_WIDTH - scaleH(500, 250), noButtons*140+(noButtons+1)*10)
1264                 # boxInfo 2 lines, gap, internet 2 lines, gap, dsl/wlan each 1 line, gap, buttons
1265                 height = 5 + 2*fontSize + 10 + 2*fontSize + 10 + 2*fontSize + 10 + 40 + 5
1266                 if fritzbox.info[FBF_tamActive] is not None:
1267                         height += fontSize
1268                 if fritzbox.info[FBF_dectActive] is not None:
1269                         height += fontSize
1270                 if fritzbox.info[FBF_faxActive] is not None:
1271                         height += fontSize
1272                 if fritzbox.info[FBF_rufumlActive] is not None:
1273                         height += fontSize
1274                 buttonsGap = (width-noButtons*140)/(noButtons+1)
1275                 buttonsVPos = height-40-5
1276
1277                 varLinePos = 4
1278                 if fritzbox.info[FBF_tamActive] is not None:
1279                         mailboxLine = """
1280                                 <widget name="FBFMailbox" position="%d,%d" size="%d,%d" font="Regular;%d" />
1281                                 <widget name="mailbox_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1282                                 <widget name="mailbox_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1283                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1284                                 <widget name="key_yellow" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1285                                 """ % (
1286                                                 40, 5+2*fontSize+10+varLinePos*fontSize+10, # position mailbox
1287                                                 width-40-20, fontSize, # size mailbox
1288                                                 fontSize-2,
1289                                                 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button mailbox
1290                                                 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button mailbox
1291                                                 noButtons*buttonsGap+(noButtons-1)*140, buttonsVPos,
1292                                                 noButtons*buttonsGap+(noButtons-1)*140, buttonsVPos,
1293                                 )
1294                         varLinePos += 1
1295                 else:
1296                         mailboxLine = ""
1297
1298                 if fritzbox.info[FBF_dectActive] is not None:
1299                         dectLine = """
1300                                 <widget name="FBFDect" position="%d,%d" size="%d,%d" font="Regular;%d" />
1301                                 <widget name="dect_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1302                                 <widget name="dect_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1303                                 """ % (
1304                                                 40, 5+2*fontSize+10+varLinePos*fontSize+10, # position dect
1305                                                 width-40-20, fontSize, # size dect
1306                                                 fontSize-2,
1307                                                 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
1308                                                 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
1309                                 )
1310                         varLinePos += 1
1311                 else:
1312                         dectLine = ""
1313
1314                 if fritzbox.info[FBF_faxActive] is not None:
1315                         faxLine = """
1316                                 <widget name="FBFFax" position="%d,%d" size="%d,%d" font="Regular;%d" />
1317                                 <widget name="fax_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1318                                 <widget name="fax_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1319                                 """ % (
1320                                                 40, 5+2*fontSize+10+varLinePos*fontSize+10, # position dect
1321                                                 width-40-20, fontSize, # size dect
1322                                                 fontSize-2,
1323                                                 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
1324                                                 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
1325                                 )
1326                         varLinePos += 1
1327                 else:
1328                         faxLine = ""
1329
1330                 if fritzbox.info[FBF_rufumlActive] is not None:
1331                         rufumlLine = """
1332                                 <widget name="FBFRufuml" position="%d,%d" size="%d,%d" font="Regular;%d" />
1333                                 <widget name="rufuml_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1334                                 <widget name="rufuml_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1335                                 """ % (
1336                                                 40, 5+2*fontSize+10+varLinePos*fontSize+10, # position dect
1337                                                 width-40-20, fontSize, # size dect
1338                                                 fontSize-2,
1339                                                 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
1340                                                 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
1341                                 )
1342                         varLinePos += 1
1343                 else:
1344                         rufumlLine = ""
1345         
1346                 self.skin = """
1347                         <screen name="FritzMenu" position="%d,%d" size="%d,%d" title="%s" >
1348                                 <widget name="FBFInfo" position="%d,%d" size="%d,%d" font="Regular;%d" />
1349                                 <widget name="FBFInternet" position="%d,%d" size="%d,%d" font="Regular;%d" />
1350                                 <widget name="internet_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1351                                 <widget name="internet_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1352                                 <widget name="FBFDsl" position="%d,%d" size="%d,%d" font="Regular;%d" />
1353                                 <widget name="dsl_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1354                                 <widget name="dsl_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1355                                 <widget name="FBFWlan" position="%d,%d" size="%d,%d" font="Regular;%d" />
1356                                 <widget name="wlan_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1357                                 <widget name="wlan_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1358                                 %s
1359                                 %s
1360                                 %s
1361                                 %s
1362                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1363                                 <widget name="key_red" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1364                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1365                                 <widget name="key_green" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1366                         </screen>""" % (
1367                                                 (DESKTOP_WIDTH - width) / 2, (DESKTOP_HEIGHT - height) / 2, # position
1368                                                 width, height, # size
1369                                                 _("FRITZ!Box Fon Status"),
1370                                                 40, 5, # position info
1371                                                 width-2*40, 2*fontSize, # size info
1372                                                 fontSize-2,
1373                                                 40, 5+2*fontSize+10, # position internet
1374                                                 width-40, 2*fontSize, # size internet
1375                                                 fontSize-2,
1376                                                 20, 5+2*fontSize+10+(fontSize-16)/2, # position button internet
1377                                                 20, 5+2*fontSize+10+(fontSize-16)/2, # position button internet
1378                                                 40, 5+2*fontSize+10+2*fontSize+10, # position dsl
1379                                                 width-40-20, fontSize, # size dsl
1380                                                 fontSize-2,
1381                                                 20, 5+2*fontSize+10+2*fontSize+10+(fontSize-16)/2, # position button dsl
1382                                                 20, 5+2*fontSize+10+2*fontSize+10+(fontSize-16)/2, # position button dsl
1383                                                 40, 5+2*fontSize+10+3*fontSize+10, # position wlan
1384                                                 width-40-20, fontSize, # size wlan
1385                                                 fontSize-2,
1386                                                 20, 5+2*fontSize+10+3*fontSize+10+(fontSize-16)/2, # position button wlan
1387                                                 20, 5+2*fontSize+10+3*fontSize+10+(fontSize-16)/2, # position button wlan
1388                                                 mailboxLine,
1389                                                 dectLine,
1390                                                 faxLine,
1391                                                 rufumlLine,
1392                                                 buttonsGap, buttonsVPos, buttonsGap, buttonsVPos,
1393                                                 buttonsGap+140+buttonsGap, buttonsVPos, buttonsGap+140+buttonsGap, buttonsVPos,
1394                                                 )
1395
1396                 Screen.__init__(self, session)
1397                 HelpableScreen.__init__(self)
1398                 # TRANSLATORS: keep it short, this is a button
1399                 self["key_red"] = Button(_("Reset"))
1400                 # TRANSLATORS: keep it short, this is a button
1401                 self["key_green"] = Button(_("Toggle WLAN"))
1402                 self._mailboxActive = False
1403                 if fritzbox.info[FBF_tamActive] is not None:
1404                         # TRANSLATORS: keep it short, this is a button
1405                         self["key_yellow"] = Button(_("Toggle Mailbox"))
1406                         self["menuActions"] = ActionMap(["OkCancelActions", "ColorActions", "NumberActions", "EPGSelectActions"],
1407                                                                                         {
1408                                                                                         "cancel": self._exit,
1409                                                                                         "ok": self._exit,
1410                                                                                         "red": self._reset,
1411                                                                                         "green": self._toggleWlan,
1412                                                                                         "yellow": (lambda: self._toggleMailbox(-1)),
1413                                                                                         "0": (lambda: self._toggleMailbox(0)),
1414                                                                                         "1": (lambda: self._toggleMailbox(1)),
1415                                                                                         "2": (lambda: self._toggleMailbox(2)),
1416                                                                                         "3": (lambda: self._toggleMailbox(3)),
1417                                                                                         "4": (lambda: self._toggleMailbox(4)),
1418                                                                                         "info": self._getInfo,
1419                                                                                         }, -2)
1420                         # TRANSLATORS: keep it short, this is a help text
1421                         self.helpList.append((self["menuActions"], "ColorActions", [("yellow", _("Toggle all mailboxes"))]))
1422                         # TRANSLATORS: keep it short, this is a help text
1423                         self.helpList.append((self["menuActions"], "NumberActions", [("0", _("Toggle 1. mailbox"))]))
1424                         # TRANSLATORS: keep it short, this is a help text
1425                         self.helpList.append((self["menuActions"], "NumberActions", [("1", _("Toggle 2. mailbox"))]))
1426                         # TRANSLATORS: keep it short, this is a help text
1427                         self.helpList.append((self["menuActions"], "NumberActions", [("2", _("Toggle 3. mailbox"))]))
1428                         # TRANSLATORS: keep it short, this is a help text
1429                         self.helpList.append((self["menuActions"], "NumberActions", [("3", _("Toggle 4. mailbox"))]))
1430                         # TRANSLATORS: keep it short, this is a help text
1431                         self.helpList.append((self["menuActions"], "NumberActions", [("4", _("Toggle 5. mailbox"))]))
1432                         self["FBFMailbox"] = Label(_('Mailbox'))
1433                         self["mailbox_inactive"] = Pixmap()
1434                         self["mailbox_active"] = Pixmap()
1435                         self["mailbox_active"].hide()
1436                 else:
1437                         self["menuActions"] = ActionMap(["OkCancelActions", "ColorActions", "EPGSelectActions"],
1438                                                                                         {
1439                                                                                         "cancel": self._exit,
1440                                                                                         "ok": self._exit,
1441                                                                                         "green": self._toggleWlan,
1442                                                                                         "red": self._reset,
1443                                                                                         "info": self._getInfo,
1444                                                                                         }, -2)
1445
1446                 # TRANSLATORS: keep it short, this is a help text
1447                 self.helpList.append((self["menuActions"], "OkCancelActions", [("cancel", _("Quit"))]))
1448                 # TRANSLATORS: keep it short, this is a help text
1449                 self.helpList.append((self["menuActions"], "OkCancelActions", [("ok", _("Quit"))]))
1450                 # TRANSLATORS: keep it short, this is a help text
1451                 self.helpList.append((self["menuActions"], "ColorActions", [("green", _("Toggle WLAN"))]))
1452                 # TRANSLATORS: keep it short, this is a help text
1453                 self.helpList.append((self["menuActions"], "ColorActions", [("red", _("Reset"))]))
1454                 # TRANSLATORS: keep it short, this is a help text
1455                 self.helpList.append((self["menuActions"], "EPGSelectActions", [("info", _("Refresh status"))]))
1456
1457                 self["FBFInfo"] = Label(_('Getting status from FRITZ!Box Fon...'))
1458
1459                 self["FBFInternet"] = Label('Internet')
1460                 self["internet_inactive"] = Pixmap()
1461                 self["internet_active"] = Pixmap()
1462                 self["internet_active"].hide()
1463
1464                 self["FBFDsl"] = Label('DSL')
1465                 self["dsl_inactive"] = Pixmap()
1466                 self["dsl_inactive"].hide()
1467                 self["dsl_active"] = Pixmap()
1468                 self["dsl_active"].hide()
1469
1470                 self["FBFWlan"] = Label('WLAN ')
1471                 self["wlan_inactive"] = Pixmap()
1472                 self["wlan_inactive"].hide()
1473                 self["wlan_active"] = Pixmap()
1474                 self["wlan_active"].hide()
1475                 self._wlanActive = False
1476
1477                 if fritzbox.info[FBF_dectActive] is not None: 
1478                         self["FBFDect"] = Label('DECT')
1479                         self["dect_inactive"] = Pixmap()
1480                         self["dect_active"] = Pixmap()
1481                         self["dect_active"].hide()
1482
1483                 if fritzbox.info[FBF_faxActive] is not None: 
1484                         self["FBFFax"] = Label('Fax')
1485                         self["fax_inactive"] = Pixmap()
1486                         self["fax_active"] = Pixmap()
1487                         self["fax_active"].hide()
1488
1489                 if fritzbox.info[FBF_rufumlActive] is not None: 
1490                         self["FBFRufuml"] = Label(_('Call redirection'))
1491                         self["rufuml_inactive"] = Pixmap()
1492                         self["rufuml_active"] = Pixmap()
1493                         self["rufuml_active"].hide()
1494
1495                 self._timer = eTimer()
1496                 self._timer.callback.append(self._getInfo)
1497                 self.onShown.append(lambda: self._timer.start(5000))
1498                 self.onHide.append(self._timer.stop)
1499                 self._getInfo()
1500
1501         def _getInfo(self):
1502                 fritzbox.getInfo(self._fillMenu)
1503
1504         def _fillMenu(self, status):
1505                 (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive) = status
1506                 self._wlanActive = (wlanState[0] == '1')
1507                 self._mailboxActive = False
1508                 try:
1509                         if not self.has_key("FBFInfo"): # screen is closed already
1510                                 return
1511
1512                         if boxInfo:
1513                                 self["FBFInfo"].setText(boxInfo.replace(', ', '\n'))
1514                         else:
1515                                 self["FBFInfo"].setText('BoxInfo ' + _('Status not available'))
1516
1517                         if ipAddress:
1518                                 if upTime:
1519                                         self["FBFInternet"].setText('Internet ' + _('IP Address:') + ' ' + ipAddress + '\n' + _('Connected since') + ' ' + upTime)
1520                                 else:
1521                                         self["FBFInternet"].setText('Internet ' + _('IP Address:') + ' ' + ipAddress)
1522                                 self["internet_inactive"].hide()
1523                                 self["internet_active"].show()
1524                         else:
1525                                 self["internet_active"].hide()
1526                                 self["internet_inactive"].show()
1527
1528                         if dslState:
1529                                 if dslState[0] == '5':
1530                                         self["dsl_inactive"].hide()
1531                                         self["dsl_active"].show()
1532                                         if dslState[1]:
1533                                                 self["FBFDsl"].setText('DSL ' + dslState[1])
1534                                 else:
1535                                         self["dsl_active"].hide()
1536                                         self["dsl_inactive"].show()
1537                         else:
1538                                 self["FBFDsl"].setText('DSL ' + _('Status not available'))
1539                                 self["dsl_active"].hide()
1540                                 self["dsl_inactive"].hide()
1541
1542                         if wlanState:
1543                                 if wlanState[0 ] == '1':
1544                                         self["wlan_inactive"].hide()
1545                                         self["wlan_active"].show()
1546                                         message = 'WLAN'
1547                                         if wlanState[1] == '0':
1548                                                 message += ' ' + _('not encrypted')
1549                                         else:
1550                                                 message += ' ' + _('encrypted')
1551                                         if wlanState[2]:
1552                                                 if wlanState[2] == '0':
1553                                                         message = message + ', ' + _('no device active')
1554                                                 elif wlanState[2] == '1':
1555                                                         message = message + ', ' + _('one device active')
1556                                                 else:
1557                                                         message = message + ', ' + wlanState[2] + ' ' + _('devices active')
1558                                         self["FBFWlan"].setText(message)
1559                                 else:
1560                                         self["wlan_active"].hide()
1561                                         self["wlan_inactive"].show()
1562                                         self["FBFWlan"].setText('WLAN')
1563                         else:
1564                                 self["FBFWlan"].setText('WLAN ' + _('Status not available'))
1565                                 self["wlan_active"].hide()
1566                                 self["wlan_inactive"].hide()
1567
1568                         if fritzbox.info[FBF_tamActive]:
1569                                 if  not tamActive or tamActive[0] == 0:
1570                                         self._mailboxActive = False
1571                                         self["mailbox_active"].hide()
1572                                         self["mailbox_inactive"].show()
1573                                         self["FBFMailbox"].setText(_('No mailbox active'))
1574                                 else:
1575                                         self._mailboxActive = True
1576                                         message = '('
1577                                         for i in range(5):
1578                                                 if tamActive[i+1]:
1579                                                         message = message + str(i) + ','
1580                                         message = message[:-1] + ')'
1581                                         self["mailbox_inactive"].hide()
1582                                         self["mailbox_active"].show()
1583                                         if tamActive[0] == 1:
1584                                                 self["FBFMailbox"].setText(_('One mailbox active') + ' ' + message)
1585                                         else:
1586                                                 self["FBFMailbox"].setText(str(tamActive[0]) + ' ' + _('mailboxes active') + ' ' + message)
1587         
1588                         if fritzbox.info[FBF_dectActive] and dectActive:
1589                                 self["dect_inactive"].hide()
1590                                 self["dect_active"].show()
1591                                 if dectActive == 0:
1592                                         self["FBFDect"].setText(_('No DECT phone registered'))
1593                                 else:
1594                                         if dectActive == 1:
1595                                                 self["FBFDect"].setText(_('One DECT phone registered'))
1596                                         else:
1597                                                 self["FBFDect"].setText(str(dectActive) + ' ' + _('DECT phones registered'))
1598
1599                         if fritzbox.info[FBF_faxActive] and faxActive:
1600                                 self["fax_inactive"].hide()
1601                                 self["fax_active"].show()
1602                                 self["FBFFax"].setText(_('Software fax active'))
1603
1604                         if fritzbox.info[FBF_rufumlActive] is not None and rufumlActive is not None:
1605                                 if rufumlActive == 0:
1606                                         self["rufuml_active"].hide()
1607                                         self["rufuml_inactive"].show()
1608                                         self["FBFRufuml"].setText(_('No call redirection active'))
1609                                 else:
1610                                         self["rufuml_inactive"].hide()
1611                                         self["rufuml_active"].show()
1612                                         if rufumlActive == 1:
1613                                                 self["FBFRufuml"].setText(_('One call redirection active'))
1614                                         else:
1615                                                 self["FBFRufuml"].setText(str(rufumlActive) + ' ' + _('call redirections active'))
1616
1617                 except KeyError:
1618                         debug("[FritzCallFBF] _fillMenu: " + traceback.format_exc())
1619
1620         def _toggleWlan(self):
1621                 if self._wlanActive:
1622                         debug("[FritzMenu] toggleWlan off")
1623                         fritzbox.changeWLAN('0')
1624                 else:
1625                         debug("[FritzMenu] toggleWlan off")
1626                         fritzbox.changeWLAN('1')
1627
1628         def _toggleMailbox(self, which):
1629                 debug("[FritzMenu] toggleMailbox")
1630                 if fritzbox.info[FBF_tamActive]:
1631                         debug("[FritzMenu] toggleMailbox off")
1632                         fritzbox.changeMailbox(which)
1633
1634         def _reset(self):
1635                 fritzbox.reset()
1636                 self._exit()
1637
1638         def _exit(self):
1639                 self._timer.stop()
1640                 self.close()
1641
1642
1643 class FritzDisplayCalls(Screen, HelpableScreen):
1644
1645         def __init__(self, session, text=""): #@UnusedVariable # pylint: disable-msg=W0613
1646                 if config.plugins.FritzCall.fullscreen.value:
1647                         self.width = DESKTOP_WIDTH
1648                         self.height = DESKTOP_HEIGHT
1649                         backMainPng = ""
1650                         if os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, DESKTOP_SKIN + "/menu/back-main.png")):
1651                                 backMainPng = DESKTOP_SKIN + "/menu/back-main.png"
1652                         elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1/menu/back-main.png")):
1653                                 backMainPng = "Kerni-HD1/menu/back-main.png"
1654                         elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1-picon/menu/back-main.png")):
1655                                 backMainPng = "Kerni-HD1-picon/menu/back-main.png"
1656                         if backMainPng:
1657                                 backMainLine = """<ePixmap position="0,0" zPosition="-10" size="%d,%d" pixmap="%s" transparent="1" />""" % (self.width, self.height, backMainPng)
1658                         else:
1659                                 backMainLine = ""
1660                         debug("[FritzDisplayCalls] backMainLine: " + backMainLine)
1661                                 
1662                         # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
1663                         self.skin = """
1664                                 <screen name="FritzDisplayCalls" position="0,0" size="%d,%d" title="%s" flags="wfNoBorder">
1665                                         %s
1666                                         <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
1667                                                 <convert type="ClockToText">Default</convert>
1668                                         </widget>
1669                                         <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
1670                                                 <convert type="ClockToText">Date</convert>
1671                                         </widget>
1672                                         <eLabel text="%s" position="%d,%d" size="%d,%d" font="Regular;%d" halign="center" backgroundColor="#0b67a2" transparent="1"/>
1673                         
1674                                         <widget name="statusbar" position="%d,%d"  size="%d,%d" font="Regular;%d" backgroundColor="#353e575e" transparent="1" />
1675                                         <eLabel                                 position="%d,%d" size="%d,%d"   zPosition="1" backgroundColor="#bbbbbb" />
1676                                         <eLabel                                 position="%d,%d" size="%d,%d"   zPosition="2" backgroundColor="#252b4247" />
1677                                         <widget name="entries" position="%d,%d" size="%d,%d" zPosition="3" scrollbarMode="showOnDemand" transparent="1" />
1678                         
1679                                         <ePixmap position="%d,%d" size="%d,%d" zPosition="10" pixmap="Kerni-HD1/buttons/red.png" transparent="1" alphatest="on" />
1680                                         <ePixmap position="%d,%d" size="%d,%d" zPosition="10" pixmap="Kerni-HD1/buttons/green.png" transparent="1" alphatest="on" />
1681                                         <ePixmap position="%d,%d" size="%d,%d" zPosition="10" pixmap="Kerni-HD1/buttons/yellow.png" transparent="1" alphatest="on" />
1682                                         <ePixmap position="%d,%d" size="%d,%d" zPosition="10" pixmap="Kerni-HD1/buttons/blue.png" transparent="1" alphatest="on" />
1683                                         <widget name="key_red" position="%d,%d" size="%d,%d" zPosition="10" font="Regular;%d" transparent="1" />
1684                                         <widget name="key_green" position="%d,%d" size="%d,%d" zPosition="10" font="Regular;%d" transparent="1" />
1685                                         <widget name="key_yellow" position="%d,%d" size="%d,%d" zPosition="10" font="Regular;%d" transparent="1" />
1686                                         <widget name="key_blue" position="%d,%d" size="%d,%d" zPosition="10" font="Regular;%d" transparent="1" />
1687                                         <ePixmap position="%d,%d" size="%d,%d" zPosition="2" pixmap="%s" transparent="1" alphatest="blend" />           
1688                                 </screen>""" % (
1689                                                         self.width, self.height, _("Phone calls"),
1690                                                         backMainLine,
1691                                                         scaleH(1130, XXX), scaleV(40, XXX), scaleH(80, XXX), scaleV(26, XXX), scaleV(26, XXX), # time
1692                                                         scaleH(890, XXX), scaleV(70, XXX), scaleH(320, XXX), scaleV(22, XXX), scaleV(20, XXX), # date
1693                                                         "FritzCall " + _("Phone calls"), scaleH(500, XXX), scaleV(63, XXX), scaleH(330, XXX), scaleV(30, XXX), scaleV(27, XXX), # eLabel
1694                                                         scaleH(80, XXX), scaleV(150, XXX), scaleH(280, XXX), scaleV(200, XXX), scaleV(22, XXX), # statusbar
1695                                                         scaleH(490, XXX), scaleV(130, XXX), scaleH(730, XXX), scaleV(420, XXX), # entries eLabel
1696                                                         scaleH(492, XXX), scaleV(132, XXX), scaleH(726, XXX), scaleV(416, XXX), # entries eLabel
1697                                                         scaleH(500, XXX), scaleV(140, XXX), scaleH(710, XXX), scaleV(400, XXX), # entries
1698                                                         scaleH(500, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # red
1699                                                         scaleH(680, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # green
1700                                                         scaleH(860, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # yellow
1701                                                         scaleH(1040, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # blue
1702                                                         scaleH(530, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # red
1703                                                         scaleH(710, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # green
1704                                                         scaleH(890, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # yellow
1705                                                         scaleH(1070, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # blue
1706                                                         scaleH(120, XXX), scaleV(430, XXX), scaleH(150, XXX), scaleV(110, XXX), resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/fritz.png") # Fritz Logo size and pixmap
1707                                                                                                                 )
1708                 else:
1709                         self.width = scaleH(1100, 570)
1710                         debug("[FritzDisplayCalls] width: " + str(self.width))
1711                         # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
1712                         self.skin = """
1713                                 <screen name="FritzDisplayCalls" position="%d,%d" size="%d,%d" title="%s" >
1714                                         <eLabel position="0,0" size="%d,2" backgroundColor="#aaaaaa" />
1715                                         <widget name="statusbar" position="%d,%d" size="%d,%d" font="Regular;%d" backgroundColor="#aaaaaa" transparent="1" />
1716                                         <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
1717                                         <widget name="entries" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" />
1718                                         <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
1719                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1720                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1721                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1722                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
1723                                         <widget name="key_red" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1724                                         <widget name="key_green" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1725                                         <widget name="key_yellow" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1726                                         <widget name="key_blue" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1727                                 </screen>""" % (
1728                                                         scaleH(90, 75), scaleV(100, 78), # position 
1729                                                         scaleH(1100, 570), scaleV(560, 430), # size
1730                                                         _("Phone calls"),
1731                                                         scaleH(1100, 570), # eLabel width
1732                                                         scaleH(40, 5), scaleV(10, 5), # statusbar position
1733                                                         scaleH(1050, 560), scaleV(25, 22), # statusbar size
1734                                                         scaleV(21, 21), # statusbar font size
1735                                                         scaleV(40, 28), # eLabel position vertical
1736                                                         scaleH(1100, 570), # eLabel width
1737                                                         scaleH(40, 5), scaleV(55, 40), # entries position
1738                                                         scaleH(1040, 560), scaleV(458, 340), # entries size
1739                                                         scaleV(518, 390), # eLabel position vertical
1740                                                         scaleH(1100, 570), # eLabel width
1741                                                         scaleH(20, 5), scaleV(525, 395), # widget red
1742                                                         scaleH(290, 145), scaleV(525, 395), # widget green
1743                                                         scaleH(560, 285), scaleV(525, 395), # widget yellow
1744                                                         scaleH(830, 425), scaleV(525, 395), # widget blue
1745                                                         scaleH(20, 5), scaleV(525, 395), scaleV(22, 21), # widget red
1746                                                         scaleH(290, 145), scaleV(525, 395), scaleV(22, 21), # widget green
1747                                                         scaleH(560, 285), scaleV(525, 395), scaleV(22, 21), # widget yellow
1748                                                         scaleH(830, 425), scaleV(525, 395), scaleV(22, 21), # widget blue
1749                                                                                                                 )
1750
1751                 Screen.__init__(self, session)
1752                 HelpableScreen.__init__(self)
1753
1754                 # TRANSLATORS: keep it short, this is a button
1755                 self["key_yellow"] = Button(_("All"))
1756                 # TRANSLATORS: keep it short, this is a button
1757                 self["key_red"] = Button(_("Missed"))
1758                 # TRANSLATORS: keep it short, this is a button
1759                 self["key_blue"] = Button(_("Incoming"))
1760                 # TRANSLATORS: keep it short, this is a button
1761                 self["key_green"] = Button(_("Outgoing"))
1762
1763                 self["setupActions"] = ActionMap(["OkCancelActions", "ColorActions"],
1764                 {
1765                         "yellow": (lambda: self.display(FBF_ALL_CALLS)),
1766                         "red": (lambda: self.display(FBF_MISSED_CALLS)),
1767                         "blue": (lambda: self.display(FBF_IN_CALLS)),
1768                         "green": (lambda: self.display(FBF_OUT_CALLS)),
1769                         "cancel": self.ok,
1770                         "ok": self.showEntry, }, - 2)
1771
1772                 # TRANSLATORS: keep it short, this is a help text
1773                 self.helpList.append((self["setupActions"], "OkCancelActions", [("ok", _("Show details of entry"))]))
1774                 # TRANSLATORS: keep it short, this is a help text
1775                 self.helpList.append((self["setupActions"], "OkCancelActions", [("cancel", _("Quit"))]))
1776                 # TRANSLATORS: keep it short, this is a help text
1777                 self.helpList.append((self["setupActions"], "ColorActions", [("yellow", _("Display all calls"))]))
1778                 # TRANSLATORS: keep it short, this is a help text
1779                 self.helpList.append((self["setupActions"], "ColorActions", [("red", _("Display missed calls"))]))
1780                 # TRANSLATORS: keep it short, this is a help text
1781                 self.helpList.append((self["setupActions"], "ColorActions", [("blue", _("Display incoming calls"))]))
1782                 # TRANSLATORS: keep it short, this is a help text
1783                 self.helpList.append((self["setupActions"], "ColorActions", [("green", _("Display outgoing calls"))]))
1784
1785                 self["statusbar"] = Label(_("Getting calls from FRITZ!Box..."))
1786                 self["entries"] = MenuList([], True, content=eListboxPythonMultiContent)
1787                 fontSize = scaleV(22, 18)
1788                 fontHeight = scaleV(24, 20)
1789                 self["entries"].l.setFont(0, gFont("Regular", fontSize))
1790                 self["entries"].l.setItemHeight(fontHeight)
1791
1792                 debug("[FritzDisplayCalls] init: '''%s'''" % config.plugins.FritzCall.fbfCalls.value)
1793                 self.display()
1794
1795         def ok(self):
1796                 self.close()
1797
1798         def display(self, which=config.plugins.FritzCall.fbfCalls.value):
1799                 debug("[FritzDisplayCalls] display")
1800                 config.plugins.FritzCall.fbfCalls.value = which
1801                 config.plugins.FritzCall.fbfCalls.save()
1802                 fritzbox.getCalls(self, lambda x: self.gotCalls(x, which), which)
1803
1804         def gotCalls(self, listOfCalls, which):
1805                 debug("[FritzDisplayCalls] gotCalls")
1806                 self.updateStatus(fbfCallsChoices[which] + " (" + str(len(listOfCalls)) + ")")
1807                 dateFieldWidth = scaleH(130, 100)
1808                 dirFieldWidth = 16
1809                 lengthFieldWidth = scaleH(45, 45)
1810                 scrollbarWidth = scaleH(35, 35)
1811                 if config.plugins.FritzCall.fullscreen.value:
1812                         remoteFieldWidth = scaleH(140, 100)
1813                         fieldWidth = 710 -dateFieldWidth -5 -dirFieldWidth -5 -lengthFieldWidth -5 -remoteFieldWidth -scrollbarWidth -5
1814                         fontSize = scaleV(24, 20)
1815                 else:
1816                         remoteFieldWidth = scaleH(160, 100)
1817                         fieldWidth = self.width -scaleH(60, 5) -dateFieldWidth -5 -dirFieldWidth -5 -lengthFieldWidth -5 -remoteFieldWidth -scrollbarWidth -5
1818                         fontSize = scaleV(24, 20)
1819                 sortlist = []
1820                 for (number, date, remote, direct, here, length) in listOfCalls:
1821                         found = re.match("(\d\d.\d\d.)\d\d( \d\d:\d\d)", date)
1822                         if found:
1823                                 date = found.group(1) + found.group(2)
1824                         if direct == FBF_OUT_CALLS:
1825                                 direct = LoadPixmap(resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/callout.png"))
1826                         elif direct == FBF_IN_CALLS:
1827                                 direct = LoadPixmap(resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/callin.png"))
1828                         else:
1829                                 direct = LoadPixmap(resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/callinfailed.png"))
1830                         # debug("[FritzDisplayCalls] gotCalls: d: %d; f: %d; d: %d; r: %d" %(dateFieldWidth, fieldWidth, dirFieldWidth, remoteFieldWidth))
1831                         sortlist.append([number,
1832                                                          (eListboxPythonMultiContent.TYPE_TEXT, 0, 0, dateFieldWidth, fontSize, 0, RT_HALIGN_LEFT, date),
1833                                                          (eListboxPythonMultiContent.TYPE_PIXMAP_ALPHATEST, dateFieldWidth+5, 0, dirFieldWidth, 16, direct),
1834                                                          (eListboxPythonMultiContent.TYPE_TEXT, dateFieldWidth+5+dirFieldWidth+5, 0, fieldWidth, fontSize, 0, RT_HALIGN_LEFT, here),
1835                                                          (eListboxPythonMultiContent.TYPE_TEXT, dateFieldWidth+5+dirFieldWidth+5+fieldWidth+5, 0, lengthFieldWidth, fontSize, 0, RT_HALIGN_LEFT, length),
1836                                                          (eListboxPythonMultiContent.TYPE_TEXT, dateFieldWidth+5+dirFieldWidth+5+fieldWidth+5+lengthFieldWidth+5, 0, remoteFieldWidth, fontSize, 0, RT_HALIGN_RIGHT, remote)
1837                                                          ])
1838
1839                 self["entries"].setList(sortlist)
1840
1841         def updateStatus(self, text):
1842                 if self.has_key("statusbar"):
1843                         self["statusbar"].setText(_("Getting calls from FRITZ!Box...") + ' ' + text)
1844
1845         def showEntry(self):
1846                 debug("[FritzDisplayCalls] showEntry")
1847                 cur = self["entries"].getCurrent()
1848                 if cur:
1849                         if cur[0]:
1850                                 debug("[FritzDisplayCalls] showEntry %s" % (cur[0]))
1851                                 number = cur[0]
1852                                 fullname = phonebook.search(cur[0])
1853                                 if fullname:
1854                                         # we have a name for this number
1855                                         name = fullname
1856                                         self.session.open(FritzOfferAction, self, number, name)
1857                                 else:
1858                                         # we don't
1859                                         fullname = resolveNumberWithAvon(number, config.plugins.FritzCall.country.value)
1860                                         if fullname:
1861                                                 name = fullname
1862                                                 self.session.open(FritzOfferAction, self, number, name)
1863                                         else:
1864                                                 self.session.open(FritzOfferAction, self, number)
1865                         else:
1866                                 # we do not even have a number...
1867                                 self.session.open(MessageBox,
1868                                                   _("UNKNOWN"),
1869                                                   type=MessageBox.TYPE_INFO)
1870
1871
1872 class FritzOfferAction(Screen):
1873
1874         def __init__(self, session, parent, number, name=""):
1875                 # the layout will completely be recalculated in finishLayout
1876                 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
1877                 self.skin = """
1878                         <screen name="FritzOfferAction" title="%s" >
1879                                 <widget name="text" size="%d,%d" font="Regular;%d" />
1880                                 <widget name="FacePixmap" size="%d,%d" alphatest="on" />
1881                                 <widget name="key_red_p" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1882                                 <widget name="key_green_p" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1883                                 <widget name="key_yellow_p" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1884                                 <widget name="key_red" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1885                                 <widget name="key_green" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1886                                 <widget name="key_yellow" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1887                         </screen>""" % (
1888                                                         _("Do what?"),
1889                                                         DESKTOP_WIDTH, DESKTOP_HEIGHT, # set maximum size
1890                                                         scaleH(22,21), # text
1891                                                         DESKTOP_WIDTH, DESKTOP_HEIGHT, # set maximum size
1892                                                         ) 
1893                 debug("[FritzOfferAction] init: %s, %s" %(number, name))
1894                 Screen.__init__(self, session)
1895         
1896                 # TRANSLATORS: keep it short, this is a button
1897                 self["key_red"] = Button(_("Lookup"))
1898                 # TRANSLATORS: keep it short, this is a button
1899                 self["key_green"] = Button(_("Call"))
1900                 # TRANSLATORS: keep it short, this is a button
1901                 self["key_yellow"] = Button(_("Save"))
1902                 # TRANSLATORS: keep it short, this is a button
1903                 # self["key_blue"] = Button(_("Search"))
1904
1905                 self["FritzOfferActions"] = ActionMap(["OkCancelActions", "ColorActions"],
1906                 {
1907                         "red": self._lookup,
1908                         "green": self._call,
1909                         "yellow": self._add,
1910                         "cancel": self._exit,
1911                         "ok": self._exit, }, - 2)
1912
1913                 self._session = session
1914                 self._number = number
1915                 self._name = name.replace("\n", ", ")
1916                 self["text"] = Label(number + "\n\n" + name.replace(", ", "\n"))
1917                 self._parent = parent
1918                 self._lookupState = 0
1919                 self["key_red_p"] = Pixmap()
1920                 self["key_green_p"] = Pixmap()
1921                 self["key_yellow_p"] = Pixmap()
1922                 self["FacePixmap"] = Pixmap()
1923                 self.onLayoutFinish.append(self._finishLayout)
1924
1925         def _finishLayout(self):
1926                 # pylint: disable-msg=W0142
1927                 debug("[FritzCall] FritzOfferAction/finishLayout number: %s/%s" % (self._number, self._name))
1928
1929                 faceFile = findFace(self._number, self._name)
1930                 picPixmap = LoadPixmap(faceFile)
1931                 if not picPixmap:       # that means most probably, that the picture is not 8 bit...
1932                         Notifications.AddNotification(MessageBox, _("Found picture\n\n%s\n\nBut did not load. Probably not PNG, 8-bit") %faceFile, type = MessageBox.TYPE_ERROR)
1933                         picPixmap = LoadPixmap(resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/icons/input_error.png"))
1934                 picSize = picPixmap.size()
1935                 self["FacePixmap"].instance.setPixmap(picPixmap)
1936
1937                 noButtons = 3
1938                 # recalculate window size
1939                 textSize = self["text"].getSize()
1940                 textSize = (textSize[0]+20, textSize[1]+20) # don't know, why, but size is too small
1941                 debug("[FritzCall] FritzOfferAction/finishLayout textsize: %s/%s" % textSize)
1942                 textSize = eSize(*textSize)
1943                 width = max(scaleH(620, 545), noButtons*145, picSize.width() + textSize.width() + 30)
1944                 height = max(picSize.height()+5, textSize.height()+5, scaleV(-1, 136)) + 5 + 40 + 5
1945                 buttonsGap = (width-noButtons*140)/(noButtons+1)
1946                 buttonsVPos = height-40-5
1947                 wSize = (width, height)
1948                 wSize = eSize(*wSize)
1949
1950                 # center the smaller vertically
1951                 hGap = (width-picSize.width()-textSize.width())/3
1952                 picPos = (hGap, (height-50-picSize.height())/2+5)
1953                 textPos = (hGap+picSize.width()+hGap, (height-50-textSize.height())/2+5)
1954
1955                 # resize screen
1956                 self.instance.resize(wSize)
1957                 # resize text
1958                 self["text"].instance.resize(textSize)
1959                 # resize pixmap
1960                 self["FacePixmap"].instance.resize(picSize)
1961                 # move buttons
1962                 buttonPos = (buttonsGap, buttonsVPos)
1963                 self["key_red_p"].instance.move(ePoint(*buttonPos))
1964                 self["key_red"].instance.move(ePoint(*buttonPos))
1965                 buttonPos = (buttonsGap+140+buttonsGap, buttonsVPos)
1966                 self["key_green_p"].instance.move(ePoint(*buttonPos))
1967                 self["key_green"].instance.move(ePoint(*buttonPos))
1968                 buttonPos = (buttonsGap+140+buttonsGap+140+buttonsGap, buttonsVPos)
1969                 self["key_yellow_p"].instance.move(ePoint(*buttonPos))
1970                 self["key_yellow"].instance.move(ePoint(*buttonPos))
1971                 # move text
1972                 self["text"].instance.move(ePoint(*textPos))
1973                 # move pixmap
1974                 self["FacePixmap"].instance.move(ePoint(*picPos))
1975                 # center window
1976                 self.instance.move(ePoint((DESKTOP_WIDTH-wSize.width())/2, (DESKTOP_HEIGHT-wSize.height())/2))
1977
1978         def _setTextAndResize(self, message):
1979                 # pylint: disable-msg=W0142
1980                 self["text"].instance.resize(eSize(*(DESKTOP_WIDTH, DESKTOP_HEIGHT)))
1981                 self["text"].setText(self._number + "\n\n" + message)
1982                 self._finishLayout()
1983
1984         def _lookup(self):
1985                 phonebookLocation = config.plugins.FritzCall.phonebookLocation.value
1986                 if self._lookupState == 0:
1987                         self._lookupState = 1
1988                         self._setTextAndResize(_("Reverse searching..."))
1989                         ReverseLookupAndNotifier(self._number, self._lookedUp, "UTF-8", config.plugins.FritzCall.country.value)
1990                         return
1991                 if self._lookupState == 1 and os.path.exists(os.path.join(phonebookLocation, "PhoneBook.csv")):
1992                         self._setTextAndResize(_("Searching in Outlook export..."))
1993                         self._lookupState = 2
1994                         self._lookedUp(self._number, FritzOutlookCSV.findNumber(self._number, os.path.join(phonebookLocation, "PhoneBook.csv"))) #@UndefinedVariable
1995                         return
1996                 else:
1997                         self._lookupState = 2
1998                 if self._lookupState == 2 and os.path.exists(os.path.join(phonebookLocation, "PhoneBook.ldif")):
1999                         self._setTextAndResize(_("Searching in LDIF..."))
2000                         self._lookupState = 0
2001                         FritzLDIF.FindNumber(self._number, open(os.path.join(phonebookLocation, "PhoneBook.ldif")), self._lookedUp)
2002                         return
2003                 else:
2004                         self._lookupState = 0
2005                         self._lookup()
2006
2007         def _lookedUp(self, number, name):
2008                 name = handleReverseLookupResult(name)
2009                 if not name:
2010                         if self._lookupState == 1:
2011                                 name = _("No result from reverse lookup")
2012                         elif self._lookupState == 2:
2013                                 name = _("No result from Outlook export")
2014                         else:
2015                                 name = _("No result from LDIF")
2016                 self._name = name
2017                 self._number = number
2018                 debug("[FritzOfferAction] lookedUp: " + str(name.replace(", ", "\n")))
2019                 self._setTextAndResize(str(name.replace(", ", "\n")))
2020
2021         def _call(self):
2022                 debug("[FritzOfferAction] add: %s" %self._number)
2023                 fritzbox.dial(self._number)
2024                 self._exit()
2025
2026         def _add(self):
2027                 debug("[FritzOfferAction] add: %s, %s" %(self._number, self._name))
2028                 phonebook.FritzDisplayPhonebook(self._session).add(self._parent, self._number, self._name)
2029                 self._exit()
2030
2031         def _exit(self):
2032                 self.close()
2033
2034
2035 class FritzCallPhonebook:
2036         def __init__(self):
2037                 debug("[FritzCallPhonebook] init")
2038                 # Beware: strings in phonebook.phonebook have to be in utf-8!
2039                 self.phonebook = {}
2040                 self.reload()
2041
2042         def reload(self):
2043                 debug("[FritzCallPhonebook] reload")
2044                 # Beware: strings in phonebook.phonebook have to be in utf-8!
2045                 self.phonebook = {}
2046
2047                 if not config.plugins.FritzCall.enable.value:
2048                         return
2049
2050                 phonebookFilename = os.path.join(config.plugins.FritzCall.phonebookLocation.value, "PhoneBook.txt")
2051                 if config.plugins.FritzCall.phonebook.value and os.path.exists(phonebookFilename):
2052                         debug("[FritzCallPhonebook] reload: read " + phonebookFilename)
2053                         phonebookTxtCorrupt = False
2054                         for line in open(phonebookFilename):
2055                                 try:
2056                                         # Beware: strings in phonebook.phonebook have to be in utf-8!
2057                                         line = line.decode("utf-8")
2058                                 except UnicodeDecodeError: # this is just for the case, somebody wrote latin1 chars into PhoneBook.txt
2059                                         try:
2060                                                 line = line.decode("iso-8859-1")
2061                                                 debug("[FritzCallPhonebook] Fallback to ISO-8859-1 in %s" % line)
2062                                                 phonebookTxtCorrupt = True
2063                                         except UnicodeDecodeError:
2064                                                 debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
2065                                                 phonebookTxtCorrupt = True
2066                                 line = line.encode("utf-8")
2067                                 if re.match("^\d+#.*$", line):
2068                                         try:
2069                                                 number, name = line.split("#")
2070                                                 if not self.phonebook.has_key(number):
2071                                                         # Beware: strings in phonebook.phonebook have to be in utf-8!
2072                                                         self.phonebook[number] = name
2073                                         except ValueError: # how could this possibly happen?!?!
2074                                                 debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
2075                                                 phonebookTxtCorrupt = True
2076                                 else:
2077                                         debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
2078                                         phonebookTxtCorrupt = True
2079
2080                         if phonebookTxtCorrupt:
2081                                 # dump phonebook to PhoneBook.txt
2082                                 debug("[FritzCallPhonebook] dump Phonebook.txt")
2083                                 os.rename(phonebookFilename, phonebookFilename + ".bck")
2084                                 fNew = open(phonebookFilename, 'w')
2085                                 # Beware: strings in phonebook.phonebook are utf-8!
2086                                 for (number, name) in self.phonebook.iteritems():
2087                                         # Beware: strings in PhoneBook.txt have to be in utf-8!
2088                                         fNew.write(number + "#" + name.encode("utf-8"))
2089                                 fNew.close()
2090
2091 #===============================================================================
2092 #               #
2093 #               # read entries from Outlook export
2094 #               #
2095 #               # not reliable with coding yet
2096 #               # 
2097 #               # import csv exported from Outlook 2007 with csv(Windows)
2098 #               csvFilename = "/tmp/PhoneBook.csv"
2099 #               if config.plugins.FritzCall.phonebook.value and os.path.exists(csvFilename):
2100 #                       try:
2101 #                               readOutlookCSV(csvFilename, self.add)
2102 #                               os.rename(csvFilename, csvFilename + ".done")
2103 #                       except ImportError:
2104 #                               debug("[FritzCallPhonebook] CSV import failed" %line)
2105 #===============================================================================
2106
2107                 
2108 #===============================================================================
2109 #               #
2110 #               # read entries from LDIF
2111 #               #
2112 #               # import ldif exported from Thunderbird 2.0.0.19
2113 #               ldifFilename = "/tmp/PhoneBook.ldif"
2114 #               if config.plugins.FritzCall.phonebook.value and os.path.exists(ldifFilename):
2115 #                       try:
2116 #                               parser = MyLDIF(open(ldifFilename), self.add)
2117 #                               parser.parse()
2118 #                               os.rename(ldifFilename, ldifFilename + ".done")
2119 #                       except ImportError:
2120 #                               debug("[FritzCallPhonebook] LDIF import failed" %line)
2121 #===============================================================================
2122                 
2123                 if fritzbox and config.plugins.FritzCall.fritzphonebook.value:
2124                         fritzbox.loadFritzBoxPhonebook()
2125
2126                 if DESKTOP_WIDTH != 1280 or DESKTOP_HEIGHT != 720:
2127                         config.plugins.FritzCall.fullscreen.value = False
2128
2129         def search(self, number):
2130                 # debug("[FritzCallPhonebook] Searching for %s" %number)
2131                 name = ""
2132                 if not self.phonebook:
2133                         return
2134
2135                 if config.plugins.FritzCall.prefix.value:
2136                         prefix = config.plugins.FritzCall.prefix.value
2137                         if number[0] != '0':
2138                                 number = prefix + number
2139                                 # debug("[FritzCallPhonebook] search: added prefix: %s" %number)
2140                         elif number[:len(prefix)] == prefix and self.phonebook.has_key(number[len(prefix):]):
2141                                 # debug("[FritzCallPhonebook] search: same prefix")
2142                                 name = self.phonebook[number[len(prefix):]]
2143                                 # debug("[FritzCallPhonebook] search: result: %s" %name)
2144                 else:
2145                         prefix = ""
2146                                 
2147                 if not name and self.phonebook.has_key(number):
2148                         name = self.phonebook[number]
2149                                 
2150                 return name.replace(", ", "\n").strip()
2151
2152         def add(self, number, name):
2153                 '''
2154                 
2155                 @param number: number of entry
2156                 @param name: name of entry, has to be in utf-8
2157                 '''
2158                 debug("[FritzCallPhonebook] add")
2159                 name = name.replace("\n", ", ").replace('#','') # this is just for safety reasons. add should only be called with newlines converted into commas
2160                 self.remove(number)
2161                 self.phonebook[number] = name
2162                 if number and number != 0:
2163                         if config.plugins.FritzCall.phonebook.value:
2164                                 try:
2165                                         name = name.strip() + "\n"
2166                                         string = "%s#%s" % (number, name)
2167                                         # Beware: strings in PhoneBook.txt have to be in utf-8!
2168                                         f = open(os.path.join(config.plugins.FritzCall.phonebookLocation.value, "PhoneBook.txt"), 'a')
2169                                         f.write(string)
2170                                         f.close()
2171                                         debug("[FritzCallPhonebook] added %s with %s to Phonebook.txt" % (number, name.strip()))
2172                                         return True
2173         
2174                                 except IOError:
2175                                         return False
2176
2177         def remove(self, number):
2178                 if number in self.phonebook:
2179                         debug("[FritzCallPhonebook] remove entry in phonebook")
2180                         del self.phonebook[number]
2181                         if config.plugins.FritzCall.phonebook.value:
2182                                 try:
2183                                         phonebookFilename = os.path.join(config.plugins.FritzCall.phonebookLocation.value, "PhoneBook.txt")
2184                                         debug("[FritzCallPhonebook] remove entry in Phonebook.txt")
2185                                         fOld = open(phonebookFilename, 'r')
2186                                         fNew = open(phonebookFilename + str(os.getpid()), 'w')
2187                                         line = fOld.readline()
2188                                         while (line):
2189                                                 if not re.match("^" + number + "#.*$", line):
2190                                                         fNew.write(line)
2191                                                 line = fOld.readline()
2192                                         fOld.close()
2193                                         fNew.close()
2194                                         os.remove(phonebookFilename)
2195                                         os.rename(phonebookFilename + str(os.getpid()), phonebookFilename)
2196                                         debug("[FritzCallPhonebook] removed %s from Phonebook.txt" % number)
2197                                         return True
2198         
2199                                 except IOError:
2200                                         pass
2201                 return False
2202
2203         class FritzDisplayPhonebook(Screen, HelpableScreen, NumericalTextInput):
2204         
2205                 def __init__(self, session):
2206                         if config.plugins.FritzCall.fullscreen.value:
2207                                 self.width = DESKTOP_WIDTH
2208                                 self.height = DESKTOP_HEIGHT
2209                                 self.entriesWidth = 700
2210                                 backMainPng = ""
2211                                 if os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, DESKTOP_SKIN + "/menu/back-main.png")):
2212                                         backMainPng = DESKTOP_SKIN + "/menu/back-main.png"
2213                                 elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1/menu/back-main.png")):
2214                                         backMainPng = "Kerni-HD1/menu/back-main.png"
2215                                 elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1-picon/menu/back-main.png")):
2216                                         backMainPng = "Kerni-HD1-picon/menu/back-main.png"
2217                                 if backMainPng:
2218                                         backMainLine = """<ePixmap position="0,0" zPosition="-10" size="%d,%d" pixmap="%s" transparent="1" />""" % (self.width, self.height, backMainPng)
2219                                 else:
2220                                         backMainLine = ""
2221                                 debug("[FritzDisplayPhonebook] backMainLine: " + backMainLine)
2222                                         
2223                                 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
2224                                 self.skin = """
2225                                         <screen name="FritzdisplayPhonebook" position="0,0" size="%d,%d" title="%s" flags="wfNoBorder">
2226                                                 %s
2227                                                 <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
2228                                                         <convert type="ClockToText">Default</convert>
2229                                                 </widget>
2230                                                 <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
2231                                                         <convert type="ClockToText">Date</convert>
2232                                                 </widget>
2233                                                 <eLabel text="%s" position="%d,%d" size="%d,%d" font="Regular;%d" halign="center" backgroundColor="#0b67a2" transparent="1"/>
2234                                 
2235                                                 <eLabel                                 position="%d,%d" size="%d,%d"   zPosition="1" backgroundColor="#bbbbbb" />
2236                                                 <eLabel                                 position="%d,%d" size="%d,%d"   zPosition="2" backgroundColor="#252b4247" />
2237                                                 <widget name="entries" position="%d,%d" size="%d,%d" zPosition="3" scrollbarMode="showOnDemand" transparent="1" />
2238                                 
2239                                                 <ePixmap position="%d,%d" size="%d,%d" zPosition="10" pixmap="Kerni-HD1/buttons/red.png" transparent="1" alphatest="on" />
2240                                                 <ePixmap position="%d,%d" size="%d,%d" zPosition="10" pixmap="Kerni-HD1/buttons/green.png" transparent="1" alphatest="on" />
2241                                                 <ePixmap position="%d,%d" size="%d,%d" zPosition="10" pixmap="Kerni-HD1/buttons/yellow.png" transparent="1" alphatest="on" />
2242                                                 <ePixmap position="%d,%d" size="%d,%d" zPosition="10" pixmap="Kerni-HD1/buttons/blue.png" transparent="1" alphatest="on" />
2243                                                 <widget name="key_red" position="%d,%d" size="%d,%d" zPosition="10" font="Regular;%d" transparent="1" />
2244                                                 <widget name="key_green" position="%d,%d" size="%d,%d" zPosition="10" font="Regular;%d" transparent="1" />
2245                                                 <widget name="key_yellow" position="%d,%d" size="%d,%d" zPosition="10" font="Regular;%d" transparent="1" />
2246                                                 <widget name="key_blue" position="%d,%d" size="%d,%d" zPosition="10" font="Regular;%d" transparent="1" />
2247                                                 <ePixmap position="%d,%d" size="%d,%d" zPosition="2" pixmap="%s" transparent="1" alphatest="blend" />   
2248                                         </screen>""" % (
2249                                                                         self.width, self.height, _("Phonebook"),
2250                                                                         backMainLine,
2251                                                                         scaleH(1130, XXX), scaleV(40, XXX), scaleH(80, XXX), scaleV(26, XXX), scaleV(26, XXX), # time
2252                                                                         scaleH(890, XXX), scaleV(70, XXX), scaleH(320, XXX), scaleV(22, XXX), scaleV(20, XXX), # date
2253                                                                         "FritzCall " + _("Phonebook"), scaleH(80, XXX), scaleV(63, XXX), scaleH(300, XXX), scaleV(30, XXX), scaleV(27, XXX), # eLabel
2254                                                                         scaleH(490, XXX), scaleV(130, XXX), scaleH(730, XXX), scaleV(420, XXX), # entries eLabel
2255                                                                         scaleH(492, XXX), scaleV(132, XXX), scaleH(726, XXX), scaleV(416, XXX), # entries eLabel
2256                                                                         scaleH(500, XXX), scaleV(140, XXX), scaleH(self.entriesWidth, XXX), scaleV(400, XXX), # entries
2257                                                                         scaleH(500, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # red
2258                                                                         scaleH(680, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # green
2259                                                                         scaleH(860, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # yellow
2260                                                                         scaleH(1040, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # blue
2261                                                                         scaleH(530, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # red
2262                                                                         scaleH(710, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # green
2263                                                                         scaleH(890, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # yellow
2264                                                                         scaleH(1070, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # blue
2265                                                                         scaleH(120, XXX), scaleV(430, XXX), scaleH(150, XXX), scaleV(110, XXX), resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/fritz.png") # Fritz Logo size and pixmap
2266                                                                                                                                 )
2267                         else:
2268                                 self.width = scaleH(1100, 570)
2269                                 self.entriesWidth = scaleH(1040, 560)
2270                                 debug("[FritzDisplayPhonebook] width: " + str(self.width))
2271                                 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
2272                                 self.skin = """
2273                                         <screen name="FritzDisplayPhonebook" position="%d,%d" size="%d,%d" title="%s" >
2274                                                 <eLabel position="0,0" size="%d,2" backgroundColor="#aaaaaa" />
2275                                                 <widget name="entries" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" />
2276                                                 <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
2277                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
2278                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
2279                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
2280                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
2281                                                 <widget name="key_red" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
2282                                                 <widget name="key_green" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
2283                                                 <widget name="key_yellow" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
2284                                                 <widget name="key_blue" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
2285                                         </screen>""" % (
2286                                                         scaleH(90, 75), scaleV(100, 73), # position 
2287                                                         scaleH(1100, 570), scaleV(560, 430), # size
2288                                                         _("Phonebook"),
2289                                                         scaleH(1100, 570), # eLabel width
2290                                                         scaleH(40, 5), scaleV(20, 5), # entries position
2291                                                         self.entriesWidth, scaleV(488, 380), # entries size
2292                                                         scaleV(518, 390), # eLabel position vertical
2293                                                         scaleH(1100, 570), # eLabel width
2294                                                         scaleH(20, 5), scaleV(525, 395), # ePixmap red
2295                                                         scaleH(290, 145), scaleV(525, 395), # ePixmap green
2296                                                         scaleH(560, 285), scaleV(525, 395), # ePixmap yellow
2297                                                         scaleH(830, 425), scaleV(525, 395), # ePixmap blue
2298                                                         scaleH(20, 5), scaleV(525, 395), scaleV(22, 21), # widget red
2299                                                         scaleH(290, 145), scaleV(525, 395), scaleV(22, 21), # widget green
2300                                                         scaleH(560, 285), scaleV(525, 395), scaleV(22, 21), # widget yellow
2301                                                         scaleH(830, 425), scaleV(525, 395), scaleV(22, 21), # widget blue
2302                                                         )
2303         
2304                         Screen.__init__(self, session)
2305                         NumericalTextInput.__init__(self)
2306                         HelpableScreen.__init__(self)
2307                 
2308                         # TRANSLATORS: keep it short, this is a button
2309                         self["key_red"] = Button(_("Delete"))
2310                         # TRANSLATORS: keep it short, this is a button
2311                         self["key_green"] = Button(_("New"))
2312                         # TRANSLATORS: keep it short, this is a button
2313                         self["key_yellow"] = Button(_("Edit"))
2314                         # TRANSLATORS: keep it short, this is a button
2315                         self["key_blue"] = Button(_("Search"))
2316         
2317                         self["setupActions"] = ActionMap(["OkCancelActions", "ColorActions"],
2318                         {
2319                                 "red": self.delete,
2320                                 "green": self.add,
2321                                 "yellow": self.edit,
2322                                 "blue": self.search,
2323                                 "cancel": self.exit,
2324                                 "ok": self.showEntry, }, - 2)
2325         
2326                         # TRANSLATORS: keep it short, this is a help text
2327                         self.helpList.append((self["setupActions"], "OkCancelActions", [("ok", _("Show details of entry"))]))
2328                         # TRANSLATORS: keep it short, this is a help text
2329                         self.helpList.append((self["setupActions"], "OkCancelActions", [("cancel", _("Quit"))]))
2330                         # TRANSLATORS: keep it short, this is a help text
2331                         self.helpList.append((self["setupActions"], "ColorActions", [("red", _("Delete entry"))]))
2332                         # TRANSLATORS: keep it short, this is a help text
2333                         self.helpList.append((self["setupActions"], "ColorActions", [("green", _("Add entry to phonebook"))]))
2334                         # TRANSLATORS: keep it short, this is a help text
2335                         self.helpList.append((self["setupActions"], "ColorActions", [("yellow", _("Edit selected entry"))]))
2336                         # TRANSLATORS: keep it short, this is a help text
2337                         self.helpList.append((self["setupActions"], "ColorActions", [("blue", _("Search (case insensitive)"))]))
2338         
2339                         self["entries"] = MenuList([], True, content=eListboxPythonMultiContent)
2340                         fontSize = scaleV(22, 18)
2341                         fontHeight = scaleV(24, 20)
2342                         self["entries"].l.setFont(0, gFont("Regular", fontSize))
2343                         self["entries"].l.setItemHeight(fontHeight)
2344                         debug("[FritzCallPhonebook] displayPhonebook init")
2345                         self.help_window = None
2346                         self.sortlist = []
2347                         self.display()
2348         
2349                 def display(self, filterNumber=""):
2350                         debug("[FritzCallPhonebook] displayPhonebook/display")
2351                         self.sortlist = []
2352                         # Beware: strings in phonebook.phonebook are utf-8!
2353                         sortlistHelp = sorted((name.lower(), name, number) for (number, name) in phonebook.phonebook.iteritems())
2354                         for (low, name, number) in sortlistHelp:
2355                                 if number == "01234567890":
2356                                         continue
2357                                 try:
2358                                         low = low.decode("utf-8")
2359                                 except UnicodeDecodeError:  # this should definitely not happen
2360                                         try:
2361                                                 low = low.decode("iso-8859-1")
2362                                         except UnicodeDecodeError:
2363                                                 debug("[FritzCallPhonebook] displayPhonebook/display: corrupt phonebook entry for %s" % number)
2364                                                 # self.session.open(MessageBox, _("Corrupt phonebook entry\nfor number %s\nDeleting.") %number, type = MessageBox.TYPE_ERROR)
2365                                                 phonebook.remove(number)
2366                                                 continue
2367                                 else:
2368                                         if filterNumber:
2369                                                 filterNumber = filterNumber.lower()
2370                                                 if low.find(filterNumber) == - 1:
2371                                                         continue
2372                                         name = name.strip().decode("utf-8")
2373                                         number = number.strip().decode("utf-8")
2374                                         found = re.match("([^,]*),.*", name)   # strip address information from the name part
2375                                         if found:
2376                                                 shortname = found.group(1)
2377                                         else:
2378                                                 shortname = name
2379                                         numberFieldWidth = scaleH(190, 150)
2380                                         fieldWidth = self.entriesWidth -5 -numberFieldWidth -10
2381                                         number = number.encode("utf-8", "replace")
2382                                         name = name.encode("utf-8", "replace")
2383                                         shortname = shortname.encode('utf-8', 'replace')
2384                                         self.sortlist.append([(number, name),
2385                                                                    (eListboxPythonMultiContent.TYPE_TEXT, 0, 0, fieldWidth, scaleH(24,20), 0, RT_HALIGN_LEFT, shortname),
2386                                                                    (eListboxPythonMultiContent.TYPE_TEXT, fieldWidth +5, 0, numberFieldWidth, scaleH(24,20), 0, RT_HALIGN_LEFT, number)
2387                                                                    ])
2388