FIX: reverselookup.xml: entry for telefonbuch.de and 11880.com
[enigma2-plugins.git] / fritzcall / src / plugin.py
1 # -*- coding: utf-8 -*-
2 #===============================================================================
3 # $Author$
4 # $Revision$
5 # $Date$
6 # $Id$
7 #==============================
8 from Screens.Screen import Screen #@UnresolvedImport
9 from Screens.MessageBox import MessageBox #@UnresolvedImport
10 from Screens.NumericalTextInputHelpDialog import NumericalTextInputHelpDialog #@UnresolvedImport
11 from Screens.InputBox import InputBox #@UnresolvedImport
12 from Screens import Standby #@UnresolvedImport
13 from Screens.HelpMenu import HelpableScreen #@UnresolvedImport
14
15 from enigma import eListboxPythonMultiContent, gFont, RT_HALIGN_LEFT, RT_HALIGN_RIGHT #@UnresolvedImport
16
17 from Components.MenuList import MenuList #@UnresolvedImport
18 from Components.ActionMap import ActionMap #@UnresolvedImport
19 from Components.Label import Label #@UnresolvedImport
20 from Components.Button import Button #@UnresolvedImport
21 from Components.config import config, ConfigSubsection, ConfigSelection, ConfigEnableDisable, getConfigListEntry, ConfigText, ConfigInteger #@UnresolvedImport
22 try:
23         from Components.config import ConfigPassword
24 except ImportError:
25         ConfigPassword = ConfigText
26 from Components.ConfigList import ConfigListScreen #@UnresolvedImport
27
28 from Plugins.Plugin import PluginDescriptor #@UnresolvedImport
29 from Tools import Notifications #@UnresolvedImport
30 from Tools.NumericalTextInput import NumericalTextInput #@UnresolvedImport
31 from Tools.Directories import resolveFilename, SCOPE_PLUGINS, SCOPE_SKIN_IMAGE #@UnresolvedImport
32 from Tools.LoadPixmap import LoadPixmap #@UnresolvedImport
33
34 from twisted.internet import reactor #@UnresolvedImport
35 from twisted.internet.protocol import ReconnectingClientFactory #@UnresolvedImport
36 from twisted.protocols.basic import LineReceiver #@UnresolvedImport
37 from twisted.web.client import getPage #@UnresolvedImport
38
39 from urllib import urlencode 
40 import re, time, os
41
42 from nrzuname import ReverseLookupAndNotifier, html2utf8
43 import FritzOutlookCSV, FritzLDIF
44 from . import _, debug #@UnresolvedImport
45
46 from enigma import getDesktop #@UnresolvedImport
47 DESKTOP_WIDTH = getDesktop(0).size().width()
48 DESKTOP_HEIGHT = getDesktop(0).size().height()
49 DESKTOP_SKIN = config.skin.primary_skin.value.replace("/skin.xml", "")
50 XXX = 0 # TODO: Platzhalter für fullscreen SD skin
51 #
52 # this is pure magic.
53 # It returns the first value, if HD (1280x720),
54 # the second if SD (720x576),
55 # else something scaled accordingly
56 #
57 def scaleH(y2, y1):
58         return scale(y2, y1, 1280, 720, DESKTOP_WIDTH)
59 def scaleV(y2, y1):
60         return scale(y2, y1, 720, 576, DESKTOP_HEIGHT)
61 def scale(y2, y1, x2, x1, x):
62         return (y2 - y1) * (x - x1) / (x2 - x1) + y1
63
64 my_global_session = None
65
66 config.plugins.FritzCall = ConfigSubsection()
67 config.plugins.FritzCall.enable = ConfigEnableDisable(default=False)
68 config.plugins.FritzCall.muteOnCall = ConfigEnableDisable(default=False)
69 config.plugins.FritzCall.hostname = ConfigText(default="fritz.box", fixed_size=False)
70 config.plugins.FritzCall.afterStandby = ConfigSelection(choices=[("none", _("show nothing")), ("inList", _("show as list")), ("each", _("show each call"))])
71 config.plugins.FritzCall.filter = ConfigEnableDisable(default=False)
72 config.plugins.FritzCall.filtermsn = ConfigText(default="", fixed_size=False)
73 config.plugins.FritzCall.filtermsn.setUseableChars('0123456789,')
74 config.plugins.FritzCall.showOutgoing = ConfigEnableDisable(default=False)
75 config.plugins.FritzCall.timeout = ConfigInteger(default=15, limits=(0, 60))
76 config.plugins.FritzCall.lookup = ConfigEnableDisable(default=False)
77 config.plugins.FritzCall.internal = ConfigEnableDisable(default=False)
78 config.plugins.FritzCall.fritzphonebook = ConfigEnableDisable(default=False)
79 config.plugins.FritzCall.phonebook = ConfigEnableDisable(default=False)
80 config.plugins.FritzCall.addcallers = ConfigEnableDisable(default=False)
81 config.plugins.FritzCall.phonebookLocation = ConfigSelection(choices=[("/etc/enigma2", _("Flash")), ("/media/usb", _("USB Stick")), ("/media/cf", _("CF Drive")), ("/media/hdd", _("Harddisk"))])
82 config.plugins.FritzCall.password = ConfigPassword(default="", fixed_size=False)
83 config.plugins.FritzCall.extension = ConfigText(default='1', fixed_size=False)
84 config.plugins.FritzCall.extension.setUseableChars('0123456789')
85 config.plugins.FritzCall.showType = ConfigEnableDisable(default=True)
86 config.plugins.FritzCall.showShortcut = ConfigEnableDisable(default=False)
87 config.plugins.FritzCall.showVanity = ConfigEnableDisable(default=False)
88 config.plugins.FritzCall.prefix = ConfigText(default="", fixed_size=False)
89 config.plugins.FritzCall.prefix.setUseableChars('0123456789')
90 config.plugins.FritzCall.fullscreen = ConfigEnableDisable(default=False)
91 config.plugins.FritzCall.debug = ConfigEnableDisable(default=False)
92
93 countryCodes = [
94         ("0049", _("Germany")),
95         ("0031", _("The Netherlands")),
96         ("0033", _("France")),
97         ("0039", _("Italy")),
98         ("0041", _("Switzerland")),
99         ("0043", _("Austria"))
100         ]
101 config.plugins.FritzCall.country = ConfigSelection(choices=countryCodes)
102
103 FBF_ALL_CALLS = "."
104 FBF_IN_CALLS = "1"
105 FBF_MISSED_CALLS = "2"
106 FBF_OUT_CALLS = "3"
107 fbfCallsChoices = {FBF_ALL_CALLS: _("All calls"),
108                                    FBF_IN_CALLS: _("Incoming calls"),
109                                    FBF_MISSED_CALLS: _("Missed calls"),
110                                    FBF_OUT_CALLS: _("Outgoing calls")
111                                    }
112 config.plugins.FritzCall.fbfCalls = ConfigSelection(choices=fbfCallsChoices)
113
114 config.plugins.FritzCall.name = ConfigText(default="", fixed_size=False)
115 config.plugins.FritzCall.number = ConfigText(default="", fixed_size=False)
116 config.plugins.FritzCall.number.setUseableChars('0123456789')
117
118 def initDebug():
119         try:
120                 os.remove("/tmp/FritzDebug.log")
121         except:
122                 pass
123
124 class FritzCallFBF:
125         def __init__(self):
126                 debug("[FritzCallFBF] __init__")
127                 self.callScreen = None
128                 self.loggedIn = False
129                 self.Callback = None
130                 self.timestamp = 0
131                 self.callList = []
132                 self.callType = config.plugins.FritzCall.fbfCalls.value
133
134         def notify(self, text):
135                 debug("[FritzCallFBF] notify")
136                 if self.callScreen:
137                         debug("[FritzCallFBF] notify: try to close callScreen")
138                         self.callScreen.close()
139                         self.callScreen = None
140                 Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_ERROR, timeout=config.plugins.FritzCall.timeout.value)
141
142         def errorLogin(self, error):
143                 text = _("FRITZ!Box Login failed! - Error: %s") % error
144                 self.notify(text)
145
146         def _gotPageLogin(self, html):
147 #               debug("[FritzCallPhonebook] _gotPageLogin"
148                 # workaround: exceptions in gotPage-callback were ignored
149                 if self.callScreen:
150                         self.callScreen.updateStatus(_("Getting calls from FRITZ!Box...") + _("login verification"))
151                 try:
152                         debug("[FritzCallFBF] _gotPageLogin: verify login")
153                         found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;Das angegebene Kennwort', html, re.S)
154                         if found:
155                                 text = _("FRITZ!Box Login failed! - Wrong Password!")
156                                 self.notify(text)
157                         else:
158                                 if self.callScreen:
159                                         self.callScreen.updateStatus(_("Getting calls from FRITZ!Box...") + _("login ok"))
160                                 self.loggedIn = True
161                 except:
162                         import traceback, sys
163                         traceback.print_exc(file=sys.stdout)
164                         #raise e
165
166         def login(self, callback=None):
167                 debug("[FritzCallFBF] Login")
168                 if config.plugins.FritzCall.password.value != "":
169                         if self.callScreen:
170                                 self.callScreen.updateStatus(_("Getting calls from FRITZ!Box...") + _("login"))
171                         parms = "login:command/password=%s" % (config.plugins.FritzCall.password.value)
172                         url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
173                         getPage(url,
174                                 method="POST",
175                                 headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
176                                                 }, postdata=parms).addCallback(self._gotPageLogin).addCallback(callback).addErrback(self.errorLogin)
177                 elif callback:
178                         callback()
179
180         def errorLoad(self, error):
181                 text = _("Could not load phonebook from FRITZ!Box - Error: %s") % error
182                 self.notify(text)
183
184         def _gotPageLoad(self, html):
185                 debug("[FritzCallFBF] _gotPageLoad")
186                 # workaround: exceptions in gotPage-callback were ignored
187                 try:
188                         self.parseFritzBoxPhonebook(html)
189                 except:
190                         import traceback, sys
191                         traceback.print_exc(file=sys.stdout)
192                         #raise e
193
194         def loadFritzBoxPhonebook(self):
195                 debug("[FritzCallFBF] loadFritzBoxPhonebook")
196                 if config.plugins.FritzCall.fritzphonebook.value:
197                         debug("[FritzCallFBF] loadFritzBoxPhonebook: logging in")
198                         self.login(self._loadFritzBoxPhonebook)
199
200         def _loadFritzBoxPhonebook(self, html=None):
201                         parms = urlencode({'getpage':'../html/de/menus/menu2.html', 'var:lang':'de', 'var:pagename':'fonbuch', 'var:menu':'fon'})
202                         url = "http://%s/cgi-bin/webcm?%s" % (config.plugins.FritzCall.hostname.value, parms)
203
204                         getPage(url).addCallback(self._gotPageLoad).addErrback(self.errorLoad)
205
206         def parseFritzBoxPhonebook(self, html):
207                 debug("[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                                 # debug(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                                                 debug("[FritzCallFBF] Adding '''%s''' with '''%s''' from FRITZ!Box Phonebook!" % (thisname, thisnumber))
250                                                 phonebook.phonebook[thisnumber] = thisname
251                                         else:
252                                                 debug("[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                                         debug("[FritzCallFBF] Adding '''%s''' with '''%s''' from FRITZ!Box Phonebook!" % (name, thisnumber))
272                                         phonebook.phonebook[thisnumber] = name
273                                 else:
274                                         debug("[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                                 # strip CbC prefix
288                                 if config.plugins.FritzCall.country.value == '0049':
289                                         if re.match('^0100\d\d', number):
290                                                 number = number[6:]
291                                         elif re.match('^010\d\d', number):
292                                                 number = number[5:]
293                                 if config.plugins.FritzCall.prefix.value and number and number[0] != '0':               # should only happen for outgoing
294                                         number = config.plugins.FritzCall.prefix.value + number
295                                 name = phonebook.search(number)
296                                 if name:
297                                         found = re.match('(.*?)\n.*', name)
298                                         if found:
299                                                 name = found.group(1)
300                                         number = name
301                         elif number == "":
302                                 number = _("UNKNOWN")
303                         # if len(number) > 20: number = number[:20]
304                         return number
305
306                 if csv:
307                         debug("[FritzCallFBF] _gotPageCalls: got csv, setting callList")
308                         if self.callScreen:
309                                 self.callScreen.updateStatus(_("Getting calls from FRITZ!Box...") + _("done"))
310                         # check for error: wrong password or password not set... TODO
311                         found = re.search('Melden Sie sich mit dem Kennwort der FRITZ!Box an', csv)
312                         if found:
313                                 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.")
314                                 # self.session.open(MessageBox, text, MessageBox.TYPE_ERROR, timeout=config.plugins.FritzCall.timeout.value)
315                                 self.notify(text)
316                                 return
317
318                         csv = csv.decode('iso-8859-1', 'replace').encode('utf-8', 'replace')
319                         lines = csv.splitlines()
320                         self.callList = lines
321                 elif self.callList:
322                         debug("[FritzCallFBF] _gotPageCalls: got no csv, but have callList")
323                         if self.callScreen:
324                                 self.callScreen.updateStatus(_("Getting calls from FRITZ!Box...") + _("done, using last list"))
325                         lines = self.callList
326                 else:
327                         debug("[FritzCallFBF] _gotPageCalls: got no csv, no callList, leaving")
328                         return
329                         
330                 callList = []
331                 for line in lines:
332                         # debug(line
333                         # Typ;Datum;Name;Rufnummer;Nebenstelle;Eigene Rufnummer;Dauer
334                         found = re.match("^(" + self.callType + ");([^;]*);([^;]*);([^;]*);([^;]*);([^;]*)", line)
335                         if found:
336                                 direct = found.group(1)
337                                 date = found.group(2)
338                                 remote = _resolveNumber(found.group(4))
339                                 if not remote and direct != FBF_OUT_CALLS and found.group(3):
340                                         remote = found.group(3)
341                                 found1 = re.match('Internet: (.*)', found.group(6))
342                                 if found1:
343                                         here = _resolveNumber(found1.group(1))
344                                 else:
345                                         here = _resolveNumber(found.group(6))
346                                 
347                                 # strip CbC prefix for Germany
348                                 number = found.group(4)
349                                 if config.plugins.FritzCall.country.value == '0049':
350                                         if re.match('^0100\d\d', number):
351                                                 number = number[6:]
352                                         elif re.match('^010\d\d', number):
353                                                 number = number[5:]
354                                 if config.plugins.FritzCall.prefix.value and number and number[0] != '0':               # should only happen for outgoing
355                                         number = config.plugins.FritzCall.prefix.value + number
356                                 callList.append((number, date, here, direct, remote))
357
358                 # debug("[FritzCallFBF] _gotPageCalls result:\n" + text
359
360                 if self.Callback is not None:
361                         # debug("[FritzCallFBF] _gotPageCalls call callback with\n" + text
362                         self.Callback(callList)
363                         self.Callback = None
364                 self.callScreen = None
365
366         def getCalls(self, callScreen, callback, type):
367                 #
368                 # call sequence must be:
369                 # - login
370                 # - getPage -> _gotPageLogin
371                 # - loginCallback (_getCalls)
372                 # - getPage -> _getCalls1
373                 debug("[FritzCallFBF] getCalls")
374                 self.callScreen = callScreen
375                 self.callType = type
376                 self.Callback = callback
377                 if (time.time() - self.timestamp) > 180: 
378                         debug("[FritzCallFBF] getCalls: outdated data, login and get new ones")
379                         self.timestamp = time.time()
380                         self.login(self._getCalls)
381                 elif not self.callList:
382                         debug("[FritzCallFBF] getCalls: time is ok, but no callList")
383                         self._getCalls1()
384                 else:
385                         debug("[FritzCallFBF] getCalls: time is ok, callList is ok")
386                         self._gotPageCalls()
387
388         def _getCalls(self, html=None):
389                 #
390                 # we need this to fill Anrufliste.csv
391                 # http://repeater1/cgi-bin/webcm?getpage=../html/de/menus/menu2.html&var:lang=de&var:menu=fon&var:pagename=foncalls
392                 #
393                 debug("[FritzCallFBF] _getCalls")
394                 if self.callScreen:
395                         self.callScreen.updateStatus(_("Getting calls from FRITZ!Box...") + _("preparing"))
396                 parms = urlencode({'getpage':'../html/de/menus/menu2.html', 'var:lang':'de', 'var:pagename':'foncalls', 'var:menu':'fon'})
397                 url = "http://%s/cgi-bin/webcm?%s" % (config.plugins.FritzCall.hostname.value, parms)
398                 getPage(url).addCallback(self._getCalls1).addErrback(self.errorCalls)
399
400         def _getCalls1(self, html=""):
401                 #
402                 # finally we should have successfully lgged in and filled the csv
403                 #
404                 debug("[FritzCallFBF] _getCalls1")
405                 if self.callScreen:
406                         self.callScreen.updateStatus(_("Getting calls from FRITZ!Box...") + _("finishing"))
407                 parms = urlencode({'getpage':'../html/de/FRITZ!Box_Anrufliste.csv'})
408                 url = "http://%s/cgi-bin/webcm?%s" % (config.plugins.FritzCall.hostname.value, parms)
409                 getPage(url).addCallback(self._gotPageCalls).addErrback(self.errorCalls)
410
411         def dial(self, number):
412                 ''' initiate a call to number '''
413                 self.number = number
414                 self.login(self._dial)
415                 
416         def _dial(self, html=None):
417                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
418                 parms = urlencode({
419                         'getpage':'../html/de/menus/menu2.html',
420                         # 'id':'uiPostForm',
421                         # 'name':'uiPostForm',
422                         'login:command/password': config.plugins.FritzCall.password.value,
423                         'var:pagename':'fonbuch',
424                         'var:menu':'home',
425                         'telcfg:settings/UseClickToDial':'1',
426                         'telcfg:settings/DialPort':config.plugins.FritzCall.extension.value,
427                         'telcfg:command/Dial':self.number
428                         })
429                 debug("[FritzCallFBF] dial url: '" + url + "' parms: '" + parms + "'")
430                 getPage(url,
431                         method="POST",
432                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
433                         headers={
434                                         'Content-Type': "application/x-www-form-urlencoded",
435                                         'Content-Length': str(len(parms))},
436                         postdata=parms).addCallback(self._okDial).addErrback(self._errorDial)
437
438         def _okDial(self, html):
439                 debug("[FritzCallFBF] okDial")
440                 linkP = open("/tmp/FritzCallDialOK.htm", "w")
441                 linkP.write(html)
442                 linkP.close()
443
444         def _errorDial(self, error):
445                 debug("[FritzCallFBF] errorDial: $s" % error)
446                 linkP = open("/tmp/FritzCallDialError.htm", "w")
447                 linkP.write(error)
448                 linkP.close()
449                 text = _("Dialling failed - Error: %s") % error
450                 self.notify(text)
451
452         def hangup(self):
453                 ''' hangup call on port; not used for now '''
454                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
455                 parms = urlencode({
456                         #'getpage':'../html/de/menus/menu2.html',
457                         'id':'uiPostForm',
458                         'name':'uiPostForm',
459                         'login:command/password': config.plugins.FritzCall.password.value,
460                         #'var:pagename':'fonbuch',
461                         #'var:menu':'home',
462                         'telcfg:settings/UseClickToDial':'1',
463                         'telcfg:settings/DialPort':config.plugins.FritzCall.extension.value,
464                         'telcfg:command/Hangup':''
465                         })
466                 debug("[FritzCallFBF] hangup url: '" + url + "' parms: '" + parms + "'")
467                 getPage(url,
468                         method="POST",
469                         headers={
470                                         'Content-Type': "application/x-www-form-urlencoded",
471                                         'Content-Length': str(len(parms))},
472                         postdata=parms)
473
474 fritzbox = FritzCallFBF()
475
476
477 class FritzDisplayCalls(Screen, HelpableScreen):
478
479
480         def __init__(self, session, text=""):
481                 if config.plugins.FritzCall.fullscreen.value:
482                         self.width = DESKTOP_WIDTH
483                         self.height = DESKTOP_HEIGHT
484                         backMainPng = ""
485                         if os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, DESKTOP_SKIN + "/menu/back-main.png")):
486                                 backMainPng = DESKTOP_SKIN + "/menu/back-main.png"
487                         elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1-picon/menu/back-main.png")):
488                                 backMainPng = "Kerni-HD1-picon/menu/back-main.png"
489                         if backMainPng:
490                                         backMainLine = """<ePixmap position="0,0" zPosition="-10" size="%d,%d" pixmap="%s" transparent="1" />""" % (self.width, self.height, backMainPng)
491                         else:
492                                 backMainLine = ""
493                         debug("[FritzDisplayCalls] backMainLine: " + backMainLine)
494                                 
495                         # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
496                         self.skin = """
497                                 <screen name="FritzDisplayCalls" position="0,0" size="%d,%d" title="%s" flags="wfNoBorder">
498                                         %s
499                                         <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
500                                                 <convert type="ClockToText">Default</convert>
501                                         </widget>
502                                         <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
503                                                 <convert type="ClockToText">Date</convert>
504                                         </widget>
505                                         <eLabel text="%s" position="%d,%d" size="%d,%d" font="Regular;%d" halign="center" backgroundColor="#0b67a2" transparent="1"/>
506                         
507                                         <widget name="statusbar" position="%d,%d"  size="%d,%d" font="Regular;%d" backgroundColor="#353e575e" transparent="1" />
508                                         <widget name="entries" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" transparent="1" />
509                         
510                                         <ePixmap pixmap="skin_default/buttons/red.png"          position="%d,%d"        size="%d,%d" alphatest="on" />
511                                         <ePixmap pixmap="skin_default/buttons/green.png"        position="%d,%d"        size="%d,%d" alphatest="on" />
512                                         <ePixmap pixmap="skin_default/buttons/yellow.png"       position="%d,%d"        size="%d,%d" alphatest="on" />
513                                         <ePixmap pixmap="skin_default/buttons/blue.png"         position="%d,%d"        size="%d,%d" alphatest="on" />
514                                         <widget name="key_red"  position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
515                                         <widget name="key_green"        position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
516                                         <widget name="key_yellow"       position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
517                                         <widget name="key_blue"         position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
518                                         <ePixmap position="%d,%d" size="%d,%d" zPosition="2" pixmap="%s" transparent="1" alphatest="blend" />           
519                                 </screen>""" % (
520                                                         self.width, self.height, _("Phone calls"),
521                                                         backMainLine,
522                                                         scaleH(1130, XXX), scaleV(40, XXX), scaleH(80, XXX), scaleV(26, XXX), scaleV(26, XXX), # time
523                                                         scaleH(900, XXX), scaleV(70, XXX), scaleH(310, XXX), scaleV(22, XXX), scaleV(20, XXX), # date
524                                                         "FritzCall " + _("Phone calls"), scaleH(500, XXX), scaleV(63, XXX), scaleH(330, XXX), scaleV(30, XXX), scaleV(27, XXX), # eLabel
525                                                         scaleH(80, XXX), scaleV(150, XXX), scaleH(280, XXX), scaleV(200, XXX), scaleV(22, XXX), # statusbar
526                                                         scaleH(420, XXX), scaleV(120, XXX), scaleH(790, XXX), scaleV(438, XXX), # entries
527                                                         scaleH(450, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # red
528                                                         scaleH(640, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # green
529                                                         scaleH(830, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # yellow
530                                                         scaleH(1020, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # blue
531                                                         scaleH(480, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # red
532                                                         scaleH(670, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # green
533                                                         scaleH(860, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # yellow
534                                                         scaleH(1050, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # blue
535                                                         scaleH(120, XXX), scaleV(430, XXX), scaleH(150, XXX), scaleV(110, XXX), resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/fritz.png") # Fritz Logo size and pixmap
536                                                                                                                 )
537                 else:
538                         self.width = scaleH(1100, 570)
539                         debug("[FritzDisplayCalls] width: " + str(self.width))
540                         # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
541                         self.skin = """
542                                 <screen name="FritzDisplayCalls" position="%d,%d" size="%d,%d" title="%s" >
543                                         <eLabel position="0,0" size="%d,2" backgroundColor="#aaaaaa" />
544                                         <widget name="statusbar" position="%d,%d" size="%d,%d" font="Regular;%d" backgroundColor="#aaaaaa" transparent="1" />
545                                         <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
546                                         <widget name="entries" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" backgroundColor="#aaaaaa" transparent="1" />
547                                         <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
548                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
549                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
550                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
551                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
552                                         <widget name="key_red" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
553                                         <widget name="key_green" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
554                                         <widget name="key_yellow" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
555                                         <widget name="key_blue" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
556                                 </screen>""" % (
557                                                         scaleH(90, 75), scaleV(100, 78), # position 
558                                                         scaleH(1100, 570), scaleV(560, 430), # size
559                                                         _("Phone calls"),
560                                                         scaleH(1100, 570), # eLabel width
561                                                         scaleH(40, 5), scaleV(10, 5), # statusbar position
562                                                         scaleH(1050, 560), scaleV(25, 22), # statusbar size
563                                                         scaleV(22, 21), # statusbar font size
564                                                         scaleV(40, 28), # eLabel position vertical
565                                                         scaleH(1100, 570), # eLabel width
566                                                         scaleH(40, 5), scaleV(55, 40), # entries position
567                                                         scaleH(1040, 560), scaleV(458, 340), # entries size
568                                                         scaleV(518, 390), # eLabel position vertical
569                                                         scaleH(1100, 570), # eLabel width
570                                                         scaleH(20, 5), scaleV(525, 395), # widget red
571                                                         scaleH(290, 145), scaleV(525, 395), # widget green
572                                                         scaleH(560, 285), scaleV(525, 395), # widget yellow
573                                                         scaleH(830, 425), scaleV(525, 395), # widget blue
574                                                         scaleH(20, 5), scaleV(525, 395), scaleV(24, 21), # widget red
575                                                         scaleH(290, 145), scaleV(525, 395), scaleV(24, 21), # widget green
576                                                         scaleH(560, 285), scaleV(525, 395), scaleV(24, 21), # widget yellow
577                                                         scaleH(830, 425), scaleV(525, 395), scaleV(24, 21), # widget blue
578                                                                                                                 )
579
580                 Screen.__init__(self, session)
581                 HelpableScreen.__init__(self)
582
583                 # TRANSLATORS: keep it short, this is a button
584                 self["key_yellow"] = Button(_("All"))
585                 # TRANSLATORS: keep it short, this is a button
586                 self["key_red"] = Button(_("Missed"))
587                 # TRANSLATORS: keep it short, this is a button
588                 self["key_blue"] = Button(_("Incoming"))
589                 # TRANSLATORS: keep it short, this is a button
590                 self["key_green"] = Button(_("Outgoing"))
591
592                 self["setupActions"] = ActionMap(["OkCancelActions", "ColorActions"],
593                 {
594                         "yellow": self.displayAllCalls,
595                         "red": self.displayMissedCalls,
596                         "blue": self.displayInCalls,
597                         "green": self.displayOutCalls,
598                         "cancel": self.ok,
599                         "ok": self.showEntry, }, - 2)
600
601                 # TRANSLATORS: this is a help text, keep it short
602                 self.helpList.append((self["setupActions"], "OkCancelActions", [("ok", _("Show details of entry"))]))
603                 # TRANSLATORS: this is a help text, keep it short
604                 self.helpList.append((self["setupActions"], "OkCancelActions", [("cancel", _("Quit"))]))
605                 # TRANSLATORS: this is a help text, keep it short
606                 self.helpList.append((self["setupActions"], "ColorActions", [("yellow", _("Display all calls"))]))
607                 # TRANSLATORS: this is a help text, keep it short
608                 self.helpList.append((self["setupActions"], "ColorActions", [("red", _("Display missed calls"))]))
609                 # TRANSLATORS: this is a help text, keep it short
610                 self.helpList.append((self["setupActions"], "ColorActions", [("blue", _("Display incoming calls"))]))
611                 # TRANSLATORS: this is a help text, keep it short
612                 self.helpList.append((self["setupActions"], "ColorActions", [("green", _("Display outgoing calls"))]))
613
614                 self["statusbar"] = Label(_("Getting calls from FRITZ!Box..."))
615                 self["entries"] = MenuList([], True, content=eListboxPythonMultiContent)
616                 fontSize = scaleV(22, 18)
617                 fontHeight = scaleV(24, 20)
618                 self["entries"].l.setFont(0, gFont("Regular", fontSize))
619                 self["entries"].l.setItemHeight(fontHeight)
620
621                 debug("[FritzDisplayCalls] init: '''%s'''" % config.plugins.FritzCall.fbfCalls.value)
622                 self.display()
623
624         def ok(self):
625                 self.close()
626
627         def displayAllCalls(self):
628                 debug("[FritzDisplayCalls] displayAllCalls")
629                 self.display(FBF_ALL_CALLS)
630
631         def displayMissedCalls(self):
632                 debug("[FritzDisplayCalls] displayMissedCalls")
633                 self.display(FBF_MISSED_CALLS)
634
635         def displayInCalls(self):
636                 debug("[FritzDisplayCalls] displayInCalls")
637                 self.display(FBF_IN_CALLS)
638
639         def displayOutCalls(self):
640                 debug("[FritzDisplayCalls] displayOutCalls")
641                 self.display(FBF_OUT_CALLS)
642
643         def display(self, which=config.plugins.FritzCall.fbfCalls.value):
644                 debug("[FritzDisplayCalls] display")
645                 config.plugins.FritzCall.fbfCalls.value = which
646                 config.plugins.FritzCall.fbfCalls.save()
647                 self.header = fbfCallsChoices[which]
648                 fritzbox.getCalls(self, self.gotCalls, which)
649
650         def gotCalls(self, callList):
651                 debug("[FritzDisplayCalls] gotCalls")
652                 self.updateStatus(self.header + " (" + str(len(callList)) + ")")
653                 sortlist = []
654                 for (number, date, remote, direct, here) in callList:
655                         found = re.match("(\d\d.\d\d.)\d\d( \d\d:\d\d)", date)
656                         if found: date = found.group(1) + found.group(2)
657                         if direct == FBF_OUT_CALLS:
658                                 dir = LoadPixmap(resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/callout.png"))
659                         elif direct == FBF_IN_CALLS:
660                                 dir = LoadPixmap(resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/callin.png"))
661                         else:
662                                 dir = LoadPixmap(resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/callinfailed.png"))
663                         dateFieldWidth = 100
664                         dirFieldWidth = 16
665                         remoteFieldWidth = 100
666                         scrollbarWidth = 45
667                         fieldWidth = self.width -dateFieldWidth -5 -dirFieldWidth -5 -remoteFieldWidth -scrollbarWidth
668                         # debug("[FritzDisplayCalls] gotCalls: d: %d; f: %d; d: %d; r: %d" %(dateFieldWidth, fieldWidth, dirFieldWidth, remoteFieldWidth))
669                         sortlist.append([number,
670                                                          (eListboxPythonMultiContent.TYPE_TEXT, 0, 0, dateFieldWidth, 20, 0, RT_HALIGN_LEFT, date),
671                                                          (eListboxPythonMultiContent.TYPE_PIXMAP_ALPHATEST, dateFieldWidth+5, 0, dirFieldWidth, 16, dir),
672                                                          (eListboxPythonMultiContent.TYPE_TEXT, dateFieldWidth+5+dirFieldWidth+5, 0, fieldWidth, 20, 0, RT_HALIGN_LEFT, here),
673                                                          (eListboxPythonMultiContent.TYPE_TEXT, dateFieldWidth+5+dirFieldWidth+5+fieldWidth+5, 0, remoteFieldWidth, 20, 0, RT_HALIGN_RIGHT, remote)
674                                                          ])
675
676                 self["entries"].setList(sortlist)
677
678         def updateStatus(self, text):
679                 self["statusbar"].setText(text)
680
681         def showEntry(self):
682                 debug("[FritzDisplayCalls] showEntry")
683                 cur = self["entries"].getCurrent()
684                 if cur:
685                         if cur[0]:
686                                 debug("[FritzDisplayCalls] showEntry %s" % (cur[0]))
687                                 number = cur[0]
688                                 fullname = phonebook.search(cur[0])
689                                 if fullname:
690                                         # we have a name for this number
691                                         name = fullname
692                                         self.session.open(FritzOfferAction, self, number, name)
693                                 else:
694                                         # we don't
695                                         self.session.open(FritzOfferAction, self, number)
696                         else:
697                                 # we do not even have a number...
698                                 self.session.open(MessageBox,
699                                                   _("UNKNOWN"),
700                                                   type=MessageBox.TYPE_INFO)
701
702
703 class FritzOfferAction(Screen):
704         # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
705         width = 430 # = 5 + 3x140 + 5; 3 Buttons
706         height = 176 # = 5 + 126 + 40 + 5; 6 lines of text possible
707         skin = """
708                 <screen name="FritzOfferAction" position="%d,%d" size="%d,%d" title="%s" >
709                         <widget name="text" position="5,5" size="%d,%d" font="Regular;21" />
710                         <ePixmap position="5,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
711                         <ePixmap position="145,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
712                         <ePixmap position="285,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
713                         <widget name="key_red" position="5,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
714                         <widget name="key_green" position="145,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
715                         <widget name="key_yellow" position="285,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
716                 </screen>""" % (
717                                                 (DESKTOP_WIDTH - width) / 2,
718                                                 (DESKTOP_HEIGHT - height) / 2,
719                                                 width,
720                                                 height,
721                                                 _("Do what?"),
722                                                 width - 10,
723                                                 height - 10 - 40,
724                                                 height - 5 - 40, height - 5 - 40, height - 5 - 40, height - 5 - 40, height - 5 - 40, height - 5 - 40 # Buttons
725                                                                                                 ) 
726
727         def __init__(self, session, parent, number, name=""):
728                 Screen.__init__(self, session)
729         
730                 # TRANSLATORS: keep it short, this is a button
731                 self["key_red"] = Button(_("Lookup"))
732                 # TRANSLATORS: keep it short, this is a button
733                 self["key_green"] = Button(_("Call"))
734                 # TRANSLATORS: keep it short, this is a button
735                 self["key_yellow"] = Button(_("Save"))
736                 # TRANSLATORS: keep it short, this is a button
737                 # self["key_blue"] = Button(_("Search"))
738
739                 self["FritzOfferActions"] = ActionMap(["OkCancelActions", "ColorActions"],
740                 {
741                         "red": self.lookup,
742                         "green": self.call,
743                         "yellow": self.add,
744                         "cancel": self.exit,
745                         "ok": self.exit, }, - 2)
746
747                 self["text"] = Label(number + "\n\n" + name.replace(", ", "\n"))
748                 self.actualNumber = number
749                 self.actualName = name
750                 self.parent = parent
751                 self.lookupState = 0
752
753         def lookup(self):
754                 phonebookLocation = config.plugins.FritzCall.phonebookLocation.value
755                 if self.lookupState == 0:
756                         self.lookupState = 1
757                         self["text"].setText(self.actualNumber + "\n\n" + _("Reverse searching..."))
758                         ReverseLookupAndNotifier(self.actualNumber, self.lookedUp, "UTF-8", config.plugins.FritzCall.country.value)
759                         return
760                 if self.lookupState == 1 and os.path.exists(phonebookLocation + "/PhoneBook.csv"):
761                         self["text"].setText(self.actualNumber + "\n\n" + _("Searching in Outlook export..."))
762                         self.lookupState = 2
763                         self.lookedUp(self.actualNumber, FritzOutlookCSV.findNumber(self.actualNumber, phonebookLocation + "/PhoneBook.csv")) #@UndefinedVariable
764                         return
765                 else:
766                         self.lookupState = 2
767                 if self.lookupState == 2 and os.path.exists(phonebookLocation + "/PhoneBook.ldif"):
768                         self["text"].setText(self.actualNumber + "\n\n" + _("Searching in LDIF..."))
769                         self.lookupState = 0
770                         FritzLDIF.findNumber(self.actualNumber, open(phonebookLocation + "/PhoneBook.ldif"), self.lookedUp)
771                         return
772                 else:
773                         self.lookupState = 0
774                         self.lookup()
775
776         def lookedUp(self, number, name):
777                 if not name:
778                         if self.lookupState == 1:
779                                 name = _("No result from reverse lookup")
780                         elif self.lookupState == 2:
781                                 name = _("No result from Outlook export")
782                         else:
783                                 name = _("No result from LDIF")
784                 self.actualNumber = number
785                 self.actualName = name
786                 message = number + "\n\n" + name.replace(", ", "\n")
787                 self["text"].setText(str(message))
788
789         def call(self):
790                 fritzbox.dial(self.actualNumber)
791                 self.exit()
792
793         def add(self):
794                 phonebook.FritzDisplayPhonebook(self.session).add(self.parent, self.actualNumber, self.actualName)
795                 self.exit()
796
797         def exit(self):
798                 self.close()
799
800
801 class FritzCallPhonebook:
802         def __init__(self):
803                 debug("[FritzCallPhonebook] init")
804                 self.phonebook = {}
805                 self.reload()
806
807         def reload(self):
808                 debug("[FritzCallPhonebook] reload")
809                 self.phonebook = {}
810
811                 if not config.plugins.FritzCall.enable.value:
812                         return
813
814                 if config.plugins.FritzCall.phonebook.value and os.path.exists(config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.txt"):
815                         debug("[FritzCallPhonebook] reload: read PhoneBook.txt")
816                         phonebookTxtCorrupt = False
817                         for line in open(config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.txt"):
818                                 try:
819                                         line = line.decode("utf-8")
820                                 except UnicodeDecodeError:
821                                         try:
822                                                 line = line.decode("iso-8859-1")
823                                                 debug("[FritzCallPhonebook] Fallback to ISO-8859-1 in %s" % line)
824                                                 phonebookTxtCorrupt = True
825                                         except:
826                                                 debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
827                                                 phonebookTxtCorrupt = True
828                                 line = line.encode("utf-8")
829                                 if re.match("^\d+#.*$", line):
830                                         try:
831                                                 number, name = line.split("#")
832                                                 if not self.phonebook.has_key(number):
833                                                         self.phonebook[number] = name
834                                         except ValueError: # how could this possibly happen?!?!
835                                                 debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
836                                                 phonebookTxtCorrupt = True
837                                 else:
838                                         debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
839                                         phonebookTxtCorrupt = True
840
841                         if phonebookTxtCorrupt:
842                                 # dump phonebook to PhoneBook.txt
843                                 debug("[FritzCallPhonebook] dump Phonebook.txt")
844                                 os.rename(config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.txt",
845                                                 config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.txt.bck")
846                                 fNew = open(config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.txt", 'w')
847                                 for (number, name) in self.phonebook.iteritems():
848                                         fNew.write(number + "#" + name.encode("utf-8"))
849                                 fNew.close()
850
851                         # TODO: we could read PhoneBook.csv and PhoneBook.ldif here also...
852                 
853                 if config.plugins.FritzCall.fritzphonebook.value:
854                         fritzbox.loadFritzBoxPhonebook()
855
856                 if DESKTOP_WIDTH <> 1280 or DESKTOP_HEIGHT <> 720:
857                         config.plugins.FritzCall.fullscreen.value = False
858
859         def search(self, number):
860                 # debug("[FritzCallPhonebook] Searching for %s" %number
861                 name = None
862                 if config.plugins.FritzCall.phonebook.value or config.plugins.FritzCall.fritzphonebook.value:
863                         if self.phonebook.has_key(number):
864                                 name = self.phonebook[number].replace(", ", "\n").strip()
865                 return name
866
867         def add(self, number, name):
868                 debug("[FritzCallPhonebook] add")
869                 name = name.replace("\n", ", ").replace('#','') # this is just for safety reasons. add should only be called with newlines converted into commas
870                 self.remove(number)
871                 self.phonebook[number] = name;
872                 if number and number <> 0:
873                         if config.plugins.FritzCall.phonebook.value:
874                                 try:
875                                         f = open(config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.txt", 'a')
876                                         name = name.strip() + "\n"
877                                         string = "%s#%s" % (number, name)
878                                         try:
879                                                 f.write(string.encode("utf-8"))
880                                                 debug("[FritzCallPhonebook] added %s with %s to Phonebook.txt" % (number, name.strip()))
881                                         except UnicodeDecodeError:
882                                                 f.write(string)
883                                                 debug("[FritzCallPhonebook] error when adding %s with utf-8, writing raw" % name.strip())
884                                         f.close()
885                                         return True
886         
887                                 except IOError:
888                                         return False
889
890         def remove(self, number):
891                 if number in self.phonebook:
892                         debug("[FritzCallPhonebook] remove entry in phonebook")
893                         del self.phonebook[number]
894                         if config.plugins.FritzCall.phonebook.value:
895                                 try:
896                                         phonebookFilename = config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.txt"
897                                         debug("[FritzCallPhonebook] remove entry in Phonebook.txt")
898                                         fOld = open(phonebookFilename, 'r')
899                                         fNew = open(phonebookFilename + str(os.getpid()), 'w')
900                                         line = fOld.readline()
901                                         while (line):
902                                                 if not re.match("^" + number + "#.*$", line):
903                                                         fNew.write(line)
904                                                 line = fOld.readline()
905                                         fOld.close()
906                                         fNew.close()
907                                         os.remove(phonebookFilename)
908                                         os.rename(phonebookFilename + str(os.getpid()), phonebookFilename)
909                                         debug("[FritzCallPhonebook] removed %s from Phonebook.txt" % number)
910                                         return True
911         
912                                 except IOError:
913                                         pass
914                 return False
915
916         class FritzDisplayPhonebook(Screen, HelpableScreen, NumericalTextInput):
917         
918                 def __init__(self, session):
919                         if config.plugins.FritzCall.fullscreen.value:
920                                 self.width = DESKTOP_WIDTH
921                                 self.height = DESKTOP_HEIGHT
922                                 backMainPng = ""
923                                 if os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, DESKTOP_SKIN + "/menu/back-main.png")):
924                                         backMainPng = DESKTOP_SKIN + "/menu/back-main.png"
925                                 elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1-picon/menu/back-main.png")):
926                                         backMainPng = "Kerni-HD1-picon/menu/back-main.png"
927                                 if backMainPng:
928                                         backMainLine = """<ePixmap position="0,0" zPosition="-10" size="%d,%d" pixmap="%s" transparent="1" />""" % (self.width, self.height, backMainPng)
929                                 else:
930                                         backMainLine = ""
931                                 debug("[FritzDisplayPhonebook] backMainLine: " + backMainLine)
932                                         
933                                 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
934                                 self.skin = """
935                                         <screen name="FritzdisplayPhonebook" position="0,0" size="%d,%d" title="%s" flags="wfNoBorder">
936                                                 %s
937                                                 <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
938                                                         <convert type="ClockToText">Default</convert>
939                                                 </widget>
940                                                 <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
941                                                         <convert type="ClockToText">Date</convert>
942                                                 </widget>
943                                                 <eLabel text="%s" position="%d,%d" size="%d,%d" font="Regular;%d" halign="center" backgroundColor="#0b67a2" transparent="1"/>
944                                 
945                                                 <widget name="entries" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" transparent="1" />
946                                 
947                                                 <ePixmap pixmap="skin_default/buttons/red.png"          position="%d,%d"        size="%d,%d" alphatest="on" />
948                                                 <ePixmap pixmap="skin_default/buttons/green.png"        position="%d,%d"        size="%d,%d" alphatest="on" />
949                                                 <ePixmap pixmap="skin_default/buttons/yellow.png"       position="%d,%d"        size="%d,%d" alphatest="on" />
950                                                 <ePixmap pixmap="skin_default/buttons/blue.png"         position="%d,%d"        size="%d,%d" alphatest="on" />
951                                                 <widget name="key_red"  position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
952                                                 <widget name="key_green"        position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
953                                                 <widget name="key_yellow"       position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
954                                                 <widget name="key_blue"         position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
955                                                 <ePixmap position="%d,%d" size="%d,%d" zPosition="2" pixmap="%s" transparent="1" alphatest="blend" />   
956                                         </screen>""" % (
957                                                                         self.width, self.height, _("Phonebook"),
958                                                                         backMainLine,
959                                                                         scaleH(1130, XXX), scaleV(40, XXX), scaleH(80, XXX), scaleV(26, XXX), scaleV(26, XXX), # time
960                                                                         scaleH(900, XXX), scaleV(70, XXX), scaleH(310, XXX), scaleV(22, XXX), scaleV(20, XXX), # date
961                                                                         "FritzCall " + _("Phonebook"), scaleH(80, XXX), scaleV(63, XXX), scaleH(300, XXX), scaleV(30, XXX), scaleV(27, XXX), # eLabel
962                                                                         scaleH(420, XXX), scaleV(120, XXX), scaleH(790, XXX), scaleV(438, XXX), # entries
963                                                                         scaleH(450, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # red
964                                                                         scaleH(640, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # green
965                                                                         scaleH(830, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # yellow
966                                                                         scaleH(1020, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # blue
967                                                                         scaleH(480, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # red
968                                                                         scaleH(670, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # green
969                                                                         scaleH(860, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # yellow
970                                                                         scaleH(1050, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # blue
971                                                                         scaleH(120, XXX), scaleV(430, XXX), scaleH(150, XXX), scaleV(110, XXX), resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/fritz.png") # Fritz Logo size and pixmap
972                                                                                                                                 )
973                         else:
974                                 self.width = scaleH(1100, 570)
975                                 debug("[FritzDisplayPhonebook] width: " + str(self.width))
976                                 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
977                                 self.skin = """
978                                         <screen name="FritzDisplayPhonebook" position="%d,%d" size="%d,%d" title="%s" >
979                                                 <eLabel position="0,0" size="%d,2" backgroundColor="#aaaaaa" />
980                                                 <widget name="entries" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" backgroundColor="#20040404" transparent="1" />
981                                                 <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
982                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
983                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
984                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
985                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
986                                                 <widget name="key_red" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
987                                                 <widget name="key_green" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
988                                                 <widget name="key_yellow" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
989                                                 <widget name="key_blue" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
990                                         </screen>""" % (
991                                                         scaleH(90, 75), scaleV(100, 73), # position 
992                                                         scaleH(1100, 570), scaleV(560, 430), # size
993                                                         _("Phonebook"),
994                                                         scaleH(1100, 570), # eLabel width
995                                                         scaleH(40, 5), scaleV(20, 5), # entries position
996                                                         scaleH(1040, 560), scaleV(488, 380), # entries size
997                                                         scaleV(518, 390), # eLabel position vertical
998                                                         scaleH(1100, 570), # eLabel width
999                                                         scaleH(20, 5), scaleV(525, 395), # ePixmap red
1000                                                         scaleH(290, 145), scaleV(525, 395), # ePixmap green
1001                                                         scaleH(560, 285), scaleV(525, 395), # ePixmap yellow
1002                                                         scaleH(830, 425), scaleV(525, 395), # ePixmap blue
1003                                                         scaleH(20, 5), scaleV(525, 395), scaleV(24, 21), # widget red
1004                                                         scaleH(290, 145), scaleV(525, 395), scaleV(24, 21), # widget green
1005                                                         scaleH(560, 285), scaleV(525, 395), scaleV(24, 21), # widget yellow
1006                                                         scaleH(830, 425), scaleV(525, 395), scaleV(24, 21), # widget blue
1007                                                         )
1008         
1009                         Screen.__init__(self, session)
1010                         NumericalTextInput.__init__(self)
1011                         HelpableScreen.__init__(self)
1012                 
1013                         # TRANSLATORS: keep it short, this is a button
1014                         self["key_red"] = Button(_("Delete"))
1015                         # TRANSLATORS: keep it short, this is a button
1016                         self["key_green"] = Button(_("New"))
1017                         # TRANSLATORS: keep it short, this is a button
1018                         self["key_yellow"] = Button(_("Edit"))
1019                         # TRANSLATORS: keep it short, this is a button
1020                         self["key_blue"] = Button(_("Search"))
1021         
1022                         self["setupActions"] = ActionMap(["OkCancelActions", "ColorActions"],
1023                         {
1024                                 "red": self.delete,
1025                                 "green": self.add,
1026                                 "yellow": self.edit,
1027                                 "blue": self.search,
1028                                 "cancel": self.exit,
1029                                 "ok": self.showEntry, }, - 2)
1030         
1031                         # TRANSLATORS: this is a help text, keep it short
1032                         self.helpList.append((self["setupActions"], "OkCancelActions", [("ok", _("Show details of entry"))]))
1033                         # TRANSLATORS: this is a help text, keep it short
1034                         self.helpList.append((self["setupActions"], "OkCancelActions", [("cancel", _("Quit"))]))
1035                         # TRANSLATORS: this is a help text, keep it short
1036                         self.helpList.append((self["setupActions"], "ColorActions", [("red", _("Delete entry"))]))
1037                         # TRANSLATORS: this is a help text, keep it short
1038                         self.helpList.append((self["setupActions"], "ColorActions", [("green", _("Add entry to phonebook"))]))
1039                         # TRANSLATORS: this is a help text, keep it short
1040                         self.helpList.append((self["setupActions"], "ColorActions", [("yellow", _("Edit selected entry"))]))
1041                         # TRANSLATORS: this is a help text, keep it short
1042                         self.helpList.append((self["setupActions"], "ColorActions", [("blue", _("Search (case insensitive)"))]))
1043         
1044                         self["entries"] = MenuList([], True, content=eListboxPythonMultiContent)
1045                         fontSize = scaleV(22, 18)
1046                         fontHeight = scaleV(24, 20)
1047                         self["entries"].l.setFont(0, gFont("Regular", fontSize))
1048                         self["entries"].l.setItemHeight(fontHeight)
1049                         debug("[FritzCallPhonebook] displayPhonebook init")
1050                         self.display()
1051         
1052                 def display(self, filter=""):
1053                         debug("[FritzCallPhonebook] displayPhonebook/display")
1054                         self.sortlist = []
1055                         sortlistHelp = sorted((name.lower(), name, number) for (number, name) in phonebook.phonebook.iteritems())
1056                         for (low, name, number) in sortlistHelp:
1057                                 if number == "01234567890":
1058                                         continue
1059                                 try:
1060                                         low = low.decode("utf-8")
1061                                 except UnicodeDecodeError:  # this should definitely not happen
1062                                         try:
1063                                                 low = low.decode("iso-8859-1")
1064                                         except:
1065                                                 debug("[FritzCallPhonebook] displayPhonebook/display: corrupt phonebook entry for %s" % number)
1066                                                 # self.session.open(MessageBox, _("Corrupt phonebook entry\nfor number %s\nDeleting.") %number, type = MessageBox.TYPE_ERROR)
1067                                                 phonebook.remove(number)
1068                                                 continue
1069                                 else:
1070                                         if filter:
1071                                                 filter = filter.lower()
1072                                                 if low.find(filter) == - 1:
1073                                                         continue
1074                                         name = name.strip().decode("utf-8")
1075                                         number = number.strip().decode("utf-8")
1076                                         found = re.match("([^,]*),.*", name)   # strip address information from the name part
1077                                         if found:
1078                                                 shortname = found.group(1)
1079                                         else:
1080                                                 shortname = name
1081                                         numberFieldWidth = 150
1082                                         fieldWidth = self.width -5 -numberFieldWidth -10
1083                                         self.sortlist.append([(number.encode("utf-8", "replace"),
1084                                                                    name.encode("utf-8", "replace")),
1085                                                                    (eListboxPythonMultiContent.TYPE_TEXT, 0, 0, fieldWidth, 20, 0, RT_HALIGN_LEFT, shortname.encode('utf-8', 'replace')),
1086                                                                    (eListboxPythonMultiContent.TYPE_TEXT, fieldWidth +5, 0, numberFieldWidth, 20, 0, RT_HALIGN_LEFT, number.encode("utf-8", "replace"))
1087                                                                    ])
1088                                 
1089                         self["entries"].setList(self.sortlist)
1090         
1091                 def showEntry(self):
1092                         cur = self["entries"].getCurrent()
1093                         if cur and cur[0]:
1094                                 debug("[FritzCallPhonebook] displayPhonebook/showEntry (%s,%s)" % (cur[0][0], cur[0][1]))
1095                                 number = cur[0][0]
1096                                 name = phonebook.search(number).replace('\n', ', ')
1097                                 self.session.open(FritzOfferAction, self, number, name)
1098         
1099                 def delete(self):
1100                         cur = self["entries"].getCurrent()
1101                         if cur and cur[0]:
1102                                 debug("[FritzCallPhonebook] displayPhonebook/delete " + cur[0][0])
1103                                 self.session.openWithCallback(
1104                                         self.deleteConfirmed,
1105                                         MessageBox,
1106                                         _("Do you really want to delete entry for\n\n%(number)s\n\n%(name)s?") 
1107                                         % { 'number':str(cur[0][0]), 'name':str(cur[0][1]).replace(", ", "\n") }
1108                                                                 )
1109                         else:
1110                                 self.session.open(MessageBox, _("No entry selected"), MessageBox.TYPE_INFO)
1111         
1112                 def deleteConfirmed(self, ret):
1113                         debug("[FritzCallPhonebook] displayPhonebook/deleteConfirmed")
1114                         #
1115                         # if ret: delete number from sortlist, delete number from phonebook.phonebook and write it to disk
1116                         #
1117                         cur = self["entries"].getCurrent()
1118                         if cur:
1119                                 if ret:
1120                                         # delete number from sortlist, delete number from phonebook.phonebook and write it to disk
1121                                         debug("[FritzCallPhonebook] displayPhonebook/deleteConfirmed: remove " + cur[0][0])
1122                                         phonebook.remove(cur[0][0])
1123                                         self.display()
1124                                 # else:
1125                                         # self.session.open(MessageBox, _("Not deleted."), MessageBox.TYPE_INFO)
1126                         else:
1127                                 self.session.open(MessageBox, _("No entry selected"), MessageBox.TYPE_INFO)
1128         
1129                 def add(self, parent=None, number="", name=""):
1130                         class addScreen(Screen, ConfigListScreen):
1131                                 '''ConfiglistScreen with two ConfigTexts for Name and Number'''
1132                                 width = 570
1133                                 height = 100
1134                                 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
1135                                 skin = """
1136                                         <screen position="%d,%d" size="%d,%d" title="%s" >
1137                                         <widget name="config" position="5,5" size="%d,%d" scrollbarMode="showOnDemand" />
1138                                         <ePixmap position="145,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1139                                         <ePixmap position="285,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1140                                         <widget name="key_red" position="145,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1141                                         <widget name="key_green" position="285,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1142                                         </screen>""" % (
1143                                                                         (DESKTOP_WIDTH - width) / 2,
1144                                                                         (DESKTOP_HEIGHT - height) / 2,
1145                                                                         width,
1146                                                                         height,
1147                                                                         _("Add entry to phonebook"),
1148                                                                         width - 5 - 5,
1149                                                                         height - 5 - 40 - 5,
1150                                                                         height - 40 - 5, height - 40 - 5, height - 40 - 5, height - 40 - 5
1151                                                                                                                                                  )
1152         
1153         
1154                                 def __init__(self, session, parent, number="", name=""):
1155                                         #
1156                                         # setup screen with two ConfigText and OK and ABORT button
1157                                         # 
1158                                         Screen.__init__(self, session)
1159                                         self.session = session
1160                                         self.parent = parent
1161                                         # TRANSLATORS: keep it short, this is a button
1162                                         self["key_red"] = Button(_("Cancel"))
1163                                         # TRANSLATORS: keep it short, this is a button
1164                                         self["key_green"] = Button(_("OK"))
1165                                         self["setupActions"] = ActionMap(["SetupActions", "ColorActions"],
1166                                         {
1167                                                 "cancel": self.cancel,
1168                                                 "red": self.cancel,
1169                                                 "green": self.add,
1170                                                 "ok": self.add,
1171                                         }, - 2)
1172         
1173                                         self.list = [ ]
1174                                         ConfigListScreen.__init__(self, self.list, session=session)
1175                                         config.plugins.FritzCall.name.value = name
1176                                         config.plugins.FritzCall.number.value = number
1177                                         self.list.append(getConfigListEntry(_("Name"), config.plugins.FritzCall.name))
1178                                         self.list.append(getConfigListEntry(_("Number"), config.plugins.FritzCall.number))
1179                                         self["config"].list = self.list
1180                                         self["config"].l.setList(self.list)
1181         
1182                                 def add(self):
1183                                         # get texts from Screen
1184                                         # add (number,name) to sortlist and phonebook.phonebook and disk
1185                                         self.number = config.plugins.FritzCall.number.value
1186                                         self.name = config.plugins.FritzCall.name.value
1187                                         if not self.number or not self.name:
1188                                                 self.session.open(MessageBox, _("Entry incomplete."), type=MessageBox.TYPE_ERROR)
1189                                                 return
1190                                         # add (number,name) to sortlist and phonebook.phonebook and disk
1191         #                                       oldname = phonebook.search(self.number)
1192         #                                       if oldname:
1193         #                                               self.session.openWithCallback(
1194         #                                                       self.overwriteConfirmed,
1195         #                                                       MessageBox,
1196         #                                                       _("Do you really want to overwrite entry for %(number)s\n\n%(name)s\n\nwith\n\n%(newname)s?")
1197         #                                                       % {
1198         #                                                       'number':self.number,
1199         #                                                       'name': oldname,
1200         #                                                       'newname':self.name.replace(", ","\n")
1201         #                                                       }
1202         #                                                       )
1203         #                                               self.close()
1204         #                                               return
1205                                         phonebook.add(self.number, self.name)
1206                                         self.close()
1207                                         self.parent.display()
1208         
1209                                 def overwriteConfirmed(self, ret):
1210                                         if ret:
1211                                                 phonebook.remove(self.number)
1212                                                 phonebook.add(self.number, self.name)
1213                                                 self.parent.display()
1214         
1215                                 def cancel(self):
1216                                         self.close()
1217         
1218                         debug("[FritzCallPhonebook] displayPhonebook/add")
1219                         if not parent:
1220                                 parent = self
1221                         self.session.open(addScreen, parent, number, name)
1222         
1223                 def edit(self):
1224                         debug("[FritzCallPhonebook] displayPhonebook/edit")
1225                         cur = self["entries"].getCurrent()
1226                         if cur is None:
1227                                 self.session.open(MessageBox, _("No entry selected"), MessageBox.TYPE_INFO)
1228                         else:
1229                                 (number, name) = cur[0]
1230                                 self.add(self, number, name)
1231         
1232                 def search(self):
1233                         debug("[FritzCallPhonebook] displayPhonebook/search")
1234                         self.help_window = self.session.instantiateDialog(NumericalTextInputHelpDialog, self)
1235                         self.help_window.show()
1236                         self.session.openWithCallback(self.doSearch, InputBox, _("Enter Search Terms"), _("Search phonebook"))
1237         
1238                 def doSearch(self, searchTerms):
1239                         if not searchTerms: searchTerms = ""
1240                         debug("[FritzCallPhonebook] displayPhonebook/doSearch: " + searchTerms)
1241                         if self.help_window:
1242                                 self.session.deleteDialog(self.help_window)
1243                                 self.help_window = None
1244                         self.display(searchTerms)
1245         
1246                 def exit(self):
1247                         self.close()
1248
1249 phonebook = FritzCallPhonebook()
1250
1251
1252 class FritzCallSetup(Screen, ConfigListScreen, HelpableScreen):
1253
1254         def __init__(self, session, args=None):
1255                 if config.plugins.FritzCall.fullscreen.value:
1256                         self.width = DESKTOP_WIDTH
1257                         self.height = DESKTOP_HEIGHT
1258                         backMainPng = ""
1259                         backMainLine = ""
1260                         if os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, DESKTOP_SKIN + "/menu/back-main.png")):
1261                                 backMainPng = DESKTOP_SKIN + "/menu/back-main.png"
1262                         elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1-picon/menu/back-main.png")):
1263                                 backMainPng = "Kerni-HD1-picon/menu/back-main.png"
1264                         if backMainPng:
1265                                 backMainLine = """<ePixmap position="0,0" zPosition="-10" size="%d,%d" pixmap="%s" transparent="1" />""" % (self.width, self.height, backMainPng)
1266                         else:
1267                                 backMainLine = ""
1268                         debug("[FritzCallSetup] backMainLine: " + backMainLine)
1269                         # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
1270                         self.skin = """
1271                                 <screen name="FritzCallSetup" position="0,0" size="%d,%d" title="%s" flags="wfNoBorder">
1272                                         %s
1273                                         <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
1274                                                 <convert type="ClockToText">Default</convert>
1275                                         </widget>
1276                                         <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
1277                                                 <convert type="ClockToText">Date</convert>
1278                                         </widget>
1279                                         <eLabel text="%s" position="%d,%d" size="%d,%d" font="Regular;%d" halign="center" backgroundColor="#0b67a2" transparent="1"/>
1280                         
1281                                         <widget name="consideration" position="%d,%d"  size="%d,%d" font="Regular;%d" halign="center" backgroundColor="#353e575e" transparent="1" />
1282                                         <widget name="config" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" transparent="1" />
1283                         
1284                                         <ePixmap pixmap="skin_default/buttons/red.png"          position="%d,%d"        size="%d,%d" alphatest="on" />
1285                                         <ePixmap pixmap="skin_default/buttons/green.png"        position="%d,%d"        size="%d,%d" alphatest="on" />
1286                                         <ePixmap pixmap="skin_default/buttons/yellow.png"       position="%d,%d"        size="%d,%d" alphatest="on" />
1287                                         <ePixmap pixmap="skin_default/buttons/blue.png"         position="%d,%d"        size="%d,%d" alphatest="on" />
1288                                         <widget name="key_red" position="%d,%d"                 size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
1289                                         <widget name="key_green"  position="%d,%d"      size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
1290                                         <widget name="key_yellow" position="%d,%d"      size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
1291                                         <widget name="key_blue" position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
1292                                         <ePixmap position="%d,%d" size="%d,%d" zPosition="2" pixmap="%s" transparent="1" alphatest="blend" />           
1293                                 </screen>""" % (
1294                                                                 self.width, self.height, _("FritzCall Setup"),
1295                                                                 backMainLine,
1296                                                                 scaleH(1130, XXX), scaleV(40, XXX), scaleH(80, XXX), scaleV(26, XXX), scaleV(26, XXX), # time
1297                                                                 scaleH(900, XXX), scaleV(70, XXX), scaleH(310, XXX), scaleV(22, XXX), scaleV(20, XXX), # date
1298                                                                 _("FritzCall Setup"), scaleH(500, XXX), scaleV(63, XXX), scaleH(330, XXX), scaleV(30, XXX), scaleV(27, XXX), # eLabel
1299                                                                 scaleH(80, XXX), scaleV(150, XXX), scaleH(250, XXX), scaleV(200, XXX), scaleV(22, XXX), # consideration
1300                                                                 scaleH(420, XXX), scaleV(125, XXX), scaleH(790, XXX), scaleV(428, XXX), # config
1301                                                                 scaleH(450, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # red
1302                                                                 scaleH(640, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # green
1303                                                                 scaleH(830, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # yellow
1304                                                                 scaleH(1020, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # blue
1305                                                                 scaleH(480, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # red
1306                                                                 scaleH(670, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # green
1307                                                                 scaleH(860, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # yellow
1308                                                                 scaleH(1050, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # blue
1309                                                                 scaleH(120, XXX), scaleV(430, XXX), scaleH(150, XXX), scaleV(110, XXX), resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/fritz.png") # Fritz Logo size and pixmap
1310                                                                                                                                 ) 
1311                 else:
1312                         self.width = scaleH(1100, 570)
1313                         debug("[FritzCallSetup] width: " + str(self.width))
1314                         # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
1315                         self.skin = """
1316                                 <screen name="FritzCallSetup" position="%d,%d" size="%d,%d" title="%s" >
1317                                 <eLabel position="0,0" size="%d,2" backgroundColor="#aaaaaa" />
1318                                 <widget name="consideration" position="%d,%d" halign="center" size="%d,%d" font="Regular;%d" backgroundColor="#20040404" transparent="1" />
1319                                 <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
1320                                 <widget name="config" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" backgroundColor="#20040404" transparent="1" />
1321                                 <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
1322                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1323                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1324                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1325                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
1326                                 <widget name="key_red" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1327                                 <widget name="key_green" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1328                                 <widget name="key_yellow" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1329                                 <widget name="key_blue" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1330                                 </screen>""" % (
1331                                                         scaleH(90, 75), scaleV(100, 73), # position 
1332                                                         scaleH(1100, 570), scaleV(560, 430), # size
1333                                                         _("FritzCall Setup") + 
1334                                                         " (" + "$Revision$"[1: - 1] + 
1335                                                         "$Date$"[7:23] + ")",
1336                                                         scaleH(1100, 570), # eLabel width
1337                                                         scaleH(40, 20), scaleV(10, 5), # consideration position
1338                                                         scaleH(1050, 530), scaleV(25, 45), # consideration size
1339                                                         scaleV(22, 20), # consideration font size
1340                                                         scaleV(40, 50), # eLabel position vertical
1341                                                         scaleH(1100, 570), # eLabel width
1342                                                         scaleH(40, 5), scaleV(60, 57), # config position
1343                                                         scaleH(1040, 560), scaleV(453, 328), # config size
1344                                                         scaleV(518, 390), # eLabel position vertical
1345                                                         scaleH(1100, 570), # eLabel width
1346                                                         scaleH(20, 5), scaleV(525, 395), # widget red
1347                                                         scaleH(290, 145), scaleV(525, 395), # widget green
1348                                                         scaleH(560, 285), scaleV(525, 395), # widget yellow
1349                                                         scaleH(830, 425), scaleV(525, 395), # widget blue
1350                                                         scaleH(20, 5), scaleV(525, 395), scaleV(24, 21), # widget red
1351                                                         scaleH(290, 145), scaleV(525, 395), scaleV(24, 21), # widget green
1352                                                         scaleH(560, 285), scaleV(525, 395), scaleV(24, 21), # widget yellow
1353                                                         scaleH(830, 425), scaleV(525, 395), scaleV(24, 21), # widget blue
1354                                                                                                                 )
1355
1356                 Screen.__init__(self, session)
1357                 HelpableScreen.__init__(self)
1358                 self.session = session
1359
1360                 self["consideration"] = Label(_("You need to enable the monitoring on your FRITZ!Box by dialing #96*5*!"))
1361                 self.list = []
1362
1363                 # Initialize Buttons
1364                 # TRANSLATORS: keep it short, this is a button
1365                 self["key_red"] = Button(_("Cancel"))
1366                 # TRANSLATORS: keep it short, this is a button
1367                 self["key_green"] = Button(_("OK"))
1368                 # TRANSLATORS: keep it short, this is a button
1369                 self["key_yellow"] = Button(_("Phone calls"))
1370                 # TRANSLATORS: keep it short, this is a button
1371                 self["key_blue"] = Button(_("Phonebook"))
1372
1373                 self["setupActions"] = ActionMap(["SetupActions", "ColorActions"],
1374                 {
1375                         "red": self.cancel,
1376                         "green": self.save,
1377                         "yellow": self.displayCalls,
1378                         "blue": self.displayPhonebook,
1379                         "cancel": self.cancel,
1380                         "save": self.save,
1381                         "ok": self.save,
1382                 }, - 2)
1383
1384                 # TRANSLATORS: this is a help text, keep it short
1385                 self.helpList.append((self["setupActions"], "SetupActions", [("ok", _("save and quit"))]))
1386                 # TRANSLATORS: this is a help text, keep it short
1387                 self.helpList.append((self["setupActions"], "SetupActions", [("save", _("save and quit"))]))
1388                 # TRANSLATORS: this is a help text, keep it short
1389                 self.helpList.append((self["setupActions"], "SetupActions", [("cancel", _("quit"))]))
1390                 # TRANSLATORS: this is a help text, keep it short
1391                 self.helpList.append((self["setupActions"], "ColorActions", [("red", _("quit"))]))
1392                 # TRANSLATORS: this is a help text, keep it short
1393                 self.helpList.append((self["setupActions"], "ColorActions", [("green", _("save and quit"))]))
1394                 # TRANSLATORS: this is a help text, keep it short
1395                 self.helpList.append((self["setupActions"], "ColorActions", [("yellow", _("display calls"))]))
1396                 # TRANSLATORS: this is a help text, keep it short
1397                 self.helpList.append((self["setupActions"], "ColorActions", [("blue", _("display phonebook"))]))
1398
1399                 ConfigListScreen.__init__(self, self.list, session=session)
1400                 self.createSetup()
1401
1402
1403         def keyLeft(self):
1404                 ConfigListScreen.keyLeft(self)
1405                 self.createSetup()
1406
1407         def keyRight(self):
1408                 ConfigListScreen.keyRight(self)
1409                 self.createSetup()
1410
1411         def createSetup(self):
1412                 self.list = [ ]
1413                 self.list.append(getConfigListEntry(_("Call monitoring"), config.plugins.FritzCall.enable))
1414                 if config.plugins.FritzCall.enable.value:
1415                         self.list.append(getConfigListEntry(_("Mute on call"), config.plugins.FritzCall.muteOnCall))
1416                         self.list.append(getConfigListEntry(_("FRITZ!Box FON address (Name or IP)"), config.plugins.FritzCall.hostname))
1417
1418                         self.list.append(getConfigListEntry(_("Show after Standby"), config.plugins.FritzCall.afterStandby))
1419
1420                         self.list.append(getConfigListEntry(_("Show Calls for specific MSN"), config.plugins.FritzCall.filter))
1421                         if config.plugins.FritzCall.filter.value:
1422                                 self.list.append(getConfigListEntry(_("MSN to show (separated by ,)"), config.plugins.FritzCall.filtermsn))
1423
1424                         self.list.append(getConfigListEntry(_("Show Outgoing Calls"), config.plugins.FritzCall.showOutgoing))
1425                         if config.plugins.FritzCall.showOutgoing.value:
1426                                 self.list.append(getConfigListEntry(_("Areacode to add to Outgoing Calls (if necessary)"), config.plugins.FritzCall.prefix))
1427                         self.list.append(getConfigListEntry(_("Timeout for Call Notifications (seconds)"), config.plugins.FritzCall.timeout))
1428                         self.list.append(getConfigListEntry(_("Reverse Lookup Caller ID (select country below)"), config.plugins.FritzCall.lookup))
1429                         if config.plugins.FritzCall.lookup.value:
1430                                 self.list.append(getConfigListEntry(_("Country"), config.plugins.FritzCall.country))
1431
1432                         self.list.append(getConfigListEntry(_("Password Accessing FRITZ!Box"), config.plugins.FritzCall.password))
1433                         self.list.append(getConfigListEntry(_("Extension number to initiate call on"), config.plugins.FritzCall.extension))
1434                         self.list.append(getConfigListEntry(_("Read PhoneBook from FRITZ!Box"), config.plugins.FritzCall.fritzphonebook))
1435                         if config.plugins.FritzCall.fritzphonebook.value:
1436                                 self.list.append(getConfigListEntry(_("Append type of number"), config.plugins.FritzCall.showType))
1437                                 self.list.append(getConfigListEntry(_("Append shortcut number"), config.plugins.FritzCall.showShortcut))
1438                                 self.list.append(getConfigListEntry(_("Append vanity name"), config.plugins.FritzCall.showVanity))
1439
1440                         self.list.append(getConfigListEntry(_("Use internal PhoneBook"), config.plugins.FritzCall.phonebook))
1441                         if config.plugins.FritzCall.phonebook.value:
1442                                 self.list.append(getConfigListEntry(_("PhoneBook Location"), config.plugins.FritzCall.phonebookLocation))
1443                                 if config.plugins.FritzCall.lookup.value:
1444                                         self.list.append(getConfigListEntry(_("Automatically add new Caller to PhoneBook"), config.plugins.FritzCall.addcallers))
1445
1446                         self.list.append(getConfigListEntry(_("Strip Leading 0"), config.plugins.FritzCall.internal))
1447                         # self.list.append(getConfigListEntry(_("Default display mode for FRITZ!Box calls"), config.plugins.FritzCall.fbfCalls))
1448                         if DESKTOP_WIDTH == 1280 and DESKTOP_HEIGHT == 720:
1449                                 self.list.append(getConfigListEntry(_("Full screen display"), config.plugins.FritzCall.fullscreen))
1450                         self.list.append(getConfigListEntry(_("Debug"), config.plugins.FritzCall.debug))
1451
1452                 self["config"].list = self.list
1453                 self["config"].l.setList(self.list)
1454
1455         def save(self):
1456 #               debug("[FritzCallSetup] save"
1457                 for x in self["config"].list:
1458                         x[1].save()
1459                 if fritz_call is not None:
1460                         fritz_call.connect()
1461                         debug("[FritzCallSetup] called phonebook.reload()")
1462                         phonebook.reload()
1463                 self.close()
1464
1465         def cancel(self):
1466 #               debug("[FritzCallSetup] cancel"
1467                 for x in self["config"].list:
1468                         x[1].cancel()
1469                 self.close()
1470
1471         def displayCalls(self):
1472                 self.session.open(FritzDisplayCalls)
1473
1474         def displayPhonebook(self):
1475                 self.session.open(phonebook.FritzDisplayPhonebook)
1476
1477
1478 standbyMode = False
1479
1480 class FritzCallList:
1481         def __init__(self):
1482                 self.callList = [ ]
1483         
1484         def add(self, event, date, number, caller, phone):
1485                 debug("[FritzCallList] add")
1486                 if len(self.callList) > 10:
1487                         if self.callList[0] != "Start":
1488                                 self.callList[0] = "Start"
1489                         del self.callList[1]
1490
1491                 self.callList.append((event, number, date, caller, phone))
1492         
1493         def display(self):
1494                 debug("[FritzCallList] display")
1495                 global standbyMode
1496                 global my_global_session
1497                 standbyMode = False
1498                 # Standby.inStandby.onClose.remove(self.display) object does not exist anymore...
1499                 # build screen from call list
1500                 text = "\n"
1501                 if self.callList[0] == "Start":
1502                         text = text + _("Last 10 calls:\n")
1503                         del self.callList[0]
1504
1505                 for call in self.callList:
1506                         (event, number, date, caller, phone) = call
1507                         if event == "RING":
1508                                 direction = "->"
1509                         else:
1510                                 direction = "<-"
1511
1512                         # shorten the date info
1513                         found = re.match(".*(\d\d.\d\d.)\d\d( \d\d:\d\d)", date)
1514                         if found: date = found.group(1) + found.group(2)
1515
1516                         # our phone could be of the form "0123456789 (home)", then we only take "home"
1517                         found = re.match(".*\((.*)\)", phone)
1518                         if found: phone = found.group(1)
1519
1520                         #  if we have an unknown number, show the number
1521                         if caller == _("UNKNOWN") and number != "":
1522                                 caller = number
1523                         else:
1524                                 # strip off the address part of the remote number, if there is any
1525                                 found = re.match("(.*)\n.*", caller)
1526                                 if found: caller = found.group(1)
1527
1528                         while (len(caller) + len(phone)) > 40:
1529                                 if len(caller) > len(phone):
1530                                         caller = caller[: - 1]
1531                                 else:
1532                                         phone = phone[: - 1]
1533
1534                         text = text + "%s %s %s %s\n" % (date, caller, direction, phone)
1535
1536                 debug("[FritzCallList] display: '%s %s %s %s'" % (date, caller, direction, phone))
1537                 # display screen
1538                 Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_INFO)
1539                 # my_global_session.open(FritzDisplayCalls, text) # TODO please HELP: from where can I get a session?
1540                 self.callList = [ ]
1541                 self.text = ""
1542
1543 callList = FritzCallList()
1544
1545 from GlobalActions import globalActionMap #@UnresolvedImport
1546 def notifyCall(event, date, number, caller, phone):
1547         if Standby.inStandby is None or config.plugins.FritzCall.afterStandby.value == "each":
1548                 if config.plugins.FritzCall.muteOnCall.value:
1549                         globalActionMap.actions["volumeMute"]()
1550                 if event == "RING":
1551                         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 }
1552                 else:
1553                         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 }
1554                 debug("[FritzCall] notifyCall:\n%s" % text)
1555                 Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_INFO, timeout=config.plugins.FritzCall.timeout.value)
1556         elif config.plugins.FritzCall.afterStandby.value == "inList":
1557                 #
1558                 # if not yet done, register function to show call list
1559                 global standbyMode
1560                 if not standbyMode :
1561                         standbyMode = True
1562                         Standby.inStandby.onHide.append(callList.display)
1563                 # add text/timeout to call list
1564                 callList.add(event, date, number, caller, phone)
1565                 debug("[FritzCall] notifyCall: added to callList")
1566         else: # this is the "None" case
1567                 debug("[FritzCall] notifyCall: standby and no show")
1568
1569
1570 #===============================================================================
1571 #               We need a separate class for each invocation of reverseLookup to retain
1572 #               the necessary data for the notification
1573 #===============================================================================
1574
1575 countries = { }
1576 reverselookupMtime = 0
1577
1578 class FritzReverseLookupAndNotifier:
1579         def __init__(self, event, number, caller, phone, date):
1580                 '''
1581                 
1582                 Initiate a reverse lookup for the given number in the configured country
1583                 
1584                 @param event: CALL or RING
1585                 @param number: number to be looked up
1586                 @param caller: caller including name and address
1587                 @param phone: Number (and name) of or own phone
1588                 @param date: date of call
1589                 '''
1590                 debug("[FritzReverseLookupAndNotifier] reverse Lookup for %s!" % number)
1591                 self.event = event
1592                 self.number = number
1593                 self.caller = caller
1594                 self.phone = phone
1595                 self.date = date
1596
1597                 if number[0] != "0":
1598                         self.notifyAndReset(number, caller)
1599                         return
1600
1601                 ReverseLookupAndNotifier(number, self.notifyAndReset, "UTF-8", config.plugins.FritzCall.country.value)
1602
1603         def notifyAndReset(self, number, caller):
1604                 '''
1605                 
1606                 this gets called with the result of the reverse lookup
1607                 
1608                 @param number: number
1609                 @param caller: name and address of remote. it comes in with name, address and city separated by commas
1610                 '''
1611                 debug("[FritzReverseLookupAndNotifier] got: " + caller)
1612 #===============================================================================
1613 #               if not caller and os.path.exists(config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.csv"):
1614 #                       caller = FritzOutlookCSV.findNumber(number, config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.csv") #@UndefinedVariable
1615 #                       debug("[FritzReverseLookupAndNotifier] got from Outlook csv: " + caller)
1616 #===============================================================================
1617 #===============================================================================
1618 #               if not caller and os.path.exists(config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.ldif"):
1619 #                       caller = FritzLDIF.findNumber(number, open(config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.ldif"))
1620 #                       debug("[FritzReverseLookupAndNotifier] got from ldif: " + caller)
1621 #===============================================================================
1622
1623                 if caller:
1624                         self.caller = caller.replace(", ", "\n").replace('#','')
1625                         if self.number != 0 and config.plugins.FritzCall.addcallers.value and self.event == "RING":
1626                                 debug("[FritzReverseLookupAndNotifier] add to phonebook")
1627                                 phonebook.add(self.number, self.caller)
1628                 else:
1629                         self.caller = _("UNKNOWN")
1630                 notifyCall(self.event, self.date, self.number, self.caller, self.phone)
1631                 # kill that object...
1632
1633 class FritzProtocol(LineReceiver):
1634         def __init__(self):
1635                 debug("[FritzProtocol] __init__")
1636                 self.resetValues()
1637
1638         def resetValues(self):
1639                 debug("[FritzProtocol] resetValues")
1640                 self.number = '0'
1641                 self.caller = None
1642                 self.phone = None
1643                 self.date = '0'
1644
1645         def notifyAndReset(self, timeout=config.plugins.FritzCall.timeout.value):
1646                 notifyCall(self.event, self.date, self.number, self.caller, self.phone)
1647                 self.resetValues()
1648
1649         def lineReceived(self, line):
1650                 debug("[FritzProtocol] lineReceived: %s" % line)
1651 #15.07.06 00:38:54;CALL;1;4;<from/extern>;<to/our msn>;
1652 #15.07.06 00:38:58;DISCONNECT;1;0;
1653 #15.07.06 00:39:22;RING;0;<from/extern>;<to/our msn>;
1654 #15.07.06 00:39:27;DISCONNECT;0;0;
1655                 a = line.split(';')
1656                 (self.date, self.event) = a[0:2]
1657
1658                 if self.event == "RING" or (self.event == "CALL" and config.plugins.FritzCall.showOutgoing.value):
1659                         phone = a[4]
1660
1661                         if self.event == "RING":
1662                                 number = a[3] 
1663                         else:
1664                                 number = a[5]
1665                                 
1666                         debug("[FritzProtocol] lineReceived phone: '''%s''' number: '''%s'''" % (phone, number))
1667
1668                         filtermsns = config.plugins.FritzCall.filtermsn.value.split(",")
1669                         for i in range(len(filtermsns)):
1670                                 filtermsns[i] = filtermsns[i].strip()
1671                         if not (config.plugins.FritzCall.filter.value and phone not in filtermsns):
1672                                 debug("[FritzProtocol] lineReceived no filter hit")
1673                                 phonename = phonebook.search(phone)                # do we have a name for the number of our side?
1674                                 if phonename is not None:
1675                                         self.phone = "%s (%s)" % (phone, phonename)
1676                                 else:
1677                                         self.phone = phone
1678
1679                                 if config.plugins.FritzCall.internal.value and len(number) > 3 and number[0] == "0":
1680                                         debug("[FritzProtocol] lineReceived: strip leading 0")
1681                                         self.number = number[1:]
1682                                 else:
1683                                         self.number = number
1684                                         if self.event == "CALL" and self.number[0] != '0':                                        # should only happen for outgoing
1685                                                 debug("[FritzProtocol] lineReceived: add local prefix")
1686                                                 self.number = config.plugins.FritzCall.prefix.value + self.number
1687
1688                                 # check, whether we are in Germany and number has call-by-call prefix. If so strip it
1689                                 if self.event == "CALL" and config.plugins.FritzCall.country.value == '0049':
1690                                         if re.match('^0100\d\d', self.number):
1691                                                 debug("[FritzProtocol] lineReceived: strip CbC 0100.. prefix")
1692                                                 self.number = self.number[6:]
1693                                         elif re.match('^010\d\d', self.number):
1694                                                 debug("[FritzProtocol] lineReceived: strip CbC 010.. prefix")
1695                                                 self.number = self.number[5:]
1696
1697                                 if self.number is not "":
1698                                         debug("[FritzProtocol] lineReceived phonebook.search: %s" % self.number)
1699                                         self.caller = phonebook.search(self.number)
1700                                         debug("[FritzProtocol] lineReceived phonebook.search reault: %s" % self.caller)
1701                                         if (self.caller is None) and config.plugins.FritzCall.lookup.value:
1702                                                 FritzReverseLookupAndNotifier(self.event, self.number, self.caller, self.phone, self.date)
1703                                                 return                                                  # reverselookup is supposed to handle the message itself 
1704
1705                                 if self.caller is None:
1706                                         self.caller = _("UNKNOWN")
1707
1708                                 self.notifyAndReset()
1709
1710 class FritzClientFactory(ReconnectingClientFactory):
1711         initialDelay = 20
1712         maxDelay = 500
1713
1714         def __init__(self):
1715                 self.hangup_ok = False
1716
1717         def startedConnecting(self, connector):
1718                 Notifications.AddNotification(MessageBox, _("Connecting to FRITZ!Box..."), type=MessageBox.TYPE_INFO, timeout=2)
1719
1720         def buildProtocol(self, addr):
1721                 Notifications.AddNotification(MessageBox, _("Connected to FRITZ!Box!"), type=MessageBox.TYPE_INFO, timeout=4)
1722                 self.resetDelay()
1723                 return FritzProtocol()
1724
1725         def clientConnectionLost(self, connector, reason):
1726                 if not self.hangup_ok:
1727                         Notifications.AddNotification(MessageBox, _("Connection to FRITZ!Box! lost\n (%s)\nretrying...") % reason.getErrorMessage(), type=MessageBox.TYPE_INFO, timeout=config.plugins.FritzCall.timeout.value)
1728                 ReconnectingClientFactory.clientConnectionLost(self, connector, reason)
1729
1730         def clientConnectionFailed(self, connector, reason):
1731                 Notifications.AddNotification(MessageBox, _("Connecting to FRITZ!Box failed\n (%s)\nretrying...") % reason.getErrorMessage(), type=MessageBox.TYPE_INFO, timeout=config.plugins.FritzCall.timeout.value)
1732                 ReconnectingClientFactory.clientConnectionFailed(self, connector, reason)
1733
1734 class FritzCall:
1735         def __init__(self):
1736                 self.dialog = None
1737                 self.d = None
1738                 self.connect()
1739
1740         def connect(self):
1741                 self.abort()
1742                 if config.plugins.FritzCall.enable.value:
1743                         f = FritzClientFactory()
1744                         self.d = (f, reactor.connectTCP(config.plugins.FritzCall.hostname.value, 1012, f)) #@UndefinedVariable
1745                         initDebug()
1746
1747         def shutdown(self):
1748                 self.abort()
1749
1750         def abort(self):
1751                 if self.d is not None:
1752                         self.d[0].hangup_ok = True
1753                         self.d[0].stopTrying()
1754                         self.d[1].disconnect()
1755                         self.d = None
1756
1757 def displayCalls(session, servicelist):
1758         session.open(FritzDisplayCalls)
1759
1760 def displayPhonebook(session, servicelist):
1761         session.open(phonebook.FritzDisplayPhonebook)
1762
1763 def main(session):
1764         session.open(FritzCallSetup)
1765
1766 fritz_call = None
1767
1768 def autostart(reason, **kwargs):
1769         global fritz_call
1770
1771         # ouch, this is a hack
1772         if kwargs.has_key("session"):
1773                 global my_global_session
1774                 my_global_session = kwargs["session"]
1775                 return
1776
1777         debug("[FRITZ!Call] - Autostart")
1778         if reason == 0:
1779                 fritz_call = FritzCall()
1780         elif reason == 1:
1781                 fritz_call.shutdown()
1782                 fritz_call = None
1783
1784 def Plugins(**kwargs):
1785         what = _("Display FRITZ!box-Fon calls on screen")
1786         what_calls = _("Phone calls")
1787         what_phonebook = _("Phonebook")
1788         return [ PluginDescriptor(name="FritzCall", description=what, where=PluginDescriptor.WHERE_PLUGINMENU, icon="plugin.png", fnc=main),
1789                 PluginDescriptor(name=what_calls, description=what_calls, where=PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=displayCalls),
1790                 PluginDescriptor(name=what_phonebook, description=what_phonebook, where=PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=displayPhonebook),
1791                 PluginDescriptor(where=[PluginDescriptor.WHERE_SESSIONSTART, PluginDescriptor.WHERE_AUTOSTART], fnc=autostart) ]