[fritzcall] FIX issues with FW 05.50 and swithc to csv based call list
[enigma2-plugins.git] / fritzcall / src / plugin.py
1 # -*- coding: utf-8 -*-
2 '''
3 Update rev
4 $Author: michael $
5 $Revision: 734 $
6 $Date: 2012-12-21 16:54:18 +0100 (Fr, 21 Dez 2012) $
7 $Id: plugin.py 734 2012-12-21 15:54:18Z michael $
8 '''
9
10 from Screens.Screen import Screen
11 from Screens.MessageBox import MessageBox
12 from Screens.NumericalTextInputHelpDialog import NumericalTextInputHelpDialog
13 from Screens.InputBox import InputBox
14 from Screens import Standby
15 from Screens.HelpMenu import HelpableScreen
16
17 from enigma import eTimer, eSize, ePoint #@UnresolvedImport # pylint: disable=E0611
18 from enigma import eDVBVolumecontrol, eConsoleAppContainer, eBackgroundFileEraser #@UnresolvedImport # pylint: disable=E0611
19 #BgFileEraser = eBackgroundFileEraser.getInstance()
20 #BgFileEraser.erase("blabla.txt")
21
22 from Components.ActionMap import ActionMap
23 from Components.Label import Label
24 from Components.Button import Button
25 from Components.Pixmap import Pixmap
26 from Components.Sources.List import List
27 from Components.config import config, ConfigSubsection, ConfigSelection, ConfigEnableDisable, getConfigListEntry, ConfigText, ConfigInteger
28 from Components.ConfigList import ConfigListScreen
29 from Components.Harddisk import harddiskmanager
30 try:
31         from Components.config import ConfigPassword
32 except ImportError:
33         ConfigPassword = ConfigText
34
35 from Plugins.Plugin import PluginDescriptor
36 from Tools import Notifications
37 from Tools.NumericalTextInput import NumericalTextInput
38 from Tools.Directories import resolveFilename, SCOPE_PLUGINS, SCOPE_SKIN_IMAGE, SCOPE_CONFIG, SCOPE_MEDIA
39 from Tools.LoadPixmap import LoadPixmap
40 from GlobalActions import globalActionMap # for muting
41
42 from twisted.internet import reactor #@UnresolvedImport
43 from twisted.internet.protocol import ReconnectingClientFactory #@UnresolvedImport
44 from twisted.protocols.basic import LineReceiver #@UnresolvedImport
45
46 import re, time, os, traceback
47
48 from nrzuname import ReverseLookupAndNotifier
49 import FritzOutlookCSV, FritzLDIF
50 from . import _, initDebug, debug #@UnresolvedImport # pylint: disable=E0611,F0401
51
52 from enigma import getDesktop
53 DESKTOP_WIDTH = getDesktop(0).size().width()
54 DESKTOP_HEIGHT = getDesktop(0).size().height()
55
56 #
57 # this is pure magic.
58 # It returns the first value, if HD (1280x720),
59 # the second if SD (720x576),
60 # else something scaled accordingly
61 # if one of the parameters is -1, scale proportionally
62 #
63 def scaleH(y2, y1):
64         if y2 == -1:
65                 y2 = y1*1280/720
66         elif y1 == -1:
67                 y1 = y2*720/1280
68         return scale(y2, y1, 1280, 720, DESKTOP_WIDTH)
69 def scaleV(y2, y1):
70         if y2 == -1:
71                 y2 = y1*720/576
72         elif y1 == -1:
73                 y1 = y2*576/720
74         return scale(y2, y1, 720, 576, DESKTOP_HEIGHT)
75 def scale(y2, y1, x2, x1, x):
76         return (y2 - y1) * (x - x1) / (x2 - x1) + y1
77
78 my_global_session = None
79
80 config.plugins.FritzCall = ConfigSubsection()
81 config.plugins.FritzCall.fwVersion = ConfigSelection(choices=[("none", _("not configured")), ("old", _("before 05.27")), ("05.27", "05.27,05.28"), "05.50"])
82 config.plugins.FritzCall.debug = ConfigEnableDisable(default=False)
83 #config.plugins.FritzCall.muteOnCall = ConfigSelection(choices=[(None, _("no")), ("ring", _("on ring")), ("connect", _("on connect"))])
84 #config.plugins.FritzCall.muteOnCall = ConfigSelection(choices=[(None, _("no")), ("ring", _("on ring"))])
85 config.plugins.FritzCall.muteOnCall = ConfigEnableDisable(default=False)
86 config.plugins.FritzCall.hostname = ConfigText(default="fritz.box", fixed_size=False)
87 config.plugins.FritzCall.afterStandby = ConfigSelection(choices=[("none", _("show nothing")), ("inList", _("show as list")), ("each", _("show each call"))])
88 config.plugins.FritzCall.filter = ConfigEnableDisable(default=False)
89 config.plugins.FritzCall.filtermsn = ConfigText(default="", fixed_size=False)
90 config.plugins.FritzCall.filtermsn.setUseableChars('0123456789,')
91 config.plugins.FritzCall.filterCallList = ConfigEnableDisable(default=True)
92 config.plugins.FritzCall.showOutgoing = ConfigEnableDisable(default=False)
93 config.plugins.FritzCall.timeout = ConfigInteger(default=15, limits=(0, 60))
94 config.plugins.FritzCall.lookup = ConfigEnableDisable(default=False)
95 config.plugins.FritzCall.internal = ConfigEnableDisable(default=False)
96 config.plugins.FritzCall.fritzphonebook = ConfigEnableDisable(default=False)
97 config.plugins.FritzCall.phonebook = ConfigEnableDisable(default=False)
98 config.plugins.FritzCall.addcallers = ConfigEnableDisable(default=False)
99 config.plugins.FritzCall.enable = ConfigEnableDisable(default=False)
100 config.plugins.FritzCall.username = ConfigText(default='BoxAdmin', fixed_size=False)
101 config.plugins.FritzCall.password = ConfigPassword(default="", fixed_size=False)
102 config.plugins.FritzCall.extension = ConfigText(default='1', fixed_size=False)
103 config.plugins.FritzCall.extension.setUseableChars('0123456789')
104 config.plugins.FritzCall.showType = ConfigEnableDisable(default=True)
105 config.plugins.FritzCall.showShortcut = ConfigEnableDisable(default=False)
106 config.plugins.FritzCall.showVanity = ConfigEnableDisable(default=False)
107 config.plugins.FritzCall.prefix = ConfigText(default="", fixed_size=False)
108 config.plugins.FritzCall.prefix.setUseableChars('0123456789')
109 config.plugins.FritzCall.connectionVerbose = ConfigEnableDisable(default=True)
110 config.plugins.FritzCall.ignoreUnknown = ConfigEnableDisable(default=False)
111 config.plugins.FritzCall.reloadPhonebookTime = ConfigInteger(default=8, limits=(0, 99))
112
113
114 def getMountedDevs():
115         def handleMountpoint(loc):
116                 # debug("[FritzCall] handleMountpoint: %s" %repr(loc))
117                 mp = loc[0]
118                 while mp[-1] == '/':
119                         mp = mp[:-1]
120                 #=======================================================================
121                 # if os.path.exists(os.path.join(mp, "PhoneBook.txt")):
122                 #       if os.access(os.path.join(mp, "PhoneBook.txt"), os.W_OK):
123                 #               desc = ' *'
124                 #       else:
125                 #               desc = ' -'
126                 # else:
127                 #       desc = ''
128                 # desc = loc[1] + desc
129                 #=======================================================================
130                 desc = loc[1]
131                 return (mp, desc + " (" + mp + ")")
132
133         mountedDevs = [(resolveFilename(SCOPE_CONFIG), _("Flash")),
134                                    (resolveFilename(SCOPE_MEDIA, "cf"), _("Compact Flash")),
135                                    (resolveFilename(SCOPE_MEDIA, "usb"), _("USB Device"))]
136         mountedDevs += map(lambda p: (p.mountpoint, (_(p.description) if p.description else "")), harddiskmanager.getMountedPartitions(True))
137         mediaDir = resolveFilename(SCOPE_MEDIA)
138         for p in os.listdir(mediaDir):
139                 if os.path.join(mediaDir, p) not in [path[0] for path in mountedDevs]:
140                         mountedDevs.append((os.path.join(mediaDir, p), _("Media directory")))
141         debug("[FritzCall] getMountedDevs1: %s" %repr(mountedDevs))
142         mountedDevs = filter(lambda path: os.path.isdir(path[0]) and os.access(path[0], os.W_OK|os.X_OK), mountedDevs)
143         # put this after the write/executable check, that is far too slow...
144         netDir = resolveFilename(SCOPE_MEDIA, "net")
145         if os.path.isdir(netDir):
146                 mountedDevs += map(lambda p: (os.path.join(netDir, p), _("Network mount")), os.listdir(netDir))
147         mountedDevs = map(handleMountpoint, mountedDevs)
148         return mountedDevs
149 config.plugins.FritzCall.phonebookLocation = ConfigSelection(choices=getMountedDevs())
150
151 countryCodes = [
152         ("0049", _("Germany")),
153         ("0031", _("The Netherlands")),
154         ("0033", _("France")),
155         ("0039", _("Italy")),
156         ("0041", _("Switzerland")),
157         ("0043", _("Austria"))
158         ]
159 config.plugins.FritzCall.country = ConfigSelection(choices=countryCodes)
160
161 FBF_ALL_CALLS = "."
162 FBF_IN_CALLS = "1"
163 FBF_MISSED_CALLS = "2"
164 FBF_OUT_CALLS = "3"
165 fbfCallsChoices = {FBF_ALL_CALLS: _("All calls"),
166                                    FBF_IN_CALLS: _("Incoming calls"),
167                                    FBF_MISSED_CALLS: _("Missed calls"),
168                                    FBF_OUT_CALLS: _("Outgoing calls")
169                                    }
170 config.plugins.FritzCall.fbfCalls = ConfigSelection(choices=fbfCallsChoices)
171
172 config.plugins.FritzCall.name = ConfigText(default="", fixed_size=False)
173 config.plugins.FritzCall.number = ConfigText(default="", fixed_size=False)
174 config.plugins.FritzCall.number.setUseableChars('0123456789')
175
176 phonebook = None
177 fritzbox = None
178
179 avon = {}
180
181 def initAvon():
182         avonFileName = resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/avon.dat")
183         if os.path.exists(avonFileName):
184                 for line in open(avonFileName):
185                         line = line.decode("iso-8859-1").encode('utf-8')
186                         if line[0] == '#':
187                                 continue
188                         parts = line.split(':')
189                         if len(parts) == 2:
190                                 avon[parts[0].replace('-','').replace('*','').replace('/','')] = parts[1]
191
192 def resolveNumberWithAvon(number, countrycode):
193         if not number or number[0] != '0':
194                 return ""
195                 
196         countrycode = countrycode.replace('00','+')
197         if number[:2] == '00':
198                 normNumber = '+' + number[2:]
199         elif number[:1] == '0':
200                 normNumber = countrycode + number[1:]
201         else: # this should can not happen, but safety first
202                 return ""
203         
204         # debug('normNumer: ' + normNumber)
205         for i in reversed(range(min(10, len(number)))):
206                 if avon.has_key(normNumber[:i]):
207                         return '[' + avon[normNumber[:i]].strip() + ']'
208         return ""
209
210 def handleReverseLookupResult(name):
211         found = re.match("NA: ([^;]*);VN: ([^;]*);STR: ([^;]*);HNR: ([^;]*);PLZ: ([^;]*);ORT: ([^;]*)", name)
212         if found:
213                 ( name, firstname, street, streetno, zipcode, city ) = (found.group(1),
214                                                                                                 found.group(2),
215                                                                                                 found.group(3),
216                                                                                                 found.group(4),
217                                                                                                 found.group(5),
218                                                                                                 found.group(6)
219                                                                                                 )
220                 if firstname:
221                         name += ' ' + firstname
222                 if street or streetno or zipcode or city:
223                         name += ', '
224                 if street:
225                         name += street
226                 if streetno:
227                         name += ' ' + streetno
228                 if (street or streetno) and (zipcode or city):
229                         name += ', '
230                 if zipcode and city:
231                         name += zipcode + ' ' + city
232                 elif zipcode:
233                         name += zipcode
234                 elif city:
235                         name += city
236         return name
237
238 from xml.dom.minidom import parse
239 cbcInfos = {}
240 def initCbC():
241         callbycallFileName = resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/callbycall_world.xml")
242         if os.path.exists(callbycallFileName):
243                 dom = parse(callbycallFileName)
244                 for top in dom.getElementsByTagName("callbycalls"):
245                         for cbc in top.getElementsByTagName("country"):
246                                 code = cbc.getAttribute("code").replace("+","00")
247                                 cbcInfos[code] = cbc.getElementsByTagName("callbycall")
248         else:
249                 debug("[FritzCall] initCbC: callbycallFileName does not exist?!?!")
250
251 def stripCbCPrefix(number, countrycode):
252         if number and number[:2] != "00" and cbcInfos.has_key(countrycode):
253                 for cbc in cbcInfos[countrycode]:
254                         if len(cbc.getElementsByTagName("length"))<1 or len(cbc.getElementsByTagName("prefix"))<1:
255                                 debug("[FritzCall] stripCbCPrefix: entries for " + countrycode + " %s invalid")
256                                 return number
257                         length = int(cbc.getElementsByTagName("length")[0].childNodes[0].data)
258                         prefix = cbc.getElementsByTagName("prefix")[0].childNodes[0].data
259                         # if re.match('^'+prefix, number):
260                         if number[:len(prefix)] == prefix:
261                                 return number[length:]
262         return number
263
264 import FritzCallFBF
265
266 class FritzAbout(Screen):
267
268         def __init__(self, session):
269                 textFieldWidth = scaleV(350, 250)
270                 width = 5 + 150 + 20 + textFieldWidth + 5 + 175 + 5
271                 height = 5 + 175 + 5 + 25 + 5
272                 self.skin = """
273                         <screen name="FritzAbout" position="center,center" size="%d,%d" title="About FritzCall" >
274                                 <widget name="text" position="175,%d" size="%d,%d" font="Regular;%d" />
275                                 <ePixmap position="5,37" size="150,110" pixmap="%s" transparent="1" alphatest="blend" />
276                                 <ePixmap position="%d,5" size="175,175" pixmap="%s" transparent="1" alphatest="blend" />
277                                 <widget name="url" position="20,185" size="%d,25" font="Regular;%d" />
278                         </screen>""" % (
279                                                         width, height, # size
280                                                         (height-scaleV(150,130)) / 2, # text vertical position
281                                                         textFieldWidth,
282                                                         scaleV(150,130), # text height
283                                                         scaleV(24,21), # text font size
284                                                         resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/fritz.png"), # 150x110
285                                                         5 + 150 + 5 + textFieldWidth + 5, # qr code horizontal offset
286                                                         resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/website.png"), # 175x175
287                                                         width-40, # url width
288                                                         scaleV(24,21) # url font size
289                                                         )
290                 Screen.__init__(self, session)
291                 self["aboutActions"] = ActionMap(["OkCancelActions"],
292                 {
293                 "cancel": self.exit,
294                 "ok": self.exit,
295                 }, -2)
296                 self["text"] = Label(
297                                                         "FritzCall Plugin" + "\n\n" +
298                                                         "$Author: michael $"[1:-2] + "\n" +
299                                                         "$Revision: 734 $"[1:-2] + "\n" + 
300                                                         "$Date: 2012-12-21 16:54:18 +0100 (Fr, 21 Dez 2012) $"[1:23] + "\n"
301                                                         )
302                 self["url"] = Label("http://wiki.blue-panel.com/index.php/FritzCall")
303                 self.onLayoutFinish.append(self.setWindowTitle)
304
305         def setWindowTitle(self):
306                 # TRANSLATORS: this is a window title.
307                 self.setTitle(_("About FritzCall"))
308
309         def exit(self):
310                 self.close()
311
312 fritzbox = None
313
314 from FritzCallFBF import FBF_dectActive, FBF_faxActive, FBF_rufumlActive, FBF_tamActive
315
316 class FritzMenu(Screen, HelpableScreen):
317         def __init__(self, session):
318                 fontSize = scaleV(24, 21) # indeed this is font size +2
319                 noButtons = 2 # reset, wlan
320
321                 if not fritzbox or not fritzbox.info:
322                         return
323
324                 if fritzbox.info[FBF_tamActive]:
325                         noButtons += 1 # toggle mailboxes
326                 width = max(DESKTOP_WIDTH - scaleH(500, 250), noButtons*140+(noButtons+1)*10)
327                 # boxInfo 2 lines, gap, internet 2 lines, gap, dsl/wlan each 1 line, gap, buttons
328                 height = 5 + 2*fontSize + 10 + 2*fontSize + 10 + 2*fontSize + 10 + 40 + 5
329                 if fritzbox.info[FBF_tamActive] is not None:
330                         height += fontSize
331                 if fritzbox.info[FBF_dectActive] is not None:
332                         height += fontSize
333                 if fritzbox.info[FBF_faxActive] is not None:
334                         height += fontSize
335                 if fritzbox.info[FBF_rufumlActive] is not None:
336                         height += fontSize
337                 buttonsGap = (width-noButtons*140)/(noButtons+1)
338                 buttonsVPos = height-40-5
339
340                 varLinePos = 4
341                 if fritzbox.info[FBF_tamActive] is not None:
342                         mailboxLine = """
343                                 <widget name="FBFMailbox" position="%d,%d" size="%d,%d" font="Regular;%d" />
344                                 <widget name="mailbox_inactive" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
345                                 <widget name="mailbox_active" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
346                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
347                                 <widget name="key_yellow" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
348                                 """ % (
349                                                 40, 5+2*fontSize+10+varLinePos*fontSize+10, # position mailbox
350                                                 width-40-20, fontSize, # size mailbox
351                                                 fontSize-2,
352                                                 "skin_default/buttons/button_green_off.png",
353                                                 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button mailbox
354                                                 "skin_default/buttons/button_green.png",
355                                                 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button mailbox
356                                                 noButtons*buttonsGap+(noButtons-1)*140, buttonsVPos,
357                                                 noButtons*buttonsGap+(noButtons-1)*140, buttonsVPos,
358                                 )
359                         varLinePos += 1
360                 else:
361                         mailboxLine = ""
362
363                 if fritzbox.info[FBF_dectActive] is not None:
364                         dectLine = """
365                                 <widget name="FBFDect" position="%d,%d" size="%d,%d" font="Regular;%d" />
366                                 <widget name="dect_inactive" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
367                                 <widget name="dect_active" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
368                                 """ % (
369                                                 40, 5+2*fontSize+10+varLinePos*fontSize+10, # position dect
370                                                 width-40-20, fontSize, # size dect
371                                                 fontSize-2,
372                                                 "skin_default/buttons/button_green_off.png",
373                                                 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
374                                                 "skin_default/buttons/button_green.png",
375                                                 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
376                                 )
377                         varLinePos += 1
378                 else:
379                         dectLine = ""
380
381                 if fritzbox.info[FBF_faxActive] is not None:
382                         faxLine = """
383                                 <widget name="FBFFax" position="%d,%d" size="%d,%d" font="Regular;%d" />
384                                 <widget name="fax_inactive" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
385                                 <widget name="fax_active" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
386                                 """ % (
387                                                 40, 5+2*fontSize+10+varLinePos*fontSize+10, # position dect
388                                                 width-40-20, fontSize, # size dect
389                                                 fontSize-2,
390                                                 "skin_default/buttons/button_green_off.png",
391                                                 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
392                                                 "skin_default/buttons/button_green.png",
393                                                 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
394                                 )
395                         varLinePos += 1
396                 else:
397                         faxLine = ""
398
399                 if fritzbox.info[FBF_rufumlActive] is not None:
400                         rufumlLine = """
401                                 <widget name="FBFRufuml" position="%d,%d" size="%d,%d" font="Regular;%d" />
402                                 <widget name="rufuml_inactive" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
403                                 <widget name="rufuml_active" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
404                                 """ % (
405                                                 40, 5+2*fontSize+10+varLinePos*fontSize+10, # position dect
406                                                 width-40-20, fontSize, # size dect
407                                                 fontSize-2,
408                                                 "skin_default/buttons/button_green_off.png",
409                                                 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
410                                                 "skin_default/buttons/button_green.png",
411                                                 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
412                                 )
413                         varLinePos += 1
414                 else:
415                         rufumlLine = ""
416         
417                 self.skin = """
418                         <screen name="FritzMenu" position="center,center" size="%d,%d" title="FRITZ!Box Fon Status" >
419                                 <widget name="FBFInfo" position="%d,%d" size="%d,%d" font="Regular;%d" />
420                                 <widget name="FBFInternet" position="%d,%d" size="%d,%d" font="Regular;%d" />
421                                 <widget name="internet_inactive" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
422                                 <widget name="internet_active" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
423                                 <widget name="FBFDsl" position="%d,%d" size="%d,%d" font="Regular;%d" />
424                                 <widget name="dsl_inactive" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
425                                 <widget name="dsl_active" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
426                                 <widget name="FBFWlan" position="%d,%d" size="%d,%d" font="Regular;%d" />
427                                 <widget name="wlan_inactive" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
428                                 <widget name="wlan_active" pixmap="%s" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
429                                 %s
430                                 %s
431                                 %s
432                                 %s
433                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
434                                 <widget name="key_red" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
435                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
436                                 <widget name="key_green" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
437                         </screen>""" % (
438                                                 width, height, # size
439                                                 40, 5, # position info
440                                                 width-2*40, 2*fontSize, # size info
441                                                 fontSize-2,
442                                                 40, 5+2*fontSize+10, # position internet
443                                                 width-40, 2*fontSize, # size internet
444                                                 fontSize-2,
445                                                 "skin_default/buttons/button_green_off.png",
446                                                 20, 5+2*fontSize+10+(fontSize-16)/2, # position button internet
447                                                 "skin_default/buttons/button_green.png",
448                                                 20, 5+2*fontSize+10+(fontSize-16)/2, # position button internet
449                                                 40, 5+2*fontSize+10+2*fontSize+10, # position dsl
450                                                 width-40-20, fontSize, # size dsl
451                                                 fontSize-2,
452                                                 "skin_default/buttons/button_green_off.png",
453                                                 20, 5+2*fontSize+10+2*fontSize+10+(fontSize-16)/2, # position button dsl
454                                                 "skin_default/buttons/button_green.png",
455                                                 20, 5+2*fontSize+10+2*fontSize+10+(fontSize-16)/2, # position button dsl
456                                                 40, 5+2*fontSize+10+3*fontSize+10, # position wlan
457                                                 width-40-20, fontSize, # size wlan
458                                                 fontSize-2,
459                                                 "skin_default/buttons/button_green_off.png",
460                                                 20, 5+2*fontSize+10+3*fontSize+10+(fontSize-16)/2, # position button wlan
461                                                 "skin_default/buttons/button_green.png",
462                                                 20, 5+2*fontSize+10+3*fontSize+10+(fontSize-16)/2, # position button wlan
463                                                 mailboxLine,
464                                                 dectLine,
465                                                 faxLine,
466                                                 rufumlLine,
467                                                 buttonsGap, buttonsVPos, "skin_default/buttons/red.png", buttonsGap, buttonsVPos,
468                                                 buttonsGap+140+buttonsGap, buttonsVPos, "skin_default/buttons/green.png", buttonsGap+140+buttonsGap, buttonsVPos,
469                                                 )
470
471                 Screen.__init__(self, session)
472                 HelpableScreen.__init__(self)
473                 # TRANSLATORS: keep it short, this is a button
474                 self["key_red"] = Button(_("Reset"))
475                 # TRANSLATORS: keep it short, this is a button
476                 self["key_green"] = Button(_("Toggle WLAN"))
477                 self._mailboxActive = False
478                 if fritzbox.info[FBF_tamActive] is not None:
479                         # TRANSLATORS: keep it short, this is a button
480                         self["key_yellow"] = Button(_("Toggle Mailbox"))
481                         self["menuActions"] = ActionMap(["OkCancelActions", "ColorActions", "NumberActions", "EPGSelectActions"],
482                                                                                         {
483                                                                                         "cancel": self._exit,
484                                                                                         "ok": self._exit,
485                                                                                         "red": self._reset,
486                                                                                         "green": self._toggleWlan,
487                                                                                         "yellow": (lambda: self._toggleMailbox(-1)),
488                                                                                         "0": (lambda: self._toggleMailbox(0)),
489                                                                                         "1": (lambda: self._toggleMailbox(1)),
490                                                                                         "2": (lambda: self._toggleMailbox(2)),
491                                                                                         "3": (lambda: self._toggleMailbox(3)),
492                                                                                         "4": (lambda: self._toggleMailbox(4)),
493                                                                                         "info": self._getInfo,
494                                                                                         }, -2)
495                         # TRANSLATORS: keep it short, this is a help text
496                         self.helpList.append((self["menuActions"], "ColorActions", [("yellow", _("Toggle all mailboxes"))]))
497                         # TRANSLATORS: keep it short, this is a help text
498                         self.helpList.append((self["menuActions"], "NumberActions", [("0", _("Toggle 1. mailbox"))]))
499                         # TRANSLATORS: keep it short, this is a help text
500                         self.helpList.append((self["menuActions"], "NumberActions", [("1", _("Toggle 2. mailbox"))]))
501                         # TRANSLATORS: keep it short, this is a help text
502                         self.helpList.append((self["menuActions"], "NumberActions", [("2", _("Toggle 3. mailbox"))]))
503                         # TRANSLATORS: keep it short, this is a help text
504                         self.helpList.append((self["menuActions"], "NumberActions", [("3", _("Toggle 4. mailbox"))]))
505                         # TRANSLATORS: keep it short, this is a help text
506                         self.helpList.append((self["menuActions"], "NumberActions", [("4", _("Toggle 5. mailbox"))]))
507                         self["FBFMailbox"] = Label(_('Mailbox'))
508                         self["mailbox_inactive"] = Pixmap()
509                         self["mailbox_active"] = Pixmap()
510                         self["mailbox_active"].hide()
511                 else:
512                         self["menuActions"] = ActionMap(["OkCancelActions", "ColorActions", "EPGSelectActions"],
513                                                                                         {
514                                                                                         "cancel": self._exit,
515                                                                                         "ok": self._exit,
516                                                                                         "green": self._toggleWlan,
517                                                                                         "red": self._reset,
518                                                                                         "info": self._getInfo,
519                                                                                         }, -2)
520
521                 # TRANSLATORS: keep it short, this is a help text
522                 self.helpList.append((self["menuActions"], "OkCancelActions", [("cancel", _("Quit"))]))
523                 # TRANSLATORS: keep it short, this is a help text
524                 self.helpList.append((self["menuActions"], "OkCancelActions", [("ok", _("Quit"))]))
525                 # TRANSLATORS: keep it short, this is a help text
526                 self.helpList.append((self["menuActions"], "ColorActions", [("green", _("Toggle WLAN"))]))
527                 # TRANSLATORS: keep it short, this is a help text
528                 self.helpList.append((self["menuActions"], "ColorActions", [("red", _("Reset"))]))
529                 # TRANSLATORS: keep it short, this is a help text
530                 self.helpList.append((self["menuActions"], "EPGSelectActions", [("info", _("Refresh status"))]))
531
532                 self["FBFInfo"] = Label(_('Getting status from FRITZ!Box Fon...'))
533
534                 self["FBFInternet"] = Label('Internet')
535                 self["internet_inactive"] = Pixmap()
536                 self["internet_active"] = Pixmap()
537                 self["internet_active"].hide()
538
539                 self["FBFDsl"] = Label('DSL')
540                 self["dsl_inactive"] = Pixmap()
541                 self["dsl_inactive"].hide()
542                 self["dsl_active"] = Pixmap()
543                 self["dsl_active"].hide()
544
545                 self["FBFWlan"] = Label('WLAN ')
546                 self["wlan_inactive"] = Pixmap()
547                 self["wlan_inactive"].hide()
548                 self["wlan_active"] = Pixmap()
549                 self["wlan_active"].hide()
550                 self._wlanActive = False
551
552                 if fritzbox.info[FBF_dectActive] is not None: 
553                         self["FBFDect"] = Label('DECT')
554                         self["dect_inactive"] = Pixmap()
555                         self["dect_active"] = Pixmap()
556                         self["dect_active"].hide()
557
558                 if fritzbox.info[FBF_faxActive] is not None: 
559                         self["FBFFax"] = Label('Fax')
560                         self["fax_inactive"] = Pixmap()
561                         self["fax_active"] = Pixmap()
562                         self["fax_active"].hide()
563
564                 if fritzbox.info[FBF_rufumlActive] is not None: 
565                         self["FBFRufuml"] = Label(_('Call redirection'))
566                         self["rufuml_inactive"] = Pixmap()
567                         self["rufuml_active"] = Pixmap()
568                         self["rufuml_active"].hide()
569
570                 self._timer = eTimer()
571                 self._timer.callback.append(self._getInfo)
572                 self.onShown.append(lambda: self._timer.start(5000))
573                 self.onHide.append(self._timer.stop)
574                 self._getInfo()
575                 self.onLayoutFinish.append(self.setWindowTitle)
576
577         def setWindowTitle(self):
578                 # TRANSLATORS: this is a window title.
579                 self.setTitle(_("FRITZ!Box Fon Status"))
580
581         def _getInfo(self):
582                 if fritzbox:
583                         fritzbox.getInfo(self._fillMenu)
584
585         def _fillMenu(self, status):
586                 (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive) = status
587                 if wlanState:
588                         self._wlanActive = (wlanState[0] == '1')
589                 self._mailboxActive = False
590                 try:
591                         if not self.has_key("FBFInfo"): # screen is closed already
592                                 return
593
594                         if boxInfo:
595                                 self["FBFInfo"].setText(boxInfo.replace(', ', '\n'))
596                         else:
597                                 self["FBFInfo"].setText('BoxInfo ' + _('Status not available'))
598
599                         if ipAddress:
600                                 if upTime:
601                                         self["FBFInternet"].setText('Internet ' + _('IP Address:') + ' ' + ipAddress + '\n' + _('Connected since') + ' ' + upTime)
602                                 else:
603                                         self["FBFInternet"].setText('Internet ' + _('IP Address:') + ' ' + ipAddress)
604                                 self["internet_inactive"].hide()
605                                 self["internet_active"].show()
606                         else:
607                                 self["internet_active"].hide()
608                                 self["internet_inactive"].show()
609
610                         if dslState:
611                                 if dslState[0] == '5':
612                                         self["dsl_inactive"].hide()
613                                         self["dsl_active"].show()
614                                         if dslState[1]:
615                                                 self["FBFDsl"].setText('DSL ' + dslState[1])
616                                 else:
617                                         self["dsl_active"].hide()
618                                         self["dsl_inactive"].show()
619                         else:
620                                 self["FBFDsl"].setText('DSL ' + _('Status not available'))
621                                 self["dsl_active"].hide()
622                                 self["dsl_inactive"].hide()
623
624                         if wlanState:
625                                 if wlanState[0 ] == '1':
626                                         self["wlan_inactive"].hide()
627                                         self["wlan_active"].show()
628                                         message = 'WLAN'
629                                         if wlanState[1] == '0':
630                                                 message += ' ' + _('not encrypted')
631                                         else:
632                                                 message += ' ' + _('encrypted')
633                                         if wlanState[2]:
634                                                 if wlanState[2] == '0':
635                                                         message = message + ', ' + _('no device active')
636                                                 elif wlanState[2] == '1':
637                                                         message = message + ', ' + _('one device active')
638                                                 else:
639                                                         message = message + ', ' + wlanState[2] + ' ' + _('devices active')
640                                         self["FBFWlan"].setText(message)
641                                 else:
642                                         self["wlan_active"].hide()
643                                         self["wlan_inactive"].show()
644                                         self["FBFWlan"].setText('WLAN')
645                         else:
646                                 self["FBFWlan"].setText('WLAN ' + _('Status not available'))
647                                 self["wlan_active"].hide()
648                                 self["wlan_inactive"].hide()
649
650                         if fritzbox.info[FBF_tamActive]:
651                                 if  not tamActive or tamActive[0] == 0:
652                                         self._mailboxActive = False
653                                         self["mailbox_active"].hide()
654                                         self["mailbox_inactive"].show()
655                                         self["FBFMailbox"].setText(_('No mailbox active'))
656                                 else:
657                                         self._mailboxActive = True
658                                         message = ''
659                                         for i in range(min(len(tamActive)-1, 5)):
660                                                 if tamActive[i+1]:
661                                                         message = message + str(i) + ','
662                                         if message:
663                                                 message = '(' + message[:-1] + ')'
664                                         self["mailbox_inactive"].hide()
665                                         self["mailbox_active"].show()
666                                         if tamActive[0] == 1:
667                                                 self["FBFMailbox"].setText(_('One mailbox active') + ' ' + message)
668                                         else:
669                                                 self["FBFMailbox"].setText(str(tamActive[0]) + ' ' + _('mailboxes active') + ' ' + message)
670         
671                         if fritzbox.info[FBF_dectActive] and dectActive and self.has_key("dect_inactive"):
672                                 self["dect_inactive"].hide()
673                                 self["dect_active"].show()
674                                 if dectActive == 0:
675                                         self["FBFDect"].setText(_('No DECT phone registered'))
676                                 else:
677                                         if dectActive == 1:
678                                                 self["FBFDect"].setText(_('One DECT phone registered'))
679                                         else:
680                                                 self["FBFDect"].setText(str(dectActive) + ' ' + _('DECT phones registered'))
681
682                         if fritzbox.info[FBF_faxActive] and faxActive:
683                                 self["fax_inactive"].hide()
684                                 self["fax_active"].show()
685                                 self["FBFFax"].setText(_('Software fax active'))
686
687                         if fritzbox.info[FBF_rufumlActive] is not None and rufumlActive is not None:
688                                 if rufumlActive == 0:
689                                         self["rufuml_active"].hide()
690                                         self["rufuml_inactive"].show()
691                                         self["FBFRufuml"].setText(_('No call redirection active'))
692                                 else:
693                                         self["rufuml_inactive"].hide()
694                                         self["rufuml_active"].show()
695                                         if rufumlActive == 1:
696                                                 self["FBFRufuml"].setText(_('One call redirection active'))
697                                         else:
698                                                 self["FBFRufuml"].setText(str(rufumlActive) + ' ' + _('call redirections active'))
699
700                 except KeyError:
701                         debug("[FritzCallFBF] _fillMenu: " + traceback.format_exc())
702
703         def _toggleWlan(self):
704                 if self._wlanActive:
705                         debug("[FritzMenu] toggleWlan off")
706                         fritzbox.changeWLAN('0')
707                 else:
708                         debug("[FritzMenu] toggleWlan off")
709                         fritzbox.changeWLAN('1')
710
711         def _toggleMailbox(self, which):
712                 debug("[FritzMenu] toggleMailbox")
713                 if fritzbox.info[FBF_tamActive]:
714                         debug("[FritzMenu] toggleMailbox off")
715                         fritzbox.changeMailbox(which)
716
717         def _reset(self):
718                 fritzbox.reset()
719                 self._exit()
720
721         def _exit(self):
722                 self._timer.stop()
723                 self.close()
724
725
726 class FritzDisplayCalls(Screen, HelpableScreen):
727
728         def __init__(self, session, text=""): #@UnusedVariable # pylint: disable=W0613
729                 self.width = DESKTOP_WIDTH * scaleH(75, 85)/100
730                 self.height = DESKTOP_HEIGHT * 0.75
731                 dateFieldWidth = scaleH(180, 105)
732                 dirFieldWidth = 16
733                 lengthFieldWidth = scaleH(55, 45)
734                 scrollbarWidth = scaleH(35, 35)
735                 entriesWidth = self.width -scaleH(40, 5) -5
736                 hereFieldWidth = entriesWidth -dirFieldWidth -5 -dateFieldWidth -5 -lengthFieldWidth -scrollbarWidth
737                 fieldWidth = entriesWidth -dirFieldWidth -5 -5 -scrollbarWidth
738                 fontSize = scaleV(22, 20)
739                 itemHeight = 2*fontSize+5
740                 entriesHeight = self.height -scaleV(15, 10) -5 -fontSize -5 -5 -5 -40 -5
741                 buttonGap = (self.width -4*140)/5
742                 buttonV = self.height -40
743                 debug("[FritzDisplayCalls] width: " + str(self.width))
744                 self.skin = """
745                         <screen name="FritzDisplayCalls" position="center,center" size="%d,%d" title="Phone calls" >
746                                 <eLabel position="0,0" size="%d,2" backgroundColor="#aaaaaa" />
747                                 <widget name="statusbar" position="%d,%d" size="%d,%d" font="Regular;%d" backgroundColor="#aaaaaa" transparent="1" />
748                                 <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
749                                 <widget source="entries" render="Listbox" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" transparent="1">
750                                         <convert type="TemplatedMultiContent">
751                                                 {"template": [
752                                                                 MultiContentEntryText(pos = (%d,%d), size = (%d,%d), font=0, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 1), # index 0 is the number, index 1 is date
753                                                                 MultiContentEntryPixmapAlphaTest(pos = (%d,%d), size = (%d,%d), png = 2), # index 1 i direction pixmap
754                                                                 MultiContentEntryText(pos = (%d,%d), size = (%d,%d), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 3), # index 2 is remote name/number
755                                                                 MultiContentEntryText(pos = (%d,%d), size = (%d,%d), font=0, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 4), # index 3 is length of call
756                                                                 MultiContentEntryText(pos = (%d,%d), size = (%d,%d), font=0, flags = RT_HALIGN_RIGHT|RT_VALIGN_CENTER, text = 5), # index 4 is my number/name for number
757                                                         ],
758                                                 "fonts": [gFont("Regular", %d), gFont("Regular", %d)],
759                                                 "itemHeight": %d
760                                                 }
761                                         </convert>
762                                 </widget>
763                                 <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
764                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
765                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
766                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
767                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
768                                 <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" />
769                                 <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" />
770                                 <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" />
771                                 <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" />
772                         </screen>""" % (
773                                                 # scaleH(90, 75), scaleV(100, 78), # position 
774                                                 self.width, self.height, # size
775                                                 self.width, # eLabel width
776                                                 scaleH(40, 5), scaleV(10, 5), # statusbar position
777                                                 self.width, fontSize+5, # statusbar size
778                                                 scaleV(21, 21), # statusbar font size
779                                                 scaleV(10, 5)+5+fontSize+5, # eLabel position vertical
780                                                 self.width, # eLabel width
781                                                 scaleH(40, 5), scaleV(10, 5)+5+fontSize+5+5, # entries position
782                                                 entriesWidth, entriesHeight, # entries size
783                                                 5+dirFieldWidth+5, fontSize+5, dateFieldWidth, fontSize, # date pos/size
784                                                 5, (itemHeight-dirFieldWidth)/2, dirFieldWidth, dirFieldWidth, # dir pos/size
785                                                 5+dirFieldWidth+5, 5, fieldWidth, fontSize, # caller pos/size
786                                                 2+dirFieldWidth+2+dateFieldWidth+5, fontSize+5, lengthFieldWidth, fontSize, # length pos/size
787                                                 2+dirFieldWidth+2+dateFieldWidth+5+lengthFieldWidth+5, fontSize+5, hereFieldWidth, fontSize, # my number pos/size
788                                                 fontSize-4, fontSize, # fontsize
789                                                 itemHeight, # itemHeight
790                                                 buttonV-5, # eLabel position vertical
791                                                 self.width, # eLabel width
792                                                 buttonGap, buttonV, "skin_default/buttons/red.png", # widget red
793                                                 2*buttonGap+140, buttonV, "skin_default/buttons/green.png", # widget green
794                                                 3*buttonGap+2*140, buttonV, "skin_default/buttons/yellow.png", # widget yellow
795                                                 4*buttonGap+3*140, buttonV, "skin_default/buttons/blue.png", # widget blue
796                                                 buttonGap, buttonV, scaleV(22, 21), # widget red
797                                                 2*buttonGap+140, buttonV, scaleV(22, 21), # widget green
798                                                 3*buttonGap+2*140, buttonV, scaleV(22, 21), # widget yellow
799                                                 4*buttonGap+3*140, buttonV, scaleV(22, 21), # widget blue
800                                                                                                                 )
801                 # debug("[FritzDisplayCalls] skin: " + self.skin)
802                 Screen.__init__(self, session)
803                 HelpableScreen.__init__(self)
804
805                 # TRANSLATORS: keep it short, this is a button
806                 self["key_yellow"] = Button(_("All"))
807                 # TRANSLATORS: keep it short, this is a button
808                 self["key_red"] = Button(_("Missed"))
809                 # TRANSLATORS: keep it short, this is a button
810                 self["key_blue"] = Button(_("Incoming"))
811                 # TRANSLATORS: keep it short, this is a button
812                 self["key_green"] = Button(_("Outgoing"))
813
814                 self["setupActions"] = ActionMap(["OkCancelActions", "ColorActions"],
815                 {
816                         "yellow": (lambda: self.display(FBF_ALL_CALLS)),
817                         "red": (lambda: self.display(FBF_MISSED_CALLS)),
818                         "blue": (lambda: self.display(FBF_IN_CALLS)),
819                         "green": (lambda: self.display(FBF_OUT_CALLS)),
820                         "cancel": self.ok,
821                         "ok": self.showEntry, }, - 2)
822
823                 # TRANSLATORS: keep it short, this is a help text
824                 self.helpList.append((self["setupActions"], "OkCancelActions", [("ok", _("Show details of entry"))]))
825                 # TRANSLATORS: keep it short, this is a help text
826                 self.helpList.append((self["setupActions"], "OkCancelActions", [("cancel", _("Quit"))]))
827                 # TRANSLATORS: keep it short, this is a help text
828                 self.helpList.append((self["setupActions"], "ColorActions", [("yellow", _("Display all calls"))]))
829                 # TRANSLATORS: keep it short, this is a help text
830                 self.helpList.append((self["setupActions"], "ColorActions", [("red", _("Display missed calls"))]))
831                 # TRANSLATORS: keep it short, this is a help text
832                 self.helpList.append((self["setupActions"], "ColorActions", [("blue", _("Display incoming calls"))]))
833                 # TRANSLATORS: keep it short, this is a help text
834                 self.helpList.append((self["setupActions"], "ColorActions", [("green", _("Display outgoing calls"))]))
835
836                 self["statusbar"] = Label(_("Getting calls from FRITZ!Box..."))
837                 self.list = []
838                 self["entries"] = List(self.list)
839                 #=======================================================================
840                 # fontSize = scaleV(22, 18)
841                 # fontHeight = scaleV(24, 20)
842                 # self["entries"].l.setFont(0, gFont("Regular", fontSize))
843                 # self["entries"].l.setItemHeight(fontHeight)
844                 #=======================================================================
845                 debug("[FritzDisplayCalls] init: '''%s'''" % config.plugins.FritzCall.fbfCalls.value)
846                 self.display(config.plugins.FritzCall.fbfCalls.value)
847                 self.onLayoutFinish.append(self.setWindowTitle)
848
849         def setWindowTitle(self):
850                 # TRANSLATORS: this is a window title.
851                 self.setTitle(_("Phone calls"))
852
853         def ok(self):
854                 self.close()
855
856         def display(self, which=None):
857                 debug("[FritzDisplayCalls] display")
858                 if which:
859                         config.plugins.FritzCall.fbfCalls.value = which
860                         config.plugins.FritzCall.fbfCalls.save()
861                 else:
862                         which = config.plugins.FritzCall.fbfCalls.value
863                 fritzbox.getCalls(self, lambda x: self.gotCalls(x, which), which)
864
865         def gotCalls(self, listOfCalls, which):
866                 debug("[FritzDisplayCalls] gotCalls")
867                 self.updateStatus(fbfCallsChoices[which] + " (" + str(len(listOfCalls)) + ")")
868
869                 directout = LoadPixmap(resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/callout.png"))
870                 directin = LoadPixmap(resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/callin.png"))
871                 directfailed = LoadPixmap(resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/callinfailed.png"))
872                 def pixDir(direct):
873                         if direct == FBF_OUT_CALLS:
874                                 direct = directout
875                         elif direct == FBF_IN_CALLS:
876                                 direct = directin
877                         else:
878                                 direct = directfailed
879                         return direct
880
881                 # debug("[FritzDisplayCalls] gotCalls: %s" %repr(listOfCalls))
882                 self.list = [(number, date[:6] + ' ' + date[9:14], pixDir(direct), remote, length, here) for (number, date, direct, remote, length, here) in listOfCalls]
883                 self["entries"].setList(self.list)
884                 #=======================================================================
885                 # if len(self.list) > 1:
886                 #       self["entries"].setIndex(1)
887                 #=======================================================================
888
889         def updateStatus(self, text):
890                 if self.has_key("statusbar"):
891                         self["statusbar"].setText(_("Getting calls from FRITZ!Box...") + ' ' + text)
892
893         def showEntry(self):
894                 debug("[FritzDisplayCalls] showEntry")
895                 cur = self["entries"].getCurrent()
896                 if cur:
897                         if cur[0]:
898                                 debug("[FritzDisplayCalls] showEntry %s" % (cur[0]))
899                                 number = cur[0]
900                                 fullname = phonebook.search(cur[0])
901                                 if fullname:
902                                         # we have a name for this number
903                                         name = fullname
904                                         self.session.open(FritzOfferAction, self, number, name)
905                                 elif cur[3]:
906                                         name = cur[3]
907                                         self.session.open(FritzOfferAction, self, number, name)
908                                 else:
909                                         # we don't
910                                         fullname = resolveNumberWithAvon(number, config.plugins.FritzCall.country.value)
911                                         if fullname:
912                                                 name = fullname
913                                                 self.session.open(FritzOfferAction, self, number, name)
914                                         else:
915                                                 self.session.open(FritzOfferAction, self, number)
916                         else:
917                                 # we do not even have a number...
918                                 self.session.open(MessageBox,
919                                                   _("UNKNOWN"),
920                                                   type=MessageBox.TYPE_INFO)
921
922
923 class FritzOfferAction(Screen):
924
925         def __init__(self, session, parent, number, name=""):
926                 # the layout will completely be recalculated in finishLayout
927                 self.skin = """
928                         <screen name="FritzOfferAction" title="Do what?" >
929                                 <widget name="text" size="%d,%d" font="Regular;%d" />
930                                 <widget name="FacePixmap" size="%d,%d" alphatest="on" />
931                                 <widget name="key_red_p" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
932                                 <widget name="key_green_p" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
933                                 <widget name="key_yellow_p" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
934                                 <widget name="key_red" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
935                                 <widget name="key_green" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
936                                 <widget name="key_yellow" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
937                         </screen>""" % (
938                                                         DESKTOP_WIDTH, DESKTOP_HEIGHT, # set maximum size
939                                                         scaleH(22,21), # text
940                                                         DESKTOP_WIDTH, DESKTOP_HEIGHT, # set maximum size
941                                                         "skin_default/buttons/red.png",
942                                                         "skin_default/buttons/green.png",
943                                                         "skin_default/buttons/yellow.png",
944                                                         ) 
945                 debug("[FritzOfferAction] init: %s, %s" %(number, name))
946                 Screen.__init__(self, session)
947         
948                 # TRANSLATORS: keep it short, this is a button
949                 self["key_red"] = Button(_("Lookup"))
950                 # TRANSLATORS: keep it short, this is a button
951                 self["key_green"] = Button(_("Call"))
952                 # TRANSLATORS: keep it short, this is a button
953                 self["key_yellow"] = Button(_("Save"))
954                 # TRANSLATORS: keep it short, this is a button
955                 # self["key_blue"] = Button(_("Search"))
956
957                 self["FritzOfferActions"] = ActionMap(["OkCancelActions", "ColorActions"],
958                 {
959                         "red": self._lookup,
960                         "green": self._call,
961                         "yellow": self._add,
962                         "cancel": self._exit,
963                         "ok": self._exit, }, - 2)
964
965                 self._session = session
966                 if config.plugins.FritzCall.internal.value and len(number) > 3 and number[0] == "0":
967                         number = number[1:]
968                 self._number = number
969                 self._name = name.replace("\n", ", ")
970                 self["text"] = Label(number + "\n\n" + name.replace(", ", "\n"))
971                 self._parent = parent
972                 self._lookupState = 0
973                 self["key_red_p"] = Pixmap()
974                 self["key_green_p"] = Pixmap()
975                 self["key_yellow_p"] = Pixmap()
976                 self["FacePixmap"] = Pixmap()
977                 self.onLayoutFinish.append(self._finishLayout)
978                 self.onLayoutFinish.append(self.setWindowTitle)
979
980         def setWindowTitle(self):
981                 # TRANSLATORS: this is a window title.
982                 self.setTitle(_("Do what?"))
983
984         def _finishLayout(self):
985                 # pylint: disable=W0142
986                 debug("[FritzCall] FritzOfferAction/finishLayout number: %s/%s" % (self._number, self._name))
987
988                 faceFile = findFace(self._number, self._name)
989                 picPixmap = LoadPixmap(faceFile)
990                 if not picPixmap:       # that means most probably, that the picture is not 8 bit...
991                         Notifications.AddNotification(MessageBox, _("Found picture\n\n%s\n\nBut did not load. Probably not PNG, 8-bit") %faceFile, type = MessageBox.TYPE_ERROR)
992                         picPixmap = LoadPixmap(resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/icons/input_error.png"))
993                 picSize = picPixmap.size()
994                 self["FacePixmap"].instance.setPixmap(picPixmap)
995
996                 noButtons = 3
997                 # recalculate window size
998                 textSize = self["text"].getSize()
999                 textSize = (textSize[0]+20, textSize[1]+20) # don't know, why, but size is too small
1000                 debug("[FritzCall] FritzOfferAction/finishLayout textsize: %s/%s" % textSize)
1001                 textSize = eSize(*textSize)
1002                 width = max(scaleH(620, 545), noButtons*145, picSize.width() + textSize.width() + 30)
1003                 height = max(picSize.height()+5, textSize.height()+5, scaleV(-1, 136)) + 5 + 40 + 5
1004                 buttonsGap = (width-noButtons*140)/(noButtons+1)
1005                 buttonsVPos = height-40-5
1006                 wSize = (width, height)
1007                 wSize = eSize(*wSize)
1008
1009                 # center the smaller vertically
1010                 hGap = (width-picSize.width()-textSize.width())/3
1011                 picPos = (hGap, (height-50-picSize.height())/2+5)
1012                 textPos = (hGap+picSize.width()+hGap, (height-50-textSize.height())/2+5)
1013
1014                 # resize screen
1015                 self.instance.resize(wSize)
1016                 # resize text
1017                 self["text"].instance.resize(textSize)
1018                 # resize pixmap
1019                 self["FacePixmap"].instance.resize(picSize)
1020                 # move buttons
1021                 buttonPos = (buttonsGap, buttonsVPos)
1022                 self["key_red_p"].instance.move(ePoint(*buttonPos))
1023                 self["key_red"].instance.move(ePoint(*buttonPos))
1024                 buttonPos = (buttonsGap+140+buttonsGap, buttonsVPos)
1025                 self["key_green_p"].instance.move(ePoint(*buttonPos))
1026                 self["key_green"].instance.move(ePoint(*buttonPos))
1027                 buttonPos = (buttonsGap+140+buttonsGap+140+buttonsGap, buttonsVPos)
1028                 self["key_yellow_p"].instance.move(ePoint(*buttonPos))
1029                 self["key_yellow"].instance.move(ePoint(*buttonPos))
1030                 # move text
1031                 self["text"].instance.move(ePoint(*textPos))
1032                 # move pixmap
1033                 self["FacePixmap"].instance.move(ePoint(*picPos))
1034                 # center window
1035                 self.instance.move(ePoint((DESKTOP_WIDTH-wSize.width())/2, (DESKTOP_HEIGHT-wSize.height())/2))
1036
1037         def _setTextAndResize(self, message):
1038                 # pylint: disable=W0142
1039                 self["text"].instance.resize(eSize(*(DESKTOP_WIDTH, DESKTOP_HEIGHT)))
1040                 self["text"].setText(self._number + "\n\n" + message)
1041                 self._finishLayout()
1042
1043         def _lookup(self):
1044                 phonebookLocation = config.plugins.FritzCall.phonebookLocation.value
1045                 if self._lookupState == 0:
1046                         self._lookupState = 1
1047                         self._setTextAndResize(_("Reverse searching..."))
1048                         ReverseLookupAndNotifier(self._number, self._lookedUp, "UTF-8", config.plugins.FritzCall.country.value)
1049                         return
1050                 if self._lookupState == 1 and os.path.exists(os.path.join(phonebookLocation, "PhoneBook.csv")):
1051                         self._setTextAndResize(_("Searching in Outlook export..."))
1052                         self._lookupState = 2
1053                         self._lookedUp(self._number, FritzOutlookCSV.findNumber(self._number, os.path.join(phonebookLocation, "PhoneBook.csv"))) #@UndefinedVariable
1054                         return
1055                 else:
1056                         self._lookupState = 2
1057                 if self._lookupState == 2 and os.path.exists(os.path.join(phonebookLocation, "PhoneBook.ldif")):
1058                         self._setTextAndResize(_("Searching in LDIF..."))
1059                         self._lookupState = 0
1060                         FritzLDIF.FindNumber(self._number, open(os.path.join(phonebookLocation, "PhoneBook.ldif")), self._lookedUp)
1061                         return
1062                 else:
1063                         self._lookupState = 0
1064                         self._lookup()
1065
1066         def _lookedUp(self, number, name):
1067                 name = handleReverseLookupResult(name)
1068                 if not name:
1069                         if self._lookupState == 1:
1070                                 name = _("No result from reverse lookup")
1071                         elif self._lookupState == 2:
1072                                 name = _("No result from Outlook export")
1073                         else:
1074                                 name = _("No result from LDIF")
1075                 self._name = name
1076                 self._number = number
1077                 debug("[FritzOfferAction] lookedUp: " + str(name.replace(", ", "\n")))
1078                 self._setTextAndResize(str(name.replace(", ", "\n")))
1079
1080         def _call(self):
1081                 if fritzbox:
1082                         debug("[FritzOfferAction] call: %s" %self._number)
1083                         self.session.open(MessageBox, _("Calling %s") %self._number, type=MessageBox.TYPE_INFO)
1084                         fritzbox.dial(self._number)
1085                 else:
1086                         debug("[FritzOfferAction] call: no fritzbox object?!?!")
1087                         self.session.open(MessageBox, _("FRITZ!Box not available for calling"), type=MessageBox.TYPE_INFO)
1088
1089         def _add(self):
1090                 debug("[FritzOfferAction] add: %s, %s" %(self._number, self._name))
1091                 phonebook.FritzDisplayPhonebook(self._session).add(self._parent, self._number, self._name)
1092                 self._exit()
1093
1094         def _exit(self):
1095                 self.close()
1096
1097
1098 class FritzCallPhonebook:
1099         def __init__(self):
1100                 debug("[FritzCallPhonebook] init")
1101                 # Beware: strings in phonebook.phonebook have to be in utf-8!
1102                 self.phonebook = {}
1103                 if config.plugins.FritzCall.reloadPhonebookTime.value > 0:
1104                         self.loop = eTimer()
1105                         self.loop.callback.append(self.startReload)
1106                         self.loop.start(config.plugins.FritzCall.reloadPhonebookTime.value*60*60*1000, 1)
1107                 self.reload()
1108
1109         def startReload(self):
1110                 self.loop.stop()
1111                 debug("[FritzCallPhonebook] reloading phonebooks " + time.ctime())
1112                 self.reload()
1113                 self.loop.start(config.plugins.FritzCall.reloadPhonebookTime.value*60*60*1000, 1)
1114
1115         def reload(self):
1116                 debug("[FritzCallPhonebook] reload")
1117                 # Beware: strings in phonebook.phonebook have to be in utf-8!
1118                 self.phonebook = {}
1119
1120                 if not config.plugins.FritzCall.enable.value:
1121                         return
1122
1123                 phonebookFilename = os.path.join(config.plugins.FritzCall.phonebookLocation.value, "PhoneBook.txt")
1124                 if config.plugins.FritzCall.phonebook.value and os.path.exists(phonebookFilename):
1125                         debug("[FritzCallPhonebook] reload: read " + phonebookFilename)
1126                         phonebookTxtCorrupt = False
1127                         self.phonebook = {}
1128                         for line in open(phonebookFilename):
1129                                 try:
1130                                         # Beware: strings in phonebook.phonebook have to be in utf-8!
1131                                         line = line.decode("utf-8")
1132                                 except UnicodeDecodeError: # this is just for the case, somebody wrote latin1 chars into PhoneBook.txt
1133                                         try:
1134                                                 line = line.decode("iso-8859-1")
1135                                                 debug("[FritzCallPhonebook] Fallback to ISO-8859-1 in %s" % line)
1136                                                 phonebookTxtCorrupt = True
1137                                         except UnicodeDecodeError:
1138                                                 debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
1139                                                 phonebookTxtCorrupt = True
1140                                 line = line.encode("utf-8")
1141                                 elems = line.split('#')
1142                                 if len(elems) == 2:
1143                                         try:
1144                                                 debug("[FritzCallPhonebook] reload: Adding '''%s''' with '''%s''' from internal phonebook!" % (elems[1].strip(), elems[0]))
1145                                                 self.phonebook[elems[0]] = elems[1]
1146                                         except ValueError: # how could this possibly happen?!?!
1147                                                 debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
1148                                                 phonebookTxtCorrupt = True
1149                                 else:
1150                                         debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
1151                                         phonebookTxtCorrupt = True
1152                                         
1153                                 #===============================================================
1154                                 # found = re.match("^(\d+)#(.*)$", line)
1155                                 # if found:
1156                                 #       try:
1157                                 #               self.phonebook[found.group(1)] = found.group(2)
1158                                 #       except ValueError: # how could this possibly happen?!?!
1159                                 #               debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
1160                                 #               phonebookTxtCorrupt = True
1161                                 # else:
1162                                 #       debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
1163                                 #       phonebookTxtCorrupt = True
1164                                 #===============================================================
1165
1166                         if phonebookTxtCorrupt:
1167                                 # dump phonebook to PhoneBook.txt
1168                                 debug("[FritzCallPhonebook] dump Phonebook.txt")
1169                                 try:
1170                                         os.rename(phonebookFilename, phonebookFilename + ".bck")
1171                                         fNew = open(phonebookFilename, 'w')
1172                                         # Beware: strings in phonebook.phonebook are utf-8!
1173                                         for (number, name) in self.phonebook.iteritems():
1174                                                 # Beware: strings in PhoneBook.txt have to be in utf-8!
1175                                                 fNew.write(number + "#" + name.encode("utf-8"))
1176                                         fNew.close()
1177                                 except (IOError, OSError):
1178                                         debug("[FritzCallPhonebook] error renaming or writing to %s" %phonebookFilename)
1179
1180                 if fritzbox and config.plugins.FritzCall.fritzphonebook.value:
1181                         fritzbox.loadFritzBoxPhonebook(self)
1182
1183 #===============================================================================
1184 #               #
1185 #               # read entries from Outlook export
1186 #               #
1187 #               # not reliable with coding yet
1188 #               # 
1189 #               # import csv exported from Outlook 2007 with csv(Windows)
1190 #               csvFilename = "/tmp/PhoneBook.csv"
1191 #               if config.plugins.FritzCall.phonebook.value and os.path.exists(csvFilename):
1192 #                       try:
1193 #                               readOutlookCSV(csvFilename, self.add)
1194 #                               os.rename(csvFilename, csvFilename + ".done")
1195 #                       except ImportError:
1196 #                               debug("[FritzCallPhonebook] CSV import failed" %line)
1197 #===============================================================================
1198
1199                 
1200 #===============================================================================
1201 #               #
1202 #               # read entries from LDIF
1203 #               #
1204 #               # import ldif exported from Thunderbird 2.0.0.19
1205 #               ldifFilename = "/tmp/PhoneBook.ldif"
1206 #               if config.plugins.FritzCall.phonebook.value and os.path.exists(ldifFilename):
1207 #                       try:
1208 #                               parser = MyLDIF(open(ldifFilename), self.add)
1209 #                               parser.parse()
1210 #                               os.rename(ldifFilename, ldifFilename + ".done")
1211 #                       except ImportError:
1212 #                               debug("[FritzCallPhonebook] LDIF import failed" %line)
1213 #===============================================================================
1214
1215         def search(self, number):
1216                 # debug("[FritzCallPhonebook] Searching for %s" %number)
1217                 name = ""
1218                 if not self.phonebook or not number:
1219                         return
1220
1221                 if config.plugins.FritzCall.prefix.value:
1222                         prefix = config.plugins.FritzCall.prefix.value
1223                         if number[0] != '0':
1224                                 number = prefix + number
1225                                 # debug("[FritzCallPhonebook] search: added prefix: %s" %number)
1226                         elif number[:len(prefix)] == prefix and self.phonebook.has_key(number[len(prefix):]):
1227                                 # debug("[FritzCallPhonebook] search: same prefix")
1228                                 name = self.phonebook[number[len(prefix):]]
1229                                 # debug("[FritzCallPhonebook] search: result: %s" %name)
1230                 else:
1231                         prefix = ""
1232                                 
1233                 if not name and self.phonebook.has_key(number):
1234                         name = self.phonebook[number]
1235                                 
1236                 return name.replace(", ", "\n").strip()
1237
1238         def add(self, number, name):
1239                 '''
1240                 
1241                 @param number: number of entry
1242                 @param name: name of entry, has to be in utf-8
1243                 '''
1244                 debug("[FritzCallPhonebook] add")
1245                 name = name.replace("\n", ", ").replace('#','') # this is just for safety reasons. add should only be called with newlines converted into commas
1246                 self.remove(number)
1247                 self.phonebook[number] = name
1248                 if number and number != 0:
1249                         if config.plugins.FritzCall.phonebook.value:
1250                                 try:
1251                                         name = name.strip() + "\n"
1252                                         string = "%s#%s" % (number, name)
1253                                         # Beware: strings in PhoneBook.txt have to be in utf-8!
1254                                         f = open(os.path.join(config.plugins.FritzCall.phonebookLocation.value, "PhoneBook.txt"), 'a')
1255                                         f.write(string)
1256                                         f.close()
1257                                         debug("[FritzCallPhonebook] added %s with %s to Phonebook.txt" % (number, name.strip()))
1258                                         return True
1259         
1260                                 except IOError:
1261                                         return False
1262
1263         def remove(self, number):
1264                 if number in self.phonebook:
1265                         debug("[FritzCallPhonebook] remove entry in phonebook")
1266                         del self.phonebook[number]
1267                         if config.plugins.FritzCall.phonebook.value:
1268                                 try:
1269                                         phonebookFilename = os.path.join(config.plugins.FritzCall.phonebookLocation.value, "PhoneBook.txt")
1270                                         debug("[FritzCallPhonebook] remove entry in Phonebook.txt")
1271                                         fOld = open(phonebookFilename, 'r')
1272                                         fNew = open(phonebookFilename + str(os.getpid()), 'w')
1273                                         line = fOld.readline()
1274                                         while (line):
1275                                                 elems = line.split('#')
1276                                                 if len(elems) == 2 and not elems[0] == number:
1277                                                         fNew.write(line)
1278                                                 line = fOld.readline()
1279                                         fOld.close()
1280                                         fNew.close()
1281                                         # os.remove(phonebookFilename)
1282                                         eBackgroundFileEraser.getInstance().erase(phonebookFilename)
1283                                         os.rename(phonebookFilename + str(os.getpid()), phonebookFilename)
1284                                         debug("[FritzCallPhonebook] removed %s from Phonebook.txt" % number)
1285                                         return True
1286         
1287                                 except (IOError, OSError):
1288                                         debug("[FritzCallPhonebook] error removing %s from %s" %(number, phonebookFilename))
1289                 return False
1290
1291         class FritzDisplayPhonebook(Screen, HelpableScreen, NumericalTextInput):
1292         
1293                 def __init__(self, session):
1294                         self.entriesWidth = DESKTOP_WIDTH * scaleH(75, 85)/100
1295                         self.height = DESKTOP_HEIGHT * 0.75
1296                         numberFieldWidth = scaleH(220, 160)
1297                         fieldWidth = self.entriesWidth -5 -numberFieldWidth -10
1298                         fontSize = scaleV(22, 18)
1299                         fontHeight = scaleV(24, 20)
1300                         buttonGap = (self.entriesWidth-4*140)/5
1301                         debug("[FritzDisplayPhonebook] width: " + str(self.entriesWidth))
1302                         self.skin = """
1303                                 <screen name="FritzDisplayPhonebook" position="center,center" size="%d,%d" title="Phonebook" >
1304                                         <eLabel position="0,0" size="%d,2" backgroundColor="#aaaaaa" />
1305                                         <widget source="entries" render="Listbox" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" transparent="1">
1306                                                 <convert type="TemplatedMultiContent">
1307                                                         {"template": [
1308                                                                         MultiContentEntryText(pos = (%d,%d), size = (%d,%d), font=0, flags = RT_HALIGN_LEFT, text = 1), # index 0 is the name, index 1 is shortname
1309                                                                         MultiContentEntryText(pos = (%d,%d), size = (%d,%d), font=0, flags = RT_HALIGN_LEFT, text = 2), # index 2 is number
1310                                                                 ],
1311                                                         "fonts": [gFont("Regular", %d)],
1312                                                         "itemHeight": %d
1313                                                         }
1314                                                 </convert>
1315                                         </widget>
1316                                         <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
1317                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
1318                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
1319                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
1320                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
1321                                         <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" />
1322                                         <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" />
1323                                         <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" />
1324                                         <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" />
1325                                 </screen>""" % (
1326                                                 # scaleH(90, 75), scaleV(100, 73), # position 
1327                                                 self.entriesWidth, self.height, # size
1328                                                 self.entriesWidth, # eLabel width
1329                                                 scaleH(40, 5), scaleV(20, 5), # entries position
1330                                                 self.entriesWidth-scaleH(40, 5), self.height-scaleV(20, 5)-5-5-40, # entries size
1331                                                 0, 0, fieldWidth, scaleH(24,20), # name pos/size
1332                                                 fieldWidth +5, 0, numberFieldWidth, scaleH(24,20), # dir pos/size
1333                                                 fontSize, # fontsize
1334                                                 fontHeight, # itemHeight
1335                                                 self.height-40-5, # eLabel position vertical
1336                                                 self.entriesWidth, # eLabel width
1337                                                 buttonGap, self.height-40, "skin_default/buttons/red.png", # ePixmap red
1338                                                 2*buttonGap+140, self.height-40, "skin_default/buttons/green.png", # ePixmap green
1339                                                 3*buttonGap+2*140, self.height-40, "skin_default/buttons/yellow.png", # ePixmap yellow
1340                                                 4*buttonGap+3*140, self.height-40, "skin_default/buttons/blue.png", # ePixmap blue
1341                                                 buttonGap, self.height-40, scaleV(22, 21), # widget red
1342                                                 2*buttonGap+140, self.height-40, scaleV(22, 21), # widget green
1343                                                 3*buttonGap+2*140, self.height-40, scaleV(22, 21), # widget yellow
1344                                                 4*buttonGap+3*140, self.height-40, scaleV(22, 21), # widget blue
1345                                                 )
1346         
1347                         # debug("[FritzDisplayCalls] skin: " + self.skin)
1348                         Screen.__init__(self, session)
1349                         NumericalTextInput.__init__(self)
1350                         HelpableScreen.__init__(self)
1351                 
1352                         # TRANSLATORS: keep it short, this is a button
1353                         self["key_red"] = Button(_("Delete"))
1354                         # TRANSLATORS: keep it short, this is a button
1355                         self["key_green"] = Button(_("New"))
1356                         # TRANSLATORS: keep it short, this is a button
1357                         self["key_yellow"] = Button(_("Edit"))
1358                         # TRANSLATORS: keep it short, this is a button
1359                         self["key_blue"] = Button(_("Search"))
1360         
1361                         self["setupActions"] = ActionMap(["OkCancelActions", "ColorActions"],
1362                         {
1363                                 "red": self.delete,
1364                                 "green": self.add,
1365                                 "yellow": self.edit,
1366                                 "blue": self.search,
1367                                 "cancel": self.exit,
1368                                 "ok": self.showEntry, }, - 2)
1369         
1370                         # TRANSLATORS: keep it short, this is a help text
1371                         self.helpList.append((self["setupActions"], "OkCancelActions", [("ok", _("Show details of entry"))]))
1372                         # TRANSLATORS: keep it short, this is a help text
1373                         self.helpList.append((self["setupActions"], "OkCancelActions", [("cancel", _("Quit"))]))
1374                         # TRANSLATORS: keep it short, this is a help text
1375                         self.helpList.append((self["setupActions"], "ColorActions", [("red", _("Delete entry"))]))
1376                         # TRANSLATORS: keep it short, this is a help text
1377                         self.helpList.append((self["setupActions"], "ColorActions", [("green", _("Add entry to phonebook"))]))
1378                         # TRANSLATORS: keep it short, this is a help text
1379                         self.helpList.append((self["setupActions"], "ColorActions", [("yellow", _("Edit selected entry"))]))
1380                         # TRANSLATORS: keep it short, this is a help text
1381                         self.helpList.append((self["setupActions"], "ColorActions", [("blue", _("Search (case insensitive)"))]))
1382         
1383                         self["entries"] = List([])
1384                         debug("[FritzCallPhonebook] displayPhonebook init")
1385                         self.help_window = None
1386                         self.sortlist = []
1387                         self.onLayoutFinish.append(self.setWindowTitle)
1388                         self.display()
1389
1390                 def setWindowTitle(self):
1391                         # TRANSLATORS: this is a window title.
1392                         self.setTitle(_("Phonebook"))
1393
1394                 def display(self, filterNumber=""):
1395                         debug("[FritzCallPhonebook] displayPhonebook/display")
1396                         self.sortlist = []
1397                         # Beware: strings in phonebook.phonebook are utf-8!
1398                         sortlistHelp = sorted((name.lower(), name, number) for (number, name) in phonebook.phonebook.iteritems())
1399                         for (low, name, number) in sortlistHelp:
1400                                 if number == "01234567890":
1401                                         continue
1402                                 try:
1403                                         low = low.decode("utf-8")
1404                                 except UnicodeDecodeError:  # this should definitely not happen
1405                                         try:
1406                                                 low = low.decode("iso-8859-1")
1407                                         except UnicodeDecodeError:
1408                                                 debug("[FritzCallPhonebook] displayPhonebook/display: corrupt phonebook entry for %s" % number)
1409                                                 # self.session.open(MessageBox, _("Corrupt phonebook entry\nfor number %s\nDeleting.") %number, type = MessageBox.TYPE_ERROR)
1410                                                 phonebook.remove(number)
1411                                                 continue
1412                                 else:
1413                                         if filterNumber:
1414                                                 filterNumber = filterNumber.lower()
1415                                                 if low.find(filterNumber) == - 1:
1416                                                         continue
1417                                         name = name.strip().decode("utf-8")
1418                                         number = number.strip().decode("utf-8")
1419                                         comma = name.find(',')
1420                                         if comma != -1:
1421                                                 shortname = name[:comma]
1422                                         else:
1423                                                 shortname = name
1424                                         number = number.encode("utf-8", "replace")
1425                                         name = name.encode("utf-8", "replace")
1426                                         shortname = shortname.encode('utf-8', 'replace')
1427                                         self.sortlist.append((name, shortname, number))
1428                                 
1429                         self["entries"].setList(self.sortlist)
1430         
1431                 def showEntry(self):
1432                         cur = self["entries"].getCurrent()
1433                         if cur:
1434                                 debug("[FritzCallPhonebook] displayPhonebook/showEntry %s" % (repr(cur)))
1435                                 number = cur[2]
1436                                 name = cur[0]
1437                                 self.session.open(FritzOfferAction, self, number, name)
1438         
1439                 def delete(self):
1440                         cur = self["entries"].getCurrent()
1441                         if cur:
1442                                 debug("[FritzCallPhonebook] displayPhonebook/delete %s" % (repr(cur)))
1443                                 self.session.openWithCallback(
1444                                         self.deleteConfirmed,
1445                                         MessageBox,
1446                                         _("Do you really want to delete entry for\n\n%(number)s\n\n%(name)s?") 
1447                                         % { 'number':str(cur[2]), 'name':str(cur[0]).replace(", ", "\n") }
1448                                                                 )
1449                         else:
1450                                 self.session.open(MessageBox, _("No entry selected"), MessageBox.TYPE_INFO)
1451         
1452                 def deleteConfirmed(self, ret):
1453                         debug("[FritzCallPhonebook] displayPhonebook/deleteConfirmed")
1454                         #
1455                         # if ret: delete number from sortlist, delete number from phonebook.phonebook and write it to disk
1456                         #
1457                         cur = self["entries"].getCurrent()
1458                         if cur:
1459                                 if ret:
1460                                         # delete number from sortlist, delete number from phonebook.phonebook and write it to disk
1461                                         debug("[FritzCallPhonebook] displayPhonebook/deleteConfirmed %s" % (repr(cur)))
1462                                         phonebook.remove(cur[2])
1463                                         self.display()
1464                                 # else:
1465                                         # self.session.open(MessageBox, _("Not deleted."), MessageBox.TYPE_INFO)
1466                         else:
1467                                 self.session.open(MessageBox, _("No entry selected"), MessageBox.TYPE_INFO)
1468         
1469                 def add(self, parent=None, number="", name=""):
1470                         class AddScreen(Screen, ConfigListScreen):
1471                                 '''ConfiglistScreen with two ConfigTexts for Name and Number'''
1472         
1473                                 def __init__(self, session, parent, number="", name=""):
1474                                         #
1475                                         # setup screen with two ConfigText and OK and ABORT button
1476                                         # 
1477                                         noButtons = 2
1478                                         width = max(scaleH(-1, 570), noButtons*140)
1479                                         height = scaleV(-1, 100) # = 5 + 126 + 40 + 5; 6 lines of text possible
1480                                         buttonsGap = (width-noButtons*140)/(noButtons+1)
1481                                         buttonsVPos = height-40-5
1482                                         self.skin = """
1483                                                 <screen position="center,center" size="%d,%d" title="Add entry to phonebook" >
1484                                                 <widget name="config" position="5,5" size="%d,%d" scrollbarMode="showOnDemand" />
1485                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
1486                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
1487                                                 <widget name="key_red" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1488                                                 <widget name="key_green" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1489                                                 </screen>""" % (
1490                                                                                 width, height,
1491                                                                                 width - 5 - 5, height - 5 - 40 - 5,
1492                                                                                 buttonsGap, buttonsVPos, "skin_default/buttons/red.png",
1493                                                                                 buttonsGap+140+buttonsGap, buttonsVPos, "skin_default/buttons/green.png",
1494                                                                                 buttonsGap, buttonsVPos,
1495                                                                                 buttonsGap+140+buttonsGap, buttonsVPos,
1496                                                                                 )
1497                                         Screen.__init__(self, session)
1498                                         self.session = session
1499                                         self.parent = parent
1500                                         # TRANSLATORS: keep it short, this is a button
1501                                         self["key_red"] = Button(_("Cancel"))
1502                                         # TRANSLATORS: keep it short, this is a button
1503                                         self["key_green"] = Button(_("OK"))
1504                                         self["setupActions"] = ActionMap(["SetupActions", "ColorActions"],
1505                                         {
1506                                                 "cancel": self.cancel,
1507                                                 "red": self.cancel,
1508                                                 "green": self.add,
1509                                                 "ok": self.add,
1510                                         }, - 2)
1511         
1512                                         self.list = [ ]
1513                                         ConfigListScreen.__init__(self, self.list, session=session)
1514                                         self.name = name
1515                                         self.number = number
1516                                         config.plugins.FritzCall.name.value = name
1517                                         config.plugins.FritzCall.number.value = number
1518                                         self.list.append(getConfigListEntry(_("Name"), config.plugins.FritzCall.name))
1519                                         self.list.append(getConfigListEntry(_("Number"), config.plugins.FritzCall.number))
1520                                         self["config"].list = self.list
1521                                         self["config"].l.setList(self.list)
1522                                         self.onLayoutFinish.append(self.setWindowTitle)
1523                         
1524                                 def setWindowTitle(self):
1525                                         # TRANSLATORS: this is a window title.
1526                                         self.setTitle(_("Add entry to phonebook"))
1527
1528                                 def add(self):
1529                                         # get texts from Screen
1530                                         # add (number,name) to sortlist and phonebook.phonebook and disk
1531                                         self.name = config.plugins.FritzCall.name.value
1532                                         self.number = config.plugins.FritzCall.number.value
1533                                         if not self.number or not self.name:
1534                                                 self.session.open(MessageBox, _("Entry incomplete."), type=MessageBox.TYPE_ERROR)
1535                                                 return
1536                                         # add (number,name) to sortlist and phonebook.phonebook and disk
1537         #                                       oldname = phonebook.search(self.number)
1538         #                                       if oldname:
1539         #                                               self.session.openWithCallback(
1540         #                                                       self.overwriteConfirmed,
1541         #                                                       MessageBox,
1542         #                                                       _("Do you really want to overwrite entry for %(number)s\n\n%(name)s\n\nwith\n\n%(newname)s?")
1543         #                                                       % {
1544         #                                                       'number':self.number,
1545         #                                                       'name': oldname,
1546         #                                                       'newname':self.name.replace(", ","\n")
1547         #                                                       }
1548         #                                                       )
1549         #                                               self.close()
1550         #                                               return
1551                                         phonebook.add(self.number, self.name)
1552                                         self.close()
1553                                         self.parent.display()
1554         
1555                                 def overwriteConfirmed(self, ret):
1556                                         if ret:
1557                                                 phonebook.remove(self.number)
1558                                                 phonebook.add(self.number, self.name)
1559                                                 self.parent.display()
1560         
1561                                 def cancel(self):
1562                                         self.close()
1563         
1564                         debug("[FritzCallPhonebook] displayPhonebook/add")
1565                         if not parent:
1566                                 parent = self
1567                         self.session.open(AddScreen, parent, number, name)
1568         
1569                 def edit(self):
1570                         debug("[FritzCallPhonebook] displayPhonebook/edit")
1571                         cur = self["entries"].getCurrent()
1572                         if cur is None:
1573                                 self.session.open(MessageBox, _("No entry selected"), MessageBox.TYPE_INFO)
1574                         else:
1575                                 self.add(self, cur[2], cur[0])
1576         
1577                 def search(self):
1578                         debug("[FritzCallPhonebook] displayPhonebook/search")
1579                         self.help_window = self.session.instantiateDialog(NumericalTextInputHelpDialog, self)
1580                         self.help_window.show()
1581                         # VirtualKeyboard instead of InputBox?
1582                         self.session.openWithCallback(self.doSearch, InputBox, _("Enter Search Terms"), _("Search phonebook"))
1583         
1584                 def doSearch(self, searchTerms):
1585                         if not searchTerms:
1586                                 searchTerms = ""
1587                         debug("[FritzCallPhonebook] displayPhonebook/doSearch: " + searchTerms)
1588                         if self.help_window:
1589                                 self.session.deleteDialog(self.help_window)
1590                                 self.help_window = None
1591                         self.display(searchTerms)
1592         
1593                 def exit(self):
1594                         self.close()
1595
1596 phonebook = FritzCallPhonebook()
1597
1598
1599
1600 class FritzCallSetup(Screen, ConfigListScreen, HelpableScreen):
1601
1602         def __init__(self, session, args=None): #@UnusedVariable # pylint: disable=W0613
1603                 self.width = scaleH(20+4*(140+90)+2*(35+40)+20, 4*140+2*35)
1604                 width = self.width
1605                 debug("[FritzCallSetup] width: " + str(self.width))
1606                 self.skin = """
1607                         <screen name="FritzCallSetup" position="center,center" size="%d,%d" title="FritzCall Setup" >
1608                         <eLabel position="0,0" size="%d,2" backgroundColor="#aaaaaa" />
1609                         <widget name="consideration" position="%d,%d" halign="center" size="%d,%d" font="Regular;%d" backgroundColor="#20040404" transparent="1" />
1610                         <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
1611                         <widget name="config" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" backgroundColor="#20040404" transparent="1" />
1612                         <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
1613                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
1614                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
1615                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
1616                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="%s" transparent="1" alphatest="on" />
1617                         <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" />
1618                         <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" />
1619                         <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" />
1620                         <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" />
1621                         <ePixmap position="%d,%d" zPosition="4" size="35,25" pixmap="%s" transparent="1" alphatest="on" />
1622                         <ePixmap position="%d,%d" zPosition="4" size="35,25" pixmap="%s" transparent="1" alphatest="on" />
1623                         </screen>""" % (
1624                                                 # (DESKTOP_WIDTH-width)/2, scaleV(100, 73), # position 
1625                                                 width, scaleV(560, 430), # size
1626                                                 width, # eLabel width
1627                                                 scaleH(40, 20), scaleV(10, 5), # consideration position
1628                                                 scaleH(width-80, width-40), scaleV(25, 45), # consideration size
1629                                                 scaleV(22, 20), # consideration font size
1630                                                 scaleV(40, 50), # eLabel position vertical
1631                                                 width, # eLabel width
1632                                                 scaleH(40, 5), scaleV(60, 57), # config position
1633                                                 scaleH(width-80, width-10), scaleV(453, 328), # config size
1634                                                 scaleV(518, 390), # eLabel position vertical
1635                                                 width, # eLabel width
1636                                                 scaleH(20, 0), scaleV(525, 395), "skin_default/buttons/red.png", # pixmap red
1637                                                 scaleH(20+140+90, 140), scaleV(525, 395), "skin_default/buttons/green.png", # pixmap green
1638                                                 scaleH(20+2*(140+90), 2*140), scaleV(525, 395), "skin_default/buttons/yellow.png", # pixmap yellow
1639                                                 scaleH(20+3*(140+90), 3*140), scaleV(525, 395), "skin_default/buttons/blue.png", # pixmap blue
1640                                                 scaleH(20, 0), scaleV(525, 395), scaleV(21, 21), # widget red
1641                                                 scaleH(20+(140+90), 140), scaleV(525, 395), scaleV(21, 21), # widget green
1642                                                 scaleH(20+2*(140+90), 2*140), scaleV(525, 395), scaleV(21, 21), # widget yellow
1643                                                 scaleH(20+3*(140+90), 3*140), scaleV(525, 395), scaleV(21, 21), # widget blue
1644                                                 scaleH(20+4*(140+90), 4*140), scaleV(532, 402), "skin_default/buttons/key_info.png", # button info
1645                                                 scaleH(20+4*(140+90)+(35+40), 4*140+35), scaleV(532, 402), "skin_default/buttons/key_menu.png", # button menu
1646                                                                                                         )
1647
1648                 Screen.__init__(self, session)
1649                 HelpableScreen.__init__(self)
1650                 self.session = session
1651
1652                 self["consideration"] = Label(_("You need to enable the monitoring on your FRITZ!Box by dialing #96*5*!"))
1653                 self.list = []
1654
1655                 # Initialize Buttons
1656                 # TRANSLATORS: keep it short, this is a button
1657                 self["key_red"] = Button(_("Cancel"))
1658                 # TRANSLATORS: keep it short, this is a button
1659                 self["key_green"] = Button(_("OK"))
1660                 # TRANSLATORS: keep it short, this is a button
1661                 self["key_yellow"] = Button(_("Phone calls"))
1662                 # TRANSLATORS: keep it short, this is a button
1663                 self["key_blue"] = Button(_("Phonebook"))
1664                 # TRANSLATORS: keep it short, this is a button
1665                 self["key_info"] = Button(_("About FritzCall"))
1666                 # TRANSLATORS: keep it short, this is a button
1667                 self["key_menu"] = Button(_("FRITZ!Box Fon Status"))
1668
1669                 self["setupActions"] = ActionMap(["ColorActions", "OkCancelActions", "MenuActions", "EPGSelectActions"],
1670                 {
1671                         "red": self.cancel,
1672                         "green": self.save,
1673                         "yellow": self.displayCalls,
1674                         "blue": self.displayPhonebook,
1675                         "cancel": self.cancel,
1676                         "ok": self.save,
1677                         "menu": self.menu,
1678                         "info": self.about,
1679                 }, - 2)
1680
1681                 # TRANSLATORS: keep it short, this is a help text
1682                 self.helpList.append((self["setupActions"], "ColorActions", [("red", _("quit"))]))
1683                 # TRANSLATORS: keep it short, this is a help text
1684                 self.helpList.append((self["setupActions"], "ColorActions", [("green", _("save and quit"))]))
1685                 # TRANSLATORS: keep it short, this is a help text
1686                 self.helpList.append((self["setupActions"], "ColorActions", [("yellow", _("display calls"))]))
1687                 # TRANSLATORS: keep it short, this is a help text
1688                 self.helpList.append((self["setupActions"], "ColorActions", [("blue", _("display phonebook"))]))
1689                 # TRANSLATORS: keep it short, this is a help text
1690                 self.helpList.append((self["setupActions"], "OkCancelActions", [("ok", _("save and quit"))]))
1691                 # TRANSLATORS: keep it short, this is a help text
1692                 self.helpList.append((self["setupActions"], "OkCancelActions", [("cancel", _("quit"))]))
1693                 # TRANSLATORS: keep it short, this is a help text
1694                 self.helpList.append((self["setupActions"], "MenuActions", [("menu", _("FRITZ!Box Fon Status"))]))
1695                 # TRANSLATORS: keep it short, this is a help text
1696                 self.helpList.append((self["setupActions"], "EPGSelectActions", [("info", _("About FritzCall"))]))
1697
1698                 ConfigListScreen.__init__(self, self.list, session=session)
1699
1700                 # get new list of locations for PhoneBook.txt
1701                 self._mountedDevs = getMountedDevs()
1702                 self.createSetup()
1703                 self.onLayoutFinish.append(self.setWindowTitle)
1704
1705         def setWindowTitle(self):
1706                 # TRANSLATORS: this is a window title.
1707                 self.setTitle(_("FritzCall Setup") + " (" + "$Revision: 734 $"[1: - 1] + "$Date: 2012-12-21 16:54:18 +0100 (Fr, 21 Dez 2012) $"[7:23] + ")")
1708
1709         def keyLeft(self):
1710                 ConfigListScreen.keyLeft(self)
1711                 self.createSetup()
1712
1713         def keyRight(self):
1714                 ConfigListScreen.keyRight(self)
1715                 self.createSetup()
1716
1717         def createSetup(self):
1718                 self.list = [ ]
1719                 self.list.append(getConfigListEntry(_("Call monitoring"), config.plugins.FritzCall.enable))
1720                 if config.plugins.FritzCall.enable.value:
1721                         self.list.append(getConfigListEntry(_("FRITZ!Box FON address (Name or IP)"), config.plugins.FritzCall.hostname))
1722                         self.list.append(getConfigListEntry(_("FRITZ!Box FON firmware version"), config.plugins.FritzCall.fwVersion))
1723
1724                         self.list.append(getConfigListEntry(_("Show after Standby"), config.plugins.FritzCall.afterStandby))
1725
1726                         self.list.append(getConfigListEntry(_("Show only calls for specific MSN"), config.plugins.FritzCall.filter))
1727                         if config.plugins.FritzCall.filter.value:
1728                                 self.list.append(getConfigListEntry(_("MSN to show (separated by ,)"), config.plugins.FritzCall.filtermsn))
1729                                 self.list.append(getConfigListEntry(_("Filter also list of calls"), config.plugins.FritzCall.filterCallList))
1730                         self.list.append(getConfigListEntry(_("Mute on call"), config.plugins.FritzCall.muteOnCall))
1731
1732                         self.list.append(getConfigListEntry(_("Show Outgoing Calls"), config.plugins.FritzCall.showOutgoing))
1733                         # not only for outgoing: config.plugins.FritzCall.showOutgoing.value:
1734                         self.list.append(getConfigListEntry(_("Areacode to add to calls without one (if necessary)"), config.plugins.FritzCall.prefix))
1735                         self.list.append(getConfigListEntry(_("Timeout for Call Notifications (seconds)"), config.plugins.FritzCall.timeout))
1736                         self.list.append(getConfigListEntry(_("Reverse Lookup Caller ID (select country below)"), config.plugins.FritzCall.lookup))
1737                         if config.plugins.FritzCall.lookup.value:
1738                                 self.list.append(getConfigListEntry(_("Country"), config.plugins.FritzCall.country))
1739
1740                         if config.plugins.FritzCall.fwVersion.value == "05.50":
1741                                 self.list.append(getConfigListEntry(_("User name Accessing FRITZ!Box"), config.plugins.FritzCall.username))
1742                         # TODO: make password unreadable?
1743                         self.list.append(getConfigListEntry(_("Password Accessing FRITZ!Box"), config.plugins.FritzCall.password))
1744                         self.list.append(getConfigListEntry(_("Extension number to initiate call on"), config.plugins.FritzCall.extension))
1745                         self.list.append(getConfigListEntry(_("Read PhoneBook from FRITZ!Box"), config.plugins.FritzCall.fritzphonebook))
1746                         if config.plugins.FritzCall.fritzphonebook.value:
1747                                 self.list.append(getConfigListEntry(_("Append type of number"), config.plugins.FritzCall.showType))
1748                                 self.list.append(getConfigListEntry(_("Append shortcut number"), config.plugins.FritzCall.showShortcut))
1749                                 self.list.append(getConfigListEntry(_("Append vanity name"), config.plugins.FritzCall.showVanity))
1750
1751                         self.list.append(getConfigListEntry(_("Use internal PhoneBook"), config.plugins.FritzCall.phonebook))
1752                         if config.plugins.FritzCall.phonebook.value:
1753                                 if config.plugins.FritzCall.phonebookLocation.value in self._mountedDevs:
1754                                         config.plugins.FritzCall.phonebookLocation.setChoices(self._mountedDevs, config.plugins.FritzCall.phonebookLocation.value)
1755                                 else:
1756                                         config.plugins.FritzCall.phonebookLocation.setChoices(self._mountedDevs)
1757                                 path = config.plugins.FritzCall.phonebookLocation.value
1758                                 # check whether we can write to PhoneBook.txt
1759                                 if os.path.exists(os.path.join(path[0], "PhoneBook.txt")):
1760                                         if not os.access(os.path.join(path[0], "PhoneBook.txt"), os.W_OK):
1761                                                 debug("[FritzCallSetup] createSetup: %s/PhoneBook.txt not writable, resetting to default" %(path[0]))
1762                                                 config.plugins.FritzCall.phonebookLocation.setChoices(self._mountedDevs)
1763                                 elif not (os.path.isdir(path[0]) and os.access(path[0], os.W_OK|os.X_OK)):
1764                                         debug("[FritzCallSetup] createSetup: directory %s not writable, resetting to default" %(path[0]))
1765                                         config.plugins.FritzCall.phonebookLocation.setChoices(self._mountedDevs)
1766
1767                                 self.list.append(getConfigListEntry(_("PhoneBook Location"), config.plugins.FritzCall.phonebookLocation))
1768                                 if config.plugins.FritzCall.lookup.value:
1769                                         self.list.append(getConfigListEntry(_("Automatically add new Caller to PhoneBook"), config.plugins.FritzCall.addcallers))
1770
1771                         if config.plugins.FritzCall.phonebook.value or config.plugins.FritzCall.fritzphonebook.value:
1772                                 self.list.append(getConfigListEntry(_("Reload interval for phonebooks (hours)"), config.plugins.FritzCall.reloadPhonebookTime))
1773
1774                         self.list.append(getConfigListEntry(_("Strip Leading 0"), config.plugins.FritzCall.internal))
1775                         # self.list.append(getConfigListEntry(_("Default display mode for FRITZ!Box calls"), config.plugins.FritzCall.fbfCalls))
1776                         self.list.append(getConfigListEntry(_("Display connection infos"), config.plugins.FritzCall.connectionVerbose))
1777                         self.list.append(getConfigListEntry(_("Ignore callers with no phone number"), config.plugins.FritzCall.ignoreUnknown))
1778                         self.list.append(getConfigListEntry(_("Debug"), config.plugins.FritzCall.debug))
1779
1780                 self["config"].list = self.list
1781                 self["config"].l.setList(self.list)
1782
1783         def save(self):
1784 #               debug("[FritzCallSetup] save"
1785                 for x in self["config"].list:
1786                         x[1].save()
1787                 if config.plugins.FritzCall.phonebookLocation.isChanged():
1788                         global phonebook
1789                         phonebook = FritzCallPhonebook()
1790                 if fritz_call:
1791                         if config.plugins.FritzCall.enable.value:
1792                                 fritz_call.connect()
1793                         else:
1794                                 fritz_call.shutdown()
1795                 self.close()
1796
1797         def cancel(self):
1798 #               debug("[FritzCallSetup] cancel"
1799                 for x in self["config"].list:
1800                         x[1].cancel()
1801                 self.close()
1802
1803         def displayCalls(self):
1804                 if config.plugins.FritzCall.enable.value:
1805                         if fritzbox:
1806                                 self.session.open(FritzDisplayCalls)
1807                         else:
1808                                 self.session.open(MessageBox, _("Cannot get calls from FRITZ!Box"), type=MessageBox.TYPE_INFO)
1809                 else:
1810                         self.session.open(MessageBox, _("Plugin not active"), type=MessageBox.TYPE_INFO)
1811
1812         def displayPhonebook(self):
1813                 if phonebook:
1814                         if config.plugins.FritzCall.enable.value:
1815                                 self.session.open(phonebook.FritzDisplayPhonebook)
1816                         else:
1817                                 self.session.open(MessageBox, _("Plugin not active"), type=MessageBox.TYPE_INFO)
1818                 else:
1819                         self.session.open(MessageBox, _("No phonebook"), type=MessageBox.TYPE_INFO)
1820
1821         def about(self):
1822                 self.session.open(FritzAbout)
1823
1824         def menu(self):
1825                 if config.plugins.FritzCall.enable.value:
1826                         if fritzbox and fritzbox.info:
1827                                 self.session.open(FritzMenu)
1828                         else:
1829                                 self.session.open(MessageBox, _("Cannot get infos from FRITZ!Box"), type=MessageBox.TYPE_INFO)
1830                 else:
1831                         self.session.open(MessageBox, _("Plugin not active"), type=MessageBox.TYPE_INFO)
1832
1833 standbyMode = False
1834
1835 class FritzCallList:
1836         def __init__(self):
1837                 self.callList = [ ]
1838         
1839         def add(self, event, date, number, caller, phone):
1840                 debug("[FritzCallList] add: %s %s" % (number, caller))
1841                 if len(self.callList) > 10:
1842                         if self.callList[0] != "Start":
1843                                 self.callList[0] = "Start"
1844                         del self.callList[1]
1845
1846                 self.callList.append((event, number, date, caller, phone))
1847         
1848         def display(self):
1849                 debug("[FritzCallList] display")
1850                 global standbyMode
1851                 standbyMode = False
1852                 # Standby.inStandby.onClose.remove(self.display) object does not exist anymore...
1853                 # build screen from call list
1854                 text = "\n"
1855
1856                 if not self.callList:
1857                         text = _("no calls") 
1858                 else:
1859                         if self.callList[0] == "Start":
1860                                 text = text + _("Last 10 calls:\n")
1861                                 del self.callList[0]
1862         
1863                         for call in self.callList:
1864                                 (event, number, date, caller, phone) = call
1865                                 if event == "RING":
1866                                         direction = "->"
1867                                 else:
1868                                         direction = "<-"
1869         
1870                                 # shorten the date info
1871                                 date = date[:6] + date[9:14]
1872         
1873                                 # our phone could be of the form "0123456789 (home)", then we only take "home"
1874                                 oBrack = phone.find('(')
1875                                 cBrack = phone.find(')')
1876                                 if oBrack != -1 and cBrack != -1:
1877                                         phone = phone[oBrack+1:cBrack]
1878         
1879                                 # should not happen, for safety reasons
1880                                 if not caller:
1881                                         caller = _("UNKNOWN")
1882                                 
1883                                 #  if we have an unknown number, show the number
1884                                 if caller == _("UNKNOWN") and number != "":
1885                                         caller = number
1886                                 else:
1887                                         # strip off the address part of the remote caller/callee, if there is any
1888                                         nl = caller.find('\n')
1889                                         if nl != -1:
1890                                                 caller = caller[:nl]
1891                                         elif caller[0] == '[' and caller[-1] == ']':
1892                                                 # that means, we've got an unknown number with a city added from avon.dat 
1893                                                 if (len(number) + 1 + len(caller) + len(phone)) <= 40:
1894                                                         caller = number + ' ' + caller
1895                                                 else:
1896                                                         caller = number
1897         
1898                                 while (len(caller) + len(phone)) > 40:
1899                                         if len(caller) > len(phone):
1900                                                 caller = caller[: - 1]
1901                                         else:
1902                                                 phone = phone[: - 1]
1903         
1904                                 text = text + "%s %s %s %s\n" % (date, caller, direction, phone)
1905                                 debug("[FritzCallList] display: '%s %s %s %s'" % (date, caller, direction, phone))
1906
1907                 # display screen
1908                 Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_INFO)
1909                 # TODO please HELP: from where can I get a session?
1910                 # my_global_session.open(FritzDisplayCalls, text)
1911                 self.callList = [ ]
1912
1913 callList = FritzCallList()
1914
1915 def findFace(number, name):
1916         debug("[FritzCall] findFace number/name: %s/%s" % (number, name))
1917         if name:
1918                 sep = name.find(',')
1919                 if sep != -1:
1920                         name = name[:sep]
1921                 sep = name.find('\n')
1922                 if sep != -1:
1923                         name = name[:sep]
1924         else:
1925                 name = _("UNKNOWN")
1926
1927         facesDir = os.path.join(config.plugins.FritzCall.phonebookLocation.value, "FritzCallFaces")
1928         numberFile = os.path.join(facesDir, number)
1929         nameFile = os.path.join(facesDir, name)
1930         facesFile = ""
1931         if number and os.path.exists(numberFile):
1932                 facesFile = numberFile
1933         elif number and os.path.exists(numberFile + ".png"):
1934                 facesFile = numberFile + ".png"
1935         elif number and os.path.exists(numberFile + ".PNG"):
1936                 facesFile = numberFile + ".PNG"
1937         elif name and os.path.exists(nameFile + ".png"):
1938                 facesFile = nameFile + ".png"
1939         elif name and os.path.exists(nameFile + ".PNG"):
1940                 facesFile = nameFile + ".PNG"
1941         else:
1942                 sep = name.find(' (')
1943                 if sep != -1:
1944                         name = name[:sep]
1945                 nameFile = os.path.join(facesDir, name)
1946                 facesFile = ""
1947                 if name and os.path.exists(nameFile + ".png"):
1948                         facesFile = nameFile + ".png"
1949                 elif name and os.path.exists(nameFile + ".PNG"):
1950                         facesFile = nameFile + ".PNG"
1951                 else:
1952                         facesFile = resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/icons/input_info.png")
1953
1954         debug("[FritzCall] findFace result: %s" % (facesFile))
1955         return facesFile
1956
1957 class MessageBoxPixmap(Screen):
1958         def __init__(self, session, text, number = "", name = "", timeout = -1):
1959                 self.skin = """
1960         <screen name="MessageBoxPixmap" position="center,center" size="600,10" title="New Call">
1961                 <widget name="text" position="115,8" size="520,0" font="Regular;%d" />
1962                 <widget name="InfoPixmap" pixmap="%s" position="5,5" size="100,100" alphatest="on" />
1963         </screen>
1964                 """ % (
1965                         # scaleH(350, 60), scaleV(175, 245),
1966                         scaleV(24, 22), resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/icons/input_info.png")
1967                         )
1968                 debug("[FritzCall] MessageBoxPixmap number: %s" % number)
1969                 Screen.__init__(self, session)
1970                 # MessageBox.__init__(self, session, text, type=MessageBox.TYPE_INFO, timeout=timeout)
1971                 self["text"] = Label(text)
1972                 self["InfoPixmap"] = Pixmap()
1973                 self._session = session
1974                 self._number = number
1975                 self._name = name
1976                 self._timerRunning = False
1977                 self._timer = None
1978                 self._timeout = timeout
1979                 self._origTitle = None
1980                 self._initTimeout()
1981                 self.onLayoutFinish.append(self._finishLayout)
1982                 self["actions"] = ActionMap(["OkCancelActions"],
1983                 {
1984                         "cancel": self._exit,
1985                         "ok": self._exit, }, - 2)
1986
1987         def _finishLayout(self):
1988                 # pylint: disable=W0142
1989                 debug("[FritzCall] MessageBoxPixmap/setInfoPixmap number: %s/%s" % (self._number, self._name))
1990
1991                 self.setTitle(_("New call"))
1992
1993                 faceFile = findFace(self._number, self._name)
1994                 picPixmap = LoadPixmap(faceFile)
1995                 if not picPixmap:       # that means most probably, that the picture is not 8 bit...
1996                         Notifications.AddNotification(MessageBox, _("Found picture\n\n%s\n\nBut did not load. Probably not PNG, 8-bit") %faceFile, type = MessageBox.TYPE_ERROR)
1997                         picPixmap = LoadPixmap(resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/icons/input_error.png"))
1998                 picSize = picPixmap.size()
1999                 self["InfoPixmap"].instance.setPixmap(picPixmap)
2000
2001                 # recalculate window size
2002                 textSize = self["text"].getSize()
2003                 textSize = (textSize[0]+20, textSize[1]+20) # don't know, why, but size is too small
2004                 textSize = eSize(*textSize)
2005                 width = max(scaleH(600, 280), picSize.width() + textSize.width() + 30)
2006                 height = max(scaleV(300, 250), picSize.height()+10, textSize.height()+10)
2007                 wSize = (width, height)
2008                 wSize = eSize(*wSize)
2009
2010                 # center the smaller vertically
2011                 hGap = (width-picSize.width()-textSize.width())/3
2012                 picPos = (hGap, (height-picSize.height())/2+1)
2013                 textPos = (hGap+picSize.width()+hGap, (height-textSize.height())/2+1)
2014
2015                 # resize screen
2016                 self.instance.resize(wSize)
2017                 # resize text
2018                 self["text"].instance.resize(textSize)
2019                 # resize pixmap
2020                 self["InfoPixmap"].instance.resize(picSize)
2021                 # move text
2022                 self["text"].instance.move(ePoint(*textPos))
2023                 # move pixmap
2024                 self["InfoPixmap"].instance.move(ePoint(*picPos))
2025                 # center window
2026                 self.instance.move(ePoint((DESKTOP_WIDTH-wSize.width())/2, (DESKTOP_HEIGHT-wSize.height())/2))
2027
2028         def _initTimeout(self):
2029                 if self._timeout > 0:
2030                         self._timer = eTimer()
2031                         self._timer.callback.append(self._timerTick)
2032                         self.onExecBegin.append(self._startTimer)
2033                         self._origTitle = None
2034                         if self.execing:
2035                                 self._timerTick()
2036                         else:
2037                                 self.onShown.append(self.__onShown)
2038                         self._timerRunning = True
2039                 else:
2040                         self._timerRunning = False
2041
2042         def __onShown(self):
2043                 self.onShown.remove(self.__onShown)
2044                 self._timerTick()
2045
2046         def _startTimer(self):
2047                 self._timer.start(1000)
2048
2049 #===============================================================================
2050 #       def stopTimer(self):
2051 #               if self._timerRunning:
2052 #                       del self._timer
2053 #                       self.setTitle(self._origTitle)
2054 #                       self._timerRunning = False
2055 #===============================================================================
2056
2057         def _timerTick(self):
2058                 if self.execing:
2059                         self._timeout -= 1
2060                         if self._origTitle is None:
2061                                 self._origTitle = self.instance.getTitle()
2062                         self.setTitle(self._origTitle + " (" + str(self._timeout) + ")")
2063                         if self._timeout == 0:
2064                                 self._timer.stop()
2065                                 self._timerRunning = False
2066                                 self._exit()
2067
2068         def _exit(self):
2069                 self.close()
2070
2071 def runUserActionScript(event, date, number, caller, phone):
2072         # user exit
2073         # call FritzCallserAction.sh in the same dir as Phonebook.txt with the following parameters:
2074         # event: "RING" (incomning) or "CALL" (outgoing)
2075         # date of event, format: "dd.mm.yy hh.mm.ss"
2076         # telephone number which is calling/is called
2077         # caller's name and address, format Name\n Street\n ZIP City
2078         # line/number which is called/which is used for calling
2079         userActionScript = os.path.join(config.plugins.FritzCall.phonebookLocation.value, "FritzCallUserAction.sh")
2080         if os.path.exists(userActionScript) and os.access(userActionScript, os.X_OK):
2081                 cmd = userActionScript + ' "' + event + '" "' + date + '" "' + number + '" "' + caller + '" "' + phone + '"'
2082                 debug("[FritzCall] runUserActionScript: calling: %s" % cmd)
2083                 eConsoleAppContainer().execute(cmd)
2084
2085 userActionList = [runUserActionScript]
2086 def registerUserAction(fun):
2087         #===========================================================================
2088         # other plugins can register a function, which is then called for each displayed call
2089         # it must take the arguments event,date,number,caller,phone
2090         #
2091         #example:
2092         # def FritzCallEvent(event,date,number,caller,phone):
2093         # ......
2094         # 
2095         # try:
2096         #       from Plugins.Extensions.FritzCall.plugin import registerUserAction as FritzCallRegisterUserAction
2097         #       FritzCallRegisterUserAction(FritzCallEvent)
2098         # except:
2099         #       print "import of FritzCall failed"
2100         #===========================================================================
2101         debug("[FritzCall] registerUserAction: register: %s" % fun.__name__)
2102         userActionList.append(fun)
2103                         
2104 mutedOnConnID = None
2105 def notifyCall(event, date, number, caller, phone, connID):
2106         if Standby.inStandby is None or config.plugins.FritzCall.afterStandby.value == "each":
2107                 if event == "RING":
2108                         global mutedOnConnID
2109                         if config.plugins.FritzCall.muteOnCall.value and not mutedOnConnID:
2110                                 debug("[FritzCall] mute on connID: %s" % connID)
2111                                 mutedOnConnID = connID
2112                                 # eDVBVolumecontrol.getInstance().volumeMute() # with this, we get no mute icon...
2113                                 if not eDVBVolumecontrol.getInstance().isMuted():
2114                                         globalActionMap.actions["volumeMute"]()
2115                         text = _("Incoming Call on %(date)s at %(time)s from\n---------------------------------------------\n%(number)s\n%(caller)s\n---------------------------------------------\nto: %(phone)s") % { 'date':date[:8], 'time':date[9:], 'number':number, 'caller':caller, 'phone':phone }
2116                 else:
2117                         text = _("Outgoing Call on %(date)s at %(time)s to\n---------------------------------------------\n%(number)s\n%(caller)s\n---------------------------------------------\nfrom: %(phone)s") % { 'date':date[:8], 'time':date[9:], 'number':number, 'caller':caller, 'phone':phone }
2118                 debug("[FritzCall] notifyCall:\n%s" % text)
2119                 # Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_INFO, timeout=config.plugins.FritzCall.timeout.value)
2120                 Notifications.AddNotification(MessageBoxPixmap, text, number=number, name=caller, timeout=config.plugins.FritzCall.timeout.value)
2121         elif config.plugins.FritzCall.afterStandby.value == "inList":
2122                 #
2123                 # if not yet done, register function to show call list
2124                 global standbyMode
2125                 if not standbyMode :
2126                         standbyMode = True
2127                         Standby.inStandby.onHide.append(callList.display) #@UndefinedVariable
2128                 # add text/timeout to call list
2129                 callList.add(event, date, number, caller, phone)
2130                 debug("[FritzCall] notifyCall: added to callList")
2131         else: # this is the "None" case
2132                 debug("[FritzCall] notifyCall: standby and no show")
2133
2134         for fun in userActionList:
2135                 debug("[FritzCall] notifyCall: call user action: %s" % fun.__name__)
2136                 fun(event, date, number, caller, phone)
2137
2138
2139 #===============================================================================
2140 #               We need a separate class for each invocation of reverseLookup to retain
2141 #               the necessary data for the notification
2142 #===============================================================================
2143
2144 countries = { }
2145 reverselookupMtime = 0
2146
2147 class FritzReverseLookupAndNotifier:
2148         def __init__(self, event, number, caller, phone, date, connID):
2149                 '''
2150                 
2151                 Initiate a reverse lookup for the given number in the configured country
2152                 
2153                 @param event: CALL or RING
2154                 @param number: number to be looked up
2155                 @param caller: caller including name and address
2156                 @param phone: Number (and name) of or own phone
2157                 @param date: date of call
2158                 '''
2159                 debug("[FritzReverseLookupAndNotifier] reverse Lookup for %s!" % number)
2160                 self.event = event
2161                 self.number = number
2162                 self.caller = caller
2163                 self.phone = phone
2164                 self.date = date
2165                 self.connID = connID
2166
2167                 if number[0] != "0":
2168                         self.notifyAndReset(number, caller)
2169                         return
2170
2171                 ReverseLookupAndNotifier(number, self.notifyAndReset, "UTF-8", config.plugins.FritzCall.country.value)
2172
2173         def notifyAndReset(self, number, caller):
2174                 '''
2175                 
2176                 this gets called with the result of the reverse lookup
2177                 
2178                 @param number: number
2179                 @param caller: name and address of remote. it comes in with name, address and city separated by commas
2180                 '''
2181                 debug("[FritzReverseLookupAndNotifier] got: " + caller)
2182                 self.number = number
2183 #===============================================================================
2184 #               if not caller and os.path.exists(config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.csv"):
2185 #                       caller = FritzOutlookCSV.findNumber(number, config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.csv") #@UndefinedVariable
2186 #                       debug("[FritzReverseLookupAndNotifier] got from Outlook csv: " + caller)
2187 #===============================================================================
2188 #===============================================================================
2189 #               if not caller and os.path.exists(config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.ldif"):
2190 #                       caller = FritzLDIF.findNumber(number, open(config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.ldif"))
2191 #                       debug("[FritzReverseLookupAndNotifier] got from ldif: " + caller)
2192 #===============================================================================
2193
2194                 name = handleReverseLookupResult(caller)
2195                 if name:
2196                         self.caller = name.replace(", ", "\n").replace('#','')
2197                         # TODO: I don't know, why we store only for incoming calls...
2198                         # if self.number != 0 and config.plugins.FritzCall.addcallers.value and self.event == "RING":
2199                         if self.number != 0 and config.plugins.FritzCall.addcallers.value:
2200                                 debug("[FritzReverseLookupAndNotifier] add to phonebook")
2201                                 phonebook.add(self.number, self.caller)
2202                 else:
2203                         name = resolveNumberWithAvon(self.number, config.plugins.FritzCall.country.value)
2204                         if not name:
2205                                 self.caller = _("UNKNOWN")
2206                         else:
2207                                 self.caller = name
2208                 notifyCall(self.event, self.date, self.number, self.caller, self.phone, self.connID)
2209                 # kill that object...
2210
2211 class FritzProtocol(LineReceiver):
2212         def __init__(self):
2213                 debug("[FritzProtocol] " + "$Revision: 734 $"[1:-1]     + "$Date: 2012-12-21 16:54:18 +0100 (Fr, 21 Dez 2012) $"[7:23] + " starting")
2214                 global mutedOnConnID
2215                 mutedOnConnID = None
2216                 self.number = '0'
2217                 self.caller = None
2218                 self.phone = None
2219                 self.date = '0'
2220                 self.event = None
2221                 self.connID = None
2222
2223         def resetValues(self):
2224                 debug("[FritzProtocol] resetValues")
2225                 self.number = '0'
2226                 self.caller = None
2227                 self.phone = None
2228                 self.date = '0'
2229                 self.event = None
2230                 self.connID = None
2231
2232         def notifyAndReset(self):
2233                 notifyCall(self.event, self.date, self.number, self.caller, self.phone, self.connID)
2234                 self.resetValues()
2235
2236         def lineReceived(self, line):
2237                 debug("[FritzProtocol] lineReceived: %s" % line)
2238 #15.07.06 00:38:54;CALL;1;4;<from/our msn>;<to/extern>;
2239 #15.07.06 00:38:58;DISCONNECT;1;0;
2240 #15.07.06 00:39:22;RING;0;<from/extern>;<to/our msn>;
2241 #15.07.06 00:39:27;DISCONNECT;0;0;
2242                 anEvent = line.split(';')
2243                 (self.date, self.event) = anEvent[0:2]
2244                 self.connID = anEvent[2]
2245
2246                 filtermsns = config.plugins.FritzCall.filtermsn.value.split(",")
2247                 for i in range(len(filtermsns)):
2248                         filtermsns[i] = filtermsns[i].strip()
2249
2250                 if config.plugins.FritzCall.ignoreUnknown.value:
2251                         if self.event == "RING":
2252                                 if not anEvent[3]:
2253                                         debug("[FritzProtocol] lineReceived: call from unknown phone; skipping")
2254                                         return
2255                                 elif not anEvent[5]:
2256                                         debug("[FritzProtocol] lineReceived: call to unknown phone; skipping")
2257                                         return
2258
2259                 # debug("[FritzProtocol] Volcontrol dir: %s" % dir(eDVBVolumecontrol.getInstance()))
2260                 # debug("[FritzCall] unmute on connID: %s?" %self.connID)
2261                 global mutedOnConnID
2262                 if self.event == "DISCONNECT" and config.plugins.FritzCall.muteOnCall.value and mutedOnConnID == self.connID:
2263                         debug("[FritzCall] unmute on connID: %s!" % self.connID)
2264                         mutedOnConnID = None
2265                         # eDVBVolumecontrol.getInstance().volumeUnMute()
2266                         if eDVBVolumecontrol.getInstance().isMuted():
2267                                 globalActionMap.actions["volumeMute"]()
2268                 # not supported so far, because, taht would mean muting on EVERY connect, regardless of RING or CALL or filter active
2269                 #=======================================================================
2270                 # elif self.event == "CONNECT" and config.plugins.FritzCall.muteOnCall.value == "connect":
2271                 #       debug("[FritzCall] mute on connID: %s" % self.connID)
2272                 #       mutedOnConnID = self.connID
2273                 #       # eDVBVolumecontrol.getInstance().volumeMute() # with this, we get no mute icon...
2274                 #       if not eDVBVolumecontrol.getInstance().isMuted():
2275                 #               globalActionMap.actions["volumeMute"]()
2276                 #=======================================================================
2277                 elif self.event == "RING" or (self.event == "CALL" and config.plugins.FritzCall.showOutgoing.value):
2278                         phone = anEvent[4]
2279                         if fritzbox and fritzbox.blacklist:
2280                                 if self.event == "RING":
2281                                         number = anEvent[3] 
2282                                         if number in fritzbox.blacklist[0]:
2283                                                 debug("[FritzProtocol] lineReceived phone: '''%s''' blacklisted number: '''%s'''" % (phone, number))
2284                                                 return 
2285                                 else:
2286                                         number = anEvent[5]
2287                                         if number in fritzbox.blacklist[1]:
2288                                                 debug("[FritzProtocol] lineReceived phone: '''%s''' blacklisted number: '''%s'''" % (phone, number))
2289                                                 return 
2290
2291                         debug("[FritzProtocol] lineReceived phone: '''%s''' number: '''%s'''" % (phone, number))
2292
2293                         if not (config.plugins.FritzCall.filter.value and phone not in filtermsns):
2294                                 debug("[FritzProtocol] lineReceived no filter hit")
2295                                 if phone:
2296                                         phonename = phonebook.search(phone)                # do we have a name for the number of our side?
2297                                         if phonename:
2298                                                 self.phone = "%s (%s)" % (phone, phonename)
2299                                         else:
2300                                                 self.phone = phone
2301                                 else:
2302                                         self.phone = _("UNKNOWN")
2303
2304                                 if not number:
2305                                         debug("[FritzProtocol] lineReceived: no number")
2306                                         self.number = _("number suppressed")
2307                                         self.caller = _("UNKNOWN")
2308                                 else:
2309                                         if config.plugins.FritzCall.internal.value and len(number) > 3 and number[0] == "0":
2310                                                 debug("[FritzProtocol] lineReceived: strip leading 0")
2311                                                 self.number = number[1:]
2312                                         else:
2313                                                 self.number = number
2314                                                 if self.event == "CALL" and self.number[0] != '0':                                        # should only happen for outgoing
2315                                                         debug("[FritzProtocol] lineReceived: add local prefix")
2316                                                         self.number = config.plugins.FritzCall.prefix.value + self.number
2317         
2318                                         # strip CbC prefixes
2319                                         if self.event == "CALL":
2320                                                 number = stripCbCPrefix(self.number, config.plugins.FritzCall.country.value)
2321         
2322                                         debug("[FritzProtocol] lineReceived phonebook.search: %s" % self.number)
2323                                         self.caller = phonebook.search(self.number)
2324                                         debug("[FritzProtocol] lineReceived phonebook.search reault: %s" % self.caller)
2325                                         if not self.caller:
2326                                                 if config.plugins.FritzCall.lookup.value:
2327                                                         FritzReverseLookupAndNotifier(self.event, self.number, self.caller, self.phone, self.date, self.connID)
2328                                                         return                                                  # reverselookup is supposed to handle the message itself
2329                                                 else:
2330                                                         self.caller = _("UNKNOWN")
2331
2332                                 self.notifyAndReset()
2333
2334 class FritzClientFactory(ReconnectingClientFactory):
2335         initialDelay = 20
2336         maxDelay = 30
2337
2338         def __init__(self):
2339                 self.hangup_ok = False
2340         def startedConnecting(self, connector): #@UnusedVariable # pylint: disable=W0613
2341                 if not config.plugins.FritzCall.fwVersion.value:
2342                         Notifications.AddNotification(MessageBox, _("FRITZ!Box firmware version not configured! Please set it in the configuration."), type=MessageBox.TYPE_INFO, timeout=0)
2343                         return
2344                 if config.plugins.FritzCall.connectionVerbose.value:
2345                         debug("[FRITZ!FritzClientFactory] - startedConnecting")
2346                         Notifications.AddNotification(MessageBox, _("Connecting to FRITZ!Box..."), type=MessageBox.TYPE_INFO, timeout=2)
2347
2348         def buildProtocol(self, addr): #@UnusedVariable # pylint: disable=W0613
2349                 global fritzbox, phonebook
2350                 if config.plugins.FritzCall.connectionVerbose.value:
2351                         debug("[FRITZ!FritzClientFactory] - buildProtocol")
2352                         Notifications.AddNotification(MessageBox, _("Connected to FRITZ!Box!"), type=MessageBox.TYPE_INFO, timeout=4)
2353                 self.resetDelay()
2354                 initDebug()
2355                 initCbC()
2356                 initAvon()
2357                 # TODO: swithc between FBF FW versions...
2358                 if config.plugins.FritzCall.fwVersion.value == "old":
2359                         fritzbox = FritzCallFBF.FritzCallFBF()
2360                 elif config.plugins.FritzCall.fwVersion.value == "05.27":
2361                         fritzbox = FritzCallFBF.FritzCallFBF_05_27()
2362                 elif config.plugins.FritzCall.fwVersion.value == "05.50":
2363                         fritzbox = FritzCallFBF.FritzCallFBF_05_50()
2364                 else:
2365                         Notifications.AddNotification(MessageBox, _("FRITZ!Box firmware version not configured! Please set it in the configuration."), type=MessageBox.TYPE_INFO, timeout=0)
2366                 phonebook = FritzCallPhonebook()
2367                 return FritzProtocol()
2368
2369         def clientConnectionLost(self, connector, reason):
2370                 global fritzbox
2371                 if not self.hangup_ok and config.plugins.FritzCall.connectionVerbose.value:
2372                         debug("[FRITZ!FritzClientFactory] - clientConnectionLost")
2373                         Notifications.AddNotification(MessageBox, _("Connection to FRITZ!Box! lost\n (%s)\nretrying...") % reason.getErrorMessage(), type=MessageBox.TYPE_INFO, timeout=config.plugins.FritzCall.timeout.value)
2374                 ReconnectingClientFactory.clientConnectionLost(self, connector, reason)
2375                 # config.plugins.FritzCall.enable.value = False
2376                 fritzbox = None
2377
2378         def clientConnectionFailed(self, connector, reason):
2379                 global fritzbox
2380                 if config.plugins.FritzCall.connectionVerbose.value:
2381                         Notifications.AddNotification(MessageBox, _("Connecting to FRITZ!Box failed\n (%s)\nretrying...") % reason.getErrorMessage(), type=MessageBox.TYPE_INFO, timeout=config.plugins.FritzCall.timeout.value)
2382                 ReconnectingClientFactory.clientConnectionFailed(self, connector, reason)
2383                 # config.plugins.FritzCall.enable.value = False
2384                 fritzbox = None
2385                 
2386 class FritzCall:
2387         def __init__(self):
2388                 self.dialog = None
2389                 self.desc = None
2390                 if config.plugins.FritzCall.enable.value:
2391                         self.connect()
2392
2393         def connect(self):
2394                 self.abort()
2395                 if config.plugins.FritzCall.enable.value:
2396                         fact = FritzClientFactory()
2397                         self.desc = (fact, reactor.connectTCP(config.plugins.FritzCall.hostname.value, 1012, fact)) #@UndefinedVariable # pylint: disable=E1101
2398
2399         def shutdown(self):
2400                 self.abort()
2401
2402         def abort(self):
2403                 if self.desc is not None:
2404                         self.desc[0].hangup_ok = True
2405                         self.desc[0].stopTrying()
2406                         self.desc[1].disconnect()
2407                         self.desc = None
2408
2409 def displayCalls(session, servicelist=None): #@UnusedVariable # pylint: disable=W0613
2410         if config.plugins.FritzCall.enable.value:
2411                 if fritzbox:
2412                         session.open(FritzDisplayCalls)
2413                 else:
2414                         Notifications.AddNotification(MessageBox, _("Cannot get calls from FRITZ!Box"), type=MessageBox.TYPE_INFO)
2415         else:
2416                 Notifications.AddNotification(MessageBox, _("Plugin not active"), type=MessageBox.TYPE_INFO)
2417
2418 def displayPhonebook(session, servicelist=None): #@UnusedVariable # pylint: disable=W0613
2419         if phonebook:
2420                 if config.plugins.FritzCall.enable.value:
2421                         session.open(phonebook.FritzDisplayPhonebook)
2422                 else:
2423                         Notifications.AddNotification(MessageBox, _("Plugin not active"), type=MessageBox.TYPE_INFO)
2424         else:
2425                 Notifications.AddNotification(MessageBox, _("No phonebook"), type=MessageBox.TYPE_INFO)
2426
2427 def displayFBFStatus(session, servicelist=None): #@UnusedVariable # pylint: disable=W0613
2428         if config.plugins.FritzCall.enable.value:
2429                 if fritzbox and fritzbox.info:
2430                         session.open(FritzMenu)
2431                 else:
2432                         Notifications.AddNotification(MessageBox, _("Cannot get infos from FRITZ!Box"), type=MessageBox.TYPE_INFO)
2433         else:
2434                 Notifications.AddNotification(MessageBox, _("Plugin not active"), type=MessageBox.TYPE_INFO)
2435
2436 def main(session, servicelist=None):
2437         session.open(FritzCallSetup)
2438
2439 fritz_call = None
2440
2441 def autostart(reason, **kwargs):
2442         global fritz_call
2443
2444         # ouch, this is a hack
2445         if kwargs.has_key("session"):
2446                 global my_global_session
2447                 my_global_session = kwargs["session"]
2448                 return
2449
2450         debug("[FRITZ!Call] - Autostart")
2451         if reason == 0:
2452                 fritz_call = FritzCall()
2453         elif reason == 1:
2454                 fritz_call.shutdown()
2455                 fritz_call = None
2456
2457 def Plugins(**kwargs): #@UnusedVariable # pylint: disable=W0613,C0103
2458         what = _("Display FRITZ!box-Fon calls on screen")
2459         what_calls = _("Phone calls")
2460         what_phonebook = _("Phonebook")
2461         what_status = _("FRITZ!Box Fon Status")
2462         return [ PluginDescriptor(name="FritzCall", description=what, where=PluginDescriptor.WHERE_PLUGINMENU, icon="plugin.png", fnc=main),
2463                 PluginDescriptor(name=what_calls, description=what_calls, where=PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=displayCalls),
2464                 PluginDescriptor(name=what_phonebook, description=what_phonebook, where=PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=displayPhonebook),
2465                 PluginDescriptor(name=what_status, description=what_status, where=PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=displayFBFStatus),
2466                 PluginDescriptor(where=[PluginDescriptor.WHERE_SESSIONSTART, PluginDescriptor.WHERE_AUTOSTART], fnc=autostart) ]