FIX: Boxinfo for latest Labor-Firmware 7270
[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                 noButtons = 3
1699                 width = max(scaleH(620,545), noButtons*140)
1700                 height = scaleV(-1,176) # = 5 + 126 + 40 + 5; 6 lines of text possible
1701                 buttonsGap = (width-noButtons*140)/(noButtons+1)
1702                 buttonsVPos = height-40-5
1703                 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
1704                 self.skin = """
1705                         <screen name="FritzOfferAction" position="%d,%d" size="%d,%d" title="%s" >
1706                                 <widget name="text" position="%d,5" size="%d,%d" font="Regular;%d" />
1707                                 <widget name="FacePixmap" pixmap="skin_default/icons/input_question.png" position="%d,%d" size="100,100" alphatest="on" />
1708                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1709                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1710                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1711                                 <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" />
1712                                 <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" />
1713                                 <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" />
1714                         </screen>""" % (
1715                                                         (DESKTOP_WIDTH - width) / 2, (DESKTOP_HEIGHT - height) / 2, width, height, _("Do what?"),
1716                                                         5+100+10, width -5 -100 -5, height -5 -40 -5, scaleH(22,21), # text
1717                                                         5, (height -5 -100 -5 -40 -5)/2, # FacePixmap
1718                                                         buttonsGap, buttonsVPos, buttonsGap+140+buttonsGap, buttonsVPos, buttonsGap+2*(140+buttonsGap), buttonsVPos,
1719                                                         buttonsGap, buttonsVPos, buttonsGap+140+buttonsGap, buttonsVPos, buttonsGap+2*(140+buttonsGap), buttonsVPos,
1720                                                         ) 
1721                 debug("[FritzOfferAction] init: %s, %s" %(number, name))
1722                 Screen.__init__(self, session)
1723         
1724                 # TRANSLATORS: keep it short, this is a button
1725                 self["key_red"] = Button(_("Lookup"))
1726                 # TRANSLATORS: keep it short, this is a button
1727                 self["key_green"] = Button(_("Call"))
1728                 # TRANSLATORS: keep it short, this is a button
1729                 self["key_yellow"] = Button(_("Save"))
1730                 # TRANSLATORS: keep it short, this is a button
1731                 # self["key_blue"] = Button(_("Search"))
1732
1733                 self["FritzOfferActions"] = ActionMap(["OkCancelActions", "ColorActions"],
1734                 {
1735                         "red": self.lookup,
1736                         "green": self.call,
1737                         "yellow": self.add,
1738                         "cancel": self.exit,
1739                         "ok": self.exit, }, - 2)
1740
1741                 self["text"] = Label(number + "\n\n" + name.replace(", ", "\n"))
1742                 self.actualNumber = number
1743                 self.actualName = name.replace("\n", ", ")
1744                 self.parent = parent
1745                 self.lookupState = 0
1746                 self["FacePixmap"] = Pixmap()
1747                 self.onLayoutFinish.append(self.setFace)
1748
1749         def setFace(self):
1750                 faceFile = findFace(self.actualNumber, self.actualName)
1751                 if faceFile:
1752                         self["FacePixmap"].instance.setPixmapFromFile(faceFile)
1753                 
1754
1755         def lookup(self):
1756                 phonebookLocation = config.plugins.FritzCall.phonebookLocation.value
1757                 if self.lookupState == 0:
1758                         self.lookupState = 1
1759                         self["text"].setText(self.actualNumber + "\n\n" + _("Reverse searching..."))
1760                         ReverseLookupAndNotifier(self.actualNumber, self.lookedUp, "UTF-8", config.plugins.FritzCall.country.value)
1761                         return
1762                 if self.lookupState == 1 and os.path.exists(os.path.join(phonebookLocation, "PhoneBook.csv")):
1763                         self["text"].setText(self.actualNumber + "\n\n" + _("Searching in Outlook export..."))
1764                         self.lookupState = 2
1765                         self.lookedUp(self.actualNumber, FritzOutlookCSV.findNumber(self.actualNumber, os.path.join(phonebookLocation, "PhoneBook.csv"))) #@UndefinedVariable
1766                         return
1767                 else:
1768                         self.lookupState = 2
1769                 if self.lookupState == 2 and os.path.exists(os.path.join(phonebookLocation, "PhoneBook.ldif")):
1770                         self["text"].setText(self.actualNumber + "\n\n" + _("Searching in LDIF..."))
1771                         self.lookupState = 0
1772                         FritzLDIF.findNumber(self.actualNumber, open(os.path.join(phonebookLocation, "PhoneBook.ldif")), self.lookedUp)
1773                         return
1774                 else:
1775                         self.lookupState = 0
1776                         self.lookup()
1777
1778         def lookedUp(self, number, name):
1779                 if not name:
1780                         if self.lookupState == 1:
1781                                 name = _("No result from reverse lookup")
1782                         elif self.lookupState == 2:
1783                                 name = _("No result from Outlook export")
1784                         else:
1785                                 name = _("No result from LDIF")
1786                 self.actualNumber = number
1787                 self.actualName = name
1788                 message = number + "\n\n" + name.replace(", ", "\n")
1789                 self["text"].setText(str(message))
1790
1791         def call(self):
1792                 debug("[FritzOfferAction] add: %s" %self.actualNumber)
1793                 fritzbox.dial(self.actualNumber)
1794                 self.exit()
1795
1796         def add(self):
1797                 debug("[FritzOfferAction] add: %s, %s" %(self.actualNumber, self.actualName))
1798                 phonebook.FritzDisplayPhonebook(self.session).add(self.parent, self.actualNumber, self.actualName)
1799                 self.exit()
1800
1801         def exit(self):
1802                 self.close()
1803
1804
1805 class FritzCallPhonebook:
1806         def __init__(self):
1807                 debug("[FritzCallPhonebook] init")
1808                 # Beware: strings in phonebook.phonebook have to be in utf-8!
1809                 self.phonebook = {}
1810                 self.reload()
1811
1812         def reload(self):
1813                 debug("[FritzCallPhonebook] reload")
1814                 # Beware: strings in phonebook.phonebook have to be in utf-8!
1815                 self.phonebook = {}
1816
1817                 if not config.plugins.FritzCall.enable.value:
1818                         return
1819
1820                 phonebookFilename = os.path.join(config.plugins.FritzCall.phonebookLocation.value, "PhoneBook.txt")
1821                 if config.plugins.FritzCall.phonebook.value and os.path.exists(phonebookFilename):
1822                         debug("[FritzCallPhonebook] reload: read " + phonebookFilename)
1823                         phonebookTxtCorrupt = False
1824                         for line in open(phonebookFilename):
1825                                 try:
1826                                         # Beware: strings in phonebook.phonebook have to be in utf-8!
1827                                         line = line.decode("utf-8")
1828                                 except UnicodeDecodeError: # this is just for the case, somebody wrote latin1 chars into PhoneBook.txt
1829                                         try:
1830                                                 line = line.decode("iso-8859-1")
1831                                                 debug("[FritzCallPhonebook] Fallback to ISO-8859-1 in %s" % line)
1832                                                 phonebookTxtCorrupt = True
1833                                         except:
1834                                                 debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
1835                                                 phonebookTxtCorrupt = True
1836                                 line = line.encode("utf-8")
1837                                 if re.match("^\d+#.*$", line):
1838                                         try:
1839                                                 number, name = line.split("#")
1840                                                 if not self.phonebook.has_key(number):
1841                                                         # Beware: strings in phonebook.phonebook have to be in utf-8!
1842                                                         self.phonebook[number] = name
1843                                         except ValueError: # how could this possibly happen?!?!
1844                                                 debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
1845                                                 phonebookTxtCorrupt = True
1846                                 else:
1847                                         debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
1848                                         phonebookTxtCorrupt = True
1849
1850                         if phonebookTxtCorrupt:
1851                                 # dump phonebook to PhoneBook.txt
1852                                 debug("[FritzCallPhonebook] dump Phonebook.txt")
1853                                 os.rename(phonebookFilename, phonebookFilename + ".bck")
1854                                 fNew = open(phonebookFilename, 'w')
1855                                 # Beware: strings in phonebook.phonebook are utf-8!
1856                                 for (number, name) in self.phonebook.iteritems():
1857                                         # Beware: strings in PhoneBook.txt have to be in utf-8!
1858                                         fNew.write(number + "#" + name.encode("utf-8"))
1859                                 fNew.close()
1860
1861 #===============================================================================
1862 #               #
1863 #               # read entries from Outlook export
1864 #               #
1865 #               # not reliable with coding yet
1866 #               # 
1867 #               # import csv exported from Outlook 2007 with csv(Windows)
1868 #               csvFilename = "/tmp/PhoneBook.csv"
1869 #               if config.plugins.FritzCall.phonebook.value and os.path.exists(csvFilename):
1870 #                       try:
1871 #                               readOutlookCSV(csvFilename, self.add)
1872 #                               os.rename(csvFilename, csvFilename + ".done")
1873 #                       except ImportError:
1874 #                               debug("[FritzCallPhonebook] CSV import failed" %line)
1875 #===============================================================================
1876
1877                 
1878 #===============================================================================
1879 #               #
1880 #               # read entries from LDIF
1881 #               #
1882 #               # import ldif exported from Thunderbird 2.0.0.19
1883 #               ldifFilename = "/tmp/PhoneBook.ldif"
1884 #               if config.plugins.FritzCall.phonebook.value and os.path.exists(ldifFilename):
1885 #                       try:
1886 #                               parser = MyLDIF(open(ldifFilename), self.add)
1887 #                               parser.parse()
1888 #                               os.rename(ldifFilename, ldifFilename + ".done")
1889 #                       except ImportError:
1890 #                               debug("[FritzCallPhonebook] LDIF import failed" %line)
1891 #===============================================================================
1892                 
1893                 if config.plugins.FritzCall.fritzphonebook.value:
1894                         fritzbox.loadFritzBoxPhonebook()
1895
1896                 if DESKTOP_WIDTH <> 1280 or DESKTOP_HEIGHT <> 720:
1897                         config.plugins.FritzCall.fullscreen.value = False
1898
1899         def search(self, number):
1900                 # debug("[FritzCallPhonebook] Searching for %s" %number
1901                 name = None
1902                 if config.plugins.FritzCall.phonebook.value or config.plugins.FritzCall.fritzphonebook.value:
1903                         if self.phonebook.has_key(number):
1904                                 name = self.phonebook[number].replace(", ", "\n").strip()
1905                 return name
1906
1907         def add(self, number, name):
1908                 '''
1909                 
1910                 @param number: number of entry
1911                 @param name: name of entry, has to be in utf-8
1912                 '''
1913                 debug("[FritzCallPhonebook] add")
1914                 name = name.replace("\n", ", ").replace('#','') # this is just for safety reasons. add should only be called with newlines converted into commas
1915                 self.remove(number)
1916                 self.phonebook[number] = name;
1917                 if number and number <> 0:
1918                         if config.plugins.FritzCall.phonebook.value:
1919                                 try:
1920                                         name = name.strip() + "\n"
1921                                         string = "%s#%s" % (number, name)
1922                                         # Beware: strings in PhoneBook.txt have to be in utf-8!
1923                                         f = open(os.path.join(config.plugins.FritzCall.phonebookLocation.value, "PhoneBook.txt"), 'a')
1924                                         f.write(string)
1925                                         f.close()
1926                                         debug("[FritzCallPhonebook] added %s with %s to Phonebook.txt" % (number, name.strip()))
1927                                         return True
1928         
1929                                 except IOError:
1930                                         return False
1931
1932         def remove(self, number):
1933                 if number in self.phonebook:
1934                         debug("[FritzCallPhonebook] remove entry in phonebook")
1935                         del self.phonebook[number]
1936                         if config.plugins.FritzCall.phonebook.value:
1937                                 try:
1938                                         phonebookFilename = os.path.join(config.plugins.FritzCall.phonebookLocation.value, "PhoneBook.txt")
1939                                         debug("[FritzCallPhonebook] remove entry in Phonebook.txt")
1940                                         fOld = open(phonebookFilename, 'r')
1941                                         fNew = open(phonebookFilename + str(os.getpid()), 'w')
1942                                         line = fOld.readline()
1943                                         while (line):
1944                                                 if not re.match("^" + number + "#.*$", line):
1945                                                         fNew.write(line)
1946                                                 line = fOld.readline()
1947                                         fOld.close()
1948                                         fNew.close()
1949                                         os.remove(phonebookFilename)
1950                                         os.rename(phonebookFilename + str(os.getpid()), phonebookFilename)
1951                                         debug("[FritzCallPhonebook] removed %s from Phonebook.txt" % number)
1952                                         return True
1953         
1954                                 except IOError:
1955                                         pass
1956                 return False
1957
1958         class FritzDisplayPhonebook(Screen, HelpableScreen, NumericalTextInput):
1959         
1960                 def __init__(self, session):
1961                         if config.plugins.FritzCall.fullscreen.value:
1962                                 self.width = DESKTOP_WIDTH
1963                                 self.height = DESKTOP_HEIGHT
1964                                 self.entriesWidth = 790
1965                                 backMainPng = ""
1966                                 if os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, DESKTOP_SKIN + "/menu/back-main.png")):
1967                                         backMainPng = DESKTOP_SKIN + "/menu/back-main.png"
1968                                 elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1/menu/back-main.png")):
1969                                         backMainPng = "Kerni-HD1/menu/back-main.png"
1970                                 elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1-picon/menu/back-main.png")):
1971                                         backMainPng = "Kerni-HD1-picon/menu/back-main.png"
1972                                 if backMainPng:
1973                                         backMainLine = """<ePixmap position="0,0" zPosition="-10" size="%d,%d" pixmap="%s" transparent="1" />""" % (self.width, self.height, backMainPng)
1974                                 else:
1975                                         backMainLine = ""
1976                                 debug("[FritzDisplayPhonebook] backMainLine: " + backMainLine)
1977                                         
1978                                 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
1979                                 self.skin = """
1980                                         <screen name="FritzdisplayPhonebook" position="0,0" size="%d,%d" title="%s" flags="wfNoBorder">
1981                                                 %s
1982                                                 <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
1983                                                         <convert type="ClockToText">Default</convert>
1984                                                 </widget>
1985                                                 <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
1986                                                         <convert type="ClockToText">Date</convert>
1987                                                 </widget>
1988                                                 <eLabel text="%s" position="%d,%d" size="%d,%d" font="Regular;%d" halign="center" backgroundColor="#0b67a2" transparent="1"/>
1989                                 
1990                                                 <widget name="entries" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" transparent="1" />
1991                                 
1992                                                 <ePixmap pixmap="skin_default/buttons/red.png"          position="%d,%d"        size="%d,%d" alphatest="on" />
1993                                                 <ePixmap pixmap="skin_default/buttons/green.png"        position="%d,%d"        size="%d,%d" alphatest="on" />
1994                                                 <ePixmap pixmap="skin_default/buttons/yellow.png"       position="%d,%d"        size="%d,%d" alphatest="on" />
1995                                                 <ePixmap pixmap="skin_default/buttons/blue.png"         position="%d,%d"        size="%d,%d" alphatest="on" />
1996                                                 <widget name="key_red"  position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
1997                                                 <widget name="key_green"        position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
1998                                                 <widget name="key_yellow"       position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
1999                                                 <widget name="key_blue"         position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
2000                                                 <ePixmap position="%d,%d" size="%d,%d" zPosition="2" pixmap="%s" transparent="1" alphatest="blend" />   
2001                                         </screen>""" % (
2002                                                                         self.width, self.height, _("Phonebook"),
2003                                                                         backMainLine,
2004                                                                         scaleH(1130, XXX), scaleV(40, XXX), scaleH(80, XXX), scaleV(26, XXX), scaleV(26, XXX), # time
2005                                                                         scaleH(900, XXX), scaleV(70, XXX), scaleH(310, XXX), scaleV(22, XXX), scaleV(20, XXX), # date
2006                                                                         "FritzCall " + _("Phonebook"), scaleH(80, XXX), scaleV(63, XXX), scaleH(300, XXX), scaleV(30, XXX), scaleV(27, XXX), # eLabel
2007                                                                         scaleH(420, XXX), scaleV(120, XXX), scaleH(self.entriesWidth, XXX), scaleV(438, XXX), # entries
2008                                                                         scaleH(450, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # red
2009                                                                         scaleH(640, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # green
2010                                                                         scaleH(830, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # yellow
2011                                                                         scaleH(1020, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # blue
2012                                                                         scaleH(480, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # red
2013                                                                         scaleH(670, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # green
2014                                                                         scaleH(860, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # yellow
2015                                                                         scaleH(1050, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # blue
2016                                                                         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
2017                                                                                                                                 )
2018                         else:
2019                                 self.width = scaleH(1100, 570)
2020                                 self.entriesWidth = scaleH(1040, 560)
2021                                 debug("[FritzDisplayPhonebook] width: " + str(self.width))
2022                                 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
2023                                 self.skin = """
2024                                         <screen name="FritzDisplayPhonebook" position="%d,%d" size="%d,%d" title="%s" >
2025                                                 <eLabel position="0,0" size="%d,2" backgroundColor="#aaaaaa" />
2026                                                 <widget name="entries" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" />
2027                                                 <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
2028                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
2029                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
2030                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
2031                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
2032                                                 <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" />
2033                                                 <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" />
2034                                                 <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" />
2035                                                 <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" />
2036                                         </screen>""" % (
2037                                                         scaleH(90, 75), scaleV(100, 73), # position 
2038                                                         scaleH(1100, 570), scaleV(560, 430), # size
2039                                                         _("Phonebook"),
2040                                                         scaleH(1100, 570), # eLabel width
2041                                                         scaleH(40, 5), scaleV(20, 5), # entries position
2042                                                         self.entriesWidth, scaleV(488, 380), # entries size
2043                                                         scaleV(518, 390), # eLabel position vertical
2044                                                         scaleH(1100, 570), # eLabel width
2045                                                         scaleH(20, 5), scaleV(525, 395), # ePixmap red
2046                                                         scaleH(290, 145), scaleV(525, 395), # ePixmap green
2047                                                         scaleH(560, 285), scaleV(525, 395), # ePixmap yellow
2048                                                         scaleH(830, 425), scaleV(525, 395), # ePixmap blue
2049                                                         scaleH(20, 5), scaleV(525, 395), scaleV(22, 21), # widget red
2050                                                         scaleH(290, 145), scaleV(525, 395), scaleV(22, 21), # widget green
2051                                                         scaleH(560, 285), scaleV(525, 395), scaleV(22, 21), # widget yellow
2052                                                         scaleH(830, 425), scaleV(525, 395), scaleV(22, 21), # widget blue
2053                                                         )
2054         
2055                         Screen.__init__(self, session)
2056                         NumericalTextInput.__init__(self)
2057                         HelpableScreen.__init__(self)
2058                 
2059                         # TRANSLATORS: keep it short, this is a button
2060                         self["key_red"] = Button(_("Delete"))
2061                         # TRANSLATORS: keep it short, this is a button
2062                         self["key_green"] = Button(_("New"))
2063                         # TRANSLATORS: keep it short, this is a button
2064                         self["key_yellow"] = Button(_("Edit"))
2065                         # TRANSLATORS: keep it short, this is a button
2066                         self["key_blue"] = Button(_("Search"))
2067         
2068                         self["setupActions"] = ActionMap(["OkCancelActions", "ColorActions"],
2069                         {
2070                                 "red": self.delete,
2071                                 "green": self.add,
2072                                 "yellow": self.edit,
2073                                 "blue": self.search,
2074                                 "cancel": self.exit,
2075                                 "ok": self.showEntry, }, - 2)
2076         
2077                         # TRANSLATORS: keep it short, this is a help text
2078                         self.helpList.append((self["setupActions"], "OkCancelActions", [("ok", _("Show details of entry"))]))
2079                         # TRANSLATORS: keep it short, this is a help text
2080                         self.helpList.append((self["setupActions"], "OkCancelActions", [("cancel", _("Quit"))]))
2081                         # TRANSLATORS: keep it short, this is a help text
2082                         self.helpList.append((self["setupActions"], "ColorActions", [("red", _("Delete entry"))]))
2083                         # TRANSLATORS: keep it short, this is a help text
2084                         self.helpList.append((self["setupActions"], "ColorActions", [("green", _("Add entry to phonebook"))]))
2085                         # TRANSLATORS: keep it short, this is a help text
2086                         self.helpList.append((self["setupActions"], "ColorActions", [("yellow", _("Edit selected entry"))]))
2087                         # TRANSLATORS: keep it short, this is a help text
2088                         self.helpList.append((self["setupActions"], "ColorActions", [("blue", _("Search (case insensitive)"))]))
2089         
2090                         self["entries"] = MenuList([], True, content=eListboxPythonMultiContent)
2091                         fontSize = scaleV(22, 18)
2092                         fontHeight = scaleV(24, 20)
2093                         self["entries"].l.setFont(0, gFont("Regular", fontSize))
2094                         self["entries"].l.setItemHeight(fontHeight)
2095                         debug("[FritzCallPhonebook] displayPhonebook init")
2096                         self.display()
2097         
2098                 def display(self, filter=""):
2099                         debug("[FritzCallPhonebook] displayPhonebook/display")
2100                         self.sortlist = []
2101                         # Beware: strings in phonebook.phonebook are utf-8!
2102                         sortlistHelp = sorted((name.lower(), name, number) for (number, name) in phonebook.phonebook.iteritems())
2103                         for (low, name, number) in sortlistHelp:
2104                                 if number == "01234567890":
2105                                         continue
2106                                 try:
2107                                         low = low.decode("utf-8")
2108                                 except (UnicodeDecodeError, UnicodeEncodeError):  # this should definitely not happen
2109                                         try:
2110                                                 low = low.decode("iso-8859-1")
2111                                         except:
2112                                                 debug("[FritzCallPhonebook] displayPhonebook/display: corrupt phonebook entry for %s" % number)
2113                                                 # self.session.open(MessageBox, _("Corrupt phonebook entry\nfor number %s\nDeleting.") %number, type = MessageBox.TYPE_ERROR)
2114                                                 phonebook.remove(number)
2115                                                 continue
2116                                 else:
2117                                         if filter:
2118                                                 filter = filter.lower()
2119                                                 if low.find(filter) == - 1:
2120                                                         continue
2121                                         name = name.strip().decode("utf-8")
2122                                         number = number.strip().decode("utf-8")
2123                                         found = re.match("([^,]*),.*", name)   # strip address information from the name part
2124                                         if found:
2125                                                 shortname = found.group(1)
2126                                         else:
2127                                                 shortname = name
2128                                         numberFieldWidth = scaleV(200,150)
2129                                         fieldWidth = self.entriesWidth -5 -numberFieldWidth -10 -scaleH(90,45)
2130                                         number = number.encode("utf-8", "replace")
2131                                         name = name.encode("utf-8", "replace")
2132                                         shortname = shortname.encode('utf-8', 'replace')
2133                                         self.sortlist.append([(number, name),
2134                                                                    (eListboxPythonMultiContent.TYPE_TEXT, 0, 0, fieldWidth, scaleH(24,20), 0, RT_HALIGN_LEFT, shortname),
2135                                                                    (eListboxPythonMultiContent.TYPE_TEXT, fieldWidth +5, 0, numberFieldWidth, scaleH(24,20), 0, RT_HALIGN_LEFT, number)
2136                                                                    ])
2137                                 
2138                         self["entries"].setList(self.sortlist)
2139         
2140                 def showEntry(self):
2141                         cur = self["entries"].getCurrent()
2142                         if cur and cur[0]:
2143                                 debug("[FritzCallPhonebook] displayPhonebook/showEntry (%s,%s)" % (cur[0][0], cur[0][1]))
2144                                 number = cur[0][0]
2145                                 name = cur[0][1]
2146                                 self.session.open(FritzOfferAction, self, number, name)
2147         
2148                 def delete(self):
2149                         cur = self["entries"].getCurrent()
2150                         if cur and cur[0]:
2151                                 debug("[FritzCallPhonebook] displayPhonebook/delete " + cur[0][0])
2152                                 self.session.openWithCallback(
2153                                         self.deleteConfirmed,
2154                                         MessageBox,
2155                                         _("Do you really want to delete entry for\n\n%(number)s\n\n%(name)s?") 
2156                                         % { 'number':str(cur[0][0]), 'name':str(cur[0][1]).replace(", ", "\n") }
2157                                                                 )
2158                         else:
2159                                 self.session.open(MessageBox, _("No entry selected"), MessageBox.TYPE_INFO)
2160         
2161                 def deleteConfirmed(self, ret):
2162                         debug("[FritzCallPhonebook] displayPhonebook/deleteConfirmed")
2163                         #
2164                         # if ret: delete number from sortlist, delete number from phonebook.phonebook and write it to disk
2165                         #
2166                         cur = self["entries"].getCurrent()
2167                         if cur:
2168                                 if ret:
2169                                         # delete number from sortlist, delete number from phonebook.phonebook and write it to disk
2170                                         debug("[FritzCallPhonebook] displayPhonebook/deleteConfirmed: remove " + cur[0][0])
2171                                         phonebook.remove(cur[0][0])
2172                                         self.display()
2173                                 # else:
2174                                         # self.session.open(MessageBox, _("Not deleted."), MessageBox.TYPE_INFO)
2175                         else:
2176                                 self.session.open(MessageBox, _("No entry selected"), MessageBox.TYPE_INFO)
2177         
2178                 def add(self, parent=None, number="", name=""):
2179                         class addScreen(Screen, ConfigListScreen):
2180                                 '''ConfiglistScreen with two ConfigTexts for Name and Number'''
2181         
2182                                 def __init__(self, session, parent, number="", name=""):
2183                                         #
2184                                         # setup screen with two ConfigText and OK and ABORT button
2185                                         # 
2186                                         noButtons = 2
2187                                         width = max(scaleH(-1,570), noButtons*140)
2188                                         height = scaleV(-1,100) # = 5 + 126 + 40 + 5; 6 lines of text possible
2189                                         buttonsGap = (width-noButtons*140)/(noButtons+1)
2190                                         buttonsVPos = height-40-5
2191                                         # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
2192                                         self.skin = """
2193                                                 <screen position="%d,%d" size="%d,%d" title="%s" >
2194                                                 <widget name="config" position="5,5" size="%d,%d" scrollbarMode="showOnDemand" />
2195                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
2196                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
2197                                                 <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" />
2198                                                 <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" />
2199                                                 </screen>""" % (
2200                                                                                 (DESKTOP_WIDTH - width) / 2,
2201                                                                                 (DESKTOP_HEIGHT - height) / 2,
2202                                                                                 width,
2203                                                                                 height,
2204                                                                                 _("Add entry to phonebook"),
2205                                                                                 width - 5 - 5,
2206                                                                                 height - 5 - 40 - 5,
2207                                                                                 buttonsGap, buttonsVPos,
2208                                                                                 buttonsGap+140+buttonsGap, buttonsVPos,
2209                                                                                 buttonsGap, buttonsVPos,
2210                                                                                 buttonsGap+140+buttonsGap, buttonsVPos,
2211                                                                                 )
2212                                         Screen.__init__(self, session)
2213                                         self.session = session
2214                                         self.parent = parent
2215                                         # TRANSLATORS: keep it short, this is a button
2216                                         self["key_red"] = Button(_("Cancel"))
2217                                         # TRANSLATORS: keep it short, this is a button
2218                                         self["key_green"] = Button(_("OK"))
2219                                         self["setupActions"] = ActionMap(["SetupActions", "ColorActions"],
2220                                         {
2221                                                 "cancel": self.cancel,
2222                                                 "red": self.cancel,
2223                                                 "green": self.add,
2224                                                 "ok": self.add,
2225                                         }, - 2)
2226         
2227                                         self.list = [ ]
2228                                         ConfigListScreen.__init__(self, self.list, session=session)
2229                                         config.plugins.FritzCall.name.value = name
2230                                         config.plugins.FritzCall.number.value = number
2231                                         self.list.append(getConfigListEntry(_("Name"), config.plugins.FritzCall.name))
2232                                         self.list.append(getConfigListEntry(_("Number"), config.plugins.FritzCall.number))
2233                                         self["config"].list = self.list
2234                                         self["config"].l.setList(self.list)
2235         
2236                                 def add(self):
2237                                         # get texts from Screen
2238                                         # add (number,name) to sortlist and phonebook.phonebook and disk
2239                                         self.number = config.plugins.FritzCall.number.value
2240                                         self.name = config.plugins.FritzCall.name.value
2241                                         if not self.number or not self.name:
2242                                                 self.session.open(MessageBox, _("Entry incomplete."), type=MessageBox.TYPE_ERROR)
2243                                                 return
2244                                         # add (number,name) to sortlist and phonebook.phonebook and disk
2245         #                                       oldname = phonebook.search(self.number)
2246         #                                       if oldname:
2247         #                                               self.session.openWithCallback(
2248         #                                                       self.overwriteConfirmed,
2249         #                                                       MessageBox,
2250         #                                                       _("Do you really want to overwrite entry for %(number)s\n\n%(name)s\n\nwith\n\n%(newname)s?")
2251         #                                                       % {
2252         #                                                       'number':self.number,
2253         #                                                       'name': oldname,
2254         #                                                       'newname':self.name.replace(", ","\n")
2255         #                                                       }
2256         #                                                       )
2257         #                                               self.close()
2258         #                                               return
2259                                         phonebook.add(self.number, self.name)
2260                                         self.close()
2261                                         self.parent.display()
2262         
2263                                 def overwriteConfirmed(self, ret):
2264                                         if ret:
2265                                                 phonebook.remove(self.number)
2266                                                 phonebook.add(self.number, self.name)
2267                                                 self.parent.display()
2268         
2269                                 def cancel(self):
2270                                         self.close()
2271         
2272                         debug("[FritzCallPhonebook] displayPhonebook/add")
2273                         if not parent:
2274                                 parent = self
2275                         self.session.open(addScreen, parent, number, name)
2276         
2277                 def edit(self):
2278                         debug("[FritzCallPhonebook] displayPhonebook/edit")
2279                         cur = self["entries"].getCurrent()
2280                         if cur is None:
2281                                 self.session.open(MessageBox, _("No entry selected"), MessageBox.TYPE_INFO)
2282                         else:
2283                                 (number, name) = cur[0]
2284                                 self.add(self, number, name)
2285         
2286                 def search(self):
2287                         debug("[FritzCallPhonebook] displayPhonebook/search")
2288                         self.help_window = self.session.instantiateDialog(NumericalTextInputHelpDialog, self)
2289                         self.help_window.show()
2290                         self.session.openWithCallback(self.doSearch, InputBox, _("Enter Search Terms"), _("Search phonebook"))
2291         
2292                 def doSearch(self, searchTerms):
2293                         if not searchTerms: searchTerms = ""
2294                         debug("[FritzCallPhonebook] displayPhonebook/doSearch: " + searchTerms)
2295                         if self.help_window:
2296                                 self.session.deleteDialog(self.help_window)
2297                                 self.help_window = None
2298                         self.display(searchTerms)
2299         
2300                 def exit(self):
2301                         self.close()
2302
2303 phonebook = FritzCallPhonebook()
2304
2305 class FritzCallSetup(Screen, ConfigListScreen, HelpableScreen):
2306
2307         def __init__(self, session, args=None):
2308                 if config.plugins.FritzCall.fullscreen.value:
2309                         self.width = DESKTOP_WIDTH
2310                         self.height = DESKTOP_HEIGHT
2311                         backMainPng = ""
2312                         backMainLine = ""
2313                         if os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, DESKTOP_SKIN + "/menu/back-main.png")):
2314                                 backMainPng = DESKTOP_SKIN + "/menu/back-main.png"
2315                         elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1/menu/back-main.png")):
2316                                 backMainPng = "Kerni-HD1/menu/back-main.png"
2317                         elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1-picon/menu/back-main.png")):
2318                                 backMainPng = "Kerni-HD1-picon/menu/back-main.png"
2319                         if backMainPng:
2320                                 backMainLine = """<ePixmap position="0,0" zPosition="-10" size="%d,%d" pixmap="%s" transparent="1" />""" % (self.width, self.height, backMainPng)
2321                         else:
2322                                 backMainLine = ""
2323                         debug("[FritzCallSetup] backMainLine: " + backMainLine)
2324                         # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
2325                         self.skin = """
2326                                 <screen name="FritzCallSetup" position="0,0" size="%d,%d" title="%s" flags="wfNoBorder">
2327                                         %s
2328                                         <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
2329                                                 <convert type="ClockToText">Default</convert>
2330                                         </widget>
2331                                         <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
2332                                                 <convert type="ClockToText">Date</convert>
2333                                         </widget>
2334                                         <eLabel text="%s" position="%d,%d" size="%d,%d" font="Regular;%d" halign="center" backgroundColor="#0b67a2" transparent="1"/>
2335                         
2336                                         <widget name="consideration" position="%d,%d"  size="%d,%d" font="Regular;%d" halign="center" backgroundColor="#353e575e" transparent="1" />
2337                                         <widget name="config" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" transparent="1" />
2338                         
2339                                         <ePixmap pixmap="skin_default/buttons/red.png"          position="%d,%d"        size="%d,%d" alphatest="on" />
2340                     &n