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