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