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