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