- changed hostname to text instead ConfigIP
[enigma2-plugins.git] / fritzcall / src / plugin.py
1 # -*- coding: utf-8 -*-
2 from Screens.Screen import Screen
3 from Screens.MessageBox import MessageBox
4 from Screens.NumericalTextInputHelpDialog import NumericalTextInputHelpDialog
5 from Screens.InputBox import InputBox
6 from Screens import Standby
7 from Screens.HelpMenu import HelpableScreen
8
9 from enigma import eListboxPythonMultiContent, gFont, RT_HALIGN_LEFT
10
11 from Components.MenuList import MenuList
12 from Components.ActionMap import ActionMap
13 from Components.Label import Label
14 from Components.Button import Button
15 from Components.config import config, ConfigSubsection, ConfigSelection, ConfigIP, ConfigEnableDisable, getConfigListEntry, ConfigText, ConfigInteger
16 from Components.ConfigList import ConfigListScreen
17 from Components.ScrollLabel import ScrollLabel
18
19 from Plugins.Plugin import PluginDescriptor
20 from Tools import Notifications
21 from Tools.NumericalTextInput import NumericalTextInput
22
23 from twisted.internet import reactor
24 from twisted.internet.protocol import ReconnectingClientFactory
25 from twisted.protocols.basic import LineReceiver
26 from twisted.web.client import getPage
27
28 from xml.dom.minidom import parse
29
30 from urllib import urlencode 
31 import re, time, os
32
33 import gettext
34 from Tools.Directories import resolveFilename, SCOPE_PLUGINS
35 try:
36         _ = gettext.translation('FritzCall', resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/locale"), [config.osd.language.getText()]).gettext
37 except IOError:
38         pass
39
40
41 my_global_session = None
42
43 config.plugins.FritzCall = ConfigSubsection()
44 config.plugins.FritzCall.enable = ConfigEnableDisable(default = False)
45 # config.plugins.FritzCall.hostname = ConfigIP(default = [192, 168, 178, 1])
46 config.plugins.FritzCall.hostname = ConfigText(default = "fritz.box", fixed_size = False)
47 config.plugins.FritzCall.afterStandby = ConfigSelection(choices = [("none", _("show nothing")), ("inList", _("show as list")), ("each", _("show each call"))])
48 config.plugins.FritzCall.filter = ConfigEnableDisable(default = False)
49 config.plugins.FritzCall.filtermsn = ConfigText(default = "", fixed_size = False)
50 config.plugins.FritzCall.filtermsn.setUseableChars('0123456789,')
51 config.plugins.FritzCall.showOutgoing = ConfigEnableDisable(default = False)
52 config.plugins.FritzCall.timeout = ConfigInteger(default = 15, limits = (0,60))
53 config.plugins.FritzCall.lookup = ConfigEnableDisable(default = False)
54 config.plugins.FritzCall.internal = ConfigEnableDisable(default = False)
55 config.plugins.FritzCall.fritzphonebook = ConfigEnableDisable(default = False)
56 config.plugins.FritzCall.phonebook = ConfigEnableDisable(default = False)
57 config.plugins.FritzCall.addcallers = ConfigEnableDisable(default = False)
58 config.plugins.FritzCall.phonebookLocation = ConfigSelection(choices = [("/etc/enigma2/PhoneBook.txt", _("Flash")), ("/media/usb/PhoneBook.txt", _("USB Stick")), ("/media/cf/PhoneBook.txt", _("CF Drive")), ("/media/hdd/PhoneBook.txt", _("Harddisk"))])
59 config.plugins.FritzCall.password = ConfigText(default = "", fixed_size = False)
60 config.plugins.FritzCall.showType = ConfigEnableDisable(default = True)
61 config.plugins.FritzCall.showShortcut = ConfigEnableDisable(default = False)
62 config.plugins.FritzCall.showVanity = ConfigEnableDisable(default = False)
63 config.plugins.FritzCall.prefix = ConfigText(default = "", fixed_size = False)
64 config.plugins.FritzCall.prefix.setUseableChars('0123456789')
65
66 countryCodes = [
67         ("0049", _("Germany")),
68         ("0031", _("The Netherlands")),
69         ("0033", _("France")),
70         ("0039", _("Italy")),
71         ("0041", _("Switzerland")),
72         ("0043", _("Austria"))
73         ]
74 config.plugins.FritzCall.country = ConfigSelection(choices = countryCodes)
75
76 FBF_ALL_CALLS = "."
77 FBF_IN_CALLS = "1"
78 FBF_MISSED_CALLS = "2"
79 FBF_OUT_CALLS = "3"
80 fbfCallsChoices = {FBF_ALL_CALLS: _("All calls"),
81                                    FBF_IN_CALLS: _("Incoming calls"),
82                                    FBF_MISSED_CALLS: _("Missed calls"),
83                                    FBF_OUT_CALLS: _("Outgoing calls")
84                                    }
85 config.plugins.FritzCall.fbfCalls = ConfigSelection(choices = fbfCallsChoices)
86
87 config.plugins.FritzCall.name = ConfigText(default = "", fixed_size = False)
88 config.plugins.FritzCall.number= ConfigText(default = "", fixed_size = False)
89 config.plugins.FritzCall.number.setUseableChars('0123456789')
90
91
92 def html2utf8(in_html):
93         try:
94                 import htmlentitydefs
95                 htmlentitynumbermask = re.compile('(&#(\d{1,5}?);)')
96                 htmlentitynamemask = re.compile('(&(\D{1,5}?);)')
97                 entities = htmlentitynamemask.finditer(in_html)
98                 entitydict = {}
99                 for x in entities:
100                         entitydict[x.group(1)] = x.group(2)
101                 for key, name in entitydict.items():
102                         try:
103                                 entitydict[key] = htmlentitydefs.name2codepoint[name]
104                         except KeyError:
105                                 pass
106                 entities = htmlentitynumbermask.finditer(in_html)
107                 for x in entities:
108                         entitydict[x.group(1)] = x.group(2)
109                 for key, codepoint in entitydict.items():
110                         try:
111                                 in_html = in_html.replace(key, (unichr(int(codepoint)).encode('utf8', "replace")))
112                         except ValueError:
113                                 pass
114         except ImportError:
115                 return in_html.replace("&", "&").replace("ß", "").replace("ä", "").replace("ö", "").replace("ü", "").replace("Ä", "").replace("Ö", "").replace("Ü", "")
116         return in_html
117
118
119 class FritzCallFBF:
120         def __init__(self):
121                 print "[FritzCallFBF] __init__"
122                 self.callScreen= None
123                 self.loggedIn = False
124                 self.Callback = None
125                 self.loginCallback = None
126                 self.timestamp = 0
127                 self.callList = []
128                 self.callType = config.plugins.FritzCall.fbfCalls.value
129
130         def notify(self, text):
131                 print "[FritzCallFBF] notify"
132                 if self.callScreen:
133                         print "[FritzCallFBF] notify: try to close callScreen"
134                         self.callScreen.close()
135                         self.callScreen = None
136                 Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_ERROR, timeout=config.plugins.FritzCall.timeout.value)
137
138         def errorLogin(self, error):
139                 text = _("FRITZ!Box Login failed! - Error: %s") %error
140                 self.notify(text)
141
142         def _gotPageLogin(self, html):
143 #               print "[FritzCallPhonebook] _gotPageLogin"
144                 # workaround: exceptions in gotPage-callback were ignored
145                 if self.callScreen:
146                         self.callScreen.updateStatus(_("Getting calls from FRITZ!Box...") + _("login verification"))
147                 try:
148                         print "[FritzCallFBF] _gotPageLogin: verify login"
149                         found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;Das angegebene Kennwort', html, re.S)
150                         if found:
151                                 text = _("FRITZ!Box Login failed! - Wrong Password!")
152                                 self.notify(text)
153                         else:
154                                 if self.callScreen:
155                                         self.callScreen.updateStatus(_("Getting calls from FRITZ!Box...") + _("login ok"))
156                                 self.loggedIn = True
157                                 self.loginCallback()
158                         loginCallback = None
159                 except:
160                         import traceback, sys
161                         traceback.print_exc(file=sys.stdout)
162                         #raise e
163
164         def login(self):
165                 print "[FritzCallFBF] Login"
166                 if config.plugins.FritzCall.password.value != "":
167                         if self.callScreen:
168                                 self.callScreen.updateStatus(_("Getting calls from FRITZ!Box...") + _("login"))
169                         parms = "login:command/password=%s" %(config.plugins.FritzCall.password.value)
170                         url = "http://%s/cgi-bin/webcm" %(config.plugins.FritzCall.hostname.value)
171                         getPage(url, method="POST", headers = {'Content-Type': "application/x-www-form-urlencoded",'Content-Length': str(len(parms))}, postdata=parms).addCallback(self._gotPageLogin).addErrback(self.errorLogin)
172                 else:
173                         self.loginCallback()
174                         self.loginCallback = None
175
176         def errorLoad(self, error):
177                 text = _("Could not load phonebook from FRITZ!Box - Error: %s") %error
178                 self.notify(text)
179
180         def _gotPageLoad(self, html):
181                 print "[FritzCallFBF] _gotPageLoad"
182                 # workaround: exceptions in gotPage-callback were ignored
183                 try:
184                         self.parseFritzBoxPhonebook(html)
185                 except:
186                         import traceback, sys
187                         traceback.print_exc(file=sys.stdout)
188                         #raise e
189
190         def loadFritzBoxPhonebook(self):
191                 print "[FritzCallFBF] loadFritzBoxPhonebook"
192                 if config.plugins.FritzCall.fritzphonebook.value:
193                         print "[FritzCallFBF] loadFritzBoxPhonebook: logging in"
194                         self.loginCallback = self._loadFritzBoxPhonebook
195                         self.login()
196
197         def _loadFritzBoxPhonebook(self):
198                         parms = urlencode({'getpage':'../html/de/menus/menu2.html', 'var:lang':'de','var:pagename':'fonbuch','var:menu':'fon'})
199                         url = "http://%s/cgi-bin/webcm?%s" %(config.plugins.FritzCall.hostname.value, parms)
200
201                         getPage(url).addCallback(self._gotPageLoad).addErrback(self.errorLoad)
202
203         def parseFritzBoxPhonebook(self, html):
204                 print "[FritzCallFBF] parseFritzBoxPhonebook"
205
206                 table = html2utf8(html.replace("\xa0"," ").decode("ISO-8859-1", "replace"))
207                 if re.search('TrFonName', table):
208                         #===============================================================================
209                         #                                New Style: 7170 / 7270 (FW 54.04.58, 54.04.63-11941) 
210                         #       We expect one line with TrFonName followed by several lines with
211                         #       TrFonNr(Type,Number,Shortcut,Vanity), which all belong to the name in TrFonName.
212                         #===============================================================================
213                         # entrymask = re.compile('(TrFonName\("[^"]+", "[^"]+", "[^"]+"\);</SCRIPT>\s+[<SCRIPT type=text/javascript>TrFonNr\("[^"]+", "[^"]+", "[^"]+", "[^"]+"\);</SCRIPT>\s+]+)<SCRIPT type=text/javascript>document.write(TrFon1());</SCRIPT>', re.DOTALL)
214                         # entrymask = re.compile('(TrFonName\("[^"]+", "[^"]+", "[^"]+"\);.*?[.*?TrFonNr\("[^"]+", "[^"]+", "[^"]+", "[^"]+"\);.*?]+).*?document.write(TrFon1());', re.DOTALL)
215                         entrymask = re.compile('(TrFonName\("[^"]+", "[^"]+", "[^"]*"\);.*?)TrFon1\(\)', re.S)
216                         entries = entrymask.finditer(html)
217                         for entry in entries:
218                                 # print entry.group(1)
219                                 found = re.match('TrFonName\("[^"]*", "([^"]+)", "[^"]*"\);', entry.group(1))
220                                 if found:
221                                         name = found.group(1)
222                                 else:
223                                         continue
224                                 detailmask = re.compile('TrFonNr\("([^"]*)", "([^"]*)", "([^"]*)", "([^"]*)"\);', re.S)
225                                 details = detailmask.finditer(entry.group(1))
226                                 for found in details:
227                                         thisname = name
228
229                                         type = found.group(1)
230                                         if config.plugins.FritzCall.showType.value:
231                                                 if type == "mobile":
232                                                         thisname = thisname + " (" +_("mobile") + ")"
233                                                 elif type == "home":
234                                                         thisname = thisname + " (" +_("home") + ")"
235                                                 elif type == "work":
236                                                         thisname = thisname + " (" +_("work") + ")"
237
238                                         if config.plugins.FritzCall.showShortcut.value and found.group(3):
239                                                 thisname = thisname + ", " + _("Shortcut") + ": " + found.group(3)
240                                         if config.plugins.FritzCall.showVanity.value and found.group(4):
241                                                 thisname = thisname + ", " + _("Vanity") + ": " + found.group(4)
242
243                                         thisnumber = found.group(2).strip()
244                                         thisname = html2utf8(thisname.strip())
245                                         if thisnumber:
246                                                 print "[FritzCallFBF] Adding '''%s''' with '''%s''' from FRITZ!Box Phonebook!" %(thisname, thisnumber)
247                                                 phonebook.phonebook[thisnumber] = thisname
248                                         else:
249                                                 print "[FritzCallFBF] ignoring empty number for %s" %thisname
250                                         continue
251
252                 elif re.search('TrFon', table):
253                         #===============================================================================
254                         #                               Old Style: 7050 (FW 14.04.33)
255                         #       We expect one line with TrFon(No,Name,Number,Shortcut,Vanity)
256                         #===============================================================================                                
257                         entrymask = re.compile('TrFon\("[^"]*", "([^"]*)", "([^"]*)", "([^"]*)", "([^"]*)"\)', re.S)
258                         entries = entrymask.finditer(html)
259                         for found in entries:
260                                 name = found.group(1).strip()
261                                 thisnumber = found.group(2).strip()
262                                 if config.plugins.FritzCall.showShortcut.value and found.group(3):
263                                         name = name + ", " + _("Shortcut") + ": " + found.group(3)
264                                 if config.plugins.FritzCall.showVanity.value and found.group(4):
265                                         name = name + ", " +_("Vanity") +": " + found.group(4)
266                                 if thisnumber:
267                                         name = html2utf8(name)
268                                         print "[FritzCallFBF] Adding '''%s''' with '''%s''' from FRITZ!Box Phonebook!" %(name, thisnumber)
269                                         phonebook.phonebook[thisnumber] = name
270                                 else:
271                                         print "[FritzCallFBF] ignoring empty number for %s" %name
272                                 continue
273                 else:
274                         self.notify(_("Could not parse FRITZ!Box Phonebook entry"))
275
276         def errorCalls(self, error):
277                 text = _("Could not load calls from FRITZ!Box - Error: %s") %error
278                 self.notify(text)
279
280         def _gotPageCalls(self, csv = ""):
281                 def _resolveNumber(number):
282                         if number.isdigit():
283                                 if config.plugins.FritzCall.internal.value and len(number) > 3 and number[0]=="0": number = number[1:]
284                                 name = phonebook.search(number)
285                                 if name:
286                                         found = re.match('(.*?)\n.*', name)
287                                         if found:
288                                                 name = found.group(1)
289                                         number = name
290                         elif number == "":
291                                 number = _("UNKNOWN")
292                         # if len(number) > 20: number = number[:20]
293                         return number
294
295                 if csv:
296                         print "[FritzCallFBF] _gotPageCalls: got csv, setting callList"
297                         if self.callScreen:
298                                 self.callScreen.updateStatus(_("Getting calls from FRITZ!Box...") + _("done"))
299                         # check for error: wrong password or password not set... TODO
300                         found = re.search('Melden Sie sich mit dem Kennwort der FRITZ!Box an', csv)
301                         if found:
302                                 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.")
303                                 # self.session.open(MessageBox, text, MessageBox.TYPE_ERROR, timeout=config.plugins.FritzCall.timeout.value)
304                                 self.notify(text)
305                                 return
306
307                         csv = csv.decode('iso-8859-1','replace').encode('utf-8','replace')
308                         lines = csv.splitlines()
309                         self.callList = lines
310                 elif self.callList:
311                         print "[FritzCallFBF] _gotPageCalls: got no csv, but have callList"
312                         if self.callScreen:
313                                 self.callScreen.updateStatus(_("Getting calls from FRITZ!Box...") + _("done, using last list"))
314                         lines = self.callList
315                 else:
316                         print "[FritzCallFBF] _gotPageCalls: got no csv, no callList, leaving"
317                         return
318                         
319                 callList = []
320                 for line in lines:
321                         # print line
322                         # Typ;Datum;Name;Rufnummer;Nebenstelle;Eigene Rufnummer;Dauer
323                         found = re.match("^(" + self.callType + ");([^;]*);([^;]*);([^;]*);([^;]*);([^;]*)", line)
324                         if found:
325                                 direct = found.group(1)
326                                 date = found.group(2)
327                                 if direct != FBF_OUT_CALLS and found.group(3):
328                                         remote = found.group(3)
329                                 else:
330                                         remote = _resolveNumber(found.group(4))
331                                 found1 = re.match('Internet: (.*)', found.group(6))
332                                 if found1:
333                                         here = _resolveNumber(found1.group(1))
334                                 else:
335                                         here = _resolveNumber(found.group(6))
336                                 callList.append((found.group(4), date, here, direct, remote))
337
338                 # print "[FritzCallFBF] _gotPageCalls result:\n" + text
339
340                 if self.Callback is not None:
341                         # print "[FritzCallFBF] _gotPageCalls call callback with\n" + text
342                         self.Callback(callList)
343                         self.Callback = None
344                 self.callScreen = None
345
346         def getCalls(self, callScreen, callback, type):
347                 #
348                 # call sequence must be:
349                 # - login
350                 # - getPage -> _gotPageLogin
351                 # - loginCallback (_getCalls)
352                 # - getPage -> _getCalls1
353                 print "[FritzCallFBF] getCalls"
354                 self.callScreen = callScreen
355                 self.callType = type
356                 self.Callback = callback
357                 if (time.time() - self.timestamp) > 180: 
358                         print "[FritzCallFBF] getCalls: outdated data, login and get new ones"
359                         self.timestamp = time.time()
360                         self.loginCallback = self._getCalls
361                         self.login()
362                 elif not self.callList:
363                         print "[FritzCallFBF] getCalls: time is ok, but no callList"
364                         self._getCalls1()
365                 else:
366                         print "[FritzCallFBF] getCalls: time is ok, callList is ok"
367                         self._gotPageCalls()
368
369         def _getCalls(self):
370                 #
371                 # we need this to fill Anrufliste.csv
372                 # http://repeater1/cgi-bin/webcm?getpage=../html/de/menus/menu2.html&var:lang=de&var:menu=fon&var:pagename=foncalls
373                 #
374                 print "[FritzCallFBF] _getCalls"
375                 if self.callScreen:
376                         self.callScreen.updateStatus(_("Getting calls from FRITZ!Box...") + _("preparing"))
377                 parms = urlencode({'getpage':'../html/de/menus/menu2.html', 'var:lang':'de','var:pagename':'foncalls','var:menu':'fon'})
378                 url = "http://%s/cgi-bin/webcm?%s" %(config.plugins.FritzCall.hostname.value, parms)
379                 getPage(url).addCallback(self._getCalls1).addErrback(self.errorCalls)
380
381         def _getCalls1(self, html = ""):
382                 #
383                 # finally we should have successfully lgged in and filled the csv
384                 #
385                 print "[FritzCallFBF] _getCalls1"
386                 if self.callScreen:
387                         self.callScreen.updateStatus(_("Getting calls from FRITZ!Box...") + _("finishing"))
388                 parms = urlencode({'getpage':'../html/de/FRITZ!Box_Anrufliste.csv'})
389                 url = "http://%s/cgi-bin/webcm?%s" %(config.plugins.FritzCall.hostname.value, parms)
390                 getPage(url).addCallback(self._gotPageCalls).addErrback(self.errorCalls)
391
392         def dial(self, number):
393                 ''' initiate a call to number '''
394                 #
395                 # TODO:
396                 # does not work... if anybody wants to make it work, feel free
397                 # I not convinced of FBF's style to establish a connection: first get the connection, then ring the local phone?!?!
398                 #  
399                 return
400                 # http://fritz.box/cgi-bin/webcm
401                 # getpage=../html/de/menus/menu2.html
402                 # var:lang=de
403                 # var:pagename=foncalls
404                 # var:menu=home
405                 # var:pagemaster=
406                 # var:showsetup=
407                 # var:showall=
408                 # var:showDialing=08001235005
409                 # telcfg:settings/UseJournal=1
410                 # telcfg:command/Dial=08001235005
411                 self.login()
412                 url = "http://%s/cgi-bin/webcm" %config.plugins.FritzCall.hostname.value
413                 parms = urlencode({
414                         'getpage':'../html/de/menus/menu2.html',
415                         'errorpage':'../html/de/menus/menu2.html',
416                         'var:lang':'de',
417                         'var:pagename':'foncalls',
418                         'var:errorpagename':'foncalls',
419                         'var:menu':'home',
420                         'var:pagemaster':'',
421                         'var:settings/time':'0,0',
422                         'var:showsetup':'',
423                         'var:showall':'',
424                         'var:showDialing':number,
425                         'var:tabFoncall':'',
426                         'var:TestPort':'',
427                         'var:kurzwahl':'',
428                         'var:kwCode':'',
429                         'var:kwVanity':'',
430                         'var:kwNumber':'',
431                         'var:kwName':'',
432                         'telcfg:settings/UseJournal':'1',
433                         'telcfg:command/Dial':number
434                         })
435                 print "[FritzCallFBF] dial url: '" + url + "' parms: '" + parms + "'"
436                 getPage(url, method="POST", headers = {'Content-Type': "application/x-www-form-urlencoded",'Content-Length': str(len(parms))}, postdata=parms)
437
438
439
440 fritzbox = FritzCallFBF()
441
442 class FritzDisplayCalls(Screen, HelpableScreen):
443
444         # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
445         skin = """
446                 <screen name="FritzDisplayCalls" position="100,90" size="570,420" title="%s" >
447                         <widget name="statusbar" position="0,0" size="570,22" font="Regular;21" />
448                         <widget name="entries" position="0,22" size="570,358" scrollbarMode="showOnDemand" />
449                         <ePixmap position="5,375" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
450                         <ePixmap position="145,375" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
451                         <ePixmap position="285,375" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
452                         <ePixmap position="425,375" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
453                         <widget name="key_red" position="5,375" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
454                         <widget name="key_green" position="145,375" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
455                         <widget name="key_yellow" position="285,375" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
456                         <widget name="key_blue" position="425,375" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
457                 </screen>""" % _("Phone calls")
458
459         def __init__(self, session, text = ""):
460                 self.skin = FritzDisplayCalls.skin
461                 Screen.__init__(self, session)
462                 HelpableScreen.__init__(self)
463
464                 # TRANSLATORS: keep it short, this is a button
465                 self["key_red"] = Button(_("All"))
466                 # TRANSLATORS: keep it short, this is a button
467                 self["key_green"] = Button(_("Missed"))
468                 # TRANSLATORS: keep it short, this is a button
469                 self["key_yellow"] = Button(_("Incoming"))
470                 # TRANSLATORS: keep it short, this is a button
471                 self["key_blue"] = Button(_("Outgoing"))
472
473                 self["setupActions"] = ActionMap(["OkCancelActions", "ColorActions"],
474                 {
475                         "red": self.displayAllCalls,
476                         "green": self.displayMissedCalls,
477                         "yellow": self.displayInCalls,
478                         "blue": self.displayOutCalls,
479                         "cancel": self.ok,
480                         "ok": self.showEntry,}, -2)
481
482                 # TRANSLATORS: this is a help text, keep it short
483                 self.helpList.append((self["setupActions"], "OkCancelActions", [("ok", _("Show details of entry"))]))
484                 # TRANSLATORS: this is a help text, keep it short
485                 self.helpList.append((self["setupActions"], "OkCancelActions", [("cancel", _("Quit"))]))
486                 # TRANSLATORS: this is a help text, keep it short
487                 self.helpList.append((self["setupActions"], "ColorActions", [("red", _("Display all calls"))]))
488                 # TRANSLATORS: this is a help text, keep it short
489                 self.helpList.append((self["setupActions"], "ColorActions", [("green", _("Display missed calls"))]))
490                 # TRANSLATORS: this is a help text, keep it short
491                 self.helpList.append((self["setupActions"], "ColorActions", [("yellow", _("Display incoming calls"))]))
492                 # TRANSLATORS: this is a help text, keep it short
493                 self.helpList.append((self["setupActions"], "ColorActions", [("blue", _("Display outgoing calls"))]))
494
495                 self["statusbar"] = Label(_("Getting calls from FRITZ!Box..."))
496                 self["entries"] = MenuList([], True, content = eListboxPythonMultiContent)
497                 self["entries"].l.setFont(0, gFont("Console", 16))
498                 self["entries"].l.setItemHeight(20)
499
500                 print "[FritzDisplayCalls] init: '''%s'''" %config.plugins.FritzCall.fbfCalls.value
501                 self.displayCalls()
502
503         def ok(self):
504                 self.close()
505
506         def displayAllCalls(self):
507                 print "[FritzDisplayCalls] displayAllCalls"
508                 config.plugins.FritzCall.fbfCalls.value = FBF_ALL_CALLS
509                 config.plugins.FritzCall.fbfCalls.save()
510                 self.displayCalls()
511
512         def displayMissedCalls(self):
513                 print "[FritzDisplayCalls] displayMissedCalls"
514                 config.plugins.FritzCall.fbfCalls.value = FBF_MISSED_CALLS
515                 config.plugins.FritzCall.fbfCalls.save()
516                 self.displayCalls()
517
518         def displayInCalls(self):
519                 print "[FritzDisplayCalls] displayInCalls"
520                 config.plugins.FritzCall.fbfCalls.value = FBF_IN_CALLS
521                 config.plugins.FritzCall.fbfCalls.save()
522                 self.displayCalls()
523
524         def displayOutCalls(self):
525                 print "[FritzDisplayCalls] displayOutCalls"
526                 config.plugins.FritzCall.fbfCalls.value = FBF_OUT_CALLS
527                 config.plugins.FritzCall.fbfCalls.save()
528                 self.displayCalls()
529
530         def displayCalls(self):
531                 print "[FritzDisplayCalls] displayCalls"
532                 self.header = fbfCallsChoices[config.plugins.FritzCall.fbfCalls.value]
533                 fritzbox.getCalls(self, self.gotCalls, config.plugins.FritzCall.fbfCalls.value)
534
535         def gotCalls(self, callList):
536                 print "[FritzDisplayCalls] gotCalls"
537                 self.updateStatus(self.header + " (" + str(len(callList)) + ")")
538                 sortlist = []
539                 for (number, date, remote, direct, here) in callList:
540                         while (len(remote) + len(here)) > 40:
541                                 if len(remote) > len(here):
542                                         remote = remote[:-1]
543                                 else:
544                                         here = here[:-1]
545                         found = re.match("(\d\d.\d\d.)\d\d( \d\d:\d\d)", date)
546                         if found: date = found.group(1) + found.group(2)
547                         if direct == FBF_OUT_CALLS:
548                                 message = date + " " + remote + " -> " + here
549                         else:
550                                 message = date + " " + here + " -> " + remote
551                         sortlist.append([number, (eListboxPythonMultiContent.TYPE_TEXT, 0, 0, 560, 20, 0, RT_HALIGN_LEFT, message)])
552                 self["entries"].setList(sortlist)
553
554         def showEntry(self):
555                 print "[FritzDisplayCalls] showEntry"
556                 cur = self["entries"].getCurrent()
557                 if cur:
558                         print "[FritzDisplayCalls] showEntry %s" % (cur[0])
559                         if cur[0]:
560                                 fullname = phonebook.search(cur[0])
561                                 if fullname:
562                                         self.session.open(MessageBox,
563                                                                         cur[0] + "\n\n" + fullname.replace(", ","\n"),
564                                                                         type = MessageBox.TYPE_INFO)
565                                 else:
566                                         # TODO: offer to add entry to phonebook
567                                         self.actualNumber = cur[0]
568                                         self.session.openWithCallback(
569                                                 self.addConfirmed,
570                                                 MessageBox,
571                                                 _("Do you want to add a phonebook entry for\n\n%s?") %cur[0]
572                                                 )
573                         else:
574                                 self.session.open(MessageBox,
575                                                   _("UNKNOWN"),
576                                                   type = MessageBox.TYPE_INFO)
577
578         def addConfirmed(self, ret):
579                 if ret:
580                         phonebook.displayPhonebook(self.session).add(self.actualNumber)
581
582         def updateStatus(self, text):
583                 self["statusbar"].setText(text)
584
585
586 class FritzCallPhonebook:
587         def __init__(self):
588                 self.phonebook = {}
589                 self.reload()
590
591         def create(self):
592                 try:
593                         f = open(config.plugins.FritzCall.phonebookLocation.value, 'w')
594                         f.write("01234567890#Name, Street, Location (Keep the Spaces!!!)\n");
595                         f.close()
596                         return True
597                 except IOError:
598                         Notifications.AddNotification(MessageBox, _("Can't create PhoneBook.txt"), type=MessageBox.TYPE_INFO, timeout=config.plugins.FritzCall.timeout.value)
599                         return False
600
601         def reload(self):
602                 print "[FritzCallPhonebook] reload"
603                 self.phonebook = {}
604
605                 if not config.plugins.FritzCall.enable.value:
606                         return
607
608                 exists = False
609                 
610                 if config.plugins.FritzCall.phonebook.value:
611                         if not os.path.exists(config.plugins.FritzCall.phonebookLocation.value):
612                                 if(self.create()):
613                                         exists = True
614                         else:
615                                 exists = True
616         
617                         if exists:
618                                 for line in open(config.plugins.FritzCall.phonebookLocation.value):
619                                         try:
620                                                 number, name = line.split("#")
621                                                 if not self.phonebook.has_key(number):
622                                                         self.phonebook[number] = name
623                                         except ValueError:
624                                                 print "[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" %line
625
626                 if config.plugins.FritzCall.fritzphonebook.value:
627                         fritzbox.loadFritzBoxPhonebook()
628
629         def search(self, number):
630                 # print "[FritzCallPhonebook] Searching for %s" %number
631                 name = None
632                 if config.plugins.FritzCall.phonebook.value or config.plugins.FritzCall.fritzphonebook.value:
633                         if self.phonebook.has_key(number):
634                                 name = self.phonebook[number].replace(", ", "\n").strip()
635                 return name
636
637         def add(self, number, name):
638                 print "[FritzCallPhonebook] add"
639                 #===============================================================================
640                 #               It could happen, that two reverseLookups are running in parallel,
641                 #               so check first, whether we have already added the number to the phonebook.
642                 #===============================================================================
643                 self.phonebook[number] = name;
644                 if number <> 0 and config.plugins.FritzCall.phonebook.value and config.plugins.FritzCall.addcallers.value:
645                         try:
646                                 f = open(config.plugins.FritzCall.phonebookLocation.value, 'a')
647                                 name = name.strip() + "\n"
648                                 string = "%s#%s" %(number, name)
649                                 f.write(string)
650                                 f.close()
651                                 print "[FritzCallPhonebook] added %s with %sto Phonebook.txt" %(number, name)
652                                 return True
653
654                         except IOError:
655                                 return False
656
657         def remove(self, number):
658                 print "[FritzCallPhonebook] remove"
659                 if number in self.phonebook:
660                         print "[FritzCallPhonebook] remove entry in phonebook"
661                         del self.phonebook[number]
662                         if config.plugins.FritzCall.phonebook.value and config.plugins.FritzCall.addcallers.value:
663                                 try:
664                                         print "[FritzCallPhonebook] remove entry in Phonebook.txt"
665                                         fOld = open(config.plugins.FritzCall.phonebookLocation.value, 'r')
666                                         fNew = open(config.plugins.FritzCall.phonebookLocation.value + str(os.getpid()), 'w')
667                                         line = fOld.readline()
668                                         while (line):
669                                                 if not re.match("^"+number+"#.*$", line):
670                                                         fNew.write(line)
671                                                 line = fOld.readline()
672                                         fOld.close()
673                                         fNew.close()
674                                         os.remove(config.plugins.FritzCall.phonebookLocation.value)
675                                         os.rename(config.plugins.FritzCall.phonebookLocation.value + str(os.getpid()),
676                                                         config.plugins.FritzCall.phonebookLocation.value)
677                                         print "[FritzCallPhonebook] removed %s from Phonebook.txt" %number
678                                         return True
679         
680                                 except IOError:
681                                         pass
682                 return False
683
684         class displayPhonebook(Screen, HelpableScreen, NumericalTextInput):
685                 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
686                 skin = """
687                         <screen name="FritzDisplayPhonebook" position="100,90" size="570,420" title="%s" >
688                                 <widget name="entries" position="5,5" size="560,370" scrollbarMode="showOnDemand" />
689                                 <ePixmap position="5,375" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
690                                 <ePixmap position="145,375" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
691                                 <ePixmap position="285,375" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
692                                 <ePixmap position="425,375" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
693                                 <widget name="key_red" position="5,375" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
694                                 <widget name="key_green" position="145,375" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
695                                 <widget name="key_yellow" position="285,375" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
696                                 <widget name="key_blue" position="425,375" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
697                         </screen>""" % _("Phonebook")
698
699                 def __init__(self, session):
700                         Screen.__init__(self, session)
701                         NumericalTextInput.__init__(self)
702                         HelpableScreen.__init__(self)
703                 
704                         # TRANSLATORS: keep it short, this is a button
705                         self["key_red"] = Button(_("Delete"))
706                         # TRANSLATORS: keep it short, this is a button
707                         self["key_green"] = Button(_("New"))
708                         # TRANSLATORS: keep it short, this is a button
709                         self["key_yellow"] = Button(_("Edit"))
710                         # TRANSLATORS: keep it short, this is a button
711                         self["key_blue"] = Button(_("Search"))
712         
713                         self["setupActions"] = ActionMap(["OkCancelActions", "ColorActions"],
714                         {
715                                 "red": self.delete,
716                                 "green": self.add,
717                                 "yellow": self.edit,
718                                 "blue": self.search,
719                                 "cancel": self.exit,
720                                 "ok": self.showEntry,}, -2)
721
722                         # TRANSLATORS: this is a help text, keep it short
723                         self.helpList.append((self["setupActions"], "OkCancelActions", [("ok", _("Show details of entry"))]))
724                         # TRANSLATORS: this is a help text, keep it short
725                         self.helpList.append((self["setupActions"], "OkCancelActions", [("cancel", _("Quit"))]))
726                         # TRANSLATORS: this is a help text, keep it short
727                         self.helpList.append((self["setupActions"], "ColorActions", [("red", _("Delete entry"))]))
728                         # TRANSLATORS: this is a help text, keep it short
729                         self.helpList.append((self["setupActions"], "ColorActions", [("green", _("Add entry to phonebook"))]))
730                         # TRANSLATORS: this is a help text, keep it short
731                         self.helpList.append((self["setupActions"], "ColorActions", [("yellow", _("Edit selected entry"))]))
732                         # TRANSLATORS: this is a help text, keep it short
733                         self.helpList.append((self["setupActions"], "ColorActions", [("blue", _("Search (case insensitive)"))]))
734
735                         self["entries"] = MenuList([], True, content = eListboxPythonMultiContent)
736                         self["entries"].l.setFont(0, gFont("Console", 16))
737                         self["entries"].l.setItemHeight(20)
738                         print "[FritzCallPhonebook] displayPhonebook init"
739                         self.display()
740
741                 def display(self, filter=""):
742                         print "[FritzCallPhonebook] displayPhonebook/display"
743                         self.sortlist = []
744                         sortlistHelp = sorted((name.lower(), name, number) for (number, name) in phonebook.phonebook.iteritems())
745                         for (low, name, number) in sortlistHelp:
746                                 if number == "01234567890":
747                                         continue
748                                 low = low.decode("utf-8")
749                                 if filter:
750                                         filter = filter.lower()
751                                         if low.find(filter) == -1:
752                                                 continue
753                                 name = name.strip().decode("utf-8")
754                                 number = number.strip().decode("utf-8")
755                                 found = re.match("([^,]*),.*", name)
756                                 if found:
757                                         shortname = found.group(1)
758                                 if len(name) > 35:
759                                         shortname = name[:35]
760                                 else:
761                                         shortname = name
762                                 message = u"%-35s  %-18s" %(shortname, number)
763                                 message = message.encode("utf-8")
764                                 # print "[FritzCallPhonebook] displayPhonebook/display: add " + message
765                                 self.sortlist.append([(number.encode("utf-8","replace"),
766                                                            name.encode("utf-8","replace")),
767                                                            (eListboxPythonMultiContent.TYPE_TEXT, 0, 0, 560, 20, 0, RT_HALIGN_LEFT, message)])
768
769                         self["entries"].setList(self.sortlist)
770
771                 def showEntry(self):
772                         cur = self["entries"].getCurrent()
773                         print "[FritzCallPhonebook] displayPhonebook/showEntry (%s,%s)" % (cur[0][0],cur[0][1])
774                         if cur:
775                                 fullname = phonebook.search(cur[0][0])
776                                 if fullname:
777                                         self.session.open(MessageBox,
778                                                           cur[0][0] + "\n\n" + fullname.replace(", ","\n"),
779                                                           type = MessageBox.TYPE_INFO)
780                                 else:
781                                         self.session.open(MessageBox,
782                                                           cur[0][0],
783                                                           type = MessageBox.TYPE_INFO)
784
785                 def delete(self):
786                         cur = self["entries"].getCurrent()
787                         print "[FritzCallPhonebook] displayPhonebook/delete " + cur[0][0]
788                         if cur:
789                                 self.session.openWithCallback(
790                                         self.deleteConfirmed,
791                                         MessageBox,
792                                         _("Do you really want to delete entry for\n\n%(number)s\n\n%(name)s?") 
793                                         % { 'number':str(cur[0][0]), 'name':str(cur[0][1]).replace(", ","\n") }
794                                 )
795                         else:
796                                 self.session.open(MessageBox,_("No entry selected"), MessageBox.TYPE_INFO)
797
798                 def deleteConfirmed(self, ret):
799                         print "[FritzCallPhonebook] displayPhonebook/deleteConfirmed"
800                         #
801                         # if ret: delete number from sortlist, delete number from phonebook.phonebook and write it to disk
802                         #
803                         cur = self["entries"].getCurrent()
804                         if cur:
805                                 if ret:
806                                         # delete number from sortlist, delete number from phonebook.phonebook and write it to disk
807                                         print "[FritzCallPhonebook] displayPhonebook/deleteConfirmed: remove " +cur[0][0]
808                                         phonebook.remove(cur[0][0])
809                                         self.display()
810                                 # else:
811                                         # self.session.open(MessageBox, _("Not deleted."), MessageBox.TYPE_INFO)
812                         else:
813                                 self.session.open(MessageBox,_("No entry selected"), MessageBox.TYPE_INFO)
814
815                 def add(self, number = ""):
816                         class addScreen(Screen, ConfigListScreen):
817                                 '''ConfiglistScreen with two ConfigTexts for Name and Number'''
818                                 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
819                                 skin = """
820                                         <screen position="100,150" size="570,130" title="%s" >
821                                         <widget name="config" position="5,5" size="560,75" scrollbarMode="showOnDemand" />
822                                         <ePixmap position="145,85" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
823                                         <ePixmap position="285,85" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
824                                         <widget name="key_red" position="145,85" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
825                                         <widget name="key_green" position="285,85" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
826                                         </screen>"""  % _("Add entry to phonebook")
827
828                                 def __init__(self, session, parent, number = ""):
829                                         #
830                                         # setup screen with two ConfigText and OK and ABORT button
831                                         # 
832                                         Screen.__init__(self, session)
833                                         self.session = session
834                                         self.parent = parent
835                                         # TRANSLATORS: keep it short, this is a button
836                                         self["key_red"] = Button(_("Cancel"))
837                                         # TRANSLATORS: keep it short, this is a button
838                                         self["key_green"] = Button(_("OK"))
839                                         self["setupActions"] = ActionMap(["SetupActions", "ColorActions"],
840                                         {
841                                                 "cancel": self.cancel,
842                                                 "red": self.cancel,
843                                                 "green": self.add,
844                                                 "ok": self.add,
845                                         }, -2)
846
847                                         self.list = [ ]
848                                         ConfigListScreen.__init__(self, self.list, session = session)
849                                         config.plugins.FritzCall.name.value = ""
850                                         config.plugins.FritzCall.number.value = number
851                                         self.list.append(getConfigListEntry(_("Name"), config.plugins.FritzCall.name))
852                                         self.list.append(getConfigListEntry(_("Number"), config.plugins.FritzCall.number))
853                                         self["config"].list = self.list
854                                         self["config"].l.setList(self.list)
855
856
857                                 def add(self):
858                                         # get texts from Screen
859                                         # add (number,name) to sortlist and phonebook.phonebook and disk
860                                         self.number = config.plugins.FritzCall.number.value
861                                         self.name = config.plugins.FritzCall.name.value
862                                         if not self.number or not self.name:
863                                                 self.session.open(MessageBox, _("Entry incomplete."), type = MessageBox.TYPE_ERROR)
864                                                 return
865                                         # add (number,name) to sortlist and phonebook.phonebook and disk
866                                         oldname = phonebook.search(self.number)
867                                         if oldname:
868                                                 self.session.openWithCallback(
869                                                         self.overwriteConfirmed,
870                                                         MessageBox,
871                                                         _("Do you really want to overwrite entry for\n%(number)s\n\n%(name)s\n\nwith\n\n%(newname)s?")
872                                                         % {
873                                                         'number':self.number,
874                                                         'name': oldname,
875                                                         'newname':self.name.replace(", ","\n")
876                                                         }
877                                                         )
878                                                 self.close()
879                                                 return
880                                         phonebook.add(self.number, self.name)
881                                         self.close()
882                                         self.parent.display()
883
884                                 def overwriteConfirmed(self, ret):
885                                         if ret:
886                                                 phonebook.add(self.number, self.name)
887                                                 self.parent.display()
888                                         self.close()
889
890                                 def cancel(self):
891                                         self.close()
892
893                         print "[FritzCallPhonebook] displayPhonebook/add"
894                         self.session.open(addScreen, self, number)
895                         # self.session.open(MessageBox, "Not yet implemented.", type = MessageBox.TYPE_INFO)
896                         # return
897
898                 def edit(self):
899                         # Edit selected Timer
900                         class editScreen(Screen, ConfigListScreen):
901                                 '''ConfiglistScreen with two ConfigTexts for Name and Number'''
902                                 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
903                                 skin = """
904                                         <screen position="100,150" size="570,130" title="%s" >
905                                         <widget name="config" position="5,5" size="560,75" scrollbarMode="showOnDemand" />
906                                         <ePixmap position="145,85" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
907                                         <ePixmap position="285,85" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
908                                         <widget name="key_red" position="145,85" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
909                                         <widget name="key_green" position="285,85" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
910                                         </screen>""" % _("Edit phonebook entry").decode("utf-8")
911
912                                 def __init__(self, session, parent, name, number):
913                                         #
914                                         # setup screen with two ConfigText and OK and ABORT button
915                                         # 
916                                         Screen.__init__(self, session)
917                                         self.session = session
918                                         self.parent = parent
919                                         # TRANSLATORS: keep it short, this is a button
920                                         self["key_red"] = Button(_("Cancel"))
921                                         # TRANSLATORS: keep it short, this is a button
922                                         self["key_green"] = Button(_("OK"))
923                                         self["setupActions"] = ActionMap(["SetupActions", "ColorActions"],
924                                         {
925                                                 "cancel": self.cancel,
926                                                 "red": self.cancel,
927                                                 "green": self.edit,
928                                                 "ok": self.edit,
929                                         }, -2)
930
931                                         self.list = [ ]
932                                         ConfigListScreen.__init__(self, self.list, session = session)
933                                         # config.plugins.FritzCall.name.value = config.plugins.FritzCall.name.value.replace(", ","\n")
934                                         self.name = name
935                                         self.number = number
936                                         config.plugins.FritzCall.name.value = name
937                                         config.plugins.FritzCall.number.value = number
938                                         self.list.append(getConfigListEntry(_("Name"), config.plugins.FritzCall.name))
939                                         self.list.append(getConfigListEntry(_("Number"), config.plugins.FritzCall.number))
940                                         self["config"].list = self.list
941                                         self["config"].l.setList(self.list)
942
943
944                                 def edit(self):
945                                         print "[FritzCallPhonebook] displayPhonebook/edit: add (%s,%s)" %(config.plugins.FritzCall.number.value,config.plugins.FritzCall.name.value)
946                                         self.newname = config.plugins.FritzCall.name.value.replace("\n",", ")
947                                         self.newnumber = config.plugins.FritzCall.number.value
948                                         if not self.number or not self.name:
949                                                 self.session.open(MessageBox, _("Entry incomplete."), type = MessageBox.TYPE_ERROR)
950                                                 return
951                                         if self.number != self.newnumber:
952                                                 if phonebook.search(self.newnumber):
953                                                         self.session.openWithCallback(
954                                                                 self.overwriteConfirmed,
955                                                                 MessageBox,
956                                                                 _("Do you really want to overwrite entry for\n%(number)s\n\n%(name)s\n\nwith\n\n%(newname)s?")
957                                                                 % {
958                                                                 'number':self.newnumber,
959                                                                 'name':phonebook.search(self.newnumber).replace(", ","\n"),
960                                                                 'newname': self.newname
961                                                                 }
962                                                                 )
963                                                         self.close()
964                                                         return
965                                                 else:
966                                                         phonebook.remove(self.number)
967                                         phonebook.add(self.newnumber, self.newname)
968                                         self.close()
969                                         self.parent.display()
970
971                                 def overwriteConfirmed(self, ret):
972                                         if ret:
973                                                 phonebook.add(self.newnumber, self.newname)
974                                                 self.parent.display()
975                                         self.close()
976                                                 
977
978                                 def cancel(self):
979                                         self.close()
980
981                         print "[FritzCallPhonebook] displayPhonebook/edit"
982                         # self.session.open(MessageBox, "Not yet implemented.", type = MessageBox.TYPE_INFO)
983                         # return
984                         cur = self["entries"].getCurrent()
985                         if cur is None:
986                                 self.session.open(MessageBox,_("No entry selected"), MessageBox.TYPE_INFO)
987                         else:
988                                 (number, name) = cur[0]
989                                 self.session.open(editScreen, self, str(name), str(number))
990
991                 def search(self):
992                         print "[FritzCallPhonebook] displayPhonebook/search"
993                         self.help_window = self.session.instantiateDialog(NumericalTextInputHelpDialog, self)
994                         self.help_window.show()
995                         self.session.openWithCallback(self.doSearch, InputBox, _("Enter Search Terms"), _("Search phonebook"))
996
997                 def doSearch(self, searchTerms):
998                         if not searchTerms: searchTerms = ""
999                         print "[FritzCallPhonebook] displayPhonebook/doSearch: " + searchTerms
1000                         if self.help_window:
1001                                 self.session.deleteDialog(self.help_window)
1002                                 self.help_window = None
1003                         self.display(searchTerms)
1004
1005                 def exit(self):
1006                         self.close()
1007
1008 phonebook = FritzCallPhonebook()
1009
1010
1011 class FritzCallSetup(Screen, ConfigListScreen, HelpableScreen):
1012         # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
1013         skin = """
1014                 <screen position="100,90" size="570,420" title="%s" >
1015                 <widget name="config" position="5,10" size="560,300" scrollbarMode="showOnDemand" />
1016                 <widget name="consideration" position="20,320" font="Regular;20" halign="center" size="510,50" />
1017                 <ePixmap position="5,375" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1018                 <ePixmap position="145,375" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1019                 <ePixmap position="285,375" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1020                 <ePixmap position="425,375" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
1021                 <widget name="key_red" position="5,375" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1022                 <widget name="key_green" position="145,375" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1023                 <widget name="key_yellow" position="285,375" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1024                 <widget name="key_blue" position="425,375" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1025                 </screen>""" % _("FritzCall Setup")
1026
1027         def __init__(self, session, args = None):
1028
1029                 Screen.__init__(self, session)
1030                 HelpableScreen.__init__(self)
1031                 self.session = session
1032
1033                 self["consideration"] = Label(_("You need to enable the monitoring on your FRITZ!Box by dialing #96*5*!"))
1034                 self.list = []
1035
1036                 # Initialize Buttons
1037                 # TRANSLATORS: keep it short, this is a button
1038                 self["key_red"] = Button(_("Cancel"))
1039                 # TRANSLATORS: keep it short, this is a button
1040                 self["key_green"] = Button(_("OK"))
1041                 # TRANSLATORS: keep it short, this is a button
1042                 self["key_yellow"] = Button(_("Phone calls"))
1043                 # TRANSLATORS: keep it short, this is a button
1044                 self["key_blue"] = Button(_("Phonebook"))
1045
1046                 self["setupActions"] = ActionMap(["SetupActions", "ColorActions"],
1047                 {
1048                         "red": self.cancel,
1049                         "green": self.save,
1050                         "yellow": self.displayCalls,
1051                         "blue": self.displayPhonebook,
1052                         "cancel": self.cancel,
1053                         "save": self.save,
1054                         "ok": self.save,
1055                 }, -2)
1056
1057                 # TRANSLATORS: this is a help text, keep it short
1058                 self.helpList.append((self["setupActions"], "SetupActions", [("ok", _("save and quit"))]))
1059                 # TRANSLATORS: this is a help text, keep it short
1060                 self.helpList.append((self["setupActions"], "SetupActions", [("save", _("save and quit"))]))
1061                 # TRANSLATORS: this is a help text, keep it short
1062                 self.helpList.append((self["setupActions"], "SetupActions", [("cancel", _("quit"))]))
1063                 # TRANSLATORS: this is a help text, keep it short
1064                 self.helpList.append((self["setupActions"], "ColorActions", [("red", _("quit"))]))
1065                 # TRANSLATORS: this is a help text, keep it short
1066                 self.helpList.append((self["setupActions"], "ColorActions", [("green", _("save and quit"))]))
1067                 # TRANSLATORS: this is a help text, keep it short
1068                 self.helpList.append((self["setupActions"], "ColorActions", [("yellow", _("display calls"))]))
1069                 # TRANSLATORS: this is a help text, keep it short
1070                 self.helpList.append((self["setupActions"], "ColorActions", [("blue", _("display phonebook"))]))
1071
1072                 ConfigListScreen.__init__(self, self.list, session = session)
1073                 self.createSetup()
1074
1075
1076         def keyLeft(self):
1077                 ConfigListScreen.keyLeft(self)
1078                 self.createSetup()
1079
1080         def keyRight(self):
1081                 ConfigListScreen.keyRight(self)
1082                 self.createSetup()
1083
1084         def createSetup(self):
1085                 self.list = [ ]
1086                 self.list.append(getConfigListEntry(_("Call monitoring"), config.plugins.FritzCall.enable))
1087                 if config.plugins.FritzCall.enable.value:
1088                         self.list.append(getConfigListEntry(_("FRITZ!Box FON address (Name or IP)"), config.plugins.FritzCall.hostname))
1089
1090                         self.list.append(getConfigListEntry(_("Show after Standby"), config.plugins.FritzCall.afterStandby))
1091
1092                         self.list.append(getConfigListEntry(_("Show Calls for specific MSN"), config.plugins.FritzCall.filter))
1093                         if config.plugins.FritzCall.filter.value:
1094                                 self.list.append(getConfigListEntry(_("MSN to show (separated by ,)"), config.plugins.FritzCall.filtermsn))
1095
1096                         self.list.append(getConfigListEntry(_("Show Outgoing Calls"), config.plugins.FritzCall.showOutgoing))
1097                         if config.plugins.FritzCall.showOutgoing.value:
1098                                 self.list.append(getConfigListEntry(_("Areacode to add to Outgoing Calls (if necessary)"), config.plugins.FritzCall.prefix))
1099                         self.list.append(getConfigListEntry(_("Timeout for Call Notifications (seconds)"), config.plugins.FritzCall.timeout))
1100                         self.list.append(getConfigListEntry(_("Reverse Lookup Caller ID (select country below)"), config.plugins.FritzCall.lookup))
1101                         if config.plugins.FritzCall.lookup.value:
1102                                 self.list.append(getConfigListEntry(_("Country"), config.plugins.FritzCall.country))
1103
1104                         self.list.append(getConfigListEntry(_("Password Accessing FRITZ!Box"), config.plugins.FritzCall.password))
1105                         self.list.append(getConfigListEntry(_("Read PhoneBook from FRITZ!Box"), config.plugins.FritzCall.fritzphonebook))
1106                         if config.plugins.FritzCall.fritzphonebook.value:
1107                                 self.list.append(getConfigListEntry(_("Append type of number"), config.plugins.FritzCall.showType))
1108                                 self.list.append(getConfigListEntry(_("Append shortcut number"), config.plugins.FritzCall.showShortcut))
1109                                 self.list.append(getConfigListEntry(_("Append vanity name"), config.plugins.FritzCall.showVanity))
1110
1111                         self.list.append(getConfigListEntry(_("Use internal PhoneBook"), config.plugins.FritzCall.phonebook))
1112                         if config.plugins.FritzCall.phonebook.value:
1113                                 self.list.append(getConfigListEntry(_("PhoneBook Location"), config.plugins.FritzCall.phonebookLocation))
1114                                 if config.plugins.FritzCall.lookup.value:
1115                                         self.list.append(getConfigListEntry(_("Automatically add new Caller to PhoneBook"), config.plugins.FritzCall.addcallers))
1116
1117                         self.list.append(getConfigListEntry(_("Strip Leading 0"), config.plugins.FritzCall.internal))
1118                         # self.list.append(getConfigListEntry(_("Default display mode for FRITZ!Box calls"), config.plugins.FritzCall.fbfCalls))
1119
1120                 self["config"].list = self.list
1121                 self["config"].l.setList(self.list)
1122
1123         def save(self):
1124 #               print "[FritzCallSetup] save"
1125                 for x in self["config"].list:
1126                         x[1].save()
1127                 if fritz_call is not None:
1128                         fritz_call.connect()
1129                         print "[FritzCallSetup] called phonebook.reload()"
1130                         phonebook.reload()
1131
1132                 self.close()
1133
1134         def cancel(self):
1135 #               print "[FritzCallSetup] cancel"
1136                 for x in self["config"].list:
1137                         x[1].cancel()
1138                 self.close()
1139
1140         def displayCalls(self):
1141                 self.session.open(FritzDisplayCalls)
1142
1143         def displayPhonebook(self):
1144                 self.session.open(phonebook.displayPhonebook)
1145
1146
1147 standbyMode = False
1148
1149 class FritzCallList:
1150         def __init__(self):
1151                 self.callList = [ ]
1152         
1153         def add(self, event, date, number, caller, phone):
1154                 print "[FritzCallList] add"
1155                 if len(self.callList) > 10:
1156                         if self.callList[0] != "Start":
1157                                 self.callList[0] = "Start"
1158                         del self.callList[1]
1159
1160                 self.callList.append((event, number, date, caller, phone))
1161         
1162         def display(self):
1163                 print "[FritzCallList] display"
1164                 global standbyMode
1165                 global my_global_session
1166                 standbyMode = False
1167                 # Standby.inStandby.onClose.remove(self.display) object does not exist anymore...
1168                 # build screen from call list
1169                 text = "\n"
1170                 if self.callList[0] == "Start":
1171                         text = text + _("Last 10 calls:\n")
1172                         del self.callList[0]
1173
1174                 for call in self.callList:
1175                         (event, number, date, caller, phone) = call
1176                         if event == "RING":
1177                                 direction = "->"
1178                         else:
1179                                 direction = "<-"
1180                         found = re.match(".*(\d\d.\d\d.)\d\d( \d\d:\d\d)", date)
1181                         if found: date = found.group(1) + found.group(2)
1182                         found = re.match(".*\((.*)\)", phone)
1183                         if found: phone = found.group(1)
1184                         # if len(phone) > 20: phone = phone[:20]
1185
1186                         if caller == _("UNKNOWN") and number != "":
1187                                 caller = number
1188                         else:
1189                                 found = re.match("(.*)\n.*", caller)
1190                                 if found: caller = found.group(1)
1191                         # if len(caller) > 20: caller = caller[:20]
1192                         while (len(caller) + len(phone)) > 40:
1193                                 if len(caller) > len(phone):
1194                                         caller = caller[:-1]
1195                                 else:
1196                                         phone = phone[:-1]
1197
1198                         text = text + "%s %s %s %s\n" %(date, caller, direction, phone)
1199
1200                 print "[FritzCallList] display: '%s %s %s %s'" %(date, caller, direction, phone)
1201                 # display screen
1202                 Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_INFO)
1203                 # my_global_session.open(FritzDisplayCalls, text) # TODO please HELP: from where can I get a session?
1204                 self.callList = [ ]
1205                 self.text = ""
1206
1207 callList = FritzCallList()
1208
1209 def notifyCall(event, date, number, caller, phone):
1210         if Standby.inStandby is None or config.plugins.FritzCall.afterStandby.value == "each":
1211                 if event == "RING":
1212                         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 }
1213                 else:
1214                         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 }
1215                 print "[FritzCall] notifyCall:\n%s" %text
1216                 Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_INFO, timeout=config.plugins.FritzCall.timeout.value)
1217         elif config.plugins.FritzCall.afterStandby.value == "inList":
1218                 #
1219                 # if not yet done, register function to show call list
1220                 global standbyMode
1221                 if not standbyMode :
1222                         standbyMode = True
1223                         Standby.inStandby.onHide.append(callList.display)
1224                 # add text/timeout to call list
1225                 callList.add(event, date, number, caller, phone)
1226                 print "[FritzCall] notifyCall: added to callList"
1227         else: # this is the "None" case
1228                 print "[FritzCall] notifyCall: standby and no show"
1229
1230
1231 #===============================================================================
1232 #               We need a separate class for each invocation of reverseLookup to retain
1233 #               the necessary data for the notification
1234 #===============================================================================
1235
1236 countries = { }
1237
1238 class FritzReverseLookupAndNotifier:
1239         def __init__(self, event, number, caller, phone, date):
1240                 print "[FritzReverseLookupAndNotifier] reverse Lookup for %s!" %number
1241                 self.event = event
1242                 self.number = number
1243                 self.caller = caller
1244                 self.phone = phone
1245                 self.date = date
1246                 self.currentWebsite = None
1247                 self.nextWebsiteNo = 0
1248
1249                 if not countries:
1250                         dom = parse(resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/reverselookup.xml"))
1251                         for top in dom.getElementsByTagName("reverselookup"):
1252                                 for country in top.getElementsByTagName("country"):
1253                                         code = country.getAttribute("code").replace("+","00")
1254                                         countries[code] = country.getElementsByTagName("website")
1255
1256                 self.countrycode = config.plugins.FritzCall.country.value
1257
1258                 if number[0] != "0":
1259                         self.caller = _("UNKNOWN")
1260                         self.notifyAndReset()
1261                         return
1262
1263                 if self.number[:2] == "00":
1264                         if countries.has_key(self.number[:3]):   #      e.g. USA
1265                                 self.countrycode = self.number[:3]
1266                         elif countries.has_key(self.number[:4]):
1267                                 self.countrycode = self.number[:4]
1268                         elif countries.has_key(self.number[:5]):
1269                                 self.countrycode = self.number[:5]
1270                         else:
1271                                 print "[FritzReverseLookupAndNotifier] Country cannot be reverse handled"
1272                                 self.caller = _("UNKNOWN")
1273                                 self.notifyAndReset()
1274                                 return
1275
1276                 if countries.has_key(self.countrycode):
1277                         print "[FritzReverseLookupAndNotifier] Found website for reverse lookup"
1278                         self.websites = countries[self.countrycode]
1279                         self.nextWebsiteNo = 1
1280                         self.handleWebsite(self.websites[0])
1281                 else:
1282                         print "[FritzReverseLookupAndNotifier] Country cannot be reverse handled"
1283                         self.caller = _("UNKNOWN")
1284                         self.notifyAndReset()
1285                         return
1286
1287         def handleWebsite(self, website):
1288                 print "[FritzReverseLookupAndNotifier] handleWebsite: " + website.getAttribute("name")
1289                 if self.number[:2] == "00":
1290                         number = website.getAttribute("prefix") + self.number.replace(self.countrycode,"")
1291                 else:
1292                         number = self.number
1293
1294                 url = website.getAttribute("url")
1295                 if re.search('$AREACODE',url) or re.search('$PFXAREACODE',url):
1296                         print "[FritzReverseLookupAndNotifier] handleWebsite: (PFX)ARECODE cannot be handled"
1297                         self.caller = _("UNKNOWN")
1298                         self.notifyAndReset()
1299                         return
1300                 #
1301                 # Apparently, there is no attribute called (pfx)areacode anymore
1302                 # So, this below will not work.
1303                 #
1304                 if re.search('\\$AREACODE',url) and website.hasAttribute("areacode"):
1305                         areaCodeLen = int(website.getAttribute("areacode"))
1306                         url = url.replace("$AREACODE","%(areacode)s").replace("$NUMBER","%(number)s")
1307                         url = url %{ 'areacode':number[:areaCodeLen], 'number':number[areaCodeLen:] }
1308                 elif re.search('\\$PFXAREACODE',url) and website.hasAttribute("pfxareacode"):
1309                         areaCodeLen = int(website.getAttribute("pfxareacode"))
1310                         url = url.replace("$PFXAREACODE","%(pfxareacode)s").replace("$NUMBER","%(number)s")
1311                         url = url %{ 'pfxareacode':number[:areaCodeLen], 'number':number[areaCodeLen:] }
1312                 elif re.search('\\$NUMBER',url): 
1313                         url = url.replace("$NUMBER","%s") %number
1314                 else:
1315                         print "[FritzReverseLookupAndNotifier] handleWebsite: cannot handle websites with no $NUMBER in url"
1316                         self.caller = _("UNKNOWN")
1317                         self.notifyAndReset()
1318                         return
1319                 print "[FritzReverseLookupAndNotifier] Url to query: " + url
1320                 url = url.encode("UTF-8", "replace")
1321                 self.currentWebsite = website
1322                 getPage(url, method="GET").addCallback(self._gotPage).addErrback(self._gotError)
1323
1324         def _gotPage(self, page):
1325                 print "[FritzReverseLookupAndNotifier] _gotPage"
1326                 found = re.match('.*content=".*?charset=([^"]+)"',page,re.S)
1327                 if found:
1328                         print "[FritzReverseLookupAndNotifier] Charset: " + found.group(1)
1329                         page = page.replace("\xa0"," ").decode(found.group(1), "replace")
1330                 else:
1331                         page = page.replace("\xa0"," ").decode("ISO-8859-1", "replace")
1332
1333                 for entry in self.currentWebsite.getElementsByTagName("entry"):
1334                         # print "[FritzReverseLookupAndNotifier] _gotPage: try entry"
1335                         details = []
1336                         for what in ["name", "street", "city", "zipcode"]:
1337                                 pat = "(.*)" + self.getPattern(entry, what)
1338                                 # print "[FritzReverseLookupAndNotifier] _gotPage: look for '''%s''' with '''%s'''" %( what, pat )
1339                                 found = re.match(pat, page, re.S|re.M)
1340                                 if found:
1341                                         # print "[FritzReverseLookupAndNotifier] _gotPage: found for '''%s''': '''%s'''" %( what, found.group(2) )
1342                                         item = found.group(2).replace("&nbsp;"," ").replace("</b>","").replace(",","")
1343                                         item = html2utf8(item)
1344                                         details.append(item.strip())
1345                                         # print "[FritzReverseLookupAndNotifier] _gotPage: got '''%s''': '''%s'''" %( what, item.strip() )
1346                                 else:
1347                                         break
1348
1349                         if len(details) != 4:
1350                                 continue
1351                         else:
1352                                 name = details[0]
1353                                 address =  details[1] + ", " + details[3] + " " + details[2]
1354                                 print "[FritzReverseLookupAndNotifier] _gotPage: Reverse lookup succeeded:\nName: %s\nAddress: %s" %(name, address)
1355                                 self.caller = "%s, %s" %(name, address)
1356                                 if self.number != 0 and config.plugins.FritzCall.addcallers.value and self.event == "RING":
1357                                         phonebook.add(self.number, self.caller)
1358
1359                                 self.caller = self.caller.replace(", ", "\n").encode("UTF-8", "replace")
1360                                 self.notifyAndReset()
1361                                 return True
1362                                 break
1363                 else:
1364                         self._gotError("[FritzReverseLookupAndNotifier] _gotPage: Nothing found at %s" %self.currentWebsite.getAttribute("name"))
1365                         
1366         def _gotError(self, error = ""):
1367                 print "[FritzReverseLookupAndNotifier] _gotError - Error: %s" %error
1368                 if self.nextWebsiteNo >= len(self.websites):
1369                         print "[FritzReverseLookupAndNotifier] _gotError: I give up"
1370                         self.caller = _("UNKNOWN")
1371                         self.notifyAndReset()
1372                         return
1373                 else:
1374                         print "[FritzReverseLookupAndNotifier] _gotError: try next website"
1375                         self.nextWebsiteNo = self.nextWebsiteNo+1
1376                         self.handleWebsite(self.websites[self.nextWebsiteNo-1])
1377
1378         def getPattern(self, website, which):
1379                 pat1 = website.getElementsByTagName(which)
1380                 if len(pat1) > 1:
1381                         print "Something strange: more than one %s for website %s" %(which, website.getAttribute("name"))
1382                 return pat1[0].childNodes[0].data
1383
1384         def notifyAndReset(self, timeout=config.plugins.FritzCall.timeout.value):
1385                 notifyCall(self.event, self.date, self.number, self.caller, self.phone)
1386                 # kill that object...
1387
1388
1389 class FritzProtocol(LineReceiver):
1390         def __init__(self):
1391                 print "[FritzProtocol] __init__"
1392                 self.resetValues()
1393
1394         def resetValues(self):
1395                 print "[FritzProtocol] resetValues"
1396                 self.number = '0'
1397                 self.caller = None
1398                 self.phone = None
1399                 self.date = '0'
1400
1401         def notifyAndReset(self, timeout=config.plugins.FritzCall.timeout.value):
1402                 notifyCall(self.event, self.date, self.number, self.caller, self.phone)
1403                 self.resetValues()
1404
1405         def lineReceived(self, line):
1406                 print "[FritzProtocol] lineReceived: %s" %line
1407 #15.07.06 00:38:54;CALL;1;4;<from/extern>;<to/our msn>;
1408 #15.07.06 00:38:58;DISCONNECT;1;0;
1409 #15.07.06 00:39:22;RING;0;<from/extern>;<to/our msn>;
1410 #15.07.06 00:39:27;DISCONNECT;0;0;
1411                 a = []
1412                 a = line.split(';')
1413                 (self.date, self.event) = a[0:2]
1414
1415                 if self.event == "RING" or (self.event == "CALL" and config.plugins.FritzCall.showOutgoing.value):
1416                         phone = a[4]
1417                          
1418                         if self.event == "RING":
1419                                 number = a[3] 
1420                         else:
1421                                 number = a[5]
1422                                 
1423                         print "[FritzProtocol] lineReceived phone: '''%s''' number: '''%s'''" % (phone, number)
1424
1425                         filtermsns = config.plugins.FritzCall.filtermsn.value.split(",")
1426                         for i in range(len(filtermsns)):
1427                                 filtermsns[i] = filtermsns[i].strip()
1428                         if not (config.plugins.FritzCall.filter.value and phone not in filtermsns):
1429                                 print "[FritzProtocol] lineReceived no filter hit"
1430                                 phonename = phonebook.search(phone)                # do we have a name for the number of our side?
1431                                 if phonename is not None:
1432                                         self.phone = "%s (%s)" %(phone, phonename)
1433                                 else:
1434                                         self.phone = phone
1435
1436                                 if config.plugins.FritzCall.internal.value and len(number) > 3 and number[0]=="0":
1437                                         self.number = number[1:]
1438                                 else:
1439                                         self.number = number
1440
1441                                 if self.event == "CALL" and self.number[0] != '0':                                        # should only happen for outgoing
1442                                         self.number = config.plugins.FritzCall.prefix.value + self.number
1443
1444                                 if self.number is not "":
1445                                         print "[FritzProtocol] lineReceived phonebook.search: %s" %self.number
1446                                         self.caller = phonebook.search(self.number)
1447                                         print "[FritzProtocol] lineReceived phonebook.search reault: %s" %self.caller
1448                                         if (self.caller is None) and config.plugins.FritzCall.lookup.value:
1449                                                 FritzReverseLookupAndNotifier(self.event, self.number, self.caller, self.phone, self.date)
1450                                                 return                                                  # reverselookup is supposed to handle the message itself 
1451
1452                                 if self.caller is None:
1453                                         self.caller = _("UNKNOWN")
1454
1455                                 self.notifyAndReset()
1456
1457 class FritzClientFactory(ReconnectingClientFactory):
1458         initialDelay = 20
1459         maxDelay = 500
1460
1461         def __init__(self):
1462                 self.hangup_ok = False
1463
1464         def startedConnecting(self, connector):
1465                 Notifications.AddNotification(MessageBox, _("Connecting to FRITZ!Box..."), type=MessageBox.TYPE_INFO, timeout=2)
1466
1467         def buildProtocol(self, addr):
1468                 Notifications.AddNotification(MessageBox, _("Connected to FRITZ!Box!"), type=MessageBox.TYPE_INFO, timeout=4)
1469                 self.resetDelay()
1470                 return FritzProtocol()
1471
1472         def clientConnectionLost(self, connector, reason):
1473                 if not self.hangup_ok:
1474                         Notifications.AddNotification(MessageBox, _("Connection to FRITZ!Box! lost\n (%s)\nretrying...") % reason.getErrorMessage(), type=MessageBox.TYPE_INFO, timeout=config.plugins.FritzCall.timeout.value)
1475                 ReconnectingClientFactory.clientConnectionLost(self, connector, reason)
1476
1477         def clientConnectionFailed(self, connector, reason):
1478                 Notifications.AddNotification(MessageBox, _("Connecting to FRITZ!Box failed\n (%s)\nretrying...") % reason.getErrorMessage(), type=MessageBox.TYPE_INFO, timeout=config.plugins.FritzCall.timeout.value)
1479                 ReconnectingClientFactory.clientConnectionFailed(self, connector, reason)
1480
1481 class FritzCall:
1482         def __init__(self):
1483                 self.dialog = None
1484                 self.d = None
1485                 self.connect()
1486
1487         def connect(self):
1488                 self.abort()
1489                 if config.plugins.FritzCall.enable.value:
1490                         f = FritzClientFactory()
1491                         self.d = (f, reactor.connectTCP(config.plugins.FritzCall.hostname.value, 1012, f))
1492
1493         def shutdown(self):
1494                 self.abort()
1495
1496         def abort(self):
1497                 if self.d is not None:
1498                         self.d[0].hangup_ok = True
1499                         self.d[0].stopTrying()
1500                         self.d[1].disconnect()
1501                         self.d = None
1502
1503 def displayCalls(session, servicelist):
1504         session.open(FritzDisplayCalls)
1505
1506 def displayPhonebook(session, servicelist):
1507         session.open(phonebook.displayPhonebook)
1508
1509 def main(session):
1510         session.open(FritzCallSetup)
1511
1512 fritz_call = None
1513
1514 def autostart(reason, **kwargs):
1515         global fritz_call
1516
1517         # ouch, this is a hack
1518         if kwargs.has_key("session"):
1519                 global my_global_session
1520                 my_global_session = kwargs["session"]
1521                 return
1522
1523         print "[FRITZ!Call] - Autostart"
1524         if reason == 0:
1525                 fritz_call = FritzCall()
1526         elif reason == 1:
1527                 fritz_call.shutdown()
1528                 fritz_call = None
1529
1530 def Plugins(**kwargs):
1531         what = _("Display FRITZ!box-Fon calls on screen")
1532         what_calls = _("Phone calls")
1533         what_phonebook = _("Phonebook")
1534         return [ PluginDescriptor(name="FritzCall", description=what, where = PluginDescriptor.WHERE_PLUGINMENU, icon = "plugin.png", fnc=main),
1535                 PluginDescriptor(name=what_calls, description=what_calls, where = PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=displayCalls),
1536                 PluginDescriptor(name=what_phonebook, description=what_phonebook, where = PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=displayPhonebook),
1537                 PluginDescriptor(where = [PluginDescriptor.WHERE_SESSIONSTART, PluginDescriptor.WHERE_AUTOSTART], fnc = autostart) ]