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