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