UPD: nrzuname results made more generic
[vuplus_dvbapp-plugin] / 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                 debug("[FritzCallFBF] _errorLogin: %s" % (error))
405                 text = _("FRITZ!Box - Error logging in: %s") % error
406                 self._notify(text)
407
408         def _logout(self):
409                 if self._md5LoginTimestamp:
410                         self._md5LoginTimestamp = None
411                         parms = urlencode({
412                                                         'getpage':'../html/de/menus/menu2.html', # 'var:pagename':'home', 'var:menu':'home', 
413                                                         'login:command/logout':'bye bye Fritz'
414                                                         })
415                         url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
416                         debug("[FritzCallFBF] logout: '" + url + "' parms: '" + parms + "'")
417                         getPage(url,
418                                 method="POST",
419                                 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
420                                 headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
421                                                 }, postdata=parms).addErrback(self._errorLogout)
422
423         def _errorLogout(self, error):
424                 debug("[FritzCallFBF] _errorLogout: %s" % (error))
425                 text = _("FRITZ!Box - Error logging out: %s") % error
426                 self._notify(text)
427
428         def loadFritzBoxPhonebook(self):
429                 debug("[FritzCallFBF] loadFritzBoxPhonebook")
430                 if config.plugins.FritzCall.fritzphonebook.value:
431                         self._phoneBookID = '0'
432                         debug("[FritzCallFBF] loadFritzBoxPhonebook: logging in")
433                         self._login(self._loadFritzBoxPhonebook)
434
435         def _loadFritzBoxPhonebook(self, html):
436                 if html:
437                         found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
438                         if found:
439                                 self._errorLoad('Login: ' + found.group(1))
440                                 return
441                 parms = urlencode({
442                                                 'getpage':'../html/de/menus/menu2.html',
443                                                 'var:lang':'de',
444                                                 'var:pagename':'fonbuch',
445                                                 'var:menu':'fon',
446                                                 'sid':self._md5Sid,
447                                                 'telcfg:settings/Phonebook/Books/Select':self._phoneBookID, # this selects always the first phonbook
448                                                 })
449                 url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
450                 debug("[FritzCallFBF] _loadFritzBoxPhonebook: '" + url + "' parms: '" + parms + "'")
451                 getPage(url,
452                         method="POST",
453                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
454                         headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
455                                         }, postdata=parms).addCallback(self._parseFritzBoxPhonebook).addErrback(self._errorLoad)
456
457         def _parseFritzBoxPhonebook(self, html):
458                 debug("[FritzCallFBF] _parseFritzBoxPhonebook")
459                 # if not re.search('document.write\(TrFon\("[^"]+", "[^"]+", "[^"]+", "[^"]+", "[^"]+"\)\)', html):
460                 if re.search('document.write\(TrFon1\(\)', html):
461                         #===============================================================================
462                         #                                New Style: 7270 (FW 54.04.58, 54.04.63-11941, 54.04.70, 54.04.74-14371, 54.04.76)
463                         #                                                       7170 (FW 29.04.70) 22.03.2009
464                         #                                                       7141 (FW 40.04.68) 22.03.2009
465                         #       We expect one line with TrFonName followed by several lines with
466                         #       TrFonNr(Type,Number,Shortcut,Vanity), which all belong to the name in TrFonName.
467                         #===============================================================================
468                         found = re.match('.*<input type="hidden" name="telcfg:settings/Phonebook/Books/Name(\d+)" value="[Dd]reambox" id="uiPostPhonebookName\d+" disabled>', html, re.S)
469                         if found:
470                                 phoneBookID = found.group(1)
471                                 debug("[FritzCallFBF] _parseFritzBoxPhonebook: found dreambox phonebook with id: " + phoneBookID)
472                                 if self._phoneBookID != phoneBookID:
473                                         self._phoneBookID = phoneBookID
474                                         debug("[FritzCallFBF] _parseFritzBoxPhonebook: reload phonebook")
475                                         self._loadFritzBoxPhonebook(self._phoneBookID) # reload with dreambox phonebook
476                                         return
477
478                         found = re.match('.*<meta http-equiv=content-type content="text/html; charset=([^"]*)">', html, re.S)
479                         if found:
480                                 charset = found.group(1)
481                                 debug("[FritzCallFBF] _parseFritzBoxPhonebook: found charset: " + charset)
482                                 html = html2unicode(html.decode(charset)).encode('utf-8') # this looks silly, but has to be
483                         else: # this is kind of emergency conversion...
484                                 try:
485                                         html = html2unicode(html.decode('utf-8')).encode('utf-8') # this looks silly, but has to be
486                                 except UnicodeDecodeError:
487                                         html = html2unicode(html.decode('iso-8859-1')).encode('utf-8') # this looks silly, but has to be
488                         entrymask = re.compile('(TrFonName\("[^"]+", "[^"]+", "[^"]*"\);.*?)document.write\(TrFon1\(\)', re.S)
489                         entries = entrymask.finditer(html)
490                         for entry in entries:
491                                 # debug(entry.group(1)
492                                 # TrFonName (id, name, category)
493                                 found = re.match('TrFonName\("[^"]*", "([^"]+)", "[^"]*"\);', entry.group(1))
494                                 if found:
495                                         name = found.group(1).replace(',','').strip()
496                                 else:
497                                         continue
498                                 # TrFonNr (type, rufnr, code, vanity)
499                                 detailmask = re.compile('TrFonNr\("([^"]*)", "([^"]*)", "([^"]*)", "([^"]*)"\);', re.S)
500                                 details = detailmask.finditer(entry.group(1))
501                                 for found in details:
502                                         thisnumber = found.group(2).strip()
503                                         if not thisnumber:
504                                                 debug("[FritzCallFBF] Ignoring entry with empty number for '''%s'''" % (name))
505                                                 continue
506                                         else:
507                                                 thisname = name
508                                                 type = found.group(1)
509                                                 if config.plugins.FritzCall.showType.value:
510                                                         if type == "mobile":
511                                                                 thisname = thisname + " (" + _("mobile") + ")"
512                                                         elif type == "home":
513                                                                 thisname = thisname + " (" + _("home") + ")"
514                                                         elif type == "work":
515                                                                 thisname = thisname + " (" + _("work") + ")"
516
517                                                 if config.plugins.FritzCall.showShortcut.value and found.group(3):
518                                                         thisname = thisname + ", " + _("Shortcut") + ": " + found.group(3)
519                                                 if config.plugins.FritzCall.showVanity.value and found.group(4):
520                                                         thisname = thisname + ", " + _("Vanity") + ": " + found.group(4)
521
522                                                 debug("[FritzCallFBF] Adding '''%s''' with '''%s''' from FRITZ!Box Phonebook!" % (thisname.strip(), thisnumber))
523                                                 # Beware: strings in phonebook.phonebook have to be in utf-8!
524                                                 phonebook.phonebook[thisnumber] = thisname
525
526                 elif re.search('document.write\(TrFon\(', html):
527                         #===============================================================================
528                         #                               Old Style: 7050 (FW 14.04.33)
529                         #       We expect one line with TrFon(No,Name,Number,Shortcut,Vanity)
530                         #   Encoding should be plain Ascii...
531                         #===============================================================================                                
532                         entrymask = re.compile('TrFon\("[^"]*", "([^"]*)", "([^"]*)", "([^"]*)", "([^"]*)"\)', re.S)
533                         entries = entrymask.finditer(html)
534                         for found in entries:
535                                 name = found.group(1).strip().replace(',','')
536                                 # debug("[FritzCallFBF] pos: %s name: %s" %(found.group(0),name))
537                                 thisnumber = found.group(2).strip()
538                                 if config.plugins.FritzCall.showShortcut.value and found.group(3):
539                                         name = name + ", " + _("Shortcut") + ": " + found.group(3)
540                                 if config.plugins.FritzCall.showVanity.value and found.group(4):
541                                         name = name + ", " + _("Vanity") + ": " + found.group(4)
542                                 if thisnumber:
543                                         name = html2unicode(unicode(name)).encode('utf-8')
544                                         debug("[FritzCallFBF] Adding '''%s''' with '''%s''' from FRITZ!Box Phonebook!" % (name, thisnumber))
545                                         # Beware: strings in phonebook.phonebook have to be in utf-8!
546                                         phonebook.phonebook[thisnumber] = name
547                                 else:
548                                         debug("[FritzCallFBF] ignoring empty number for %s" % name)
549                                 continue
550                 elif self._md5Sid=='0000000000000000': # retry, it could be a race condition
551                         debug("[FritzCallFBF] _parseFritzBoxPhonebook: retry loading phonebook")
552                         self.loadFritzBoxPhonebook()
553                 else:
554                         self._notify(_("Could not parse FRITZ!Box Phonebook entry"))
555
556         def _errorLoad(self, error):
557                 debug("[FritzCallFBF] _errorLoad: %s" % (error))
558                 text = _("FRITZ!Box - Could not load phonebook: %s") % error
559                 self._notify(text)
560
561         def getCalls(self, callScreen, callback, type):
562                 #
563                 # call sequence must be:
564                 # - login
565                 # - getPage -> _gotPageLogin
566                 # - loginCallback (_getCalls)
567                 # - getPage -> _getCalls1
568                 debug("[FritzCallFBF] getCalls")
569                 self._callScreen = callScreen
570                 self._callType = type
571                 if (time.time() - self._callTimestamp) > 180: 
572                         debug("[FritzCallFBF] getCalls: outdated data, login and get new ones: " + time.ctime(self._callTimestamp) + " time: " + time.ctime())
573                         self._callTimestamp = time.time()
574                         self._login(lambda x:self._getCalls(callback,x))
575                 elif not self._callList:
576                         debug("[FritzCallFBF] getCalls: time is ok, but no callList")
577                         self._getCalls1(callback)
578                 else:
579                         debug("[FritzCallFBF] getCalls: time is ok, callList is ok")
580                         self._gotPageCalls(callback)
581
582         def _getCalls(self, callback, html):
583                 if html:
584                         found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
585                         if found:
586                                 self._errorCalls('Login: ' + found.group(1))
587                                 return
588                 #
589                 # we need this to fill Anrufliste.csv
590                 # http://repeater1/cgi-bin/webcm?getpage=../html/de/menus/menu2.html&var:lang=de&var:menu=fon&var:pagename=foncalls
591                 #
592                 debug("[FritzCallFBF] _getCalls")
593                 if html:
594                         found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
595                         if found:
596                                 text = _("FRITZ!Box - Error logging in: %s") + found.group(1)
597                                 self._notify(text)
598                                 return
599
600                 if self._callScreen:
601                         self._callScreen.updateStatus(_("preparing"))
602                 parms = urlencode({'getpage':'../html/de/menus/menu2.html', 'var:lang':'de', 'var:pagename':'foncalls', 'var:menu':'fon', 'sid':self._md5Sid})
603                 url = "http://%s/cgi-bin/webcm?%s" % (config.plugins.FritzCall.hostname.value, parms)
604                 getPage(url).addCallback(lambda x:self._getCalls1(callback)).addErrback(self._errorCalls) #@UnusedVariable
605
606         def _getCalls1(self, callback):
607                 #
608                 # finally we should have successfully lgged in and filled the csv
609                 #
610                 debug("[FritzCallFBF] _getCalls1")
611                 if self._callScreen:
612                         self._callScreen.updateStatus(_("finishing"))
613                 parms = urlencode({'getpage':'../html/de/FRITZ!Box_Anrufliste.csv', 'sid':self._md5Sid})
614                 url = "http://%s/cgi-bin/webcm?%s" % (config.plugins.FritzCall.hostname.value, parms)
615                 getPage(url).addCallback(lambda x:self._gotPageCalls(callback,x)).addErrback(self._errorCalls)
616
617         def _gotPageCalls(self, callback, csv=""):
618                 def _resolveNumber(number):
619                         if number.isdigit():
620                                 if config.plugins.FritzCall.internal.value and len(number) > 3 and number[0] == "0": number = number[1:]
621                                 # strip CbC prefix
622                                 number = stripCbCPrefix(number, config.plugins.FritzCall.country.value)
623                                 if config.plugins.FritzCall.prefix.value and number and number[0] != '0':               # should only happen for outgoing
624                                         number = config.plugins.FritzCall.prefix.value + number
625                                 name = phonebook.search(number)
626                                 if name:
627                                         found = re.match('(.*?)\n.*', name)
628                                         if found:
629                                                 name = found.group(1)
630                                         number = name
631                                 else:
632                                         name = resolveNumberWithAvon(number, config.plugins.FritzCall.country.value)
633                                         if name:
634                                                 number = number + ' ' + name
635                         elif number == "":
636                                 number = _("UNKNOWN")
637                         # if len(number) > 20: number = number[:20]
638                         return number
639
640                 if csv:
641                         debug("[FritzCallFBF] _gotPageCalls: got csv, setting callList")
642                         if self._callScreen:
643                                 self._callScreen.updateStatus(_("done"))
644                         # check for error: wrong password or password not set... TODO
645                         found = re.search('Melden Sie sich mit dem Kennwort der FRITZ!Box an', csv)
646                         if found:
647                                 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.")
648                                 # self.session.open(MessageBox, text, MessageBox.TYPE_ERROR, timeout=config.plugins.FritzCall.timeout.value)
649                                 self._notify(text)
650                                 return
651
652                         csv = csv.decode('iso-8859-1', 'replace').encode('utf-8', 'replace')
653                         lines = csv.splitlines()
654                         self._callList = lines
655                 elif self._callList:
656                         debug("[FritzCallFBF] _gotPageCalls: got no csv, but have callList")
657                         if self._callScreen:
658                                 self._callScreen.updateStatus(_("done, using last list"))
659                         lines = self._callList
660                 else:
661                         debug("[FritzCallFBF] _gotPageCalls: got no csv, no callList, leaving")
662                         return
663                         
664                 _callList = []
665                 for line in lines:
666                         # Typ;Datum;Name;Rufnummer;Nebenstelle;Eigene Rufnummer;Dauer
667                         found = re.match("^(" + self._callType + ");([^;]*);([^;]*);([^;]*);([^;]*);([^;]*);([^;]*)", line)
668                         if found:
669                                 direct = found.group(1)
670                                 date = found.group(2)
671                                 length = found.group(7)
672                                 remote = _resolveNumber(found.group(4))
673                                 if not remote and direct != FBF_OUT_CALLS and found.group(3):
674                                         remote = found.group(3)
675                                 found1 = re.match('Internet: (.*)', found.group(6))
676                                 if found1:
677                                         here = _resolveNumber(found1.group(1))
678                                 else:
679                                         here = _resolveNumber(found.group(6))
680                                 
681                                 number = stripCbCPrefix(found.group(4), config.plugins.FritzCall.country.value)
682                                 if config.plugins.FritzCall.prefix.value and number and number[0] != '0':               # should only happen for outgoing
683                                         number = config.plugins.FritzCall.prefix.value + number
684                                 _callList.append((number, date, here, direct, remote, length))
685
686                 # debug("[FritzCallFBF] _gotPageCalls result:\n" + text
687
688                 if callback:
689                         # debug("[FritzCallFBF] _gotPageCalls call callback with\n" + text
690                         callback(_callList)
691                 self._callScreen = None
692
693         def _errorCalls(self, error):
694                 debug("[FritzCallFBF] _errorCalls: %s" % (error))
695                 text = _("FRITZ!Box - Could not load calls: %s") % error
696                 self._notify(text)
697
698         def dial(self, number):
699                 ''' initiate a call to number '''
700                 self.number = number
701                 self._login(self._dial)
702                 
703         def _dial(self, html):
704                 if html:
705                         found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
706                         if found:
707                                 self._errorDial('Login: ' + found.group(1))
708                                 return
709                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
710                 parms = urlencode({
711                         'getpage':'../html/de/menus/menu2.html',
712                         'var:pagename':'fonbuch',
713                         'var:menu':'home',
714                         'telcfg:settings/UseClickToDial':'1',
715                         'telcfg:settings/DialPort':config.plugins.FritzCall.extension.value,
716                         'telcfg:command/Dial':self.number,
717                         'sid':self._md5Sid
718                         })
719                 debug("[FritzCallFBF] dial url: '" + url + "' parms: '" + parms + "'")
720                 getPage(url,
721                         method="POST",
722                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
723                         headers={
724                                         'Content-Type': "application/x-www-form-urlencoded",
725                                         'Content-Length': str(len(parms))},
726                         postdata=parms).addCallback(self._okDial).addErrback(self._errorDial)
727
728         def _okDial(self, html): #@UnusedVariable
729                 debug("[FritzCallFBF] okDial")
730
731         def _errorDial(self, error):
732                 debug("[FritzCallFBF] errorDial: $s" % error)
733                 text = _("FRITZ!Box - Dialling failed: %s") % error
734                 self._notify(text)
735
736         def changeWLAN(self, statusWLAN):
737                 ''' get status info from FBF '''
738                 debug("[FritzCallFBF] changeWLAN start")
739                 if not statusWLAN or (statusWLAN <> '1' and statusWLAN <> '0'):
740                         return
741                 self.statusWLAN = statusWLAN
742                 self._login(self._changeWLAN)
743                 
744         def _changeWLAN(self, html):
745                 if html:
746                         found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
747                         if found:
748                                 self._errorChangeWLAN('Login: ' + found.group(1))
749                                 return
750                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
751                 parms = urlencode({
752                         'getpage':'../html/de/menus/menu2.html',
753                         'var:lang':'de',
754                         'var:pagename':'wlan',
755                         'var:menu':'wlan',
756                         'wlan:settings/ap_enabled':str(self.statusWLAN),
757                         'sid':self._md5Sid
758                         })
759                 debug("[FritzCallFBF] changeWLAN url: '" + url + "' parms: '" + parms + "'")
760                 getPage(url,
761                         method="POST",
762                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
763                         headers={
764                                         'Content-Type': "application/x-www-form-urlencoded",
765                                         'Content-Length': str(len(parms))},
766                         postdata=parms).addCallback(self._okChangeWLAN).addErrback(self._errorChangeWLAN)
767
768         def _okChangeWLAN(self, html): #@UnusedVariable
769                 debug("[FritzCallFBF] okDial")
770
771         def _errorChangeWLAN(self, error):
772                 debug("[FritzCallFBF] _errorChangeWLAN: $s" % error)
773                 text = _("FRITZ!Box - Failed changing WLAN: %s") % error
774                 self._notify(text)
775
776         def changeMailbox(self, whichMailbox):
777                 ''' switch mailbox on/off '''
778                 debug("[FritzCallFBF] changeMailbox start: " + str(whichMailbox))
779                 self.whichMailbox = whichMailbox
780                 self._login(self._changeMailbox)
781
782         def _changeMailbox(self, html):
783                 if html:
784                         found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
785                         if found:
786                                 self._errorChangeMailbox('Login: ' + found.group(1))
787                                 return
788                 debug("[FritzCallFBF] _changeMailbox")
789                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
790                 if self.whichMailbox == -1:
791                         for i in range(5):
792                                 if self.info[FBF_tamActive][i+1]:
793                                         state = '0'
794                                 else:
795                                         state = '1'
796                                 parms = urlencode({
797                                         'tam:settings/TAM'+str(i)+'/Active':state,
798                                         'sid':self._md5Sid
799                                         })
800                                 debug("[FritzCallFBF] changeMailbox url: '" + url + "' parms: '" + parms + "'")
801                                 getPage(url,
802                                         method="POST",
803                                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
804                                         headers={
805                                                         'Content-Type': "application/x-www-form-urlencoded",
806                                                         'Content-Length': str(len(parms))},
807                                         postdata=parms).addCallback(self._okChangeMailbox).addErrback(self._errorChangeMailbox)
808                 elif self.whichMailbox > 4:
809                         debug("[FritzCallFBF] changeMailbox invalid mailbox number")
810                 else:
811                         if self.info[FBF_tamActive][self.whichMailbox+1]:
812                                 state = '0'
813                         else:
814                                 state = '1'
815                         parms = urlencode({
816                                 'tam:settings/TAM'+str(self.whichMailbox)+'/Active':state,
817                                 'sid':self._md5Sid
818                                 })
819                         debug("[FritzCallFBF] changeMailbox url: '" + url + "' parms: '" + parms + "'")
820                         getPage(url,
821                                 method="POST",
822                                 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
823                                 headers={
824                                                 'Content-Type': "application/x-www-form-urlencoded",
825                                                 'Content-Length': str(len(parms))},
826                                 postdata=parms).addCallback(self._okChangeMailbox).addErrback(self._errorChangeMailbox)
827
828         def _okChangeMailbox(self, html): #@UnusedVariable
829                 debug("[FritzCallFBF] _okChangeMailbox")
830
831         def _errorChangeMailbox(self, error):
832                 debug("[FritzCallFBF] _errorChangeMailbox: $s" % error)
833                 text = _("FRITZ!Box - Failed changing Mailbox: %s") % error
834                 self._notify(text)
835
836         def getInfo(self, callback):
837                 ''' get status info from FBF '''
838                 debug("[FritzCallFBF] getInfo")
839                 self._login(lambda x:self._getInfo(callback,x))
840                 
841         def _getInfo(self, callback, html):
842                 # http://192.168.178.1/cgi-bin/webcm?getpage=../html/de/menus/menu2.html&var:lang=de&var:pagename=home&var:menu=home
843                 debug("[FritzCallFBF] _getInfo: verify login")
844                 if html:
845                         found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
846                         if found:
847                                 self._errorGetInfo('Login: ' + found.group(1))
848                                 return
849
850                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
851                 parms = urlencode({
852                         'getpage':'../html/de/menus/menu2.html',
853                         'var:lang':'de',
854                         'var:pagename':'home',
855                         'var:menu':'home',
856                         'sid':self._md5Sid
857                         })
858                 debug("[FritzCallFBF] _getInfo url: '" + url + "' parms: '" + parms + "'")
859                 getPage(url,
860                         method="POST",
861                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
862                         headers={
863                                         'Content-Type': "application/x-www-form-urlencoded",
864                                         'Content-Length': str(len(parms))},
865                         postdata=parms).addCallback(lambda x:self._okGetInfo(callback,x)).addErrback(self._errorGetInfo)
866
867         def _okGetInfo(self, callback, html):
868                 def readInfo(html):
869                         if self.info:
870                                 (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive) = self.info
871                         else:
872                                 (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive) = (None, None, None, None, None, None, None, None, None)
873
874                         debug("[FritzCallFBF] _okGetInfo/readinfo")
875                         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)
876                         if found:
877                                 boxInfo = found.group(1)+ ', ' + found.group(2)
878                                 boxInfo = boxInfo.replace('&nbsp;',' ')
879                                 # debug("[FritzCallFBF] _okGetInfo Boxinfo: " + boxInfo)
880                         else:
881                                 found = re.match('.*<p class="ac">([^<]*)</p>', html, re.S)
882                                 if found:
883                                         # debug("[FritzCallFBF] _okGetInfo Boxinfo: " + found.group(1))
884                                         boxInfo = found.group(1)
885
886                         if html.find('home_coninf.txt') != -1:
887                                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
888                                 parms = urlencode({
889                                         'getpage':'../html/de/home/home_coninf.txt',
890                                         'sid':self._md5Sid
891                                         })
892                                 # debug("[FritzCallFBF] get coninfo: url: '" + url + "' parms: '" + parms + "'")
893                                 getPage(url,
894                                         method="POST",
895                                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
896                                         headers={
897                                                         'Content-Type': "application/x-www-form-urlencoded",
898                                                         'Content-Length': str(len(parms))},
899                                         postdata=parms).addCallback(lambda x:self._okSetConInfo(callback,x)).addErrback(self._errorGetInfo)
900                         else:
901                                 found = re.match('.*if \(isNaN\(jetzt\)\)\s*return "";\s*var str = "([^"]*)";', html, re.S)
902                                 if found:
903                                         # debug("[FritzCallFBF] _okGetInfo Uptime: " + found.group(1))
904                                         upTime = found.group(1)
905                                 else:
906                                         found = re.match('.*str = g_pppSeit \+"([^<]*)<br>"\+mldIpAdr;', html, re.S)
907                                         if found:
908                                                 # debug("[FritzCallFBF] _okGetInfo Uptime: " + found.group(1))
909                                                 upTime = found.group(1)
910         
911                                 found = re.match(".*IpAdrDisplay\('([.\d]+)'\)", html, re.S)
912                                 if found:
913                                         # debug("[FritzCallFBF] _okGetInfo IpAdrDisplay: " + found.group(1))
914                                         ipAddress = found.group(1)
915
916                         if html.find('g_tamActive') != -1:
917                                 entries = re.compile('if \("(\d)" == "1"\) {\s*g_tamActive \+= 1;\s*}', re.S).finditer(html)
918                                 tamActive = [0, False, False, False, False, False]
919                                 i=1
920                                 for entry in entries:
921                                         state = entry.group(1)
922                                         if state == '1':
923                                                 tamActive[0] += 1
924                                                 tamActive[i] = True
925                                         i += 1
926                                 # debug("[FritzCallFBF] _okGetInfo tamActive: " + str(tamActive))
927                 
928                         if html.find('home_dect.txt') != -1:
929                                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
930                                 parms = urlencode({
931                                         'getpage':'../html/de/home/home_dect.txt',
932                                         'sid':self._md5Sid
933                                         })
934                                 # debug("[FritzCallFBF] get coninfo: url: '" + url + "' parms: '" + parms + "'")
935                                 getPage(url,
936                                         method="POST",
937                                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
938                                         headers={
939                                                         'Content-Type': "application/x-www-form-urlencoded",
940                                                         'Content-Length': str(len(parms))},
941                                         postdata=parms).addCallback(lambda x:self._okSetDect(callback,x)).addErrback(self._errorGetInfo)
942                         else:
943                                 if html.find('countDect2') != -1:
944                                         entries = re.compile('if \("1" == "1"\) countDect2\+\+;', re.S).findall(html)
945                                         dectActive = len(entries)
946                                         # debug("[FritzCallFBF] _okGetInfo dectActive: " + str(dectActive))
947
948                         found = re.match('.*var g_intFaxActive = "0";\s*if \("1" != ""\) {\s*g_intFaxActive = "1";\s*}\s*', html, re.S)
949                         if found:
950                                 faxActive = True
951                                 # debug("[FritzCallFBF] _okGetInfo faxActive")
952
953                         if html.find('cntRufumleitung') != -1:
954                                 entries = re.compile('mode = "1";\s*ziel = "[^"]+";\s*if \(mode == "1" \|\| ziel != ""\)\s*{\s*g_RufumleitungAktiv = true;', re.S).findall(html)
955                                 rufumlActive = len(entries)
956                                 entries = re.compile('if \("([^"]*)"=="([^"]*)"\) isAllIncoming\+\+;', re.S).finditer(html)
957                                 isAllIncoming = 0
958                                 for entry in entries:
959                                         # debug("[FritzCallFBF] _okGetInfo rufumlActive add isAllIncoming")
960                                         if entry.group(1) == entry.group(2): isAllIncoming += 1
961                                 if isAllIncoming==2 and rufumlActive>0: rufumlActive -= 1
962                                 # debug("[FritzCallFBF] _okGetInfo rufumlActive: " + str(rufumlActive))
963
964                         # /cgi-bin/webcm?getpage=../html/de/home/home_dsl.txt
965                         # { "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": "" } 
966                         if html.find('home_dsl.txt') != -1:
967                                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
968                                 parms = urlencode({
969                                         'getpage':'../html/de/home/home_dsl.txt',
970                                         'sid':self._md5Sid
971                                         })
972                                 # debug("[FritzCallFBF] get dsl state: url: '" + url + "' parms: '" + parms + "'")
973                                 getPage(url,
974                                         method="POST",
975                                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
976                                         headers={
977                                                         'Content-Type': "application/x-www-form-urlencoded",
978                                                         'Content-Length': str(len(parms))},
979                                         postdata=parms).addCallback(lambda x:self._okSetDslState(callback,x)).addErrback(self._errorGetInfo)
980                         else:
981                                 found = re.match('.*function DslStateDisplay \(state\){\s*var state = "(\d+)";', html, re.S)
982                                 if found:
983                                         # debug("[FritzCallFBF] _okGetInfo DslState: " + found.group(1))
984                                         dslState = [ found.group(1), None ] # state, speed
985                                         found = re.match('.*function DslStateDisplay \(state\){\s*var state = "\d+";.*?if \("3130" != "0"\) str = "([^"]*)";', html, re.S)
986                                         if found:
987                                                 # debug("[FritzCallFBF] _okGetInfo DslSpeed: " + found.group(1).strip())
988                                                 dslState[1] = found.group(1).strip()
989                 
990                         # /cgi-bin/webcm?getpage=../html/de/home/home_wlan.txt
991                         # { "ap_enabled": "1", "active_stations": "0", "encryption": "4", "wireless_stickandsurf_enabled": "0", "is_macfilter_active": "0", "wmm_enabled": "1", "wlan_state": [ "end" ] }
992                         if html.find('home_wlan.txt') != -1:
993                                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
994                                 parms = urlencode({
995                                         'getpage':'../html/de/home/home_wlan.txt',
996                                         'sid':self._md5Sid
997                                         })
998                                 # debug("[FritzCallFBF] get wlan state: url: '" + url + "' parms: '" + parms + "'")
999                                 getPage(url,
1000                                         method="POST",
1001                                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
1002                                         headers={
1003                                                         'Content-Type': "application/x-www-form-urlencoded",
1004                                                         'Content-Length': str(len(parms))},
1005                                         postdata=parms).addCallback(lambda x:self._okSetWlanState(callback,x)).addErrback(self._errorGetInfo)
1006                         else:
1007                                 found = re.match('.*function WlanStateLed \(state\){.*?return StateLed\("(\d+)"\);\s*}', html, re.S)
1008                                 if found:
1009                                         # debug("[FritzCallFBF] _okGetInfo WlanState: " + found.group(1))
1010                                         wlanState = [ found.group(1), 0, 0 ] # state, encryption, number of devices
1011                                         found = re.match('.*var (?:g_)?encryption = "(\d+)";', html, re.S)
1012                                         if found:
1013                                                 # debug("[FritzCallFBF] _okGetInfo WlanEncrypt: " + found.group(1))
1014                                                 wlanState[1] = found.group(1)
1015
1016                         return (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive)
1017
1018                 debug("[FritzCallFBF] _okGetInfo")
1019                 info = readInfo(html)
1020                 debug("[FritzCallFBF] _okGetInfo info: " + str(info))
1021                 self.info = info
1022                 if callback:
1023                         callback(info)
1024
1025         def _okSetDect(self, callback, html):
1026                 # debug("[FritzCallFBF] _okSetDect: " + html)
1027                 # found = re.match('.*"connection_status":"(\d+)".*"connection_ip":"([.\d]+)".*"connection_detail":"([^"]+)".*"connection_uptime":"([^"]+)"', html, re.S)
1028                 if html.find('"dect_enabled": "1"') != -1:
1029                         # debug("[FritzCallFBF] _okSetDect: dect_enabled")
1030                         found = re.match('.*"dect_device_list":.*\[([^\]]*)\]', html, re.S)
1031                         if found:
1032                                 # debug("[FritzCallFBF] _okSetDect: dect_device_list: %s" %(found.group(1)))
1033                                 entries = re.compile('"1"', re.S).findall(found.group(1))
1034                                 dectActive = len(entries)
1035                                 (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dummy, faxActive, rufumlActive) = self.info
1036                                 self.info = (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive)
1037                                 debug("[FritzCallFBF] _okSetDect info: " + str(self.info))
1038                 if callback:
1039                         callback(self.info)
1040
1041         def _okSetConInfo(self, callback, html):
1042                 # debug("[FritzCallFBF] _okSetConInfo: " + html)
1043                 # found = re.match('.*"connection_status":"(\d+)".*"connection_ip":"([.\d]+)".*"connection_detail":"([^"]+)".*"connection_uptime":"([^"]+)"', html, re.S)
1044                 found = re.match('.*"connection_ip": "([.\d]+)".*"connection_uptime": "([^"]+)"', html, re.S)
1045                 if found:
1046                         # debug("[FritzCallFBF] _okSetConInfo: ipAddress: %s upTime: %s" %( found.group(1), found.group(2)))
1047                         ipAddress = found.group(1)
1048                         upTime = found.group(2)
1049                         (boxInfo, dummy, dummy, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive) = self.info
1050                         self.info = (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive)
1051                         debug("[FritzCallFBF] _okSetWlanState info: " + str(self.info))
1052                 if callback:
1053                         callback(self.info)
1054
1055         def _okSetWlanState(self, callback, html):
1056                 # debug("[FritzCallFBF] _okSetWlanState: " + html)
1057                 found = re.match('.*"ap_enabled": "(\d+)"', html, re.S)
1058                 if found:
1059                         # debug("[FritzCallFBF] _okSetWlanState: ap_enabled: " + found.group(1))
1060                         wlanState = [ found.group(1), None, None ]
1061                         found = re.match('.*"encryption": "(\d+)"', html, re.S)
1062                         if found:
1063                                 # debug("[FritzCallFBF] _okSetWlanState: encryption: " + found.group(1))
1064                                 wlanState[1] = found.group(1)
1065                         found = re.match('.*"active_stations": "(\d+)"', html, re.S)
1066                         if found:
1067                                 # debug("[FritzCallFBF] _okSetWlanState: active_stations: " + found.group(1))
1068                                 wlanState[2] = found.group(1)
1069                         (boxInfo, upTime, ipAddress, dummy, dslState, tamActive, dectActive, faxActive, rufumlActive) = self.info
1070                         self.info = (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive)
1071                         debug("[FritzCallFBF] _okSetWlanState info: " + str(self.info))
1072                 if callback:
1073                         callback(self.info)
1074
1075         def _okSetDslState(self, callback, html):
1076                 # debug("[FritzCallFBF] _okSetDslState: " + html)
1077                 found = re.match('.*"dsl_carrier_state": "(\d+)"', html, re.S)
1078                 if found:
1079                         # debug("[FritzCallFBF] _okSetDslState: dsl_carrier_state: " + found.group(1))
1080                         dslState = [ found.group(1), None ]
1081                         found = re.match('.*"dsl_ds_nrate": "(\d+)"', html, re.S)
1082                         if found:
1083                                 # debug("[FritzCallFBF] _okSetDslState: dsl_ds_nrate: " + found.group(1))
1084                                 dslState[1] = found.group(1)
1085                         found = re.match('.*"dsl_us_nrate": "(\d+)"', html, re.S)
1086                         if found:
1087                                 # debug("[FritzCallFBF] _okSetDslState: dsl_us_nrate: " + found.group(1))
1088                                 dslState[1] = dslState[1] + '/' + found.group(1)
1089                         (boxInfo, upTime, ipAddress, wlanState, dummy, tamActive, dectActive, faxActive, rufumlActive) = self.info
1090                         self.info = (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive)
1091                         debug("[FritzCallFBF] _okSetDslState info: " + str(self.info))
1092                 if callback:
1093                         callback(self.info)
1094
1095         def _errorGetInfo(self, error):
1096                 debug("[FritzCallFBF] _errorGetInfo: %s" % (error))
1097                 text = _("FRITZ!Box - Error getting status: %s") % error
1098                 self._notify(text)
1099                 # linkP = open("/tmp/FritzCall_errorGetInfo.htm", "w")
1100                 # linkP.write(error)
1101                 # linkP.close()
1102
1103         def reset(self):
1104                 self._login(self._reset)
1105
1106         def _reset(self, html):
1107                 # 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
1108                 if html:
1109                         found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
1110                         if found:
1111                                 self._errorReset('Login: ' + found.group(1))
1112                                 return
1113                 if self._callScreen:
1114                         self._callScreen.close()
1115                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
1116                 parms = urlencode({
1117                         'getpage':'../html/reboot.html',
1118                         'var:lang':'de',
1119                         'var:pagename':'reset',
1120                         'var:menu':'system',
1121                         'logic:command/reboot':'../gateway/commands/saveconfig.html',
1122                         'sid':self._md5Sid
1123                         })
1124                 debug("[FritzCallFBF] _reset url: '" + url + "' parms: '" + parms + "'")
1125                 getPage(url,
1126                         method="POST",
1127                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
1128                         headers={
1129                                         'Content-Type': "application/x-www-form-urlencoded",
1130                                         'Content-Length': str(len(parms))},
1131                         postdata=parms)
1132
1133         def _okReset(self, html): #@UnusedVariable
1134                 debug("[FritzCallFBF] _okReset")
1135
1136         def _errorReset(self, error):
1137                 debug("[FritzCallFBF] _errorReset: %s" % (error))
1138                 text = _("FRITZ!Box - Error resetting: %s") % error
1139                 self._notify(text)
1140
1141 #===============================================================================
1142 #       def hangup(self):
1143 #               ''' hangup call on port; not used for now '''
1144 #               url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
1145 #               parms = urlencode({
1146 #                       'id':'uiPostForm',
1147 #                       'name':'uiPostForm',
1148 #                       'login:command/password': config.plugins.FritzCall.password.value,
1149 #                       'telcfg:settings/UseClickToDial':'1',
1150 #                       'telcfg:settings/DialPort':config.plugins.FritzCall.extension.value,
1151 #                       'telcfg:command/Hangup':'',
1152 #                       'sid':self._md5Sid
1153 #                       })
1154 #               debug("[FritzCallFBF] hangup url: '" + url + "' parms: '" + parms + "'")
1155 #               getPage(url,
1156 #                       method="POST",
1157 #                       headers={
1158 #                                       'Content-Type': "application/x-www-form-urlencoded",
1159 #                                       'Content-Length': str(len(parms))},
1160 #                       postdata=parms)
1161 #===============================================================================
1162
1163 fritzbox = FritzCallFBF()
1164
1165 class FritzMenu(Screen,HelpableScreen):
1166         def __init__(self, session):
1167                 fontSize = scaleV(24,21) # indeed this is font size +2
1168                 noButtons = 2 # reset, wlan
1169
1170                 if not fritzbox or not fritzbox.info: return
1171
1172                 if fritzbox.info[FBF_tamActive]:
1173                         noButtons += 1 # toggle mailboxes
1174                 width = max(DESKTOP_WIDTH - scaleH(500,250), noButtons*140+(noButtons+1)*10)
1175                 # boxInfo 2 lines, gap, internet 2 lines, gap, dsl/wlan each 1 line, gap, buttons
1176                 height = 5 + 2*fontSize + 10 + 2*fontSize + 10 + 2*fontSize + 10 + 40 + 5
1177                 if fritzbox.info[FBF_tamActive] is not None: height += fontSize
1178                 if fritzbox.info[FBF_dectActive] is not None: height += fontSize
1179                 if fritzbox.info[FBF_faxActive] is not None: height += fontSize
1180                 if fritzbox.info[FBF_rufumlActive] is not None: height += fontSize
1181                 buttonsGap = (width-noButtons*140)/(noButtons+1)
1182                 buttonsVPos = height-40-5
1183
1184                 varLinePos = 4
1185                 if fritzbox.info[FBF_tamActive] is not None:
1186                         mailboxLine = """
1187                                 <widget name="FBFMailbox" position="%d,%d" size="%d,%d" font="Regular;%d" />
1188                                 <widget name="mailbox_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1189                                 <widget name="mailbox_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1190                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1191                                 <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" />
1192                                 """ % (
1193                                                 40, 5+2*fontSize+10+varLinePos*fontSize+10, # position mailbox
1194                                                 width-40-20, fontSize, # size mailbox
1195                                                 fontSize-2,
1196                                                 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button mailbox
1197                                                 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button mailbox
1198                                                 noButtons*buttonsGap+(noButtons-1)*140, buttonsVPos,
1199                                                 noButtons*buttonsGap+(noButtons-1)*140, buttonsVPos,
1200                                 )
1201                         varLinePos += 1
1202                 else:
1203                         mailboxLine = ""
1204
1205                 if fritzbox.info[FBF_dectActive] is not None:
1206                         dectLine = """
1207                                 <widget name="FBFDect" position="%d,%d" size="%d,%d" font="Regular;%d" />
1208                                 <widget name="dect_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1209                                 <widget name="dect_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1210                                 """ %(
1211                                                 40, 5+2*fontSize+10+varLinePos*fontSize+10, # position dect
1212                                                 width-40-20, fontSize, # size dect
1213                                                 fontSize-2,
1214                                                 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
1215                                                 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
1216                                 )
1217                         varLinePos += 1
1218                 else:
1219                         dectLine = ""
1220
1221                 if fritzbox.info[FBF_faxActive] is not None:
1222                         faxLine = """
1223                                 <widget name="FBFFax" position="%d,%d" size="%d,%d" font="Regular;%d" />
1224                                 <widget name="fax_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1225                                 <widget name="fax_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1226                                 """ %(
1227                                                 40, 5+2*fontSize+10+varLinePos*fontSize+10, # position dect
1228                                                 width-40-20, fontSize, # size dect
1229                                                 fontSize-2,
1230                                                 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
1231                                                 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
1232                                 )
1233                         varLinePos += 1
1234                 else:
1235                         faxLine = ""
1236
1237                 if fritzbox.info[FBF_rufumlActive] is not None:
1238                         rufumlLine = """
1239                                 <widget name="FBFRufuml" position="%d,%d" size="%d,%d" font="Regular;%d" />
1240                                 <widget name="rufuml_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1241                                 <widget name="rufuml_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1242                                 """ %(
1243                                                 40, 5+2*fontSize+10+varLinePos*fontSize+10, # position dect
1244                                                 width-40-20, fontSize, # size dect
1245                                                 fontSize-2,
1246                                                 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
1247                                                 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
1248                                 )
1249                         varLinePos += 1
1250                 else:
1251                         rufumlLine = ""
1252         
1253                 self.skin = """
1254                         <screen name="FritzMenu" position="%d,%d" size="%d,%d" title="%s" >
1255                                 <widget name="FBFInfo" position="%d,%d" size="%d,%d" font="Regular;%d" />
1256                                 <widget name="FBFInternet" position="%d,%d" size="%d,%d" font="Regular;%d" />
1257                                 <widget name="internet_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1258                                 <widget name="internet_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1259                                 <widget name="FBFDsl" position="%d,%d" size="%d,%d" font="Regular;%d" />
1260                                 <widget name="dsl_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1261                                 <widget name="dsl_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1262                                 <widget name="FBFWlan" position="%d,%d" size="%d,%d" font="Regular;%d" />
1263                                 <widget name="wlan_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1264                                 <widget name="wlan_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1265                                 %s
1266                                 %s
1267                                 %s
1268                                 %s
1269                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1270                                 <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" />
1271                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1272                                 <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" />
1273                         </screen>""" % (
1274                                                 (DESKTOP_WIDTH - width) / 2, (DESKTOP_HEIGHT - height) / 2, # position
1275                                                 width, height, # size
1276                                                 _("FRITZ!Box Fon Status"),
1277                                                 40, 5, # position info
1278                                                 width-2*40, 2*fontSize, # size info
1279                                                 fontSize-2,
1280                                                 40, 5+2*fontSize+10, # position internet
1281                                                 width-40, 2*fontSize, # size internet
1282                                                 fontSize-2,
1283                                                 20, 5+2*fontSize+10+(fontSize-16)/2, # position button internet
1284                                                 20, 5+2*fontSize+10+(fontSize-16)/2, # position button internet
1285                                                 40, 5+2*fontSize+10+2*fontSize+10, # position dsl
1286                                                 width-40-20, fontSize, # size dsl
1287                                                 fontSize-2,
1288                                                 20, 5+2*fontSize+10+2*fontSize+10+(fontSize-16)/2, # position button dsl
1289                                                 20, 5+2*fontSize+10+2*fontSize+10+(fontSize-16)/2, # position button dsl
1290                                                 40, 5+2*fontSize+10+3*fontSize+10, # position wlan
1291                                                 width-40-20, fontSize, # size wlan
1292                                                 fontSize-2,
1293                                                 20, 5+2*fontSize+10+3*fontSize+10+(fontSize-16)/2, # position button wlan
1294                                                 20, 5+2*fontSize+10+3*fontSize+10+(fontSize-16)/2, # position button wlan
1295                                                 mailboxLine,
1296                                                 dectLine,
1297                                                 faxLine,
1298                                                 rufumlLine,
1299                                                 buttonsGap, buttonsVPos, buttonsGap, buttonsVPos,
1300                                                 buttonsGap+140+buttonsGap, buttonsVPos, buttonsGap+140+buttonsGap, buttonsVPos,
1301                                                 )
1302
1303                 Screen.__init__(self, session)
1304                 HelpableScreen.__init__(self)
1305                 # TRANSLATORS: keep it short, this is a button
1306                 self["key_red"] = Button(_("Reset"))
1307                 # TRANSLATORS: keep it short, this is a button
1308                 self["key_green"] = Button(_("Toggle WLAN"))
1309                 self._mailboxActive = False
1310                 if fritzbox.info[FBF_tamActive] is not None:
1311                         # TRANSLATORS: keep it short, this is a button
1312                         self["key_yellow"] = Button(_("Toggle Mailbox"))
1313                         self["menuActions"] = ActionMap(["OkCancelActions", "ColorActions", "NumberActions", "EPGSelectActions"],
1314                                                                                         {
1315                                                                                         "cancel": self._exit,
1316                                                                                         "ok": self._exit,
1317                                                                                         "red": self._reset,
1318                                                                                         "green": self._toggleWlan,
1319                                                                                         "yellow": (lambda: self._toggleMailbox(-1)),
1320                                                                                         "0": (lambda: self._toggleMailbox(0)),
1321                                                                                         "1": (lambda: self._toggleMailbox(1)),
1322                                                                                         "2": (lambda: self._toggleMailbox(2)),
1323                                                                                         "3": (lambda: self._toggleMailbox(3)),
1324                                                                                         "4": (lambda: self._toggleMailbox(4)),
1325                                                                                         "info": self._getInfo,
1326                                                                                         }, -2)
1327                         # TRANSLATORS: keep it short, this is a help text
1328                         self.helpList.append((self["menuActions"], "ColorActions", [("yellow", _("Toggle all mailboxes"))]))
1329                         # TRANSLATORS: keep it short, this is a help text
1330                         self.helpList.append((self["menuActions"], "NumberActions", [("0", _("Toggle 1. mailbox"))]))
1331                         # TRANSLATORS: keep it short, this is a help text
1332                         self.helpList.append((self["menuActions"], "NumberActions", [("1", _("Toggle 2. mailbox"))]))
1333                         # TRANSLATORS: keep it short, this is a help text
1334                         self.helpList.append((self["menuActions"], "NumberActions", [("2", _("Toggle 3. mailbox"))]))
1335                         # TRANSLATORS: keep it short, this is a help text
1336                         self.helpList.append((self["menuActions"], "NumberActions", [("3", _("Toggle 4. mailbox"))]))
1337                         # TRANSLATORS: keep it short, this is a help text
1338                         self.helpList.append((self["menuActions"], "NumberActions", [("4", _("Toggle 5. mailbox"))]))
1339                         self["FBFMailbox"] = Label(_('Mailbox'))
1340                         self["mailbox_inactive"] = Pixmap()
1341                         self["mailbox_active"] = Pixmap()
1342                         self["mailbox_active"].hide()
1343                 else:
1344                         self["menuActions"] = ActionMap(["OkCancelActions", "ColorActions", "EPGSelectActions"],
1345                                                                                         {
1346                                                                                         "cancel": self._exit,
1347                                                                                         "ok": self._exit,
1348                                                                                         "green": self._toggleWlan,
1349                                                                                         "red": self._reset,
1350                                                                                         "info": self._getInfo,
1351                                                                                         }, -2)
1352
1353                 # TRANSLATORS: keep it short, this is a help text
1354                 self.helpList.append((self["menuActions"], "OkCancelActions", [("cancel", _("Quit"))]))
1355                 # TRANSLATORS: keep it short, this is a help text
1356                 self.helpList.append((self["menuActions"], "OkCancelActions", [("ok", _("Quit"))]))
1357                 # TRANSLATORS: keep it short, this is a help text
1358                 self.helpList.append((self["menuActions"], "ColorActions", [("green", _("Toggle WLAN"))]))
1359                 # TRANSLATORS: keep it short, this is a help text
1360                 self.helpList.append((self["menuActions"], "ColorActions", [("red", _("Reset"))]))
1361                 # TRANSLATORS: keep it short, this is a help text
1362                 self.helpList.append((self["menuActions"], "EPGSelectActions", [("info", _("Refresh status"))]))
1363
1364                 self["FBFInfo"] = Label(_('Getting status from FRITZ!Box Fon...'))
1365
1366                 self["FBFInternet"] = Label('Internet')
1367                 self["internet_inactive"] = Pixmap()
1368                 self["internet_active"] = Pixmap()
1369                 self["internet_active"].hide()
1370
1371                 self["FBFDsl"] = Label('DSL')
1372                 self["dsl_inactive"] = Pixmap()
1373                 self["dsl_inactive"].hide()
1374                 self["dsl_active"] = Pixmap()
1375                 self["dsl_active"].hide()
1376
1377                 self["FBFWlan"] = Label('WLAN ')
1378                 self["wlan_inactive"] = Pixmap()
1379                 self["wlan_inactive"].hide()
1380                 self["wlan_active"] = Pixmap()
1381                 self["wlan_active"].hide()
1382                 self._wlanActive = False
1383
1384                 if fritzbox.info[FBF_dectActive] is not None: 
1385                         self["FBFDect"] = Label('DECT')
1386                         self["dect_inactive"] = Pixmap()
1387                         self["dect_active"] = Pixmap()
1388                         self["dect_active"].hide()
1389
1390                 if fritzbox.info[FBF_faxActive] is not None: 
1391                         self["FBFFax"] = Label('Fax')
1392                         self["fax_inactive"] = Pixmap()
1393                         self["fax_active"] = Pixmap()
1394                         self["fax_active"].hide()
1395
1396                 if fritzbox.info[FBF_rufumlActive] is not None: 
1397                         self["FBFRufuml"] = Label(_('Call redirection'))
1398                         self["rufuml_inactive"] = Pixmap()
1399                         self["rufuml_active"] = Pixmap()
1400                         self["rufuml_active"].hide()
1401
1402                 self._timer = eTimer()
1403                 self._timer.callback.append(self._getInfo)
1404                 self.onShown.append(lambda: self._timer.start(5000))
1405                 self.onHide.append(lambda: self._timer.stop())
1406                 self._getInfo()
1407
1408         def _getInfo(self):
1409                 fritzbox.getInfo(self._fillMenu)
1410
1411         def _fillMenu(self, status):
1412                 (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive) = status
1413                 self._wlanActive = (wlanState[0] == '1')
1414                 self._mailboxActive = False
1415                 try:
1416                         if boxInfo:
1417                                 self["FBFInfo"].setText(boxInfo.replace(', ', '\n'))
1418                         else:
1419                                 self["FBFInfo"].setText('BoxInfo ' + _('Status not available'))
1420
1421                         if ipAddress:
1422                                 if upTime:
1423                                         self["FBFInternet"].setText('Internet ' + _('IP Address:') + ' ' + ipAddress + '\n' + _('Connected since') + ' ' + upTime)
1424                                 else:
1425                                         self["FBFInternet"].setText('Internet ' + _('IP Address:') + ' ' + ipAddress)
1426                                 self["internet_inactive"].hide()
1427                                 self["internet_active"].show()
1428                         else:
1429                                 self["internet_active"].hide()
1430                                 self["internet_inactive"].show()
1431
1432                         if dslState:
1433                                 if dslState[0]=='5':
1434                                         self["dsl_inactive"].hide()
1435                                         self["dsl_active"].show()
1436                                         if dslState[1]:
1437                                                 self["FBFDsl"].setText('DSL ' + dslState[1])
1438                                 else:
1439                                         self["dsl_active"].hide()
1440                                         self["dsl_inactive"].show()
1441                         else:
1442                                 self["FBFDsl"].setText('DSL ' + _('Status not available'))
1443                                 self["dsl_active"].hide()
1444                                 self["dsl_inactive"].hide()
1445
1446                         if wlanState:
1447                                 if wlanState[0]=='1':
1448                                         self["wlan_inactive"].hide()
1449                                         self["wlan_active"].show()
1450                                         message = 'WLAN'
1451                                         if wlanState[1]=='0':
1452                                                 message += ' ' + _('not encrypted')
1453                                         else:
1454                                                 message += ' ' + _('encrypted')
1455                                         if wlanState[2]:
1456                                                 if wlanState[2]=='0':
1457                                                         message = message + ', ' + _('no device active')
1458                                                 elif wlanState[2]=='1':
1459                                                         message = message + ', ' + _('one device active')
1460                                                 else:
1461                                                         message = message + ', ' + wlanState[2] + ' ' + _('devices active')
1462                                         self["FBFWlan"].setText(message)
1463                                 else:
1464                                         self["wlan_active"].hide()
1465                                         self["wlan_inactive"].show()
1466                                         self["FBFWlan"].setText('WLAN')
1467                         else:
1468                                 self["FBFWlan"].setText('WLAN ' + _('Status not available'))
1469                                 self["wlan_active"].hide()
1470                                 self["wlan_inactive"].hide()
1471
1472                         if fritzbox.info[FBF_tamActive]:
1473                                 if  not tamActive or tamActive[0] == 0:
1474                                         self._mailboxActive = False
1475                                         self["mailbox_active"].hide()
1476                                         self["mailbox_inactive"].show()
1477                                         self["FBFMailbox"].setText(_('No mailbox active'))
1478                                 else:
1479                                         self._mailboxActive = True
1480                                         message = '('
1481                                         for i in range(5):
1482                                                 if tamActive[i+1]:
1483                                                         message = message + str(i) + ','
1484                                         message = message[:-1] + ')'
1485                                         self["mailbox_inactive"].hide()
1486                                         self["mailbox_active"].show()
1487                                         if tamActive[0] == 1:
1488                                                 self["FBFMailbox"].setText(_('One mailbox active') + ' ' + message)
1489                                         else:
1490                                                 self["FBFMailbox"].setText(str(tamActive[0]) + ' ' + _('mailboxes active') + ' ' + message)
1491         
1492                         if fritzbox.info[FBF_dectActive] and dectActive:
1493                                 self["dect_inactive"].hide()
1494                                 self["dect_active"].show()
1495                                 if dectActive == 0:
1496                                         self["FBFDect"].setText(_('No DECT phone registered'))
1497                                 else:
1498                                         if dectActive == 1:
1499                                                 self["FBFDect"].setText(_('One DECT phone registered'))
1500                                         else:
1501                                                 self["FBFDect"].setText(str(dectActive) + ' ' + _('DECT phones registered'))
1502
1503                         if fritzbox.info[FBF_faxActive] and faxActive:
1504                                 self["fax_inactive"].hide()
1505                                 self["fax_active"].show()
1506                                 self["FBFFax"].setText(_('Software fax active'))
1507
1508                         if fritzbox.info[FBF_rufumlActive] is not None and rufumlActive is not None:
1509                                 if rufumlActive == 0:
1510                                         self["rufuml_active"].hide()
1511                                         self["rufuml_inactive"].show()
1512                                         self["FBFRufuml"].setText(_('No call redirection active'))
1513                                 else:
1514                                         self["rufuml_inactive"].hide()
1515                                         self["rufuml_active"].show()
1516                                         if rufumlActive == 1:
1517                                                 self["FBFRufuml"].setText(_('One call redirection active'))
1518                                         else:
1519                                                 self["FBFRufuml"].setText(str(rufumlActive) + ' ' + _('call redirections active'))
1520
1521                 except KeyError:
1522                         debug("[FritzCallFBF] _fillMenu: " + traceback.format_exc())
1523
1524         def _toggleWlan(self):
1525                 if self._wlanActive:
1526                         debug("[FritzMenu] toggleWlan off")
1527                         fritzbox.changeWLAN('0')
1528                 else:
1529                         debug("[FritzMenu] toggleWlan off")
1530                         fritzbox.changeWLAN('1')
1531
1532         def _toggleMailbox(self, which):
1533                 debug("[FritzMenu] toggleMailbox")
1534                 if fritzbox.info[FBF_tamActive]:
1535                         debug("[FritzMenu] toggleMailbox off")
1536                         fritzbox.changeMailbox(which)
1537
1538         def _reset(self):
1539                 fritzbox.reset()
1540                 self._exit()
1541
1542         def _exit(self):
1543                 self._timer.stop()
1544                 self.close()
1545
1546
1547 class FritzDisplayCalls(Screen, HelpableScreen):
1548
1549         def __init__(self, session, text=""): #@UnusedVariable
1550                 if config.plugins.FritzCall.fullscreen.value:
1551                         self.width = DESKTOP_WIDTH
1552                         self.height = DESKTOP_HEIGHT
1553                         backMainPng = ""
1554                         if os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, DESKTOP_SKIN + "/menu/back-main.png")):
1555                                 backMainPng = DESKTOP_SKIN + "/menu/back-main.png"
1556                         elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1/menu/back-main.png")):
1557                                 backMainPng = "Kerni-HD1/menu/back-main.png"
1558                         elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1-picon/menu/back-main.png")):
1559                                 backMainPng = "Kerni-HD1-picon/menu/back-main.png"
1560                         if backMainPng:
1561                                         backMainLine = """<ePixmap position="0,0" zPosition="-10" size="%d,%d" pixmap="%s" transparent="1" />""" % (self.width, self.height, backMainPng)
1562                         else:
1563                                 backMainLine = ""
1564                         debug("[FritzDisplayCalls] backMainLine: " + backMainLine)
1565                                 
1566                         # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
1567                         self.skin = """
1568                                 <screen name="FritzDisplayCalls" position="0,0" size="%d,%d" title="%s" flags="wfNoBorder">
1569                                         %s
1570                                         <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
1571                                                 <convert type="ClockToText">Default</convert>
1572                                         </widget>
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">Date</convert>
1575                                         </widget>
1576                                         <eLabel text="%s" position="%d,%d" size="%d,%d" font="Regular;%d" halign="center" backgroundColor="#0b67a2" transparent="1"/>
1577                         
1578                                         <widget name="statusbar" position="%d,%d"  size="%d,%d" font="Regular;%d" backgroundColor="#353e575e" transparent="1" />
1579                                         <widget name="entries" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" transparent="1" />
1580                         
1581                                         <ePixmap pixmap="skin_default/buttons/red.png"          position="%d,%d"        size="%d,%d" alphatest="on" />
1582                                         <ePixmap pixmap="skin_default/buttons/green.png"        position="%d,%d"        size="%d,%d" alphatest="on" />
1583                                         <ePixmap pixmap="skin_default/buttons/yellow.png"       position="%d,%d"        size="%d,%d" alphatest="on" />
1584                                         <ePixmap pixmap="skin_default/buttons/blue.png"         position="%d,%d"        size="%d,%d" alphatest="on" />
1585                                         <widget name="key_red"  position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
1586                                         <widget name="key_green"        position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
1587                                         <widget name="key_yellow"       position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
1588                                         <widget name="key_blue"         position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
1589                                         <ePixmap position="%d,%d" size="%d,%d" zPosition="2" pixmap="%s" transparent="1" alphatest="blend" />           
1590                                 </screen>""" % (
1591                                                         self.width, self.height, _("Phone calls"),
1592                                                         backMainLine,
1593                                                         scaleH(1130, XXX), scaleV(40, XXX), scaleH(80, XXX), scaleV(26, XXX), scaleV(26, XXX), # time
1594                                                         scaleH(890, XXX), scaleV(70, XXX), scaleH(320, XXX), scaleV(22, XXX), scaleV(20, XXX), # date
1595                                                         "FritzCall " + _("Phone calls"), scaleH(500, XXX), scaleV(63, XXX), scaleH(330, XXX), scaleV(30, XXX), scaleV(27, XXX), # eLabel
1596                                                         scaleH(80, XXX), scaleV(150, XXX), scaleH(280, XXX), scaleV(200, XXX), scaleV(22, XXX), # statusbar
1597                                                         scaleH(420, XXX), scaleV(120, XXX), scaleH(790, XXX), scaleV(438, XXX), # entries
1598                                                         scaleH(450, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # red
1599                                                         scaleH(640, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # green
1600                                                         scaleH(830, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # yellow
1601                                                         scaleH(1020, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # blue
1602                                                         scaleH(480, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # red
1603                                                         scaleH(670, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # green
1604                                                         scaleH(860, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # yellow
1605                                                         scaleH(1050, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # blue
1606                                                         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
1607                                                                                                                 )
1608                 else:
1609                         self.width = scaleH(1100, 570)
1610                         debug("[FritzDisplayCalls] width: " + str(self.width))
1611                         # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
1612                         self.skin = """
1613                                 <screen name="FritzDisplayCalls" position="%d,%d" size="%d,%d" title="%s" >
1614                                         <eLabel position="0,0" size="%d,2" backgroundColor="#aaaaaa" />
1615                                         <widget name="statusbar" position="%d,%d" size="%d,%d" font="Regular;%d" backgroundColor="#aaaaaa" transparent="1" />
1616                                         <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
1617                                         <widget name="entries" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" />
1618                                         <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
1619                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1620                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1621                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1622                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
1623                                         <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" />
1624                                         <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" />
1625                                         <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" />
1626                                         <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" />
1627                                 </screen>""" % (
1628                                                         scaleH(90, 75), scaleV(100, 78), # position 
1629                                                         scaleH(1100, 570), scaleV(560, 430), # size
1630                                                         _("Phone calls"),
1631                                                         scaleH(1100, 570), # eLabel width
1632                                                         scaleH(40, 5), scaleV(10, 5), # statusbar position
1633                                                         scaleH(1050, 560), scaleV(25, 22), # statusbar size
1634                                                         scaleV(21, 21), # statusbar font size
1635                                                         scaleV(40, 28), # eLabel position vertical
1636                                                         scaleH(1100, 570), # eLabel width
1637                                                         scaleH(40, 5), scaleV(55, 40), # entries position
1638                                                         scaleH(1040, 560), scaleV(458, 340), # entries size
1639                                                         scaleV(518, 390), # eLabel position vertical
1640                                                         scaleH(1100, 570), # eLabel width
1641                                                         scaleH(20, 5), scaleV(525, 395), # widget red
1642                                                         scaleH(290, 145), scaleV(525, 395), # widget green
1643                                                         scaleH(560, 285), scaleV(525, 395), # widget yellow
1644                                                         scaleH(830, 425), scaleV(525, 395), # widget blue
1645                                                         scaleH(20, 5), scaleV(525, 395), scaleV(22, 21), # widget red
1646                                                         scaleH(290, 145), scaleV(525, 395), scaleV(22, 21), # widget green
1647                                                         scaleH(560, 285), scaleV(525, 395), scaleV(22, 21), # widget yellow
1648                                                         scaleH(830, 425), scaleV(525, 395), scaleV(22, 21), # widget blue
1649                                                                                                                 )
1650
1651                 Screen.__init__(self, session)
1652                 HelpableScreen.__init__(self)
1653
1654                 # TRANSLATORS: keep it short, this is a button
1655                 self["key_yellow"] = Button(_("All"))
1656                 # TRANSLATORS: keep it short, this is a button
1657                 self["key_red"] = Button(_("Missed"))
1658                 # TRANSLATORS: keep it short, this is a button
1659                 self["key_blue"] = Button(_("Incoming"))
1660                 # TRANSLATORS: keep it short, this is a button
1661                 self["key_green"] = Button(_("Outgoing"))
1662
1663                 self["setupActions"] = ActionMap(["OkCancelActions", "ColorActions"],
1664                 {
1665                         "yellow": (lambda: self.display(FBF_ALL_CALLS)),
1666                         "red": (lambda: self.display(FBF_MISSED_CALLS)),
1667                         "blue": (lambda: self.display(FBF_IN_CALLS)),
1668                         "green": (lambda: self.display(FBF_OUT_CALLS)),
1669                         "cancel": self.ok,
1670                         "ok": self.showEntry, }, - 2)
1671
1672                 # TRANSLATORS: keep it short, this is a help text
1673                 self.helpList.append((self["setupActions"], "OkCancelActions", [("ok", _("Show details of entry"))]))
1674                 # TRANSLATORS: keep it short, this is a help text
1675                 self.helpList.append((self["setupActions"], "OkCancelActions", [("cancel", _("Quit"))]))
1676                 # TRANSLATORS: keep it short, this is a help text
1677                 self.helpList.append((self["setupActions"], "ColorActions", [("yellow", _("Display all calls"))]))
1678                 # TRANSLATORS: keep it short, this is a help text
1679                 self.helpList.append((self["setupActions"], "ColorActions", [("red", _("Display missed calls"))]))
1680                 # TRANSLATORS: keep it short, this is a help text
1681                 self.helpList.append((self["setupActions"], "ColorActions", [("blue", _("Display incoming calls"))]))
1682                 # TRANSLATORS: keep it short, this is a help text
1683                 self.helpList.append((self["setupActions"], "ColorActions", [("green", _("Display outgoing calls"))]))
1684
1685                 self["statusbar"] = Label(_("Getting calls from FRITZ!Box..."))
1686                 self["entries"] = MenuList([], True, content=eListboxPythonMultiContent)
1687                 fontSize = scaleV(22, 18)
1688                 fontHeight = scaleV(24, 20)
1689                 self["entries"].l.setFont(0, gFont("Regular", fontSize))
1690                 self["entries"].l.setItemHeight(fontHeight)
1691
1692                 debug("[FritzDisplayCalls] init: '''%s'''" % config.plugins.FritzCall.fbfCalls.value)
1693                 self.display()
1694
1695         def ok(self):
1696                 self.close()
1697
1698         def display(self, which=config.plugins.FritzCall.fbfCalls.value):
1699                 debug("[FritzDisplayCalls] display")
1700                 config.plugins.FritzCall.fbfCalls.value = which
1701                 config.plugins.FritzCall.fbfCalls.save()
1702                 self.header = fbfCallsChoices[which]
1703                 fritzbox.getCalls(self, self.gotCalls, which)
1704
1705         def gotCalls(self, callList):
1706                 debug("[FritzDisplayCalls] gotCalls")
1707                 self.updateStatus(self.header + " (" + str(len(callList)) + ")")
1708                 dateFieldWidth = scaleH(130,100)
1709                 dirFieldWidth = 16
1710                 lengthFieldWidth = scaleH(45,45)
1711                 scrollbarWidth = scaleH(35,35)
1712                 if config.plugins.FritzCall.fullscreen.value:
1713                         remoteFieldWidth = scaleH(140,100)
1714                         fieldWidth = 790 -dateFieldWidth -5 -dirFieldWidth -5 -lengthFieldWidth -5 -remoteFieldWidth -scrollbarWidth -5
1715                         fontSize = scaleV(24,20)
1716                 else:
1717                         remoteFieldWidth = scaleH(160,100)
1718                         fieldWidth = self.width -scaleH(60, 5) -dateFieldWidth -5 -dirFieldWidth -5 -lengthFieldWidth -5 -remoteFieldWidth -scrollbarWidth -5
1719                         fontSize = scaleV(24,20)
1720                 sortlist = []
1721                 for (number, date, remote, direct, here, length) in callList:
1722                         found = re.match("(\d\d.\d\d.)\d\d( \d\d:\d\d)", date)
1723                         if found: date = found.group(1) + found.group(2)
1724                         if direct == FBF_OUT_CALLS:
1725                                 dir = LoadPixmap(resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/callout.png"))
1726                         elif direct == FBF_IN_CALLS:
1727                                 dir = LoadPixmap(resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/callin.png"))
1728                         else:
1729                                 dir = LoadPixmap(resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/callinfailed.png"))
1730                         # debug("[FritzDisplayCalls] gotCalls: d: %d; f: %d; d: %d; r: %d" %(dateFieldWidth, fieldWidth, dirFieldWidth, remoteFieldWidth))
1731                         sortlist.append([number,
1732                                                          (eListboxPythonMultiContent.TYPE_TEXT, 0, 0, dateFieldWidth, fontSize, 0, RT_HALIGN_LEFT, date),
1733                                                          (eListboxPythonMultiContent.TYPE_PIXMAP_ALPHATEST, dateFieldWidth+5, 0, dirFieldWidth, 16, dir),
1734                                                          (eListboxPythonMultiContent.TYPE_TEXT, dateFieldWidth+5+dirFieldWidth+5, 0, fieldWidth, fontSize, 0, RT_HALIGN_LEFT, here),
1735                                                          (eListboxPythonMultiContent.TYPE_TEXT, dateFieldWidth+5+dirFieldWidth+5+fieldWidth+5, 0, lengthFieldWidth, fontSize, 0, RT_HALIGN_LEFT, length),
1736                                                          (eListboxPythonMultiContent.TYPE_TEXT, dateFieldWidth+5+dirFieldWidth+5+fieldWidth+5+lengthFieldWidth+5, 0, remoteFieldWidth, fontSize, 0, RT_HALIGN_RIGHT, remote)
1737                                                          ])
1738
1739                 self["entries"].setList(sortlist)
1740
1741         def updateStatus(self, text):
1742                 self["statusbar"].setText(_("Getting calls from FRITZ!Box...") + ' ' + text)
1743
1744         def showEntry(self):
1745                 debug("[FritzDisplayCalls] showEntry")
1746                 cur = self["entries"].getCurrent()
1747                 if cur:
1748                         if cur[0]:
1749                                 debug("[FritzDisplayCalls] showEntry %s" % (cur[0]))
1750                                 number = cur[0]
1751                                 fullname = phonebook.search(cur[0])
1752                                 if fullname:
1753                                         # we have a name for this number
1754                                         name = fullname
1755                                         self.session.open(FritzOfferAction, self, number, name)
1756                                 else:
1757                                         # we don't
1758                                         fullname = resolveNumberWithAvon(number, config.plugins.FritzCall.country.value)
1759                                         if fullname:
1760                                                 name = fullname
1761                                                 self.session.open(FritzOfferAction, self, number, name)
1762                                         else:
1763                                                 self.session.open(FritzOfferAction, self, number)
1764                         else:
1765                                 # we do not even have a number...
1766                                 self.session.open(MessageBox,
1767                                                   _("UNKNOWN"),
1768                                                   type=MessageBox.TYPE_INFO)
1769
1770
1771 class FritzOfferAction(Screen):
1772
1773         def __init__(self, session, parent, number, name=""):
1774                 # the layout will completely be recalculated in finishLayout
1775                 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
1776                 self.skin = """
1777                         <screen name="FritzOfferAction" title="%s" >
1778                                 <widget name="text" size="%d,%d" font="Regular;%d" />
1779                                 <widget name="FacePixmap" size="%d,%d" alphatest="on" />
1780                                 <widget name="key_red_p" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1781                                 <widget name="key_green_p" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1782                                 <widget name="key_yellow_p" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1783                                 <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" />
1784                                 <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" />
1785                                 <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" />
1786                         </screen>""" % (
1787                                                         _("Do what?"),
1788                                                         DESKTOP_WIDTH, DESKTOP_HEIGHT, # set maximum size
1789                                                         scaleH(22,21), # text
1790                                                         DESKTOP_WIDTH, DESKTOP_HEIGHT, # set maximum size
1791                                                         ) 
1792                 debug("[FritzOfferAction] init: %s, %s" %(number, name))
1793                 Screen.__init__(self, session)
1794         
1795                 # TRANSLATORS: keep it short, this is a button
1796                 self["key_red"] = Button(_("Lookup"))
1797                 # TRANSLATORS: keep it short, this is a button
1798                 self["key_green"] = Button(_("Call"))
1799                 # TRANSLATORS: keep it short, this is a button
1800                 self["key_yellow"] = Button(_("Save"))
1801                 # TRANSLATORS: keep it short, this is a button
1802                 # self["key_blue"] = Button(_("Search"))
1803
1804                 self["FritzOfferActions"] = ActionMap(["OkCancelActions", "ColorActions"],
1805                 {
1806                         "red": self.lookup,
1807                         "green": self.call,
1808                         "yellow": self.add,
1809                         "cancel": self.exit,
1810                         "ok": self.exit, }, - 2)
1811
1812                 self.number = number
1813                 self.name = name.replace("\n", ", ")
1814                 self["text"] = Label(number + "\n\n" + name.replace(", ", "\n"))
1815                 self.parent = parent
1816                 self.lookupState = 0
1817                 self["key_red_p"] = Pixmap()
1818                 self["key_green_p"] = Pixmap()
1819                 self["key_yellow_p"] = Pixmap()
1820                 self["FacePixmap"] = Pixmap()
1821                 self.onLayoutFinish.append(self.finishLayout)
1822
1823         def finishLayout(self):
1824                 debug("[FritzCall] FritzOfferAction/finishLayout number: %s/%s" % (self.number, self.name))
1825
1826                 picPixmap = LoadPixmap(findFace(self.number, self.name))
1827                 if not picPixmap:       # that means most probably, that the picture is not 8 bit...
1828                         picPixmap = LoadPixmap(resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/icons/input_question.png"))
1829                 picSize = picPixmap.size()
1830                 self["FacePixmap"].instance.setPixmap(picPixmap)
1831
1832                 noButtons = 3
1833                 # recalculate window size
1834                 textSize = self["text"].getSize()
1835                 textSize = (textSize[0]+20, textSize[1]+20) # don't know, why, but size is too small
1836                 debug("[FritzCall] FritzOfferAction/finishLayout textsize: %s/%s" % textSize)
1837                 textSize = eSize(*textSize)
1838                 width = max(scaleH(620,545), noButtons*145, picSize.width() + textSize.width() + 30)
1839                 height = max(picSize.height()+5, textSize.height()+5, scaleV(-1,136)) + 5 + 40 + 5
1840                 buttonsGap = (width-noButtons*140)/(noButtons+1)
1841                 buttonsVPos = height-40-5
1842                 wSize = (width, height)
1843                 wSize = eSize(*wSize)
1844
1845                 # center the smaller vertically
1846                 hGap = (width-picSize.width()-textSize.width())/3
1847                 picPos = (hGap,(height-50-picSize.height())/2+5)
1848                 textPos = (hGap+picSize.width()+hGap, (height-50-textSize.height())/2+5)
1849
1850                 # resize screen
1851                 self.instance.resize(wSize)
1852                 # resize text
1853                 self["text"].instance.resize(textSize)
1854                 # resize pixmap
1855                 self["FacePixmap"].instance.resize(picSize)
1856                 # move buttons
1857                 buttonPos = (buttonsGap, buttonsVPos)
1858                 self["key_red_p"].instance.move(ePoint(*buttonPos))
1859                 self["key_red"].instance.move(ePoint(*buttonPos))
1860                 buttonPos = (buttonsGap+140+buttonsGap, buttonsVPos)
1861                 self["key_green_p"].instance.move(ePoint(*buttonPos))
1862                 self["key_green"].instance.move(ePoint(*buttonPos))
1863                 buttonPos = (buttonsGap+140+buttonsGap+140+buttonsGap, buttonsVPos)
1864                 self["key_yellow_p"].instance.move(ePoint(*buttonPos))
1865                 self["key_yellow"].instance.move(ePoint(*buttonPos))
1866                 # move text
1867                 self["text"].instance.move(ePoint(*textPos))
1868                 # move pixmap
1869                 self["FacePixmap"].instance.move(ePoint(*picPos))
1870                 # center window
1871                 self.instance.move(ePoint((DESKTOP_WIDTH-wSize.width())/2, (DESKTOP_HEIGHT-wSize.height())/2))
1872
1873         def setTextAndResize(self, message):
1874                 self["text"].instance.resize(eSize(*(DESKTOP_WIDTH,DESKTOP_HEIGHT)))
1875                 self["text"].setText(self.number + "\n\n" + message)
1876                 self.finishLayout()
1877
1878         def lookup(self):
1879                 phonebookLocation = config.plugins.FritzCall.phonebookLocation.value
1880                 if self.lookupState == 0:
1881                         self.lookupState = 1
1882                         self.setTextAndResize(_("Reverse searching..."))
1883                         ReverseLookupAndNotifier(self.number, self.lookedUp, "UTF-8", config.plugins.FritzCall.country.value)
1884                         return
1885                 if self.lookupState == 1 and os.path.exists(os.path.join(phonebookLocation, "PhoneBook.csv")):
1886                         self.setTextAndResize(_("Searching in Outlook export..."))
1887                         self.lookupState = 2
1888                         self.lookedUp(self.number, FritzOutlookCSV.findNumber(self.number, os.path.join(phonebookLocation, "PhoneBook.csv"))) #@UndefinedVariable
1889                         return
1890                 else:
1891                         self.lookupState = 2
1892                 if self.lookupState == 2 and os.path.exists(os.path.join(phonebookLocation, "PhoneBook.ldif")):
1893                         self.setTextAndResize(_("Searching in LDIF..."))
1894                         self.lookupState = 0
1895                         FritzLDIF.findNumber(self.number, open(os.path.join(phonebookLocation, "PhoneBook.ldif")), self.lookedUp)
1896                         return
1897                 else:
1898                         self.lookupState = 0
1899                         self.lookup()
1900
1901         def lookedUp(self, number, name):
1902                 name = handleReverseLookupResult(name)
1903                 if not name:
1904                         if self.lookupState == 1:
1905                                 name = _("No result from reverse lookup")
1906                         elif self.lookupState == 2:
1907                                 name = _("No result from Outlook export")
1908                         else:
1909                                 name = _("No result from LDIF")
1910                 self.name = name
1911                 self.number = number
1912                 debug("[FritzOfferAction] lookedUp: " + str(name.replace(", ", "\n")))
1913                 self.setTextAndResize(str(name.replace(", ", "\n")))
1914
1915         def call(self):
1916                 debug("[FritzOfferAction] add: %s" %self.number)
1917                 fritzbox.dial(self.number)
1918                 self.exit()
1919
1920         def add(self):
1921                 debug("[FritzOfferAction] add: %s, %s" %(self.number, self.name))
1922                 phonebook.FritzDisplayPhonebook(self.session).add(self.parent, self.number, self.name)
1923                 self.exit()
1924
1925         def exit(self):
1926                 self.close()
1927
1928
1929 class FritzCallPhonebook:
1930         def __init__(self):
1931                 debug("[FritzCallPhonebook] init")
1932                 # Beware: strings in phonebook.phonebook have to be in utf-8!
1933                 self.phonebook = {}
1934                 self.reload()
1935
1936         def reload(self):
1937                 debug("[FritzCallPhonebook] reload")
1938                 # Beware: strings in phonebook.phonebook have to be in utf-8!
1939                 self.phonebook = {}
1940
1941                 if not config.plugins.FritzCall.enable.value:
1942                         return
1943
1944                 phonebookFilename = os.path.join(config.plugins.FritzCall.phonebookLocation.value, "PhoneBook.txt")
1945                 if config.plugins.FritzCall.phonebook.value and os.path.exists(phonebookFilename):
1946                         debug("[FritzCallPhonebook] reload: read " + phonebookFilename)
1947                         phonebookTxtCorrupt = False
1948                         for line in open(phonebookFilename):
1949                                 try:
1950                                         # Beware: strings in phonebook.phonebook have to be in utf-8!
1951                                         line = line.decode("utf-8")
1952                                 except UnicodeDecodeError: # this is just for the case, somebody wrote latin1 chars into PhoneBook.txt
1953                                         try:
1954                                                 line = line.decode("iso-8859-1")
1955                                                 debug("[FritzCallPhonebook] Fallback to ISO-8859-1 in %s" % line)
1956                                                 phonebookTxtCorrupt = True
1957                                         except:
1958                                                 debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
1959                                                 phonebookTxtCorrupt = True
1960                                 line = line.encode("utf-8")
1961                                 if re.match("^\d+#.*$", line):
1962                                         try:
1963                                                 number, name = line.split("#")
1964                                                 if not self.phonebook.has_key(number):
1965                                                         # Beware: strings in phonebook.phonebook have to be in utf-8!
1966                                                         self.phonebook[number] = name
1967                                         except ValueError: # how could this possibly happen?!?!
1968                                                 debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
1969                                                 phonebookTxtCorrupt = True
1970                                 else:
1971                                         debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
1972                                         phonebookTxtCorrupt = True
1973
1974                         if phonebookTxtCorrupt:
1975                                 # dump phonebook to PhoneBook.txt
1976                                 debug("[FritzCallPhonebook] dump Phonebook.txt")
1977                                 os.rename(phonebookFilename, phonebookFilename + ".bck")
1978                                 fNew = open(phonebookFilename, 'w')
1979                                 # Beware: strings in phonebook.phonebook are utf-8!
1980                                 for (number, name) in self.phonebook.iteritems():
1981                                         # Beware: strings in PhoneBook.txt have to be in utf-8!
1982                                         fNew.write(number + "#" + name.encode("utf-8"))
1983                                 fNew.close()
1984
1985 #===============================================================================
1986 #               #
1987 #               # read entries from Outlook export
1988 #               #
1989 #               # not reliable with coding yet
1990 #               # 
1991 #               # import csv exported from Outlook 2007 with csv(Windows)
1992 #               csvFilename = "/tmp/PhoneBook.csv"
1993 #               if config.plugins.FritzCall.phonebook.value and os.path.exists(csvFilename):
1994 #                       try:
1995 #                               readOutlookCSV(csvFilename, self.add)
1996 #                               os.rename(csvFilename, csvFilename + ".done")
1997 #                       except ImportError:
1998 #                               debug("[FritzCallPhonebook] CSV import failed" %line)
1999 #===============================================================================
2000
2001                 
2002 #===============================================================================
2003 #               #
2004 #               # read entries from LDIF
2005 #               #
2006 #               # import ldif exported from Thunderbird 2.0.0.19
2007 #               ldifFilename = "/tmp/PhoneBook.ldif"
2008 #               if config.plugins.FritzCall.phonebook.value and os.path.exists(ldifFilename):
2009 #                       try:
2010 #                               parser = MyLDIF(open(ldifFilename), self.add)
2011 #                               parser.parse()
2012 #                               os.rename(ldifFilename, ldifFilename + ".done")
2013 #                       except ImportError:
2014 #                               debug("[FritzCallPhonebook] LDIF import failed" %line)
2015 #===============================================================================
2016                 
2017                 if config.plugins.FritzCall.fritzphonebook.value:
2018                         fritzbox.loadFritzBoxPhonebook()
2019
2020                 if DESKTOP_WIDTH <> 1280 or DESKTOP_HEIGHT <> 720:
2021                         config.plugins.FritzCall.fullscreen.value = False
2022
2023         def search(self, number):
2024                 # debug("[FritzCallPhonebook] Searching for %s" %number
2025                 name = None
2026                 if config.plugins.FritzCall.phonebook.value or config.plugins.FritzCall.fritzphonebook.value:
2027                         if self.phonebook.has_key(number):
2028                                 name = self.phonebook[number].replace(", ", "\n").strip()
2029                 return name
2030
2031         def add(self, number, name):
2032                 '''
2033                 
2034                 @param number: number of entry
2035                 @param name: name of entry, has to be in utf-8
2036                 '''
2037                 debug("[FritzCallPhonebook] add")
2038                 name = name.replace("\n", ", ").replace('#','') # this is just for safety reasons. add should only be called with newlines converted into commas
2039                 self.remove(number)
2040                 self.phonebook[number] = name;
2041                 if number and number <> 0:
2042                         if config.plugins.FritzCall.phonebook.value:
2043                                 try:
2044                                         name = name.strip() + "\n"
2045                                         string = "%s#%s" % (number, name)
2046                                         # Beware: strings in PhoneBook.txt have to be in utf-8!
2047                                         f = open(os.path.join(config.plugins.FritzCall.phonebookLocation.value, "PhoneBook.txt"), 'a')
2048                                         f.write(string)
2049                                         f.close()
2050                                         debug("[FritzCallPhonebook] added %s with %s to Phonebook.txt" % (number, name.strip()))
2051                                         return True
2052         
2053                                 except IOError:
2054                                         return False
2055
2056         def remove(self, number):
2057                 if number in self.phonebook:
2058                         debug("[FritzCallPhonebook] remove entry in phonebook")
2059                         del self.phonebook[number]
2060                         if config.plugins.FritzCall.phonebook.value:
2061                                 try:
2062                                         phonebookFilename = os.path.join(config.plugins.FritzCall.phonebookLocation.value, "PhoneBook.txt")
2063                                         debug("[FritzCallPhonebook] remove entry in Phonebook.txt")
2064                                         fOld = open(phonebookFilename, 'r')
2065                                         fNew = open(phonebookFilename + str(os.getpid()), 'w')
2066                                         line = fOld.readline()
2067                                         while (line):
2068                                                 if not re.match("^" + number + "#.*$", line):
2069                                                         fNew.write(line)
2070                                                 line = fOld.readline()
2071                                         fOld.close()
2072                                         fNew.close()
2073                                         os.remove(phonebookFilename)
2074                                         os.rename(phonebookFilename + str(os.getpid()), phonebookFilename)
2075                                         debug("[FritzCallPhonebook] removed %s from Phonebook.txt" % number)
2076                                         return True
2077         
2078                                 except IOError:
2079                                         pass
2080                 return False
2081
2082         class FritzDisplayPhonebook(Screen, HelpableScreen, NumericalTextInput):
2083         
2084                 def __init__(self, session):
2085                         if config.plugins.FritzCall.fullscreen.value:
2086                                 self.width = DESKTOP_WIDTH
2087                                 self.height = DESKTOP_HEIGHT
2088                                 self.entriesWidth = 790
2089                                 backMainPng = ""
2090                                 if os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, DESKTOP_SKIN + "/menu/back-main.png")):
2091                                         backMainPng = DESKTOP_SKIN + "/menu/back-main.png"
2092                                 elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1/menu/back-main.png")):
2093                                         backMainPng = "Kerni-HD1/menu/back-main.png"
2094                                 elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1-picon/menu/back-main.png")):
2095                                         backMainPng = "Kerni-HD1-picon/menu/back-main.png"
2096                                 if backMainPng:
2097                                         backMainLine = """<ePixmap position="0,0" zPosition="-10" size="%d,%d" pixmap="%s" transparent="1" />""" % (self.width, self.height, backMainPng)
2098                                 else:
2099                                         backMainLine = ""
2100                                 debug("[FritzDisplayPhonebook] backMainLine: " + backMainLine)
2101                                         
2102                                 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
2103                                 self.skin = """
2104                                         <screen name="FritzdisplayPhonebook" position="0,0" size="%d,%d" title="%s" flags="wfNoBorder">
2105                                                 %s
2106                                                 <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
2107                                                         <convert type="ClockToText">Default</convert>
2108                                                 </widget>
2109                                                 <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
2110                                                         <convert type="ClockToText">Date</convert>
2111                                                 </widget>
2112                                                 <eLabel text="%s" position="%d,%d" size="%d,%d" font="Regular;%d" halign="center" backgroundColor="#0b67a2" transparent="1"/>
2113                                 
2114                                                 <widget name="entries" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" transparent="1" />
2115                                 
2116                                                 <ePixmap pixmap="skin_default/buttons/red.png"          position="%d,%d"        size="%d,%d" alphatest="on" />
2117                                                 <ePixmap pixmap="skin_default/buttons/green.png"        position="%d,%d"        size="%d,%d" alphatest="on" />
2118                                                 <ePixmap pixmap="skin_default/buttons/yellow.png"       position="%d,%d"        size="%d,%d" alphatest="on" />
2119                                                 <ePixmap pixmap="skin_default/buttons/blue.png"         position="%d,%d"        size="%d,%d" alphatest="on" />
2120                                                 <widget name="key_red"  position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
2121                                                 <widget name="key_green"        position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
2122                                                 <widget name="key_yellow"       position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
2123                                                 <widget name="key_blue"         position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
2124                                                 <ePixmap position="%d,%d" size="%d,%d" zPosition="2" pixmap="%s" transparent="1" alphatest="blend" />   
2125                                         </screen>""" % (
2126                                                                         self.width, self.height, _("Phonebook"),
2127                                                                         backMainLine,
2128                                                                         scaleH(1130, XXX), scaleV(40, XXX), scaleH(80, XXX), scaleV(26, XXX), scaleV(26, XXX), # time
2129                                                                         scaleH(890, XXX), scaleV(70, XXX), scaleH(320, XXX), scaleV(22, XXX), scaleV(20, XXX), # date
2130                                                                         "FritzCall " + _("Phonebook"), scaleH(80, XXX), scaleV(63, XXX), scaleH(300, XXX), scaleV(30, XXX), scaleV(27, XXX), # eLabel
2131                                                                         scaleH(420, XXX), scaleV(120, XXX), scaleH(self.entriesWidth, XXX), scaleV(438, XXX), # entries
2132                                                                         scaleH(450, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # red
2133                                                                         scaleH(640, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # green
2134                                                                         scaleH(830, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # yellow
2135                                                                         scaleH(1020, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # blue
2136                                                                         scaleH(480, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # red
2137                                                                         scaleH(670, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # green
2138                                                                         scaleH(860, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # yellow
2139                                                                         scaleH(1050, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # blue
2140                                                                         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
2141                                                                                                                                 )
2142                         else:
2143                                 self.width = scaleH(1100, 570)
2144                                 self.entriesWidth = scaleH(1040, 560)
2145                                 debug("[FritzDisplayPhonebook] width: " + str(self.width))
2146                                 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
2147                                 self.skin = """
2148                                         <screen name="FritzDisplayPhonebook" position="%d,%d" size="%d,%d" title="%s" >
2149                                                 <eLabel position="0,0" size="%d,2" backgroundColor="#aaaaaa" />
2150                                                 <widget name="entries" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" />
2151                                                 <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
2152                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
2153                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
2154                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
2155                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
2156                                                 <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" />
2157                                                 <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" />
2158                                                 <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" />
2159                                                 <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" />
2160                                         </screen>""" % (
2161                                                         scaleH(90, 75), scaleV(100, 73), # position 
2162                                                         scaleH(1100, 570), scaleV(560, 430), # size
2163                                                         _("Phonebook"),
2164                                                         scaleH(1100, 570), # eLabel width
2165                                                         scaleH(40, 5), scaleV(20, 5), # entries position
2166                                                         self.entriesWidth, scaleV(488, 380), # entries size
2167                                                         scaleV(518, 390), # eLabel position vertical
2168                                                         scaleH(1100, 570), # eLabel width
2169                                                         scaleH(20, 5), scaleV(525, 395), # ePixmap red
2170                                                         scaleH(290, 145), scaleV(525, 395), # ePixmap green
2171                                                         scaleH(560, 285), scaleV(525, 395), # ePixmap yellow
2172                                                         scaleH(830, 425), scaleV(525, 395), # ePixmap blue
2173                                                         scaleH(20, 5), scaleV(525, 395), scaleV(22, 21), # widget red
2174                                                         scaleH(290, 145), scaleV(525, 395), scaleV(22, 21), # widget green
2175                                                         scaleH(560, 285), scaleV(525, 395), scaleV(22, 21), # widget yellow
2176                                                         scaleH(830, 425), scaleV(525, 395), scaleV(22, 21), # widget blue
2177                                                         )
2178         
2179                         Screen.__init__(self, session)
2180                         NumericalTextInput.__init__(self)
2181                         HelpableScreen.__init__(self)
2182                 
2183                         # TRANSLATORS: keep it short, this is a button
2184                         self["key_red"] = Button(_("Delete"))
2185                         # TRANSLATORS: keep it short, this is a button
2186                         self["key_green"] = Button(_("New"))
2187                         # TRANSLATORS: keep it short, this is a button
2188                         self["key_yellow"] = Button(_("Edit"))
2189                         # TRANSLATORS: keep it short, this is a button
2190                         self["key_blue"] = Button(_("Search"))
2191         
2192                         self["setupActions"] = ActionMap(["OkCancelActions", "ColorActions"],
2193                         {
2194                                 "red": self.delete,
2195                                 "green": self.add,
2196                                 "yellow": self.edit,
2197                                 "blue": self.search,
2198                                 "cancel": self.exit,
2199                                 "ok": self.showEntry, }, - 2)
2200         
2201                         # TRANSLATORS: keep it short, this is a help text
2202                         self.helpList.append((self["setupActions"], "OkCancelActions", [("ok", _("Show details of entry"))]))
2203                         # TRANSLATORS: keep it short, this is a help text
2204                         self.helpList.append((self["setupActions"], "OkCancelActions", [("cancel", _("Quit"))]))
2205                         # TRANSLATORS: keep it short, this is a help text
2206                         self.helpList.append((self["setupActions"], "ColorActions", [("red", _("Delete entry"))]))
2207                         # TRANSLATORS: keep it short, this is a help text
2208                         self.helpList.append((self["setupActions"], "ColorActions", [("green", _("Add entry to phonebook"))]))
2209                         # TRANSLATORS: keep it short, this is a help text
2210                         self.helpList.append((self["setupActions"], "ColorActions", [("yellow", _("Edit selected entry"))]))
2211                         # TRANSLATORS: keep it short, this is a help text
2212                         self.helpList.append((self["setupActions"], "ColorActions", [("blue", _("Search (case insensitive)"))]))
2213         
2214                         self["entries"] = MenuList([], True, content=eListboxPythonMultiContent)
2215                         fontSize = scaleV(22, 18)
2216                         fontHeight = scaleV(24, 20)
2217                         self["entries"].l.setFont(0, gFont("Regular", fontSize))
2218                         self["entries"].l.setItemHeight(fontHeight)
2219                         debug("[FritzCallPhonebook] displayPhonebook init")
2220                         self.display()
2221         
2222                 def display(self, filter=""):
2223                         debug("[FritzCallPhonebook] displayPhonebook/display")
2224                         self.sortlist = []
2225                         # Beware: strings in phonebook.phonebook are utf-8!
2226                         sortlistHelp = sorted((name.lower(), name, number) for (number, name) in phonebook.phonebook.iteritems())
2227                         for (low, name, number) in sortlistHelp:
2228                                 if number == "01234567890":
2229                                         continue
2230                                 try:
2231                                         low = low.decode("utf-8")
2232                                 except (UnicodeDecodeError, UnicodeEncodeError):  # this should definitely not happen
2233                                         try:
2234                                                 low = low.decode("iso-8859-1")
2235                                         except:
2236                                                 debug("[FritzCallPhonebook] displayPhonebook/display: corrupt phonebook entry for %s" % number)
2237                                                 # self.session.open(MessageBox, _("Corrupt phonebook entry\nfor number %s\nDeleting.") %number, type = MessageBox.TYPE_ERROR)
2238                                                 phonebook.remove(number)
2239                                                 continue
2240                                 else:
2241                                         if filter:
2242                                                 filter = filter.lower()
2243                                                 if low.find(filter) == - 1:
2244                                                         continue
2245                                         name = name.strip().decode("utf-8")
2246                                         number = number.strip().decode("utf-8")
2247                                         found = re.match("([^,]*),.*", name)   # strip address information from the name part
2248                                         if found:
2249                                                 shortname = found.group(1)
2250                                         else:
2251                                                 shortname = name
2252                                         numberFieldWidth = scaleV(200,150)
2253                                         fieldWidth = self.entriesWidth -5 -numberFieldWidth -10 -scaleH(90,45)
2254                                         number = number.encode("utf-8", "replace")
2255                                         name = name.encode("utf-8", "replace")
2256                                         shortname = shortname.encode('utf-8', 'replace')
2257                                         self.sortlist.append([(number, name),
2258                                                                    (eListboxPythonMultiContent.TYPE_TEXT, 0, 0, fieldWidth, scaleH(24,20), 0, RT_HALIGN_LEFT, shortname),
2259                                                                    (eListboxPythonMultiContent.TYPE_TEXT, fieldWidth +5, 0, numberFieldWidth, scaleH(24,20), 0, RT_HALIGN_LEFT, number)
2260                                                                    ])
2261                                 
2262                         self["entries"].setList(self.sortlist)
2263         
2264                 def showEntry(self):
2265                         cur = self["entries"].getCurrent()
2266                         if cur and cur[0]:
2267                                 debug("[FritzCallPhonebook] displayPhonebook/showEntry (%s,%s)" % (cur[0][0], cur[0][1]))
2268                                 number = cur[0][0]
2269                                 name = cur[0][1]
2270                                 self.session.open(FritzOfferAction, self, number, name)
2271         
2272                 def delete(self):
2273                         cur = self["entries"].getCurrent()
2274                         if cur and cur[0]:
2275                                 debug("[FritzCallPhonebook] displayPhonebook/delete " + cur[0][0])
2276                                 self.session.openWithCallback(
2277                                         self.deleteConfirmed,
2278                                         MessageBox,
2279                                         _("Do you really want to delete entry for\n\n%(number)s\n\n%(name)s?") 
2280                                         % { 'number':str(cur[0][0]), 'name':str(cur[0][1]).replace(", ", "\n") }
2281                                                                 )
2282                         else:
2283                                 self.session.open(MessageBox, _("No entry selected"), MessageBox.TYPE_INFO)
2284         
2285                 def deleteConfirmed(self, ret):
2286                         debug("[FritzCallPhonebook] displayPhonebook/deleteConfirmed")
2287                         #
2288                         # if ret: delete number from sortlist, delete number from phonebook.phonebook and write it to disk
2289                         #
2290                         cur = self["entries"].getCurrent()
2291                         if cur:
2292                                 if ret:
2293                                         # delete number from sortlist, delete number from phonebook.phonebook and write it to disk
2294                                         debug("[FritzCallPhonebook] displayPhonebook/deleteConfirmed: remove " + cur[0][0])
2295                                         phonebook.remove(cur[0][0])
2296                                         self.display()
2297                                 # else:
2298                                         # self.session.open(MessageBox, _("Not deleted."), MessageBox.TYPE_INFO)
2299                         else:
2300                                 self.session.open(MessageBox, _("No entry selected"), MessageBox.TYPE_INFO)
2301         
2302                 def add(self, parent=None, number="", name=""):
2303                         class addScreen(Screen, ConfigListScreen):
2304                                 '''ConfiglistScreen with two ConfigTexts for Name and Number'''
2305         
2306                                 def __init__(self, session, parent, number="", name=""):
2307                                         #
2308                                         # setup screen with two ConfigText and OK and ABORT button
2309                                         # 
2310                                         noButtons = 2
2311                                         width = max(scaleH(-1,570), noButtons*140)
2312                                         height = scaleV(-1,100) # = 5 + 126 + 40 + 5; 6 lines of text possible
2313                                         buttonsGap = (width-noButtons*140)/(noButtons+1)
2314                                         buttonsVPos = height-40-5
2315                                         # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
2316                                         self.skin = """
2317                                                 <screen position="%d,%d" size="%d,%d" title="%s" >
2318                                                 <widget name="config" position="5,5" size="%d,%d" scrollbarMode="showOnDemand" />
2319                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
2320                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
2321                                                 <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" />
2322                                                 <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" />
2323                                                 </screen>""" % (
2324                                                                                 (DESKTOP_WIDTH - width) / 2,
2325                                                                                 (DESKTOP_HEIGHT - height) / 2,
2326                                                                                 width,
2327                                                                                 height,
2328                                                                                 _("Add entry to phonebook"),
2329                                                                                 width - 5 - 5,
2330                                                                                 height - 5 - 40 - 5,
2331                                                                                 buttonsGap, buttonsVPos,
2332                                                                                 buttonsGap+140+buttonsGap, buttonsVPos,
2333                                                                                 buttonsGap, buttonsVPos,
2334                                                                                 buttonsGap+140+buttonsGap, buttonsVPos,
2335                                                                                 )
2336                                         Screen.__init__(self, session)
2337                                         self.session = session
2338                                         self.parent = parent
2339                                         # TRANSLATORS: keep it short, this is a button
2340                                         self["key_red"] = Button(_("Cancel"))
2341                                         # TRANSLATORS: keep it short, this is a button
2342                                         self["key_green"] = Button(_("OK"))
2343                                         self["setupActions"] = ActionMap(["SetupActions", "ColorActions"],
2344                                         {
2345                                                 "cancel": self.cancel,
2346                                                 "red": self.cancel,
2347                                                 "green": self.add,
2348                                                 "ok": self.add,
2349                                         }, - 2)
2350         
2351                                         self.list = [ ]
2352                                         ConfigListScreen.__init__(self, self.list, session=session)
2353                                         config.plugins.FritzCall.name.value = name
2354                                         config.plugins.FritzCall.number.value = number
2355                                         self.list.append(getConfigListEntry(_("Name"), config.plugins.FritzCall.name))
2356                                         self.list.append(getConfigListEntry(_("Number"), config.plugins.FritzCall.number))
2357                                         self["config"].list = self.list
2358                                         self["config"].l.setList(self.list)
2359         
2360                                 def add(self):
2361                                         # get texts from Screen
2362                                         # add (number,name) to sortlist and phonebook.phonebook and disk
2363                                         self.number = config.plugins.FritzCall.number.value
2364                                         self.name = config.plugins.FritzCall.name.value
2365                                         if not self.number or not self.name:
2366                                                 self.session.open(MessageBox, _("Entry incomplete."), type=MessageBox.TYPE_ERROR)
2367                                                 return
2368                                         # add (number,name) to sortlist and phonebook.phonebook and disk
2369         #                                       oldname = phonebook.search(self.number)
2370         #                                       if oldname:
2371         #                                               self.session.openWithCallback(
2372         #                                                       self.overwriteConfirmed,
2373         #                                                       MessageBox,
2374         #                                                       _("Do you really want to overwrite entry for %(number)s\n\n%(name)s\n\nwith\n\n%(newname)s?")
2375         #                                                       % {
2376         #                                                       'number':self.number,
2377         #                                                       'name': oldname,
2378         #                                                       'newname':self.name.replace(", ","\n")
2379         #                                                       }
2380         #                                                       )
2381         #                                               self.close()
2382         #                                               return
2383                                         phonebook.add(self.number, self.name)
2384                                         self.close()
2385                                         self.parent.display()
2386         
2387                                 def overwriteConfirmed(self, ret):
2388                                         if ret:
2389                                                 phonebook.remove(self.number)
2390                                                 phonebook.add(self.number, self.name)
2391                                                 self.parent.display()
2392         
2393                                 def cancel(self):
2394                                         self.close()
2395         
2396                         debug("[FritzCallPhonebook] displayPhonebook/add")
2397                         if not parent:
2398                                 parent = self
2399                         self.session.open(addScreen, parent, number, name)
2400         
2401                 def edit(self):
2402                         debug("[FritzCallPhonebook] displayPhonebook/edit")
2403                         cur = self["entries"].getCurrent()
2404                         if cur is None:
2405                                 self.session.open(MessageBox, _("No entry selected"), MessageBox.TYPE_INFO)
2406                         else:
2407                                 (number, name) = cur[0]
2408                                 self.add(self, number, name)
2409         
2410                 def search(self):
2411                         debug("[FritzCallPhonebook] displayPhonebook/search")
2412                         self.help_window = self.session.instantiateDialog(NumericalTextInputHelpDialog, self)
2413                         self.help_window.show()
2414                         self.session.openWithCallback(self.doSearch, InputBox, _("Enter Search Terms"), _("Search phonebook"))
2415         
2416                 def doSearch(self, searchTerms):
2417                         if not searchTerms: searchTerms = ""
2418                         debug("[FritzCallPhonebook] displayPhonebook/doSearch: " + searchTerms)
2419                         if self.help_window:
2420                                 self.session.deleteDialog(self.help_window)
2421                                 self.help_window = None
2422                         self.display(searchTerms)
2423         
2424                 def exit(self):
2425                         self.close()
2426
2427 phonebook = FritzCallPhonebook()
2428
2429 class FritzCallSetup(Screen, ConfigListScreen, HelpableScreen):
2430
2431         def __init__(self, session, args=None): #@UnusedVariable
2432                 if config.plugins.FritzCall.fullscreen.value:
2433                         self.width = DESKTOP_WIDTH
2434                         self.height = DESKTOP_HEIGHT
2435                         backMainPng = ""
2436                         backMainLine = ""
2437                         if os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, DESKTOP_SKIN + "/menu/back-main.png")):
2438                                 backMainPng = DESKTOP_SKIN + "/menu/back-main.png"
2439                         elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1/menu/back-main.png")):
2440                                 backMainPng = "Kerni-HD1/menu/back-main.png"
2441                         elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1-picon/menu/back-main.png")):
2442                                 backMainPng = "Kerni-HD1-picon/menu/back-main.png"
2443                         if backMainPng:
2444                                 backMainLine = """<ePixmap position="0,0" zPosition="-10" size="%d,%d" pixmap="%s" transparent="1" />""" % (self.width, self.height, backMainPng)
2445                         else:
2446                                 backMainLine = ""
2447                         debug("[FritzCallSetup] backMainLine: " + backMainLine)
2448                         # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
2449                         self.skin = """
2450                                 <screen name="FritzCallSetup" position="0,0" size="%d,%d" title="%s" flags="wfNoBorder">
2451                                         %s
2452                                         <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
2453                                                 <convert type="ClockToText">Default</convert>
2454                                         </widget>
2455                                         <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
2456                                                 <convert type="ClockToText">Date</convert>
2457                                         </widget>
2458                                         <eLabel text="%s" position="%d,%d" size="%d,%d" font="Regular;%d" halign="center" backgroundColor="#0b67a2" transparent="1"/>
2459                         
2460                                         <widget name="consideration" position="%d,%d"  size="%d,%d" font="Regular;%d" halign="center" backgroundColor="#353e575e" transparent="1" />
2461                                         <widget name="config" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" transparent="1" />
2462                         
2463                                         <ePixmap pixmap="skin_default/buttons/red.png"          position="%d,%d"        size="%d,%d" alphatest="on" />
2464                                         <ePixmap pixmap="skin_default/buttons/green.png"        position="%d,%d"        size="%d,%d" alphatest="on" />
2465                                         <ePixmap pixmap="skin_default/buttons/yellow.png"       position="%d,%d"        size="%d,%d" alphatest="on" />
2466                                         <ePixmap pixmap="skin_default/buttons/blue.png"         position="%d,%d"        size="%d,%d" alphatest="on" />
2467                                         <ePixmap pixmap="skin_default/buttons/key_info.png"     position="%d,%d"        size="%d,%d" alphatest="on" />
2468                                         <ePixmap pixmap="skin_default/buttons/key_menu.png"     position="%d,%d"        size="%d,%d" alphatest="on" />
2469                                         <widget name="key_red" position="%d,%d"                 size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
2470                                         <widget name="key_green"  position="%d,%d"      size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
2471                                         <widget name="key_yellow" position="%d,%d"      size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
2472                                         <widget name="key_blue" position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
2473                                         <ePixmap position="%d,%d" size="%d,%d" zPosition="2" pixmap="%s" transparent="1" alphatest="blend" />           
2474                                 </screen>""" % (
2475                                                                 self.width, self.height, _("FritzCall Setup"),
2476                                                                 backMainLine,
2477                                                                 scaleH(1130, XXX), scaleV(40, XXX), scaleH(80, XXX), scaleV(26, XXX), scaleV(26, XXX), # time
2478                                                                 scaleH(890, XXX), scaleV(70, XXX), scaleH(320, XXX), scaleV(22, XXX), scaleV(20, XXX), # date
2479                                                                 _("FritzCall Setup"), scaleH(500, XXX), scaleV(63, XXX), scaleH(330, XXX), scaleV(30, XXX), scaleV(27, XXX), # eLabel
2480                                                                 scaleH(80, XXX), scaleV(150, XXX), scaleH(250, XXX), scaleV(200, XXX), scaleV(22, XXX), # consideration
2481                                                                 scaleH(420, XXX), scaleV(125, XXX), scaleH(790, XXX), scaleV(428, XXX), # config
2482                                                                 scaleH(150, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # red
2483                                                                 scaleH(350, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # green
2484                                                                 scaleH(550, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # yellow
2485                                                                 scaleH(750, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # blue
2486                                                                 scaleH(1050, XXX), scaleV(586, XXX), scaleH(35, XXX), scaleV(24, XXX), # info
2487                                                                 scaleH(1150, XXX), scaleV(586, XXX), scaleH(35, XXX), scaleV(24, XXX), # menu
2488                                                                 scaleH(175, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # red
2489                                                                 scaleH(375, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # green
2490                                                                 scaleH(575, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # yellow
2491                                                                 scaleH(775, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # blue
2492                                                                 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
2493                                                                                                                                 ) 
2494                 else:
2495                         self.width = scaleH(20+4*(140+90)+2*(35+40)+20, 4*140+2*35)
2496                         width = self.width
2497                         debug("[FritzCallSetup] width: " + str(self.width))
2498                         # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
2499                         self.skin = """
2500                                 <screen name="FritzCallSetup" position="%d,%d" size="%d,%d" title="%s" >
2501                                 <eLabel position="0,0" size="%d,2" backgroundColor="#aaaaaa" />
2502                                 <widget name="consideration" position="%d,%d" halign="center" size="%d,%d" font="Regular;%d" backgroundColor="#20040404" transparent="1" />
2503                                 <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
2504                                 <widget name="config" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" backgroundColor="#20040404" transparent="1" />
2505                                 <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
2506                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
2507                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
2508                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
2509                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
2510                                 <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" />
2511                                 <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" />
2512                                 <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" />
2513                                 <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" />
2514                                 <ePixmap position="%d,%d" zPosition="4" size="35,25" pixmap="skin_default/buttons/key_info.png" transparent="1" alphatest="on" />
2515                                 <ePixmap position="%d,%d" zPosition="4" size="35,25" pixmap="skin_default/buttons/key_menu.png" transparent="1" alphatest="on" />
2516                                 </screen>""" % (
2517                                                         (DESKTOP_WIDTH-width)/2, scaleV(100, 73), # position 
2518                                                         width, scaleV(560, 430), # size
2519                                                         _("FritzCall Setup") + 
2520                                                         " (" + "$Revision$"[1: - 1] + 
2521                                                         "$Date$"[7:23] + ")",
2522                                                         width, # eLabel width
2523                                                         scaleH(40, 20), scaleV(10, 5), # consideration position
2524                                                         scaleH(width-80, width-40), scaleV(25, 45), # consideration size
2525                                                         scaleV(22, 20), # consideration font size
2526                                                         scaleV(40, 50), # eLabel position vertical
2527                                                         width, # eLabel width
2528                                                         scaleH(40, 5), scaleV(60, 57), # config position
2529                                                         scaleH(width-80, width-10), scaleV(453, 328), # config size
2530                                                         scaleV(518, 390), # eLabel position vertical
2531                                                         width, # eLabel width
2532                                                         scaleH(20, 0), scaleV(525, 395), # pixmap red
2533                                                         scaleH(20+140+90, 140), scaleV(525, 395), # pixmap green
2534                                                         scaleH(20+2*(140+90), 2*140), scaleV(525, 395), # pixmap yellow
2535                                                         scaleH(20+3*(140+90), 3*140), scaleV(525, 395), # pixmap blue
2536                                                         scaleH(20, 0), scaleV(525, 395), scaleV(21, 21), # pixmap red
2537                                                         scaleH(20+(140+90), 140), scaleV(525, 395), scaleV(21, 21), # widget green
2538                                                         scaleH(20+2*(140+90), 2*140), scaleV(525, 395), scaleV(21, 21), # widget yellow
2539                                                         scaleH(20+3*(140+90), 3*140), scaleV(525, 395), scaleV(21, 21), # widget blue
2540                                                         scaleH(20+4*(140+90), 4*140), scaleV(532, 402), # button info
2541                                                         scaleH(20+4*(140+90)+(35+40), 4*140+35), scaleV(532, 402) # button menu
2542                                                                                                                 )
2543
2544                 Screen.__init__(self, session)
2545                 HelpableScreen.__init__(self)
2546                 self.session = session
2547
2548                 self["consideration"] = Label(_("You need to enable the monitoring on your FRITZ!Box by dialing #96*5*!"))
2549                 self.list = []
2550
2551                 # Initialize Buttons
2552                 # TRANSLATORS: keep it short, this is a button
2553                 self["key_red"] = Button(_("Cancel"))
2554                 # TRANSLATORS: keep it short, this is a button
2555                 self["key_green"] = Button(_("OK"))
2556                 # TRANSLATORS: keep it short, this is a button
2557                 self["key_yellow"] = Button(_("Phone calls"))
2558                 # TRANSLATORS: keep it short, this is a button
2559                 self["key_blue"] = Button(_("Phonebook"))
2560                 # TRANSLATORS: keep it short, this is a button
2561                 self["key_info"] = Button(_("About FritzCall"))
2562                 # TRANSLATORS: keep it short, this is a button
2563                 self["key_menu"] = Button(_("FRITZ!Box Fon Status"))
2564
2565                 self["setupActions"] = ActionMap(["ColorActions", "OkCancelActions", "MenuActions", "EPGSelectActions"],
2566                 {
2567                         "red": self.cancel,
2568                         "green": self.save,
2569                         "yellow": self.displayCalls,
2570                         "blue": self.displayPhonebook,
2571                         "cancel": self.cancel,
2572                         "ok": self.save,
2573                         "menu": self.menu,
2574                         "info": self.about,
2575                 }, - 2)
2576
2577                 # TRANSLATORS: keep it short, this is a help text
2578                 self.helpList.append((self["setupActions"], "ColorActions", [("red", _("quit"))]))
2579                 # TRANSLATORS: keep it short, this is a help text
2580                 self.helpList.append((self["setupActions"], "ColorActions", [("green", _("save and quit"))]))
2581                 # TRANSLATORS: keep it short, this is a help text
2582                 self.helpList.append((self["setupActions"], "ColorActions", [("yellow", _("display calls"))]))
2583                 # TRANSLATORS: keep it short, this is a help text
2584                 self.helpList.append((self["setupActions"], "ColorActions", [("blue", _("display phonebook"))]))
2585                 # TRANSLATORS: keep it short, this is a help text
2586                 self.helpList.append((self["setupActions"], "OkCancelActions", [("ok", _("save and quit"))]))
2587                 # TRANSLATORS: keep it short, this is a help text
2588                 self.helpList.append((self["setupActions"], "OkCancelActions", [("cancel", _("quit"))]))
2589                 # TRANSLATORS: keep it short, this is a help text
2590                 self.helpList.append((self["setupActions"], "MenuActions", [("menu", _("FRITZ!Box Fon Status"))]))
2591                 # TRANSLATORS: keep it short, this is a help text
2592                 self.helpList.append((self["setupActions"], "EPGSelectActions", [("info", _("About FritzCall"))]))
2593
2594                 ConfigListScreen.__init__(self, self.list, session=session)
2595                 self.createSetup()
2596
2597
2598         def keyLeft(self):
2599                 ConfigListScreen.keyLeft(self)
2600                 self.createSetup()
2601
2602         def keyRight(self):
2603                 ConfigListScreen.keyRight(self)
2604                 self.createSetup()
2605
2606         def createSetup(self):
2607                 self.list = [ ]
2608                 self.list.append(getConfigListEntry(_("Call monitoring"), config.plugins.FritzCall.enable))
2609                 if config.plugins.FritzCall.enable.value:
2610                         self.list.append(getConfigListEntry(_("Mute on call"), config.plugins.FritzCall.muteOnCall))
2611                         self.list.append(getConfigListEntry(_("FRITZ!Box FON address (Name or IP)"), config.plugins.FritzCall.hostname))
2612
2613                         self.list.append(getConfigListEntry(_("Show after Standby"), config.plugins.FritzCall.afterStandby))
2614
2615                         self.list.append(getConfigListEntry(_("Show Calls for specific MSN"), config.plugins.FritzCall.filter))
2616                         if config.plugins.FritzCall.filter.value:
2617                                 self.list.append(getConfigListEntry(_("MSN to show (separated by ,)"), config.plugins.FritzCall.filtermsn))
2618
2619                         self.list.append(getConfigListEntry(_("Show Outgoing Calls"), config.plugins.FritzCall.showOutgoing))
2620                         if config.plugins.FritzCall.showOutgoing.value:
2621                                 self.list.append(getConfigListEntry(_("Areacode to add to Outgoing Calls (if necessary)"), config.plugins.FritzCall.prefix))
2622                         self.list.append(getConfigListEntry(_("Timeout for Call Notifications (seconds)"), config.plugins.FritzCall.timeout))
2623                         self.list.append(getConfigListEntry(_("Reverse Lookup Caller ID (select country below)"), config.plugins.FritzCall.lookup))
2624                         if config.plugins.FritzCall.lookup.value:
2625                                 self.list.append(getConfigListEntry(_("Country"), config.plugins.FritzCall.country))
2626
2627                         self.list.append(getConfigListEntry(_("Password Accessing FRITZ!Box"), config.plugins.FritzCall.password))
2628                         self.list.append(getConfigListEntry(_("Extension number to initiate call on"), config.plugins.FritzCall.extension))
2629                         self.list.append(getConfigListEntry(_("Read PhoneBook from FRITZ!Box"), config.plugins.FritzCall.fritzphonebook))
2630                         if config.plugins.FritzCall.fritzphonebook.value:
2631                                 self.list.append(getConfigListEntry(_("Append type of number"), config.plugins.FritzCall.showType))
2632                                 self.list.append(getConfigListEntry(_("Append shortcut number"), config.plugins.FritzCall.showShortcut))
2633                                 self.list.append(getConfigListEntry(_("Append vanity name"), config.plugins.FritzCall.showVanity))
2634
2635                         self.list.append(getConfigListEntry(_("Use internal PhoneBook"), config.plugins.FritzCall.phonebook))
2636                         if config.plugins.FritzCall.phonebook.value:
2637                                 self.list.append(getConfigListEntry(_("PhoneBook Location"), config.plugins.FritzCall.phonebookLocation))
2638                                 if config.plugins.FritzCall.lookup.value:
2639                                         self.list.append(getConfigListEntry(_("Automatically add new Caller to PhoneBook"), config.plugins.FritzCall.addcallers))
2640
2641                         self.list.append(getConfigListEntry(_("Strip Leading 0"), config.plugins.FritzCall.internal))
2642                         # self.list.append(getConfigListEntry(_("Default display mode for FRITZ!Box calls"), config.plugins.FritzCall.fbfCalls))
2643                         if DESKTOP_WIDTH == 1280 and DESKTOP_HEIGHT == 720:
2644                                 self.list.append(getConfigListEntry(_("Full screen display"), config.plugins.FritzCall.fullscreen))
2645                         self.list.append(getConfigListEntry(_("Debug"), config.plugins.FritzCall.debug))
2646
2647                 self["config"].list = self.list
2648                 self["config"].l.setList(self.list)
2649
2650         def save(self):
2651 #               debug("[FritzCallSetup] save"
2652                 for x in self["config"].list:
2653                         x[1].save()
2654                 if fritz_call:
2655                         fritz_call.connect()
2656                 self.close()
2657
2658         def cancel(self):
2659 #               debug("[FritzCallSetup] cancel"
2660                 for x in self["config"].list:
2661                         x[1].cancel()
2662                 self.close()
2663
2664         def displayCalls(self):
2665                 self.session.open(FritzDisplayCalls)
2666
2667         def displayPhonebook(self):
2668                 self.session.open(phonebook.FritzDisplayPhonebook)
2669
2670         def about(self):
2671                 self.session.open(FritzAbout)
2672
2673         def menu(self):
2674                 self.session.open(FritzMenu)
2675
2676 standbyMode = False
2677
2678 class FritzCallList:
2679         def __init__(self):
2680                 self.callList = [ ]
2681         
2682         def add(self, event, date, number, caller, phone):
2683                 debug("[FritzCallList] add")
2684                 if len(self.callList) > 10:
2685                         if self.callList[0] != "Start":
2686                                 self.callList[0] = "Start"
2687                         del self.callList[1]
2688
2689                 self.callList.append((event, number, date, caller, phone))
2690         
2691         def display(self):
2692                 debug("[FritzCallList] display")
2693                 global standbyMode
2694                 global my_global_session
2695                 standbyMode = False
2696                 # Standby.inStandby.onClose.remove(self.display) object does not exist anymore...
2697                 # build screen from call list
2698                 text = "\n"
2699                 if self.callList[0] == "Start":
2700                         text = text + _("Last 10 calls:\n")
2701                         del self.callList[0]
2702
2703                 for call in self.callList:
2704                         (event, number, date, caller, phone) = call
2705                         if event == "RING":
2706                                 direction = "->"
2707                         else:
2708                                 direction = "<-"
2709
2710                         # shorten the date info
2711                         found = re.match(".*(\d\d.\d\d.)\d\d( \d\d:\d\d)", date)
2712                         if found: date = found.group(1) + found.group(2)
2713
2714                         # our phone could be of the form "0123456789 (home)", then we only take "home"
2715                         found = re.match(".*\((.*)\)", phone)
2716                         if found: phone = found.group(1)
2717
2718                         #  if we have an unknown number, show the number
2719                         if caller == _("UNKNOWN") and number != "":
2720                                 caller = number
2721                         else:
2722                                 # strip off the address part of the remote number, if there is any
2723                                 found = re.match("(.*)\n.*", caller)
2724                                 if found: caller = found.group(1)
2725
2726                         while (len(caller) + len(phone)) > 40:
2727                                 if len(caller) > len(phone):
2728                                         caller = caller[: - 1]
2729                                 else:
2730                                         phone = phone[: - 1]
2731
2732                         text = text + "%s %s %s %s\n" % (date, caller, direction, phone)
2733
2734                 debug("[FritzCallList] display: '%s %s %s %s'" % (date, caller, direction, phone))
2735                 # display screen
2736                 Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_INFO)
2737                 # my_global_session.open(FritzDisplayCalls, text) # TODO please HELP: from where can I get a session?
2738                 self.callList = [ ]
2739                 self.text = ""
2740
2741 callList = FritzCallList()
2742
2743 def findFace(number,name):
2744         debug("[FritzCall] findFace number/name: %s/%s" % (number, name))
2745         sep = name.find(',')
2746         if sep != -1: name = name[:sep]
2747         sep = name.find('\n')
2748         if sep != -1: name = name[:sep]
2749
2750         #===========================================================================
2751         # facesFile = ""
2752         # for filename in os.listdir(os.path.join(config.plugins.FritzCall.phonebookLocation.value, "FritzCallFaces")):
2753         #       debug("[FritzCall] findFace check: %s name: %s" % (repr(filename.decode('utf-8')), repr(name.decode('utf-8'))))
2754         #       if re.match("^" + number + "\.(png|PNG|jpg|JPG)$", filename) or re.match("^" + name.decode('utf-8') + "\.(png|PNG|jpg|JPG)$", filename.decode('utf-8')):
2755         #               facesFile = os.path.join(config.plugins.FritzCall.phonebookLocation.value, "FritzCallFaces", filename)
2756         #               break
2757         # else:
2758         #       facesFile = resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/icons/input_info.png")
2759         #===========================================================================
2760         facesDir = os.path.join(config.plugins.FritzCall.phonebookLocation.value, "FritzCallFaces")
2761         numberFile = os.path.join(facesDir, number)
2762         nameFile = os.path.join(facesDir, name)
2763         facesFile = ""
2764         if number and os.path.exists(numberFile):
2765                 facesFile = numberFile
2766         elif number and os.path.exists(numberFile + ".png"):
2767                 facesFile = numberFile + ".png"
2768         elif number and os.path.exists(numberFile + ".PNG"):
2769                 facesFile = numberFile + ".PNG"
2770         elif name and os.path.exists(nameFile + ".png"):
2771                 facesFile = nameFile + ".png"
2772         elif name and os.path.exists(nameFile + ".PNG"):
2773                 facesFile = nameFile + ".PNG"
2774         else:
2775                 facesFile = resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/icons/input_info.png")
2776
2777         debug("[FritzCall] findFace result: %s" % (facesFile))
2778         return facesFile
2779
2780 class MessageBoxPixmap(Screen):
2781         def __init__(self, session, text, number = "", name = "", timeout = -1):
2782                 self.skin = """
2783         <screen name="MessageBoxPixmap" position="%d,%d" size="600,10" title="Message">
2784                 <widget name="text" position="115,8" size="520,0" font="Regular;%d" />
2785                 <widget name="InfoPixmap" pixmap="skin_default/icons/input_info.png" position="5,5" size="100,100" alphatest="on" />
2786         </screen>
2787                 """ %(
2788                         scaleH(350,60), scaleV(175,245),
2789                         scaleV(24,22)
2790                         )
2791                 debug("[FritzCall] MessageBoxPixmap number: %s" % number)
2792                 Screen.__init__(self, session)
2793                 # MessageBox.__init__(self, session, text, type=MessageBox.TYPE_INFO, timeout=timeout)
2794                 self["text"] = Label(text)
2795                 self["InfoPixmap"] = Pixmap()
2796                 self.number = number
2797                 self.name = name
2798                 self.timerRunning = False
2799                 self.initTimeout(timeout)
2800                 self.onLayoutFinish.append(self.finishLayout)
2801                 self["actions"] = ActionMap(["OkCancelActions"],
2802                 {
2803                         "cancel": self.exit,
2804                         "ok": self.exit, }, - 2)
2805
2806         def finishLayout(self):
2807                 debug("[FritzCall] MessageBoxPixmap/setInfoPixmap number: %s/%s" % (self.number, self.name))
2808
2809                 picPixmap = LoadPixmap(findFace(self.number, self.name))
2810                 picSize = picPixmap.size()
2811                 self["InfoPixmap"].instance.setPixmap(picPixmap)
2812
2813                 # recalculate window size
2814                 textSize = self["text"].getSize()
2815                 textSize = (textSize[0]+20, textSize[1]+20) # don't know, why, but size is too small
2816                 textSize = eSize(*textSize)
2817                 width = max(scaleH(600,280), picSize.width() + textSize.width() + 30)
2818                 height = max(scaleV(300,250), picSize.height()+10, textSize.height()+10)
2819                 wSize = (width, height)
2820                 wSize = eSize(*wSize)
2821
2822                 # center the smaller vertically
2823                 hGap = (width-picSize.width()-textSize.width())/3
2824                 picPos = (hGap,(height-picSize.height())/2+1)
2825                 textPos = (hGap+picSize.width()+hGap, (height-textSize.height())/2+1)
2826
2827                 # resize screen
2828                 self.instance.resize(wSize)
2829                 # resize text
2830                 self["text"].instance.resize(textSize)
2831                 # resize pixmap
2832                 self["InfoPixmap"].instance.resize(picSize)
2833                 # move text
2834                 self["text"].instance.move(ePoint(*textPos))
2835                 # move pixmap
2836                 self["InfoPixmap"].instance.move(ePoint(*picPos))
2837                 # center window
2838                 self.instance.move(ePoint((DESKTOP_WIDTH-wSize.width())/2, (DESKTOP_HEIGHT-wSize.height())/2))
2839
2840         def initTimeout(self, timeout):
2841                 self.timeout = timeout
2842                 if timeout > 0:
2843                         self.timer = eTimer()
2844                         self.timer.callback.append(self.timerTick)
2845                         self.onExecBegin.append(self.startTimer)
2846                         self.origTitle = None
2847                         if self.execing:
2848                                 self.timerTick()
2849                         else:
2850                                 self.onShown.append(self.__onShown)
2851                         self.timerRunning = True
2852                 else:
2853                         self.timerRunning = False
2854
2855         def __onShown(self):
2856                 self.onShown.remove(self.__onShown)
2857                 self.timerTick()
2858
2859         def startTimer(self):
2860                 self.timer.start(1000)
2861
2862 #===============================================================================
2863 #       def stopTimer(self):
2864 #               if self.timerRunning:
2865 #                       del self.timer
2866 #                       self.setTitle(self.origTitle)
2867 #                       self.timerRunning = False
2868 #===============================================================================
2869
2870         def timerTick(self):
2871                 if self.execing:
2872                         self.timeout -= 1
2873                         if self.origTitle is None:
2874                                 self.origTitle = self.instance.getTitle()
2875                         self.setTitle(self.origTitle + " (" + str(self.timeout) + ")")
2876                         if self.timeout == 0:
2877                                 self.timer.stop()
2878                                 self.timerRunning = False
2879                                 self.exit()
2880
2881         def exit(self):
2882                 self.close()
2883
2884
2885 from GlobalActions import globalActionMap
2886 def notifyCall(event, date, number, caller, phone):
2887         if Standby.inStandby is None or config.plugins.FritzCall.afterStandby.value == "each":
2888                 if config.plugins.FritzCall.muteOnCall.value:
2889                         globalActionMap.actions["volumeMute"]()
2890                 if event == "RING":
2891                         text = _("Incoming Call on %(date)s from\n---------------------------------------------\n%(number)s\n%(caller)s\n---------------------------------------------\nto: %(phone)s") % { 'date':date, 'number':number, 'caller':caller, 'phone':phone }
2892                 else:
2893                         text = _("Outgoing Call on %(date)s to\n---------------------------------------------\n%(number)s\n%(caller)s\n---------------------------------------------\nfrom: %(phone)s") % { 'date':date, 'number':number, 'caller':caller, 'phone':phone }
2894                 debug("[FritzCall] notifyCall:\n%s" % text)
2895                 # Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_INFO, timeout=config.plugins.FritzCall.timeout.value)
2896                 Notifications.AddNotification(MessageBoxPixmap, text, number=number, name=caller, timeout=config.plugins.FritzCall.timeout.value)
2897         elif config.plugins.FritzCall.afterStandby.value == "inList":
2898                 #
2899                 # if not yet done, register function to show call list
2900                 global standbyMode
2901                 if not standbyMode :
2902                         standbyMode = True
2903                         Standby.inStandby.onHide.append(callList.display) #@UndefinedVariable
2904                 # add text/timeout to call list
2905                 callList.add(event, date, number, caller, phone)
2906                 debug("[FritzCall] notifyCall: added to callList")
2907         else: # this is the "None" case
2908                 debug("[FritzCall] notifyCall: standby and no show")
2909
2910
2911 #===============================================================================
2912 #               We need a separate class for each invocation of reverseLookup to retain
2913 #               the necessary data for the notification
2914 #===============================================================================
2915
2916 countries = { }
2917 reverselookupMtime = 0
2918
2919 class FritzReverseLookupAndNotifier:
2920         def __init__(self, event, number, caller, phone, date):
2921                 '''
2922                 
2923                 Initiate a reverse lookup for the given number in the configured country
2924                 
2925                 @param event: CALL or RING
2926                 @param number: number to be looked up
2927                 @param caller: caller including name and address
2928                 @param phone: Number (and name) of or own phone
2929                 @param date: date of call
2930                 '''
2931                 debug("[FritzReverseLookupAndNotifier] reverse Lookup for %s!" % number)
2932                 self.event = event
2933                 self.number = number
2934                 self.caller = caller
2935                 self.phone = phone
2936                 self.date = date
2937
2938                 if number[0] != "0":
2939                         self.notifyAndReset(number, caller)
2940                         return
2941
2942                 ReverseLookupAndNotifier(number, self.notifyAndReset, "UTF-8", config.plugins.FritzCall.country.value)
2943
2944         def notifyAndReset(self, number, caller):
2945                 '''
2946                 
2947                 this gets called with the result of the reverse lookup
2948                 
2949                 @param number: number
2950                 @param caller: name and address of remote. it comes in with name, address and city separated by commas
2951                 '''
2952                 debug("[FritzReverseLookupAndNotifier] got: " + caller)
2953                 self.number = number
2954 #===============================================================================
2955 #               if not caller and os.path.exists(config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.csv"):
2956 #                       caller = FritzOutlookCSV.findNumber(number, config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.csv") #@UndefinedVariable
2957 #                       debug("[FritzReverseLookupAndNotifier] got from Outlook csv: " + caller)
2958 #===============================================================================
2959 #===============================================================================
2960 #               if not caller and os.path.exists(config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.ldif"):
2961 #                       caller = FritzLDIF.findNumber(number, open(config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.ldif"))
2962 #                       debug("[FritzReverseLookupAndNotifier] got from ldif: " + caller)
2963 #===============================================================================
2964
2965                 name = handleReverseLookupResult(caller)
2966                 if name:
2967                         self.caller = name.replace(", ", "\n").replace('#','')
2968                         if self.number != 0 and config.plugins.FritzCall.addcallers.value and self.event == "RING":
2969                                 debug("[FritzReverseLookupAndNotifier] add to phonebook")
2970                                 phonebook.add(self.number, self.caller)
2971                 else:
2972                         self.caller = resolveNumberWithAvon(self.number, config.plugins.FritzCall.country.value)
2973                         if not self.caller:
2974                                 self.caller = _("UNKNOWN")
2975                 notifyCall(self.event, self.date, self.number, self.caller, self.phone)
2976                 # kill that object...
2977
2978 class FritzProtocol(LineReceiver):
2979         def __init__(self):
2980                 debug("[FritzProtocol] " + "$Revision$"[1:-1]   + "$Date$"[7:23] + " starting")
2981                 self.resetValues()
2982
2983         def resetValues(self):
2984                 debug("[FritzProtocol] resetValues")
2985                 self.number = '0'
2986                 self.caller = None
2987                 self.phone = None
2988                 self.date = '0'
2989
2990         def notifyAndReset(self):
2991                 notifyCall(self.event, self.date, self.number, self.caller, self.phone)
2992                 self.resetValues()
2993
2994         def lineReceived(self, line):
2995                 debug("[FritzProtocol] lineReceived: %s" % line)
2996 #15.07.06 00:38:54;CALL;1;4;<from/extern>;<to/our msn>;
2997 #15.07.06 00:38:58;DISCONNECT;1;0;
2998 #15.07.06 00:39:22;RING;0;<from/extern>;<to/our msn>;
2999 #15.07.06 00:39:27;DISCONNECT;0;0;
3000                 a = line.split(';')
3001                 (self.date, self.event) = a[0:2]
3002
3003                 if self.event == "RING" or (self.event == "CALL" and config.plugins.FritzCall.showOutgoing.value):
3004                         phone = a[4]
3005
3006                         if self.event == "RING":
3007                                 number = a[3] 
3008                         else:
3009                                 number = a[5]
3010                                 
3011                         debug("[FritzProtocol] lineReceived phone: '''%s''' number: '''%s'''" % (phone, number))
3012
3013                         filtermsns = config.plugins.FritzCall.filtermsn.value.split(",")
3014                         for i in range(len(filtermsns)):
3015                                 filtermsns[i] = filtermsns[i].strip()
3016                         if not (config.plugins.FritzCall.filter.value and phone not in filtermsns):
3017                                 debug("[FritzProtocol] lineReceived no filter hit")
3018                                 phonename = phonebook.search(phone)                # do we have a name for the number of our side?
3019                                 if phonename is not None:
3020                                         self.phone = "%s (%s)" % (phone, phonename)
3021                                 else:
3022                                         self.phone = phone
3023
3024                                 if config.plugins.FritzCall.internal.value and len(number) > 3 and number[0] == "0":
3025                                         debug("[FritzProtocol] lineReceived: strip leading 0")
3026                                         self.number = number[1:]
3027                                 else:
3028                                         self.number = number
3029                                         if self.event == "CALL" and self.number[0] != '0':                                        # should only happen for outgoing
3030                                                 debug("[FritzProtocol] lineReceived: add local prefix")
3031                                                 self.number = config.plugins.FritzCall.prefix.value + self.number
3032
3033                                 # strip CbC prefixes
3034                                 if self.event == "CALL":
3035                                         number = stripCbCPrefix(self.number, config.plugins.FritzCall.country.value)
3036
3037                                 if self.number is not "":
3038                                         debug("[FritzProtocol] lineReceived phonebook.search: %s" % self.number)
3039                                         self.caller = phonebook.search(self.number)
3040                                         debug("[FritzProtocol] lineReceived phonebook.search reault: %s" % self.caller)
3041                                         if (self.caller is None) and config.plugins.FritzCall.lookup.value:
3042                                                 FritzReverseLookupAndNotifier(self.event, self.number, self.caller, self.phone, self.date)
3043                                                 return                                                  # reverselookup is supposed to handle the message itself 
3044
3045                                 if self.caller is None:
3046                                         self.caller = _("UNKNOWN")
3047
3048                                 self.notifyAndReset()
3049
3050 class FritzClientFactory(ReconnectingClientFactory):
3051         initialDelay = 20
3052         maxDelay = 30
3053
3054         def __init__(self):
3055                 self.hangup_ok = False
3056
3057         def startedConnecting(self, connector): #@UnusedVariable
3058                 Notifications.AddNotification(MessageBox, _("Connecting to FRITZ!Box..."), type=MessageBox.TYPE_INFO, timeout=2)
3059
3060         def buildProtocol(self, addr): #@UnusedVariable
3061                 global fritzbox, phonebook
3062                 Notifications.AddNotification(MessageBox, _("Connected to FRITZ!Box!"), type=MessageBox.TYPE_INFO, timeout=4)
3063                 self.resetDelay()
3064                 initDebug()
3065                 initCbC()
3066                 initAvon()
3067                 fritzbox = FritzCallFBF()
3068                 phonebook = FritzCallPhonebook()
3069                 return FritzProtocol()
3070
3071         def clientConnectionLost(self, connector, reason):
3072                 if not self.hangup_ok:
3073                         Notifications.AddNotification(MessageBox, _("Connection to FRITZ!Box! lost\n (%s)\nretrying...") % reason.getErrorMessage(), type=MessageBox.TYPE_INFO, timeout=config.plugins.FritzCall.timeout.value)
3074                 ReconnectingClientFactory.clientConnectionLost(self, connector, reason)
3075
3076         def clientConnectionFailed(self, connector, reason):
3077                 Notifications.AddNotification(MessageBox, _("Connecting to FRITZ!Box failed\n (%s)\nretrying...") % reason.getErrorMessage(), type=MessageBox.TYPE_INFO, timeout=config.plugins.FritzCall.timeout.value)
3078                 ReconnectingClientFactory.clientConnectionFailed(self, connector, reason)
3079
3080 class FritzCall:
3081         def __init__(self):
3082                 self.dialog = None
3083                 self.d = None
3084                 self.connect()
3085
3086         def connect(self):
3087                 self.abort()
3088                 if config.plugins.FritzCall.enable.value:
3089                         f = FritzClientFactory()
3090                         self.d = (f, reactor.connectTCP(config.plugins.FritzCall.hostname.value, 1012, f)) #@UndefinedVariable
3091
3092         def shutdown(self):
3093                 self.abort()
3094
3095         def abort(self):
3096                 if self.d is not None:
3097                         self.d[0].hangup_ok = True
3098                         self.d[0].stopTrying()
3099                         self.d[1].disconnect()
3100                         self.d = None
3101
3102 def displayCalls(session, servicelist=None): #@UnusedVariable
3103         session.open(FritzDisplayCalls)
3104
3105 def displayPhonebook(session, servicelist=None): #@UnusedVariable
3106         session.open(phonebook.FritzDisplayPhonebook)
3107
3108 def displayFBFStatus(session, servicelist=None): #@UnusedVariable
3109         session.open(FritzMenu)
3110
3111 def main(session):
3112         session.open(FritzCallSetup)
3113
3114 fritz_call = None
3115
3116 def autostart(reason, **kwargs):
3117         global fritz_call
3118
3119         # ouch, this is a hack
3120         if kwargs.has_key("session"):
3121                 global my_global_session
3122                 my_global_session = kwargs["session"]
3123                 return
3124
3125         debug("[FRITZ!Call] - Autostart")
3126         if reason == 0:
3127                 fritz_call = FritzCall()
3128         elif reason == 1:
3129                 fritz_call.shutdown()
3130                 fritz_call = None
3131
3132 def Plugins(**kwargs): #@UnusedVariable
3133         what = _("Display FRITZ!box-Fon calls on screen")
3134         what_calls = _("Phone calls")
3135         what_phonebook = _("Phonebook")
3136         what_status = _("FRITZ!Box Fon Status")
3137         return [ PluginDescriptor(name="FritzCall", description=what, where=PluginDescriptor.WHERE_PLUGINMENU, icon="plugin.png", fnc=main),
3138                 PluginDescriptor(name=what_calls, description=what_calls, where=PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=displayCalls),
3139                 PluginDescriptor(name=what_phonebook, description=what_phonebook, where=PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=displayPhonebook),
3140                 PluginDescriptor(name=what_status, description=what_status, where=PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=displayFBFStatus),
3141                 PluginDescriptor(where=[PluginDescriptor.WHERE_SESSIONSTART, PluginDescriptor.WHERE_AUTOSTART], fnc=autostart) ]