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