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