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