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