- Added choices to the list with calls from the FBF: all, in, missed, out
[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 import Standby
5
6 from Components.ActionMap import ActionMap
7 from Components.Label import Label
8 from Components.Button import Button
9 from Components.config import config, ConfigSubsection, ConfigSelection, ConfigIP, ConfigEnableDisable, getConfigListEntry, ConfigText, ConfigInteger
10 from Components.ConfigList import ConfigListScreen
11 from Components.ScrollLabel import ScrollLabel
12
13 from Plugins.Plugin import PluginDescriptor
14 from Tools import Notifications
15
16 from twisted.internet import reactor
17 from twisted.internet.protocol import ReconnectingClientFactory
18 from twisted.protocols.basic import LineReceiver
19 from twisted.web.client import getPage
20
21 from xml.dom.minidom import parse
22
23 from os import path as os_path
24 from urllib import urlencode 
25 import re, time
26
27 import gettext
28 from Tools.Directories import resolveFilename, SCOPE_PLUGINS
29 try:
30                 _ = gettext.translation('FritzCall', resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/locale"), [config.osd.language.getText()]).gettext
31 except IOError:
32                 pass
33
34
35 my_global_session = None
36
37 config.plugins.FritzCall = ConfigSubsection()
38 config.plugins.FritzCall.enable = ConfigEnableDisable(default = False)
39 config.plugins.FritzCall.hostname = ConfigIP(default = [192, 168, 178, 1])
40 config.plugins.FritzCall.afterStandby = ConfigSelection(choices = [("none", _("show nothing")), ("inList", _("show as list")), ("each", _("show each call"))])
41 config.plugins.FritzCall.filter = ConfigEnableDisable(default = False)
42 config.plugins.FritzCall.filtermsn = ConfigText(default = "", fixed_size = False)
43 config.plugins.FritzCall.filtermsn.setUseableChars('0123456789,')
44 config.plugins.FritzCall.showOutgoing = ConfigEnableDisable(default = False)
45 config.plugins.FritzCall.timeout = ConfigInteger(default = 15, limits = (0,60))
46 config.plugins.FritzCall.lookup = ConfigEnableDisable(default = False)
47 config.plugins.FritzCall.internal = ConfigEnableDisable(default = False)
48 config.plugins.FritzCall.fritzphonebook = ConfigEnableDisable(default = False)
49 config.plugins.FritzCall.phonebook = ConfigEnableDisable(default = False)
50 config.plugins.FritzCall.addcallers = ConfigEnableDisable(default = False)
51 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"))])
52 config.plugins.FritzCall.password = ConfigText(default = "", fixed_size = False)
53 config.plugins.FritzCall.showType = ConfigEnableDisable(default = True)
54 config.plugins.FritzCall.showShortcut = ConfigEnableDisable(default = False)
55 config.plugins.FritzCall.showVanity = ConfigEnableDisable(default = False)
56 config.plugins.FritzCall.prefix = ConfigText(default = "", fixed_size = False)
57 config.plugins.FritzCall.prefix.setUseableChars('0123456789')
58
59 countryCodes = [
60         ("0049", _("Germany")),
61         ("0031", _("The Netherlands")),
62         ("0033", _("France")),
63         ("0039", _("Italy")),
64         ("0041", _("Switzerland")),
65         ("0043", _("Austria"))
66         ]
67 config.plugins.FritzCall.country = ConfigSelection(choices = countryCodes)
68
69 FBF_ALL_CALLS = "."
70 FBF_IN_CALLS = "1"
71 FBF_MISSED_CALLS = "2"
72 FBF_OUT_CALLS = "3"
73 fbfCallsChoices = {FBF_ALL_CALLS: _("All calls"),
74                                    FBF_IN_CALLS: _("Incoming calls"),
75                                    FBF_MISSED_CALLS: _("Missed calls"),
76                                    FBF_OUT_CALLS: _("Outgoing calls")
77                                    }
78 config.plugins.FritzCall.fbfCalls = ConfigSelection(choices = fbfCallsChoices)
79
80 def html2utf8(in_html):
81         try:
82                 import htmlentitydefs
83                 htmlentitynumbermask = re.compile('(&#(\d{1,5}?);)')
84                 htmlentitynamemask = re.compile('(&(\D{1,5}?);)')
85                 entities = htmlentitynamemask.finditer(in_html)
86                 entitydict = {}
87                 for x in entities:
88                         entitydict[x.group(1)] = x.group(2)
89                 for key, name in entitydict.items():
90                         try:
91                                 entitydict[key] = htmlentitydefs.name2codepoint[name]
92                         except KeyError:
93                                 pass
94                 entities = htmlentitynumbermask.finditer(in_html)
95                 for x in entities:
96                         entitydict[x.group(1)] = x.group(2)
97                 for key, codepoint in entitydict.items():
98                         try:
99                                 in_html = in_html.replace(key, (unichr(int(codepoint)).encode('utf8', "replace")))
100                         except ValueError:
101                                 pass
102         except ImportError:
103                 return in_html.replace("&", "&").replace("ß", "").replace("ä", "").replace("ö", "").replace("ü", "").replace("Ä", "").replace("Ö", "").replace("Ü", "")
104         return in_html
105
106
107 class FritzCallFBF:
108         def __init__(self):
109                 print "[FritzCallFBF] __init__"
110                 self.callScreen= None
111                 self.loggedIn = False
112                 self.Callback = None
113                 self.loginCallback = None
114                 self.timestamp = 0
115                 self.callList = []
116                 self.callType = config.plugins.FritzCall.fbfCalls.value
117
118         def notify(self, text):
119                 print "[FritzCallFBF] notify"
120                 if self.callScreen:
121                         print "[FritzCallFBF] notify: try to close callScreen"
122                         self.callScreen.close()
123                         self.callScreen = None
124                 Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_ERROR, timeout=config.plugins.FritzCall.timeout.value)
125
126         def errorLogin(self, error):
127                 text = _("FRITZ!Box Login failed! - Error: %s") %error
128                 self.notify(text)
129
130         def _gotPageLogin(self, html):
131 #               print "[FritzCallPhonebook] _gotPageLogin"
132                 # workaround: exceptions in gotPage-callback were ignored
133                 if self.callScreen:
134                         self.callScreen.updateStatus(_("Getting calls from FRITZ!Box...") + _("login verification"))
135                 try:
136                         print "[FritzCallFBF] _gotPageLogin: verify login"
137                         found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;Das angegebene Kennwort', html, re.S)
138                         if found:
139                                 text = _("FRITZ!Box Login failed! - Wrong Password!")
140                                 self.notify(text)
141                         else:
142                                 if self.callScreen:
143                                         self.callScreen.updateStatus(_("Getting calls from FRITZ!Box...") + _("login ok"))
144                                 self.loggedIn = True
145                                 self.loginCallback()
146                         loginCallback = None
147                 except:
148                         import traceback, sys
149                         traceback.print_exc(file=sys.stdout)
150                         #raise e
151
152         def login(self):
153                 print "[FritzCallFBF] Login"
154                 if config.plugins.FritzCall.password.value != "":
155                         if self.callScreen:
156                                 self.callScreen.updateStatus(_("Getting calls from FRITZ!Box...") + _("login"))
157                         host = "%d.%d.%d.%d" %tuple(config.plugins.FritzCall.hostname.value)
158                         uri =  "/cgi-bin/webcm"
159                         parms = "login:command/password=%s" %(config.plugins.FritzCall.password.value)
160                         url = "http://%s%s" %(host, uri)
161                         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)
162                 else:
163                         self.loginCallback()
164                         self.loginCallback = None
165
166         def errorLoad(self, error):
167                 text = _("Could not load phonebook from FRITZ!Box - Error: %s") %error
168                 self.notify(text)
169
170         def _gotPageLoad(self, html):
171                 print "[FritzCallFBF] _gotPageLoad"
172                 # workaround: exceptions in gotPage-callback were ignored
173                 try:
174                         self.parseFritzBoxPhonebook(html)
175                 except:
176                         import traceback, sys
177                         traceback.print_exc(file=sys.stdout)
178                         #raise e
179
180         def loadFritzBoxPhonebook(self):
181                 print "[FritzCallFBF] loadFritzBoxPhonebook"
182                 if config.plugins.FritzCall.fritzphonebook.value:
183                         print "[FritzCallFBF] loadFritzBoxPhonebook: logging in"
184                         self.loginCallback = self._loadFritzBoxPhonebook
185                         self.login()
186
187         def _loadFritzBoxPhonebook(self):
188                         host = "%d.%d.%d.%d" %tuple(config.plugins.FritzCall.hostname.value)
189                         uri = "/cgi-bin/webcm"# % tuple(config.plugins.FritzCall.hostname.value)
190                         parms = urlencode({'getpage':'../html/de/menus/menu2.html', 'var:lang':'de','var:pagename':'fonbuch','var:menu':'fon'})
191                         url = "http://%s%s?%s" %(host, uri, parms)
192
193                         getPage(url).addCallback(self._gotPageLoad).addErrback(self.errorLoad)
194
195         def parseFritzBoxPhonebook(self, html):
196                 print "[FritzCallFBF] parseFritzBoxPhonebook"
197
198                 table = html2utf8(html.replace("\xa0"," ").decode("ISO-8859-1", "replace"))
199                 if re.search('TrFonName', table):
200                         #===============================================================================
201                         #                                New Style: 7170 / 7270 (FW 54.04.58, 54.04.63-11941) 
202                         #       We expect one line with TrFonName followed by several lines with
203                         #       TrFonNr(Type,Number,Shortcut,Vanity), which all belong to the name in TrFonName.
204                         #===============================================================================
205                         # entrymask = re.compile('(TrFonName\("[^"]+", "[^"]+", "[^"]+"\);</SCRIPT>\s+[<SCRIPT type=text/javascript>TrFonNr\("[^"]+", "[^"]+", "[^"]+", "[^"]+"\);</SCRIPT>\s+]+)<SCRIPT type=text/javascript>document.write(TrFon1());</SCRIPT>', re.DOTALL)
206                         # entrymask = re.compile('(TrFonName\("[^"]+", "[^"]+", "[^"]+"\);.*?[.*?TrFonNr\("[^"]+", "[^"]+", "[^"]+", "[^"]+"\);.*?]+).*?document.write(TrFon1());', re.DOTALL)
207                         entrymask = re.compile('(TrFonName\("[^"]+", "[^"]+", "[^"]*"\);.*?)TrFon1\(\)', re.S)
208                         entries = entrymask.finditer(html)
209                         for entry in entries:
210                                 # print entry.group(1)
211                                 found = re.match('TrFonName\("[^"]*", "([^"]+)", "[^"]*"\);', entry.group(1))
212                                 if found:
213                                         name = found.group(1)
214                                 else:
215                                         continue
216                                 detailmask = re.compile('TrFonNr\("([^"]*)", "([^"]*)", "([^"]*)", "([^"]*)"\);', re.S)
217                                 details = detailmask.finditer(entry.group(1))
218                                 for found in details:
219                                         thisname = name
220
221                                         type = found.group(1)
222                                         if config.plugins.FritzCall.showType.value:
223                                                 if type == "mobile":
224                                                         thisname = thisname + " (" +_("mobile") + ")"
225                                                 elif type == "home":
226                                                         thisname = thisname + " (" +_("home") + ")"
227                                                 elif type == "work":
228                                                         thisname = thisname + " (" +_("work") + ")"
229
230                                         if config.plugins.FritzCall.showShortcut.value and found.group(3):
231                                                 thisname = thisname + ", " + _("Shortcut") + ": " + found.group(3)
232                                         if config.plugins.FritzCall.showVanity.value and found.group(4):
233                                                 thisname = thisname + ", " + _("Vanity") + ": " + found.group(4)
234
235                                         thisnumber = found.group(2).strip()
236                                         thisname = html2utf8(thisname.strip())
237                                         if thisnumber:
238                                                 print "[FritzCallFBF] Adding '''%s''' with '''%s''' from FRITZ!Box Phonebook!" %(thisname, thisnumber)
239                                                 phonebook.phonebook[thisnumber] = thisname
240                                         else:
241                                                 print "[FritzCallFBF] ignoring empty number for %s" %thisname
242                                         continue
243
244                 elif re.search('TrFon', table):
245                         #===============================================================================
246                         #                               Old Style: 7050 (FW 14.04.33)
247                         #       We expect one line with TrFon(No,Name,Number,Shortcut,Vanity)
248                         #===============================================================================                                
249                         entrymask = re.compile('TrFon\("[^"]*", "([^"]*)", "([^"]*)", "([^"]*)", "([^"]*)"\)', re.S)
250                         entries = entrymask.finditer(html)
251                         for found in entries:
252                                 name = found.group(1).strip()
253                                 thisnumber = found.group(2).strip()
254                                 if config.plugins.FritzCall.showShortcut.value and found.group(3):
255                                         name = name + ", " + _("Shortcut") + ": " + found.group(3)
256                                 if config.plugins.FritzCall.showVanity.value and found.group(4):
257                                         name = name + ", " +_("Vanity") +": " + found.group(4)
258                                 if thisnumber:
259                                         name = html2utf8(name)
260                                         print "[FritzCallFBF] Adding '''%s''' with '''%s''' from FRITZ!Box Phonebook!" %(name, thisnumber)
261                                         phonebook.phonebook[thisnumber] = name
262                                 else:
263                                         print "[FritzCallFBF] ignoring empty number for %s" %name
264                                 continue
265                 else:
266                         self.notify(_("Could not parse FRITZ!Box Phonebook entry"))
267
268         def errorCalls(self, error):
269                 text = _("Could not load calls from FRITZ!Box - Error: %s") %error
270                 self.notify(text)
271
272         def _gotPageCalls(self, csv = ""):
273                 def _resolveNumber(number):
274                         if number.isdigit():
275                                 if config.plugins.FritzCall.internal.value and len(number) > 3 and number[0]=="0": number = number[1:]
276                                 name = phonebook.search(number)
277                                 if name:
278                                         found = re.match('(.*?)\n.*', name)
279                                         if found:
280                                                 name = found.group(1)
281                                         number = name
282                         elif number == "":
283                                 number = _("UNKNOWN")
284                         # if len(number) > 20: number = number[:20]
285                         return number
286
287                 if csv:
288                         print "[FritzCallFBF] _gotPageCalls: got csv, setting callList"
289                         if self.callScreen:
290                                 self.callScreen.updateStatus(_("Getting calls from FRITZ!Box...") + _("done"))
291                         # check for error: wrong password or password not set... TODO
292                         found = re.search('Melden Sie sich mit dem Kennwort der FRITZ!Box an', csv)
293                         if found:
294                                 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.")
295                                 # self.session.open(MessageBox, text, MessageBox.TYPE_ERROR, timeout=config.plugins.FritzCall.timeout.value)
296                                 self.notify(text)
297                                 return
298
299                         csv = csv.decode('iso-8859-1','replace').encode('utf-8','replace')
300                         lines = csv.splitlines()
301                         self.callList = lines
302                 elif self.callList:
303                         print "[FritzCallFBF] _gotPageCalls: got no csv, but have callList"
304                         if self.callScreen:
305                                 self.callScreen.updateStatus(_("Getting calls from FRITZ!Box...") + _("done, using last list"))
306                         lines = self.callList
307                 else:
308                         print "[FritzCallFBF] _gotPageCalls: got no csv, no callList, leaving"
309                         return
310                         
311                 text = ""
312                 noCalls = 0
313                 for line in lines:
314                         # print line
315                         # Typ;Datum;Name;Rufnummer;Nebenstelle;Eigene Rufnummer;Dauer
316                         found = re.match("^(" + self.callType + ");([^;]*);([^;]*);([^;]*);([^;]*);([^;]*)", line)
317                         if found:
318                                 direct = found.group(1)
319                                 date = found.group(2)
320                                 if direct != FBF_OUT_CALLS and found.group(3):
321                                         caller = found.group(3)
322                                 else:
323                                         caller = _resolveNumber(found.group(4))
324                                 found1 = re.match('Internet: (.*)', found.group(6))
325                                 if found1:
326                                         callee = _resolveNumber(found1.group(1))
327                                 else:
328                                         callee = _resolveNumber(found.group(6))
329                                 while (len(caller) + len(callee)) > 40:
330                                         if len(caller) > len(callee):
331                                                 caller = caller[:-1]
332                                         else:
333                                                 callee = callee[:-1]
334                                 found = re.match("(\d\d.\d\d.)\d\d( \d\d:\d\d)", date)
335                                 if found: date = found.group(1) + found.group(2)
336                                 if direct == FBF_OUT_CALLS:
337                                         text = text + date + "\t" + callee + " -> " + caller + "\n"
338                                 else:
339                                         text = text + date + "\t" + caller + " -> " + callee + "\n"
340                                 noCalls += 1
341
342                 # print "[FritzCallFBF] _gotPageCalls result:\n" + text
343
344                 if self.Callback is not None:
345                         # print "[FritzCallFBF] _gotPageCalls call callback with\n" + text
346                         self.Callback(text = text, count = noCalls)
347                         self.Callback = None
348                 self.callScreen = None
349
350         def getCalls(self, callScreen, callback, type):
351                 #
352                 # call sequence must be:
353                 # - login
354                 # - getPage -> _gotPageLogin
355                 # - loginCallback (_getCalls)
356                 # - getPage -> _getCalls1
357                 print "[FritzCallFBF] getCalls"
358                 self.callScreen = callScreen
359                 self.callType = type
360                 self.Callback = callback
361                 if (time.time() - self.timestamp) > 180: 
362                         print "[FritzCallFBF] getCalls: outdated data, login and get new ones"
363                         self.timestamp = time.time()
364                         self.loginCallback = self._getCalls
365                         self.login()
366                 elif not self.callList:
367                         print "[FritzCallFBF] getCalls: time is ok, but no callList"
368                         self._getCalls1()
369                 else:
370                         print "[FritzCallFBF] getCalls: time is ok, callList is ok"
371                         self._gotPageCalls()
372
373         def _getCalls(self):
374                 #
375                 # we need this to fill Anrufliste.csv
376                 # http://repeater1/cgi-bin/webcm?getpage=../html/de/menus/menu2.html&var:lang=de&var:menu=fon&var:pagename=foncalls
377                 #
378                 print "[FritzCallFBF] _getCalls"
379                 if self.callScreen:
380                         self.callScreen.updateStatus(_("Getting calls from FRITZ!Box...") + _("preparing"))
381                 host = "%d.%d.%d.%d" %tuple(config.plugins.FritzCall.hostname.value)
382                 parms = urlencode({'getpage':'../html/de/menus/menu2.html', 'var:lang':'de','var:pagename':'foncalls','var:menu':'fon'})
383                 url = "http://%s/cgi-bin/webcm?%s" %(host, parms)
384                 getPage(url).addCallback(self._getCalls1).addErrback(self.errorCalls)
385
386         def _getCalls1(self, html = ""):
387                 #
388                 # finally we should have successfully lgged in and filled the csv
389                 #
390                 print "[FritzCallFBF] _getCalls1"
391                 if self.callScreen:
392                         self.callScreen.updateStatus(_("Getting calls from FRITZ!Box...") + _("finishing"))
393                 host = "%d.%d.%d.%d" %tuple(config.plugins.FritzCall.hostname.value)
394                 parms = urlencode({'getpage':'../html/de/FRITZ!Box_Anrufliste.csv'})
395                 url = "http://%s/cgi-bin/webcm?%s" %(host, parms)
396                 getPage(url).addCallback(self._gotPageCalls).addErrback(self.errorCalls)
397
398 fritzbox = FritzCallFBF()
399
400 class FritzDisplayCalls(Screen):
401
402         skin = """
403                 <screen name="FritzDisplayCalls" position="100,90" size="550,420" title="%s" >
404                         <widget name="statusbar" position="0,0" size="550,22" font="Regular;21" />
405                         <widget name="list" position="0,22" size="550,358" font="Regular;19" />
406                         <ePixmap position="5,375" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
407                         <ePixmap position="145,375" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
408                         <ePixmap position="285,375" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
409                         <ePixmap position="425,375" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
410                         <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" />
411                         <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" />
412                         <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" />
413                         <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" />
414                 </screen>""" % _("Phone calls")
415
416         def __init__(self, session, text = ""):
417                 self.skin = FritzDisplayCalls.skin
418                 Screen.__init__(self, session)
419
420                 # TRANSLATORS: keep it short, this is a button
421                 self["key_red"] = Button(_("All"))
422                 # TRANSLATORS: keep it short, this is a button
423                 self["key_green"] = Button(_("Missed"))
424                 # TRANSLATORS: keep it short, this is a button
425                 self["key_yellow"] = Button(_("Incoming"))
426                 # TRANSLATORS: keep it short, this is a button
427                 self["key_blue"] = Button(_("Outgoing"))
428
429                 self["setupActions"] = ActionMap(["OkCancelActions", "DirectionActions", "ColorActions"],
430                 {
431                         "red": self.displayAllCalls,
432                         "green": self.displayMissedCalls,
433                         "yellow": self.displayInCalls,
434                         "blue": self.displayOutCalls,
435                         "down": self.pageDown,
436                         "up": self.pageUp,
437                         "right": self.pageDown,
438                         "left": self.pageUp,
439                         "cancel": self.ok,
440                         "save": self.ok,
441                         "ok": self.ok,}, -2)
442                 
443                 self["statusbar"] = Label(_("Getting calls from FRITZ!Box..."))
444                 self["list"] = ScrollLabel("")
445                 print "[FritzDisplayCalls] init: '''%s'''" %config.plugins.FritzCall.fbfCalls.value
446                 self.displayCalls()
447
448         def ok(self):
449                 self.close()
450
451         def pageDown(self):
452                 self["list"].pageDown()
453
454         def pageUp(self):
455                 self["list"].pageUp()
456
457         def displayAllCalls(self):
458                 print "[FritzDisplayCalls] displayAllCalls"
459                 config.plugins.FritzCall.fbfCalls.value = FBF_ALL_CALLS
460                 config.plugins.FritzCall.fbfCalls.save()
461                 self.displayCalls()
462
463         def displayMissedCalls(self):
464                 print "[FritzDisplayCalls] displayMissedCalls"
465                 config.plugins.FritzCall.fbfCalls.value = FBF_MISSED_CALLS
466                 config.plugins.FritzCall.fbfCalls.save()
467                 self.displayCalls()
468
469         def displayInCalls(self):
470                 print "[FritzDisplayCalls] displayInCalls"
471                 config.plugins.FritzCall.fbfCalls.value = FBF_IN_CALLS
472                 config.plugins.FritzCall.fbfCalls.save()
473                 self.displayCalls()
474
475         def displayOutCalls(self):
476                 print "[FritzDisplayCalls] displayOutCalls"
477                 config.plugins.FritzCall.fbfCalls.value = FBF_OUT_CALLS
478                 config.plugins.FritzCall.fbfCalls.save()
479                 self.displayCalls()
480
481         def displayCalls(self):
482                 print "[FritzDisplayCalls] displayCalls"
483                 self.header = fbfCallsChoices[config.plugins.FritzCall.fbfCalls.value]
484                 fritzbox.getCalls(self, self.gotCalls, config.plugins.FritzCall.fbfCalls.value)
485
486         def gotCalls(self, text, count):
487                 # print "[FritzDisplayCalls] gotCalls:\n" + text
488                 self["statusbar"].setText(self.header + " (" + str(count) + ")")
489                 self["list"].setText(text)
490
491         def updateStatus(self, text):
492                 self["statusbar"].setText(text)
493
494
495 class FritzCallPhonebook:
496         def __init__(self):
497                 self.phonebook = {}
498                 self.reload()
499
500         def create(self):
501                 try:
502                         f = open(config.plugins.FritzCall.phonebookLocation.value, 'w')
503                         f.write("01234567890#Name, Street, Location (Keep the Spaces!!!)\n");
504                         f.close()
505                         return True
506                 except IOError:
507                         return False
508
509         def reload(self):
510                 print "[FritzCallPhonebook] reload"
511                 self.phonebook.clear()
512
513                 if not config.plugins.FritzCall.enable.value:
514                         return
515
516                 exists = False
517                 
518                 if config.plugins.FritzCall.phonebook.value:
519                         if not os_path.exists(config.plugins.FritzCall.phonebookLocation.value):
520                                 if(self.create()):
521                                         exists = True
522                         else:
523                                 exists = True
524         
525                         if exists:
526                                 for line in open(config.plugins.FritzCall.phonebookLocation.value):
527                                         try:
528                                                 number, name = line.split("#")
529                                                 if not self.phonebook.has_key(number):
530                                                         self.phonebook[number] = name
531                                         except ValueError:
532                                                 print "[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" %line
533
534                 if config.plugins.FritzCall.fritzphonebook.value:
535                         fritzbox.loadFritzBoxPhonebook()
536
537         def search(self, number):
538                 # print "[FritzCallPhonebook] Searching for %s" %number
539                 name = None
540                 if config.plugins.FritzCall.phonebook.value or config.plugins.FritzCall.fritzphonebook.value:
541                         if self.phonebook.has_key(number):
542                                 name = self.phonebook[number].replace(", ", "\n").strip()
543                 return name
544
545         def add(self, number, name):
546                 print "[FritzCallPhonebook] add"
547 #===============================================================================
548 #               It could happen, that two reverseLookups are running in parallel,
549 #               so check first, whether we have already added the number to the phonebook.
550 #===============================================================================
551                 if phonebook.search(number) is None and number <> 0 and config.plugins.FritzCall.phonebook.value and config.plugins.FritzCall.addcallers.value:
552                         try:
553                                 f = open(config.plugins.FritzCall.phonebookLocation.value, 'a')
554                                 name = name.strip() + "\n"
555                                 string = "%s#%s" %(number, name)
556                                 self.phonebook[number] = name;
557                                 f.write(string)
558                                 f.close()
559                                 print "[FritzCallPhonebook] added %s with %sto Phonebook.txt" %(number, name)
560                                 return True
561
562                         except IOError:
563                                 return False
564
565 phonebook = FritzCallPhonebook()
566
567 class FritzCallSetup(ConfigListScreen, Screen):
568         skin = """
569                 <screen position="100,90" size="550,420" title="%s" >
570                 <widget name="config" position="20,10" size="510,300" scrollbarMode="showOnDemand" />
571                 <widget name="consideration" position="20,320" font="Regular;20" halign="center" size="510,50" />
572                 <ePixmap position="5,375" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
573                 <ePixmap position="145,375" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
574                 <ePixmap position="285,375" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
575                 <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" />
576                 <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" />
577                 <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" />
578                 </screen>""" % _("FritzCall Setup")
579
580         def __init__(self, session, args = None):
581
582                 Screen.__init__(self, session)
583                 self.session = session
584
585                 self["consideration"] = Label(_("You need to enable the monitoring on your FRITZ!Box by dialing #96*5*!"))
586                 self.list = []
587
588                 # Initialize Buttons
589                 # TRANSLATORS: keep it short, this is a button
590                 self["key_red"] = Button(_("Cancel"))
591                 # TRANSLATORS: keep it short, this is a button
592                 self["key_green"] = Button(_("OK"))
593                 # TRANSLATORS: keep it short, this is a button
594                 self["key_yellow"] = Button(_("Phone calls"))
595
596                 self["setupActions"] = ActionMap(["SetupActions", "ColorActions"],
597                 {
598                         "cancel": self.cancel,
599                         "red": self.cancel,     # not strictly needed, better for clarity
600                         "save": self.save,
601                         "green": self.save,     # not strictly needed, better for clarity
602                         "yellow": self.displayCalls,
603                         "ok": self.save,
604                 }, -2)
605
606                 ConfigListScreen.__init__(self, self.list, session = session)
607                 self.createSetup()
608
609
610         def keyLeft(self):
611                 ConfigListScreen.keyLeft(self)
612                 self.createSetup()
613
614         def keyRight(self):
615                 ConfigListScreen.keyRight(self)
616                 self.createSetup()
617
618         def createSetup(self):
619                 self.list = [ ]
620                 self.list.append(getConfigListEntry(_("Call monitoring"), config.plugins.FritzCall.enable))
621                 if config.plugins.FritzCall.enable.value:
622                         self.list.append(getConfigListEntry(_("FRITZ!Box FON IP address"), config.plugins.FritzCall.hostname))
623
624                         self.list.append(getConfigListEntry(_("Show after Standby"), config.plugins.FritzCall.afterStandby))
625
626                         self.list.append(getConfigListEntry(_("Show Calls for specific MSN"), config.plugins.FritzCall.filter))
627                         if config.plugins.FritzCall.filter.value:
628                                 self.list.append(getConfigListEntry(_("MSN to show (separated by ,)"), config.plugins.FritzCall.filtermsn))
629
630                         self.list.append(getConfigListEntry(_("Show Outgoing Calls"), config.plugins.FritzCall.showOutgoing))
631                         if config.plugins.FritzCall.showOutgoing.value:
632                                 self.list.append(getConfigListEntry(_("Areacode to add to Outgoing Calls (if necessary)"), config.plugins.FritzCall.prefix))
633                         self.list.append(getConfigListEntry(_("Timeout for Call Notifications (seconds)"), config.plugins.FritzCall.timeout))
634                         self.list.append(getConfigListEntry(_("Reverse Lookup Caller ID (select country below)"), config.plugins.FritzCall.lookup))
635                         if config.plugins.FritzCall.lookup.value:
636                                 self.list.append(getConfigListEntry(_("Country"), config.plugins.FritzCall.country))
637
638                         self.list.append(getConfigListEntry(_("Password Accessing FRITZ!Box"), config.plugins.FritzCall.password))
639                         self.list.append(getConfigListEntry(_("Read PhoneBook from FRITZ!Box"), config.plugins.FritzCall.fritzphonebook))
640                         if config.plugins.FritzCall.fritzphonebook.value:
641                                 self.list.append(getConfigListEntry(_("Append type of number"), config.plugins.FritzCall.showType))
642                                 self.list.append(getConfigListEntry(_("Append shortcut number"), config.plugins.FritzCall.showShortcut))
643                                 self.list.append(getConfigListEntry(_("Append vanity name"), config.plugins.FritzCall.showVanity))
644
645                         self.list.append(getConfigListEntry(_("Use internal PhoneBook"), config.plugins.FritzCall.phonebook))
646                         if config.plugins.FritzCall.phonebook.value:
647                                 self.list.append(getConfigListEntry(_("PhoneBook Location"), config.plugins.FritzCall.phonebookLocation))
648                                 if config.plugins.FritzCall.lookup.value:
649                                         self.list.append(getConfigListEntry(_("Automatically add new Caller to PhoneBook"), config.plugins.FritzCall.addcallers))
650
651                         self.list.append(getConfigListEntry(_("Strip Leading 0"), config.plugins.FritzCall.internal))
652                         # self.list.append(getConfigListEntry(_("Default display mode for FRITZ!Box calls"), config.plugins.FritzCall.fbfCalls))
653
654                 self["config"].list = self.list
655                 self["config"].l.setList(self.list)
656
657         def save(self):
658 #               print "[FritzCallSetup] save"
659                 for x in self["config"].list:
660                         x[1].save()
661                 if fritz_call is not None:
662                         fritz_call.connect()
663
664                         if config.plugins.FritzCall.phonebook.value:
665                                 if not os_path.exists(config.plugins.FritzCall.phonebookLocation.value):
666                                         if not phonebook.create():
667                                                 Notifications.AddNotification(MessageBox, _("Can't create PhoneBook.txt"), type=MessageBox.TYPE_INFO, timeout=config.plugins.FritzCall.timeout.value)
668                                 else:
669                                         print "[FritzCallSetup] called phonebook.reload()"
670                                         phonebook.reload()
671
672                 self.close()
673
674         def cancel(self):
675 #               print "[FritzCallSetup] cancel"
676                 for x in self["config"].list:
677                         x[1].cancel()
678                 self.close()
679
680         def displayCalls(self):
681                 self.session.open(FritzDisplayCalls)
682
683
684 standbyMode = False
685
686 class FritzCallList:
687         def __init__(self):
688                 self.callList = [ ]
689         
690         def add(self, event, date, number, caller, phone):
691                 print "[FritzCallList] add"
692                 if len(self.callList) > 10:
693                         if self.callList[0] != "Start":
694                                 self.callList[0] = "Start"
695                         del self.callList[1]
696
697                 self.callList.append((event, number, date, caller, phone))
698         
699         def display(self):
700                 print "[FritzCallList] display"
701                 global standbyMode
702                 global my_global_session
703                 standbyMode = False
704                 # Standby.inStandby.onClose.remove(self.display) object does not exist anymore...
705                 # build screen from call list
706                 text = "\n"
707                 if self.callList[0] == "Start":
708                         text = text + _("Last 10 calls:\n")
709                         del self.callList[0]
710
711                 for call in self.callList:
712                         (event, number, date, caller, phone) = call
713                         if event == "RING":
714                                 direction = "->"
715                         else:
716                                 direction = "<-"
717                         found = re.match(".*(\d\d.\d\d.)\d\d( \d\d:\d\d)", date)
718                         if found: date = found.group(1) + found.group(2)
719                         found = re.match(".*\((.*)\)", phone)
720                         if found: phone = found.group(1)
721                         # if len(phone) > 20: phone = phone[:20]
722
723                         if caller == _("UNKNOWN") and number != "":
724                                 caller = number
725                         else:
726                                 found = re.match("(.*)\n.*", caller)
727                                 if found: caller = found.group(1)
728                         # if len(caller) > 20: caller = caller[:20]
729                         while (len(caller) + len(phone)) > 40:
730                                 if len(caller) > len(phone):
731                                         caller = caller[:-1]
732                                 else:
733                                         phone = phone[:-1]
734
735                         text = text + "%s %s %s %s\n" %(date, caller, direction, phone)
736
737                 print "[FritzCallList] display: '%s %s %s %s'" %(date, caller, direction, phone)
738                 # display screen
739                 Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_INFO)
740                 # my_global_session.open(FritzDisplayCalls, text) # TODO please HELP: from where can I get a session?
741                 self.callList = [ ]
742                 self.text = ""
743
744 callList = FritzCallList()
745
746 def notifyCall(event, date, number, caller, phone):
747         if Standby.inStandby is None or config.plugins.FritzCall.afterStandby.value == "each":
748                 if event == "RING":
749                         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 }
750                 else:
751                         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 }
752                 print "[FritzCall] notifyCall:\n%s" %text
753                 Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_INFO, timeout=config.plugins.FritzCall.timeout.value)
754         elif config.plugins.FritzCall.afterStandby.value == "inList":
755                 #
756                 # if not yet done, register function to show call list
757                 global standbyMode
758                 if not standbyMode :
759                         standbyMode = True
760                         Standby.inStandby.onHide.append(callList.display)
761                 # add text/timeout to call list
762                 callList.add(event, date, number, caller, phone)
763                 print "[FritzCall] notifyCall: added to callList"
764         else: # this is the "None" case
765                 print "[FritzCall] notifyCall: standby and no show"
766
767
768 #===============================================================================
769 #               We need a separate class for each invocation of reverseLookup to retain
770 #               the necessary data for the notification
771 #===============================================================================
772
773 countries = { }
774
775 class FritzReverseLookupAndNotifier:
776         def __init__(self, event, number, caller, phone, date):
777                 print "[FritzReverseLookupAndNotifier] reverse Lookup for %s!" %number
778                 self.event = event
779                 self.number = number
780                 self.caller = caller
781                 self.phone = phone
782                 self.date = date
783                 self.currentWebsite = None
784                 self.nextWebsiteNo = 0
785
786                 if not countries:
787                         dom = parse(resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/reverselookup.xml"))
788                         for top in dom.getElementsByTagName("reverselookup"):
789                                 for country in top.getElementsByTagName("country"):
790                                         code = country.getAttribute("code").replace("+","00")
791                                         countries[code] = country.getElementsByTagName("website")
792
793                 self.countrycode = config.plugins.FritzCall.country.value
794
795                 if number[0] != "0":
796                         self.caller = _("UNKNOWN")
797                         self.notifyAndReset()
798                         return
799
800                 if self.number[:2] == "00":
801                         if countries.has_key(self.number[:3]):   #      e.g. USA
802                                 self.countrycode = self.number[:3]
803                         elif countries.has_key(self.number[:4]):
804                                 self.countrycode = self.number[:4]
805                         elif countries.has_key(self.number[:5]):
806                                 self.countrycode = self.number[:5]
807                         else:
808                                 print "[FritzReverseLookupAndNotifier] Country cannot be reverse handled"
809                                 self.caller = _("UNKNOWN")
810                                 self.notifyAndReset()
811                                 return
812
813                 if countries.has_key(self.countrycode):
814                         print "[FritzReverseLookupAndNotifier] Found website for reverse lookup"
815                         self.websites = countries[self.countrycode]
816                         self.nextWebsiteNo = 1
817                         self.handleWebsite(self.websites[0])
818                 else:
819                         print "[FritzReverseLookupAndNotifier] Country cannot be reverse handled"
820                         self.caller = _("UNKNOWN")
821                         self.notifyAndReset()
822                         return
823
824         def handleWebsite(self, website):
825                 print "[FritzReverseLookupAndNotifier] handleWebsite: " + website.getAttribute("name")
826                 if self.number[:2] == "00":
827                         number = website.getAttribute("prefix") + self.number.replace(self.countrycode,"")
828                 else:
829                         number = self.number
830
831                 url = website.getAttribute("url")
832                 if re.search('$AREACODE',url) or re.search('$PFXAREACODE',url):
833                         print "[FritzReverseLookupAndNotifier] handleWebsite: (PFX)ARECODE cannot be handled"
834                         self.caller = _("UNKNOWN")
835                         self.notifyAndReset()
836                         return
837                 #
838                 # Apparently, there is no attribute called (pfx)areacode anymore
839                 # So, this below will not work.
840                 #
841                 if re.search('\\$AREACODE',url) and website.hasAttribute("areacode"):
842                         areaCodeLen = int(website.getAttribute("areacode"))
843                         url = url.replace("$AREACODE","%(areacode)s").replace("$NUMBER","%(number)s")
844                         url = url %{ 'areacode':number[:areaCodeLen], 'number':number[areaCodeLen:] }
845                 elif re.search('\\$PFXAREACODE',url) and website.hasAttribute("pfxareacode"):
846                         areaCodeLen = int(website.getAttribute("pfxareacode"))
847                         url = url.replace("$PFXAREACODE","%(pfxareacode)s").replace("$NUMBER","%(number)s")
848                         url = url %{ 'pfxareacode':number[:areaCodeLen], 'number':number[areaCodeLen:] }
849                 elif re.search('\\$NUMBER',url): 
850                         url = url.replace("$NUMBER","%s") %number
851                 else:
852                         print "[FritzReverseLookupAndNotifier] handleWebsite: cannot handle websites with no $NUMBER in url"
853                         self.caller = _("UNKNOWN")
854                         self.notifyAndReset()
855                         return
856                 print "[FritzReverseLookupAndNotifier] Url to query: " + url
857                 url = url.encode("UTF-8", "replace")
858                 self.currentWebsite = website
859                 getPage(url, method="GET").addCallback(self._gotPage).addErrback(self._gotError)
860
861         def _gotPage(self, page):
862                 print "[FritzReverseLookupAndNotifier] _gotPage"
863                 found = re.match('.*content=".*?charset=([^"]+)"',page,re.S)
864                 if found:
865                         print "[FritzReverseLookupAndNotifier] Charset: " + found.group(1)
866                         page = page.replace("\xa0"," ").decode(found.group(1), "replace")
867                 else:
868                         page = page.replace("\xa0"," ").decode("ISO-8859-1", "replace")
869
870                 for entry in self.currentWebsite.getElementsByTagName("entry"):
871                         # print "[FritzReverseLookupAndNotifier] _gotPage: try entry"
872                         details = []
873                         for what in ["name", "street", "city", "zipcode"]:
874                                 pat = "(.*)" + self.getPattern(entry, what)
875                                 # print "[FritzReverseLookupAndNotifier] _gotPage: look for '''%s''' with '''%s'''" %( what, pat )
876                                 found = re.match(pat, page, re.S|re.M)
877                                 if found:
878                                         # print "[FritzReverseLookupAndNotifier] _gotPage: found for '''%s''': '''%s'''" %( what, found.group(2) )
879                                         item = found.group(2).replace("&nbsp;"," ").replace("</b>","").replace(",","")
880                                         item = html2utf8(item)
881                                         details.append(item.strip())
882                                         # print "[FritzReverseLookupAndNotifier] _gotPage: got '''%s''': '''%s'''" %( what, item.strip() )
883                                 else:
884                                         break
885
886                         if len(details) != 4:
887                                 continue
888                         else:
889                                 name = details[0]
890                                 address =  details[1] + ", " + details[3] + " " + details[2]
891                                 print "[FritzReverseLookupAndNotifier] _gotPage: Reverse lookup succeeded:\nName: %s\nAddress: %s" %(name, address)
892                                 self.caller = "%s, %s" %(name, address)
893                                 if self.number != 0 and config.plugins.FritzCall.addcallers.value and self.event == "RING":
894                                         phonebook.add(self.number, self.caller)
895
896                                 self.caller = self.caller.replace(", ", "\n").encode("UTF-8", "replace")
897                                 self.notifyAndReset()
898                                 return True
899                                 break
900                 else:
901                         self._gotError("[FritzReverseLookupAndNotifier] _gotPage: Nothing found at %s" %self.currentWebsite.getAttribute("name"))
902                         
903         def _gotError(self, error = ""):
904                 print "[FritzReverseLookupAndNotifier] _gotError - Error: %s" %error
905                 if self.nextWebsiteNo >= len(self.websites):
906                         print "[FritzReverseLookupAndNotifier] _gotError: I give up"
907                         self.caller = _("UNKNOWN")
908                         self.notifyAndReset()
909                         return
910                 else:
911                         print "[FritzReverseLookupAndNotifier] _gotError: try next website"
912                         self.nextWebsiteNo = self.nextWebsiteNo+1
913                         self.handleWebsite(self.websites[self.nextWebsiteNo-1])
914
915         def getPattern(self, website, which):
916                 pat1 = website.getElementsByTagName(which)
917                 if len(pat1) > 1:
918                         print "Something strange: more than one %s for website %s" %(which, website.getAttribute("name"))
919                 return pat1[0].childNodes[0].data
920
921         def notifyAndReset(self, timeout=config.plugins.FritzCall.timeout.value):
922                 notifyCall(self.event, self.date, self.number, self.caller, self.phone)
923                 # kill that object...
924
925
926 class FritzProtocol(LineReceiver):
927         def __init__(self):
928                 print "[FritzProtocol] __init__"
929                 self.resetValues()
930
931         def resetValues(self):
932                 print "[FritzProtocol] resetValues"
933                 self.number = '0'
934                 self.caller = None
935                 self.phone = None
936                 self.date = '0'
937
938         def notifyAndReset(self, timeout=config.plugins.FritzCall.timeout.value):
939                 notifyCall(self.event, self.date, self.number, self.caller, self.phone)
940                 self.resetValues()
941
942         def lineReceived(self, line):
943                 print "[FritzProtocol] lineReceived: %s" %line
944 #15.07.06 00:38:54;CALL;1;4;<from/extern>;<to/our msn>;
945 #15.07.06 00:38:58;DISCONNECT;1;0;
946 #15.07.06 00:39:22;RING;0;<from/extern>;<to/our msn>;
947 #15.07.06 00:39:27;DISCONNECT;0;0;
948                 a = []
949                 a = line.split(';')
950                 (self.date, self.event) = a[0:2]
951
952                 if self.event == "RING" or (self.event == "CALL" and config.plugins.FritzCall.showOutgoing.value):
953                         phone = a[4]
954                          
955                         if self.event == "RING":
956                                 number = a[3] 
957                         else:
958                                 number = a[5]
959                                 
960                         print "[FritzProtocol] lineReceived phone: '''%s''' number: '''%s'''" % (phone, number)
961
962                         filtermsns = config.plugins.FritzCall.filtermsn.value.split(",")
963                         for i in range(len(filtermsns)):
964                                 filtermsns[i] = filtermsns[i].strip()
965                         if not (config.plugins.FritzCall.filter.value and phone not in filtermsns):
966                                 print "[FritzProtocol] lineReceived no filter hit"
967                                 phonename = phonebook.search(phone)                # do we have a name for the number of our side?
968                                 if phonename is not None:
969                                         self.phone = "%s (%s)" %(phone, phonename)
970                                 else:
971                                         self.phone = phone
972
973                                 if config.plugins.FritzCall.internal.value and len(number) > 3 and number[0]=="0":
974                                         self.number = number[1:]
975                                 else:
976                                         self.number = number
977
978                                 if self.event == "CALL" and self.number[0] != '0':                                        # should only happen for outgoing
979                                         self.number = config.plugins.FritzCall.prefix.value + self.number
980
981                                 if self.number is not "":
982                                         print "[FritzProtocol] lineReceived phonebook.search: %s" %self.number
983                                         self.caller = phonebook.search(self.number)
984                                         print "[FritzProtocol] lineReceived phonebook.search reault: %s" %self.caller
985                                         if (self.caller is None) and config.plugins.FritzCall.lookup.value:
986                                                 FritzReverseLookupAndNotifier(self.event, self.number, self.caller, self.phone, self.date)
987                                                 return                                                  # reverselookup is supposed to handle the message itself 
988
989                                 if self.caller is None:
990                                         self.caller = _("UNKNOWN")
991
992                                 self.notifyAndReset()
993
994 class FritzClientFactory(ReconnectingClientFactory):
995         initialDelay = 20
996         maxDelay = 500
997
998         def __init__(self):
999                 self.hangup_ok = False
1000
1001         def startedConnecting(self, connector):
1002                 Notifications.AddNotification(MessageBox, _("Connecting to FRITZ!Box..."), type=MessageBox.TYPE_INFO, timeout=2)
1003
1004         def buildProtocol(self, addr):
1005                 Notifications.AddNotification(MessageBox, _("Connected to FRITZ!Box!"), type=MessageBox.TYPE_INFO, timeout=4)
1006                 self.resetDelay()
1007                 return FritzProtocol()
1008
1009         def clientConnectionLost(self, connector, reason):
1010                 if not self.hangup_ok:
1011                         Notifications.AddNotification(MessageBox, _("Connection to FRITZ!Box! lost\n (%s)\nretrying...") % reason.getErrorMessage(), type=MessageBox.TYPE_INFO, timeout=config.plugins.FritzCall.timeout.value)
1012                 ReconnectingClientFactory.clientConnectionLost(self, connector, reason)
1013
1014         def clientConnectionFailed(self, connector, reason):
1015                 Notifications.AddNotification(MessageBox, _("Connecting to FRITZ!Box failed\n (%s)\nretrying...") % reason.getErrorMessage(), type=MessageBox.TYPE_INFO, timeout=config.plugins.FritzCall.timeout.value)
1016                 ReconnectingClientFactory.clientConnectionFailed(self, connector, reason)
1017
1018 class FritzCall:
1019         def __init__(self):
1020                 self.dialog = None
1021                 self.d = None
1022                 self.connect()
1023
1024         def connect(self):
1025                 self.abort()
1026                 if config.plugins.FritzCall.enable.value:
1027                         f = FritzClientFactory()
1028                         self.d = (f, reactor.connectTCP("%d.%d.%d.%d" % tuple(config.plugins.FritzCall.hostname.value), 1012, f))
1029
1030         def shutdown(self):
1031                 self.abort()
1032
1033         def abort(self):
1034                 if self.d is not None:
1035                         self.d[0].hangup_ok = True
1036                         self.d[0].stopTrying()
1037                         self.d[1].disconnect()
1038                         self.d = None
1039
1040 def displayCalls(session, servicelist):
1041         session.open(FritzDisplayCalls)
1042
1043 def main(session):
1044         session.open(FritzCallSetup)
1045
1046 fritz_call = None
1047
1048 def autostart(reason, **kwargs):
1049         global fritz_call
1050
1051         # ouch, this is a hack
1052         if kwargs.has_key("session"):
1053                 global my_global_session
1054                 my_global_session = kwargs["session"]
1055                 return
1056
1057         print "[FRITZ!Call] - Autostart"
1058         if reason == 0:
1059                 fritz_call = FritzCall()
1060         elif reason == 1:
1061                 fritz_call.shutdown()
1062                 fritz_call = None
1063
1064 def Plugins(**kwargs):
1065         what = _("Display FRITZ!box-Fon calls on screen")
1066         what_calls = _("Phone calls")
1067         return [ PluginDescriptor(name="FritzCall", description=what, where = PluginDescriptor.WHERE_PLUGINMENU, icon = "plugin.png", fnc=main),
1068                 PluginDescriptor(name=what_calls, description=what_calls, where = PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=displayCalls),
1069                 PluginDescriptor(where = [PluginDescriptor.WHERE_SESSIONSTART, PluginDescriptor.WHERE_AUTOSTART], fnc = autostart) ]