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