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