InfoBarTunerState: update for italian translation.
[enigma2-plugins.git] / fritzcall / src / FritzCallFBF.py
1 # -*- coding: utf-8 -*-
2 '''
3 Created on 30.09.2012
4 $Author: michael $
5 $Revision: 848 $
6 $Date: 2014-02-16 13:39:32 +0100 (Sun, 16 Feb 2014) $
7 $Id: FritzCallFBF.py 848 2014-02-16 12:39:32Z michael $
8 '''
9
10 # C0111 (Missing docstring)
11 # C0103 (Invalid name)
12 # C0301 (line too long)
13 # W0603 (global statement)
14 # W0141 (map, filter, etc.)
15 # W0110 lambda with map,filter
16 # W0403 Relative import
17 # W1401 Anomalous backslash in string
18 # pylint: disable=C0111,C0103,C0301,W0603,W0141,W0403,W1401
19
20 from . import _, __, debug #@UnresolvedImport # pylint: disable=W0611,F0401
21 from plugin import config, fritzbox, stripCbCPrefix, resolveNumberWithAvon, FBF_IN_CALLS, FBF_OUT_CALLS, FBF_MISSED_CALLS
22 from Tools import Notifications
23 from Screens.MessageBox import MessageBox
24 from twisted.web.client import getPage #@UnresolvedImport
25 from nrzuname import html2unicode
26
27 from urllib import urlencode 
28 import re, time, hashlib
29
30 FBF_boxInfo = 0
31 FBF_upTime = 1
32 FBF_ipAddress = 2
33 FBF_wlanState = 3
34 FBF_dslState = 4
35 FBF_tamActive = 5
36 FBF_dectActive = 6
37 FBF_faxActive = 7
38 FBF_rufumlActive = 8
39
40 def resolveNumber(number, default=None, phonebook=None):
41         if number.isdigit():
42                 if config.plugins.FritzCall.internal.value and len(number) > 3 and number[0] == "0":
43                         number = number[1:]
44                 # strip CbC prefix
45                 number = stripCbCPrefix(number, config.plugins.FritzCall.country.value)
46                 if config.plugins.FritzCall.prefix.value and number and number[0] != '0':               # should only happen for outgoing
47                         number = config.plugins.FritzCall.prefix.value + number
48                 name = None
49                 if phonebook:
50                         name = phonebook.search(number, default)
51                 if name:
52                         #===========================================================
53                         # found = re.match('(.*?)\n.*', name)
54                         # if found:
55                         #       name = found.group(1)
56                         #===========================================================
57                         end = name.find('\n')
58                         if end != -1:
59                                 name = name[:end]
60                         number = name
61                 elif default:
62                         number = default
63                 else:
64                         name = resolveNumberWithAvon(number, config.plugins.FritzCall.country.value)
65                         if name:
66                                 number = number + ' ' + name
67         elif number == "":
68                 number = _("UNKNOWN")
69         # if len(number) > 20: number = number[:20]
70         return number
71
72 def cleanNumber(number):
73         number = number.replace('(','').replace(')','').replace(' ','').replace('-','')
74         if number[0] == '+':
75                 number = '00' + number[1:]
76         if config.plugins.FritzCall.country.value and number.startswith(config.plugins.FritzCall.country.value):
77                 number = '0' + number[len(config.plugins.FritzCall.country.value):]
78         return number
79                 
80 class FritzCallFBF:
81         def __init__(self):
82                 debug("[FritzCallFBF] __init__")
83                 self._callScreen = None
84                 self._md5LoginTimestamp = None
85                 self._md5Sid = '0000000000000000'
86                 self._callTimestamp = 0
87                 self._callList = []
88                 self._callType = config.plugins.FritzCall.fbfCalls.value
89                 self.info = None # (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, guestAccess)
90                 self.getInfo(None)
91                 self.blacklist = ([], [])
92                 self.readBlacklist()
93                 self.phonebook = None
94                 self._phoneBookID = 0
95                 self.phonebooksFBF = []
96
97         def _notify(self, text):
98                 debug("[FritzCallFBF] notify: " + text)
99                 self._md5LoginTimestamp = None
100                 if self._callScreen:
101                         debug("[FritzCallFBF] notify: try to close callScreen")
102                         self._callScreen.close()
103                         self._callScreen = None
104                 Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_ERROR, timeout=config.plugins.FritzCall.timeout.value)
105                         
106         def _login(self, callback=None):
107                 debug("[FritzCallFBF] _login")
108                 if self._callScreen:
109                         self._callScreen.updateStatus(_("login"))
110                 if self._md5LoginTimestamp and ((time.time() - self._md5LoginTimestamp) < float(9.5*60)) and self._md5Sid != '0000000000000000': # new login after 9.5 minutes inactivity 
111                         debug("[FritzCallFBF] _login: renew timestamp: " + time.ctime(self._md5LoginTimestamp) + " time: " + time.ctime())
112                         self._md5LoginTimestamp = time.time()
113                         callback(None)
114                 else:
115                         debug("[FritzCallFBF] _login: not logged in or outdated login")
116                         # http://fritz.box/cgi-bin/webcm?getpage=../html/login_sid.xml
117                         parms = urlencode({'getpage':'../html/login_sid.xml'})
118                         url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
119                         debug("[FritzCallFBF] _login: '" + url + "' parms: '" + parms + "'")
120                         getPage(url,
121                                 method="POST",
122                                 headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
123                                                 }, postdata=parms).addCallback(lambda x: self._md5Login(callback,x)).addErrback(lambda x:self._oldLogin(callback,x))
124
125         def _oldLogin(self, callback, error): 
126                 debug("[FritzCallFBF] _oldLogin: " + repr(error))
127                 self._md5LoginTimestamp = None
128                 if config.plugins.FritzCall.password.value != "":
129                         parms = "login:command/password=%s" % (config.plugins.FritzCall.password.value)
130                         url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
131                         debug("[FritzCallFBF] _oldLogin: '" + url + "' parms: '" + parms + "'")
132                         getPage(url,
133                                 method="POST",
134                                 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
135                                 headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
136                                                 }, postdata=parms).addCallback(self._gotPageLogin).addCallback(callback).addErrback(self._errorLogin)
137                 elif callback:
138                         debug("[FritzCallFBF] _oldLogin: no password, calling " + repr(callback))
139                         callback(None)
140
141         def _md5Login(self, callback, sidXml):
142                 def buildResponse(challenge, text):
143                         debug("[FritzCallFBF] _md5Login7buildResponse: challenge: " + challenge + ' text: ' + __(text))
144                         text = (challenge + '-' + text).decode('utf-8','ignore').encode('utf-16-le')
145                         for i in range(len(text)):
146                                 if ord(text[i]) > 255:
147                                         text[i] = '.'
148                         md5 = hashlib.md5()
149                         md5.update(text) # pylint: disable=E1101
150                         debug("[FritzCallFBF] md5Login/buildResponse: " + md5.hexdigest())
151                         return challenge + '-' + md5.hexdigest()
152
153                 debug("[FritzCallFBF] _md5Login")
154                 found = re.match('.*<SID>([^<]*)</SID>', sidXml, re.S)
155                 if found:
156                         self._md5Sid = found.group(1)
157                         debug("[FritzCallFBF] _md5Login: SID "+ self._md5Sid)
158                 else:
159                         debug("[FritzCallFBF] _md5Login: no sid! That must be an old firmware.")
160                         self._oldLogin(callback, 'No error')
161                         return
162
163                 debug("[FritzCallFBF] _md5Login: renew timestamp: " + time.ctime(self._md5LoginTimestamp) + " time: " + time.ctime())
164                 self._md5LoginTimestamp = time.time()
165                 if sidXml.find('<iswriteaccess>0</iswriteaccess>') != -1:
166                         debug("[FritzCallFBF] _md5Login: logging in")
167                         found = re.match('.*<Challenge>([^<]*)</Challenge>', sidXml, re.S)
168                         if found:
169                                 challenge = found.group(1)
170                                 debug("[FritzCallFBF] _md5Login: challenge " + challenge)
171                         else:
172                                 challenge = None
173                                 debug("[FritzCallFBF] _md5Login: login necessary and no challenge! That is terribly wrong.")
174                         parms = urlencode({
175                                                         'getpage':'../html/de/menus/menu2.html', # 'var:pagename':'home', 'var:menu':'home', 
176                                                         'login:command/response': buildResponse(challenge, config.plugins.FritzCall.password.value),
177                                                         })
178                         url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
179                         debug("[FritzCallFBF] _md5Login: '" + url + "' parms: '" + parms + "'")
180                         getPage(url,
181                                 method="POST",
182                                 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
183                                 headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
184                                                 }, postdata=parms).addCallback(self._gotPageLogin).addCallback(callback).addErrback(self._errorLogin)
185                 elif callback: # we assume value 1 here, no login necessary
186                         debug("[FritzCallFBF] _md5Login: no login necessary")
187                         callback(None)
188
189         def _gotPageLogin(self, html):
190                 if self._callScreen:
191                         self._callScreen.updateStatus(_("login verification"))
192                 debug("[FritzCallFBF] _gotPageLogin: verify login")
193                 start = html.find('<p class="errorMessage">FEHLER:&nbsp;')
194                 if start != -1:
195                         start = start + len('<p class="errorMessage">FEHLER:&nbsp;')
196                         text = _("FRITZ!Box - Error logging in\n\n") + html[start : html.find('</p>', start)]
197                         self._notify(text)
198                 else:
199                         if self._callScreen:
200                                 self._callScreen.updateStatus(_("login ok"))
201
202                 found = re.match('.*<input type="hidden" name="sid" value="([^\"]*)"', html, re.S)
203                 if found:
204                         self._md5Sid = found.group(1)
205                         debug("[FritzCallFBF] _gotPageLogin: found sid: " + self._md5Sid)
206
207         def _errorLogin(self, error):
208                 global fritzbox
209                 debug("[FritzCallFBF] _errorLogin: %s" % (error))
210                 text = _("FRITZ!Box - Error logging in: %s\nDisabling plugin.") % error.getErrorMessage()
211                 # config.plugins.FritzCall.enable.value = False
212                 fritzbox = None
213                 self._notify(text)
214
215         def _logout(self):
216                 if self._md5LoginTimestamp:
217                         self._md5LoginTimestamp = None
218                         parms = urlencode({
219                                                         'getpage':'../html/de/menus/menu2.html', # 'var:pagename':'home', 'var:menu':'home', 
220                                                         'login:command/logout':'bye bye Fritz'
221                                                         })
222                         url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
223                         debug("[FritzCallFBF] logout: '" + url + "' parms: '" + parms + "'")
224                         getPage(url,
225                                 method="POST",
226                                 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
227                                 headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
228                                                 }, postdata=parms).addErrback(self._errorLogout)
229
230         def _errorLogout(self, error):
231                 debug("[FritzCallFBF] _errorLogout: %s" % (error))
232                 text = _("FRITZ!Box - Error logging out: %s") % error.getErrorMessage()
233                 self._notify(text)
234
235         def loadFritzBoxPhonebook(self, phonebook):
236                 debug("[FritzCallFBF] loadFritzBoxPhonebook")
237                 if config.plugins.FritzCall.fritzphonebook.value:
238                         self.phonebook = phonebook
239                         self._phoneBookID = '0'
240                         debug("[FritzCallFBF] loadFritzBoxPhonebook: logging in")
241                         self._login(self._loadFritzBoxPhonebook)
242
243         def _loadFritzBoxPhonebook(self, html):
244                 if html:
245                         #===================================================================
246                         # found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
247                         # if found:
248                         #       self._errorLoad('Login: ' + found.group(1))
249                         #       return
250                         #===================================================================
251                         start = html.find('<p class="errorMessage">FEHLER:&nbsp;')
252                         if start != -1:
253                                 start = start + len('<p class="errorMessage">FEHLER:&nbsp;')
254                                 self._notify('Login: ' + html[start, html.find('</p>', start)])
255                                 return
256                 parms = urlencode({
257                                                 'getpage':'../html/de/menus/menu2.html',
258                                                 'var:lang':'de',
259                                                 'var:pagename':'fonbuch',
260                                                 'var:menu':'fon',
261                                                 'sid':self._md5Sid,
262                                                 'telcfg:settings/Phonebook/Books/Select':self._phoneBookID, # this selects always the first phonbook first
263                                                 })
264                 url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
265                 debug("[FritzCallFBF] _loadFritzBoxPhonebook: '" + url + "' parms: '" + parms + "'")
266                 getPage(url,
267                         method="POST",
268                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
269                         headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
270                                         }, postdata=parms).addCallback(self._parseFritzBoxPhonebook).addErrback(self._errorLoad)
271
272         def _parseFritzBoxPhonebook(self, html):
273
274                 # debug("[FritzCallFBF] _parseFritzBoxPhonebook")
275
276                 # first, let us get the charset
277                 found = re.match('.*<meta http-equiv=content-type content="text/html; charset=([^"]*)">', html, re.S)
278                 if found:
279                         charset = found.group(1)
280                         debug("[FritzCallFBF] _parseFritzBoxPhonebook: found charset: " + charset)
281                         html = html2unicode(html.replace(chr(0xf6),'').decode(charset)).encode('utf-8')
282                 else: # this is kind of emergency conversion...
283                         try:
284                                 debug("[FritzCallFBF] _parseFritzBoxPhonebook: try charset utf-8")
285                                 charset = 'utf-8'
286                                 html = html2unicode(html.decode('utf-8')).encode('utf-8') # this looks silly, but has to be
287                         except UnicodeDecodeError:
288                                 debug("[FritzCallFBF] _parseFritzBoxPhonebook: try charset iso-8859-1")
289                                 charset = 'iso-8859-1'
290                                 html = html2unicode(html.decode('iso-8859-1')).encode('utf-8') # this looks silly, but has to be
291
292                 # if re.search('document.write\(TrFon1\(\)', html):
293                 if html.find('document.write(TrFon1()') != -1:
294                         #===============================================================================
295                         #                                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)
296                         #                                                       7170 (FW 29.04.70) 22.03.2009
297                         #                                                       7141 (FW 40.04.68) 22.03.2009
298                         #  We expect one line with
299                         #   TrFonName(Entry umber, Name, ???, Path to picture)
300                         #  followed by several lines with
301                         #       TrFonNr(Type,Number,Shortcut,Vanity), which all belong to the name in TrFonName.
302                         # 
303                         #  Photo could be fetched with http://192.168.0.1/lua/photo.lua?photo=<Path to picture[7:]&sid=????
304                         #===============================================================================
305                         debug("[FritzCallFBF] _parseFritzBoxPhonebook: discovered newer firmware")
306                         found = re.match('.*<input type="hidden" name="telcfg:settings/Phonebook/Books/Name\d+" value="(?:' + config.plugins.FritzCall.fritzphonebookName.value +')" id="uiPostPhonebookName\d+" disabled>\s*<input type="hidden" name="telcfg:settings/Phonebook/Books/Id\d+" value="(\d+)" id="uiPostPhonebookId\d+" disabled>', html, re.S)
307                         if found:
308                                 phoneBookID = found.group(1)
309                                 debug("[FritzCallFBF] _parseFritzBoxPhonebook: found dreambox phonebook with id: " + phoneBookID)
310                                 if self._phoneBookID != phoneBookID:
311                                         self._phoneBookID = phoneBookID
312                                         debug("[FritzCallFBF] _parseFritzBoxPhonebook: reload phonebook")
313                                         self._loadFritzBoxPhonebook(None) # reload with dreambox phonebook
314                                         return
315
316                         entrymask = re.compile('(TrFonName\("[^"]+", "[^"]+", "[^"]*"(?:, "[^"]*")?\);.*?)document.write\(TrFon1\(\)', re.S)
317                         entries = entrymask.finditer(html)
318                         for entry in entries:
319                                 # TrFonName (id, name, category)
320                                 # TODO: replace re.match?
321                                 found = re.match('TrFonName\("[^"]*", "([^"]+)", "[^"]*"(?:, "[^"]*")?\);', entry.group(1))
322                                 if found:
323                                         # debug("[FritzCallFBF] _parseFritzBoxPhonebook: name: %s" %found.group(1))
324                                         name = found.group(1).replace(',','').strip()
325                                 else:
326                                         debug("[FritzCallFBF] _parseFritzBoxPhonebook: could not find name")
327                                         continue
328                                 # TrFonNr (type, rufnr, code, vanity)
329                                 detailmask = re.compile('TrFonNr\("([^"]*)", "([^"]*)", "([^"]*)", "([^"]*)"\);', re.S)
330                                 details = detailmask.finditer(entry.group(1))
331                                 for found in details:
332                                         thisnumber = found.group(2).strip()
333                                         if not thisnumber:
334                                                 debug("[FritzCallFBF] Ignoring entry with empty number for '''%s'''" % (__(name)))
335                                                 continue
336                                         else:
337                                                 thisname = name
338                                                 callType = found.group(1)
339                                                 if config.plugins.FritzCall.showType.value:
340                                                         if callType == "mobile":
341                                                                 thisname = thisname + " (" + _("mobile") + ")"
342                                                         elif callType == "home":
343                                                                 thisname = thisname + " (" + _("home") + ")"
344                                                         elif callType == "work":
345                                                                 thisname = thisname + " (" + _("work") + ")"
346
347                                                 if config.plugins.FritzCall.showShortcut.value and found.group(3):
348                                                         thisname = thisname + ", " + _("Shortcut") + ": " + found.group(3)
349                                                 if config.plugins.FritzCall.showVanity.value and found.group(4):
350                                                         thisname = thisname + ", " + _("Vanity") + ": " + found.group(4)
351
352                                                 thisnumber = cleanNumber(thisnumber)
353                                                 # Beware: strings in phonebook.phonebook have to be in utf-8!
354                                                 if not self.phonebook.phonebook.has_key(thisnumber):
355                                                         # debug("[FritzCallFBF] Adding '''%s''' with '''%s'''" % (__(thisname.strip()), __(thisnumber, False)))
356                                                         self.phonebook.phonebook[thisnumber] = thisname
357                                                 else:
358                                                         pass
359                                                         # debug("[FritzCallFBF] Ignoring '''%s''' with '''%s'''" % (thisname.strip(), thisnumber))
360
361                 # elif re.search('document.write\(TrFon\(', html):
362                 elif html.find('document.write(TrFon(') != -1:
363                         #===============================================================================
364                         #                               Old Style: 7050 (FW 14.04.33)
365                         #       We expect one line with TrFon(No,Name,Number,Shortcut,Vanity)
366                         #   Encoding should be plain Ascii...
367                         #===============================================================================                                
368                         entrymask = re.compile('TrFon\("[^"]*", "([^"]*)", "([^"]*)", "([^"]*)", "([^"]*)"\)', re.S)
369                         entries = entrymask.finditer(html)
370                         for found in entries:
371                                 name = found.group(1).strip().replace(',','')
372                                 # debug("[FritzCallFBF] pos: %s name: %s" %(found.group(0),name))
373                                 thisnumber = found.group(2).strip()
374                                 if config.plugins.FritzCall.showShortcut.value and found.group(3):
375                                         name = name + ", " + _("Shortcut") + ": " + found.group(3)
376                                 if config.plugins.FritzCall.showVanity.value and found.group(4):
377                                         name = name + ", " + _("Vanity") + ": " + found.group(4)
378                                 if thisnumber:
379                                         # name = name.encode('utf-8')
380                                         # Beware: strings in phonebook.phonebook have to be in utf-8!
381                                         if not self.phonebook.phonebook.has_key(thisnumber):
382                                                 # debug("[FritzCallFBF] Adding '''%s''' with '''%s'''" % (name, __(thisnumber)))
383                                                 self.phonebook.phonebook[thisnumber] = name
384                                         else:
385                                                 debug("[FritzCallFBF] Ignoring '''%s''' with '''%s'''" % (__(name), __(thisnumber)))
386                                 else:
387                                         debug("[FritzCallFBF] ignoring empty number for %s" % name)
388                                 continue
389                 elif self._md5Sid == '0000000000000000': # retry, it could be a race condition
390                         debug("[FritzCallFBF] _parseFritzBoxPhonebook: retry loading phonebook")
391                         self.loadFritzBoxPhonebook(self.phonebook)
392                 else:
393                         debug("[FritzCallFBF] _parseFritzBoxPhonebook: could not read FBF phonebook; wrong version?")
394                         self._notify(_("Could not read FRITZ!Box phonebook; wrong version?"))
395
396         def _errorLoad(self, error):
397                 debug("[FritzCallFBF] _errorLoad: %s" % (error))
398                 text = _("FRITZ!Box - Could not load phonebook: %s") % error.getErrorMessage()
399                 self._notify(text)
400
401         def getCalls(self, callScreen, callback, callType):
402                 #
403                 # call sequence must be:
404                 # - login
405                 # - getPage -> _gotPageLogin
406                 # - loginCallback (_getCalls)
407                 # - getPage -> _getCalls1
408                 debug("[FritzCallFBF] getCalls")
409                 self._callScreen = callScreen
410                 self._callType = callType
411                 if (time.time() - self._callTimestamp) > 180: 
412                         debug("[FritzCallFBF] getCalls: outdated data, login and get new ones: " + time.ctime(self._callTimestamp) + " time: " + time.ctime())
413                         self._callTimestamp = time.time()
414                         self._login(lambda x:self._getCalls(callback, x))
415                 elif not self._callList:
416                         debug("[FritzCallFBF] getCalls: time is ok, but no callList")
417                         self._getCalls1(callback)
418                 else:
419                         debug("[FritzCallFBF] getCalls: time is ok, callList is ok")
420                         self._gotPageCalls(callback)
421
422         def _getCalls(self, callback, html):
423                 if html:
424                         #===================================================================
425                         # found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
426                         # if found:
427                         #       self._errorCalls('Login: ' + found.group(1))
428                         #       return
429                         #===================================================================
430                         start = html.find('<p class="errorMessage">FEHLER:&nbsp;')
431                         if start != -1:
432                                 start = start + len('<p class="errorMessage">FEHLER:&nbsp;')
433                                 self._notify('Login: ' + html[start, html.find('</p>', start)])
434                                 return
435                 #
436                 # we need this to fill Anrufliste.csv
437                 # http://repeater1/cgi-bin/webcm?getpage=../html/de/menus/menu2.html&var:lang=de&var:menu=fon&var:pagename=foncalls
438                 #
439                 debug("[FritzCallFBF] _getCalls")
440                 if html:
441                         #===================================================================
442                         # found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
443                         # if found:
444                         #       text = _("FRITZ!Box - Error logging in: %s") + found.group(1)
445                         #       self._notify(text)
446                         #       return
447                         #===================================================================
448                         start = html.find('<p class="errorMessage">FEHLER:&nbsp;')
449                         if start != -1:
450                                 start = start + len('<p class="errorMessage">FEHLER:&nbsp;')
451                                 self._notify(_("FRITZ!Box - Error logging in: %s") + html[start, html.find('</p>', start)])
452                                 return
453
454                 if self._callScreen:
455                         self._callScreen.updateStatus(_("preparing"))
456                 parms = urlencode({'getpage':'../html/de/menus/menu2.html', 'var:lang':'de', 'var:pagename':'foncalls', 'var:menu':'fon', 'sid':self._md5Sid})
457                 url = "http://%s/cgi-bin/webcm?%s" % (config.plugins.FritzCall.hostname.value, parms)
458                 getPage(url).addCallback(lambda x:self._getCalls1(callback)).addErrback(self._errorCalls) #@UnusedVariable # pylint: disable=W0613
459
460         def _getCalls1(self, callback):
461                 #
462                 # finally we should have successfully lgged in and filled the csv
463                 #
464                 debug("[FritzCallFBF] _getCalls1")
465                 if self._callScreen:
466                         self._callScreen.updateStatus(_("finishing"))
467                 parms = urlencode({'getpage':'../html/de/FRITZ!Box_Anrufliste.csv', 'sid':self._md5Sid})
468                 url = "http://%s/cgi-bin/webcm?%s" % (config.plugins.FritzCall.hostname.value, parms)
469                 getPage(url).addCallback(lambda x:self._gotPageCalls(callback, x)).addErrback(self._errorCalls)
470
471         def _gotPageCalls(self, callback, csvIn=""):
472
473                 if csvIn:
474                         debug("[FritzCallFBF] _gotPageCalls: got csv, setting callList")
475                         if self._callScreen:
476                                 self._callScreen.updateStatus(_("done"))
477                         if csvIn.find('Melden Sie sich mit dem Kennwort der FRITZ!Box an') != -1:
478                                 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.")
479                                 # self.session.open(MessageBox, text, MessageBox.TYPE_ERROR, timeout=config.plugins.FritzCall.timeout.value)
480                                 self._notify(text)
481                                 return
482
483                         csvIn = csvIn.decode('iso-8859-1', 'replace').encode('utf-8', 'replace')
484                         lines = csvIn.splitlines()
485                         self._callList = lines
486                 elif self._callList:
487                         debug("[FritzCallFBF] _gotPageCalls: got no csv, but have callList")
488                         if self._callScreen:
489                                 self._callScreen.updateStatus(_("done, using last list"))
490                         lines = self._callList
491                 else:
492                         debug("[FritzCallFBF] _gotPageCalls: Could not get call list; wrong version?")
493                         self._notify(_("Could not get call list; wrong version?"))
494                         return
495                         
496                 callListL = []
497                 if config.plugins.FritzCall.filter.value and config.plugins.FritzCall.filterCallList.value:
498                         filtermsns = map(lambda x: x.strip(), config.plugins.FritzCall.filtermsn.value.split(","))
499                         # TODO: scramble filtermsns
500                         debug("[FritzCallFBF] _gotPageCalls: filtermsns %s" % (repr(filtermsns)))
501
502                 # Typ;Datum;Name;Rufnummer;Nebenstelle;Eigene Rufnummer;Dauer
503                 # 0  ;1    ;2   ;3                ;4              ;5                       ;6
504                 lines = map(lambda line: line.split(';'), lines)
505                 lines = filter(lambda line: (len(line)==7 and (line[0]=="Typ" or self._callType == '.' or line[0] == self._callType)), lines)
506
507                 for line in lines:
508                         # debug("[FritzCallFBF] _gotPageCalls: line %s" % (line))
509                         direct = line[0]
510                         date = line[1]
511                         length = line[6]
512                         if config.plugins.FritzCall.phonebook.value and line[2]:
513                                 remote = resolveNumber(line[3], line[2] + " (FBF)", self.phonebook)
514                         else:
515                                 remote = resolveNumber(line[3], line[2], self.phonebook)
516                         here = line[5]
517                         start = here.find('Internet: ')
518                         if start != -1:
519                                 start += len('Internet: ')
520                                 here = here[start:]
521                         else:
522                                 here = line[5]
523                         if direct != "Typ" and config.plugins.FritzCall.filter.value and config.plugins.FritzCall.filterCallList.value:
524                                 # debug("[FritzCallFBF] _gotPageCalls: check %s" % (here))
525                                 if here not in filtermsns:
526                                         # debug("[FritzCallFBF] _gotPageCalls: skip %s" % (here))
527                                         continue
528                         here = resolveNumber(here, line[4], self.phonebook)
529
530                         number = stripCbCPrefix(line[3], config.plugins.FritzCall.country.value)
531                         if config.plugins.FritzCall.prefix.value and number and number[0] != '0':               # should only happen for outgoing
532                                 number = config.plugins.FritzCall.prefix.value + number
533                         callListL.append((number, date, direct, remote, length, here))
534
535                 if callback:
536                         # debug("[FritzCallFBF] _gotPageCalls call callback with\n" + repr(callListL))
537                         callback(callListL)
538                 self._callScreen = None
539
540         def _errorCalls(self, error):
541                 debug("[FritzCallFBF] _errorCalls: %s" % (error))
542                 text = _("FRITZ!Box - Could not load calls: %s") % error.getErrorMessage()
543                 self._notify(text)
544
545         def dial(self, number):
546                 ''' initiate a call to number '''
547                 self._login(lambda x: self._dial(number, x))
548                 
549         def _dial(self, number, html):
550                 if html:
551                         #===================================================================
552                         # found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
553                         # if found:
554                         #       self._errorDial('Login: ' + found.group(1))
555                         #       return
556                         #===================================================================
557                         start = html.find('<p class="errorMessage">FEHLER:&nbsp;')
558                         if start != -1:
559                                 start = start + len('<p class="errorMessage">FEHLER:&nbsp;')
560                                 self._errorDial('Login: ' + html[start, html.find('</p>', start)])
561                                 return
562                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
563                 parms = urlencode({
564                         'getpage':'../html/de/menus/menu2.html',
565                         'var:pagename':'fonbuch',
566                         'var:menu':'home',
567                         'telcfg:settings/UseClickToDial':'1',
568                         'telcfg:settings/DialPort':config.plugins.FritzCall.extension.value,
569                         'telcfg:command/Dial':number,
570                         'sid':self._md5Sid
571                         })
572                 debug("[FritzCallFBF] dial url: '" + url + "' parms: '" + parms + "'")
573                 getPage(url,
574                         method="POST",
575                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
576                         headers={
577                                         'Content-Type': "application/x-www-form-urlencoded",
578                                         'Content-Length': str(len(parms))},
579                         postdata=parms).addCallback(self._okDial).addErrback(self._errorDial)
580
581         def _okDial(self, html): #@UnusedVariable # pylint: disable=W0613
582                 debug("[FritzCallFBF] okDial")
583
584         def _errorDial(self, error):
585                 debug("[FritzCallFBF] errorDial: $s" % error)
586                 text = _("FRITZ!Box - Dialling failed: %s") % error.getErrorMessage()
587                 self._notify(text)
588
589         def changeWLAN(self, statusWLAN, callback):
590                 ''' get status info from FBF '''
591                 debug("[FritzCallFBF] changeWLAN start")
592                 if not statusWLAN or (statusWLAN != '1' and statusWLAN != '0'):
593                         return
594                 self._login(lambda x: self._changeWLAN(statusWLAN, callback, x))
595                 
596         def _changeWLAN(self, statusWLAN, callback, html):
597                 if html:
598                         #===================================================================
599                         # found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
600                         # if found:
601                         #       self._errorChangeWLAN('Login: ' + found.group(1))
602                         #       return
603                         #===================================================================
604                         start = html.find('<p class="errorMessage">FEHLER:&nbsp;')
605                         if start != -1:
606                                 start = start + len('<p class="errorMessage">FEHLER:&nbsp;')
607                                 self._errorChangeWLAN(callback, 'Login: ' + html[start, html.find('</p>', start)])
608                                 return
609                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
610                 parms = urlencode({
611                         'getpage':'../html/de/menus/menu2.html',
612                         'var:lang':'de',
613                         'var:pagename':'wlan',
614                         'var:menu':'wlan',
615                         'wlan:settings/ap_enabled':str(statusWLAN),
616                         'sid':self._md5Sid
617                         })
618                 debug("[FritzCallFBF] changeWLAN url: '" + url + "' parms: '" + parms + "'")
619                 getPage(url,
620                         method="POST",
621                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
622                         headers={
623                                         'Content-Type': "application/x-www-form-urlencoded",
624                                         'Content-Length': str(len(parms))},
625                         postdata=parms).addCallback(self._okChangeWLAN, callback).addErrback(self._errorChangeWLAN, callback)
626
627         def _okChangeWLAN(self, callback, html): #@UnusedVariable # pylint: disable=W0613
628                 debug("[FritzCallFBF] _okChangeWLAN")
629                 callback()
630
631         def _errorChangeWLAN(self, callback, error):
632                 debug("[FritzCallFBF] _errorChangeWLAN: $s" % error)
633                 text = _("FRITZ!Box - Failed changing WLAN: %s") % error.getErrorMessage()
634                 self._notify(text)
635                 callback()
636
637         def changeMailbox(self, whichMailbox, callback):
638                 ''' switch mailbox on/off '''
639                 debug("[FritzCallFBF] changeMailbox start: " + str(whichMailbox))
640                 self._login(lambda x: self._changeMailbox(whichMailbox, callback, x))
641
642         def _changeMailbox(self, whichMailbox, callback, html):
643                 if html:
644                         #===================================================================
645                         # found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
646                         # if found:
647                         #       self._errorChangeMailbox('Login: ' + found.group(1))
648                         #       return
649                         #===================================================================
650                         start = html.find('<p class="errorMessage">FEHLER:&nbsp;')
651                         if start != -1:
652                                 start = start + len('<p class="errorMessage">FEHLER:&nbsp;')
653                                 self._errorChangeMailbox(callback, 'Login: ' + html[start, html.find('</p>', start)])
654                                 return
655                 debug("[FritzCallFBF] _changeMailbox")
656                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
657                 if whichMailbox == -1:
658                         for i in range(5):
659                                 if self.info[FBF_tamActive][i+1]:
660                                         state = '0'
661                                 else:
662                                         state = '1'
663                                 parms = urlencode({
664                                         'tam:settings/TAM'+str(i)+'/Active':state,
665                                         'sid':self._md5Sid
666                                         })
667                                 debug("[FritzCallFBF] changeMailbox url: '" + url + "' parms: '" + parms + "'")
668                                 getPage(url,
669                                         method="POST",
670                                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
671                                         headers={
672                                                         'Content-Type': "application/x-www-form-urlencoded",
673                                                         'Content-Length': str(len(parms))},
674                                         postdata=parms).addCallback(self._okChangeMailbox, callback).addErrback(self._errorChangeMailbox, callback)
675                 elif whichMailbox > 4:
676                         debug("[FritzCallFBF] changeMailbox invalid mailbox number")
677                 else:
678                         if self.info[FBF_tamActive][whichMailbox+1]:
679                                 state = '0'
680                         else:
681                                 state = '1'
682                         parms = urlencode({
683                                 'tam:settings/TAM'+str(whichMailbox)+'/Active':state,
684                                 'sid':self._md5Sid
685                                 })
686                         debug("[FritzCallFBF] changeMailbox url: '" + url + "' parms: '" + parms + "'")
687                         getPage(url,
688                                 method="POST",
689                                 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
690                                 headers={
691                                                 'Content-Type': "application/x-www-form-urlencoded",
692                                                 'Content-Length': str(len(parms))},
693                                 postdata=parms).addCallback(self._okChangeMailbox, callback).addErrback(self._errorChangeMailbox, callback)
694
695         def _okChangeMailbox(self, callback, html): #@UnusedVariable # pylint: disable=W0613
696                 debug("[FritzCallFBF] _okChangeMailbox")
697                 callback()
698
699         def _errorChangeMailbox(self, callback, error):
700                 debug("[FritzCallFBF] _errorChangeMailbox: $s" % error)
701                 text = _("FRITZ!Box - Failed changing Mailbox: %s") % error.getErrorMessage()
702                 self._notify(text)
703                 callback()
704
705         def getInfo(self, callback):
706                 ''' get status info from FBF '''
707                 debug("[FritzCallFBF] getInfo")
708                 self._login(lambda x:self._getInfo(callback, x))
709                 
710         def _getInfo(self, callback, html):
711                 # http://192.168.178.1/cgi-bin/webcm?getpage=../html/de/menus/menu2.html&var:lang=de&var:pagename=home&var:menu=home
712                 debug("[FritzCallFBF] _getInfo: verify login")
713                 if html:
714                         #===================================================================
715                         # found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
716                         # if found:
717                         #       self._errorGetInfo('Login: ' + found.group(1))
718                         #       return
719                         #===================================================================
720                         start = html.find('<p class="errorMessage">FEHLER:&nbsp;')
721                         if start != -1:
722                                 start = start + len('<p class="errorMessage">FEHLER:&nbsp;')
723                                 self._errorGetInfo('Login: ' + html[start, html.find('</p>', start)])
724                                 return
725
726                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
727                 parms = urlencode({
728                         'getpage':'../html/de/menus/menu2.html',
729                         'var:lang':'de',
730                         'var:pagename':'home',
731                         'var:menu':'home',
732                         'sid':self._md5Sid
733                         })
734                 debug("[FritzCallFBF] _getInfo url: '" + url + "' parms: '" + parms + "'")
735                 getPage(url,
736                         method="POST",
737                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
738                         headers={
739                                         'Content-Type': "application/x-www-form-urlencoded",
740                                         'Content-Length': str(len(parms))},
741                         postdata=parms).addCallback(lambda x:self._okGetInfo(callback,x)).addErrback(self._errorGetInfo)
742
743         def _okGetInfo(self, callback, html):
744                 def readInfo(html):
745                         if self.info:
746                                 (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive, guestAccess) = self.info
747                         else:
748                                 (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive, guestAccess) = (None, None, None, None, None, None, None, None, None, None)
749
750                         debug("[FritzCallFBF] _okGetInfo/readinfo")
751                         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)
752                         if found:
753                                 boxInfo = found.group(1)+ ', ' + found.group(2)
754                                 boxInfo = boxInfo.replace('&nbsp;',' ')
755                                 # debug("[FritzCallFBF] _okGetInfo Boxinfo: " + boxInfo)
756                         else:
757                                 found = re.match('.*<p class="ac">([^<]*)</p>', html, re.S)
758                                 if found:
759                                         # debug("[FritzCallFBF] _okGetInfo Boxinfo: " + found.group(1))
760                                         boxInfo = found.group(1)
761
762                         if html.find('home_coninf.txt') != -1:
763                                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
764                                 parms = urlencode({
765                                         'getpage':'../html/de/home/home_coninf.txt',
766                                         'sid':self._md5Sid
767                                         })
768                                 # debug("[FritzCallFBF] get coninfo: url: '" + url + "' parms: '" + parms + "'")
769                                 getPage(url,
770                                         method="POST",
771                                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
772                                         headers={
773                                                         'Content-Type': "application/x-www-form-urlencoded",
774                                                         'Content-Length': str(len(parms))},
775                                         postdata=parms).addCallback(lambda x:self._okSetConInfo(callback,x)).addErrback(self._errorGetInfo)
776                         else:
777                                 found = re.match('.*if \(isNaN\(jetzt\)\)\s*return "";\s*var str = "([^"]*)";', html, re.S)
778                                 if found:
779                                         # debug("[FritzCallFBF] _okGetInfo Uptime: " + found.group(1))
780                                         upTime = found.group(1)
781                                 else:
782                                         found = re.match('.*str = g_pppSeit \+"([^<]*)<br>"\+mldIpAdr;', html, re.S)
783                                         if found:
784                                                 # debug("[FritzCallFBF] _okGetInfo Uptime: " + found.group(1))
785                                                 upTime = found.group(1)
786         
787                                 found = re.match(".*IpAdrDisplay\('([.\d]+)'\)", html, re.S)
788                                 if found:
789                                         # debug("[FritzCallFBF] _okGetInfo IpAdrDisplay: " + found.group(1))
790                                         ipAddress = found.group(1)
791
792                         if html.find('g_tamActive') != -1:
793                                 entries = re.compile('if \("(\d)" == "1"\) {\s*g_tamActive \+= 1;\s*}', re.S).finditer(html)
794                                 tamActive = [0, False, False, False, False, False]
795                                 i = 1
796                                 for entry in entries:
797                                         state = entry.group(1)
798                                         if state == '1':
799                                                 tamActive[0] += 1
800                                                 tamActive[i] = True
801                                         i += 1
802                                 # debug("[FritzCallFBF] _okGetInfo tamActive: " + str(tamActive))
803                 
804                         if html.find('home_dect.txt') != -1:
805                                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
806                                 parms = urlencode({
807                                         'getpage':'../html/de/home/home_dect.txt',
808                                         'sid':self._md5Sid
809                                         })
810                                 # debug("[FritzCallFBF] get coninfo: url: '" + url + "' parms: '" + parms + "'")
811                                 getPage(url,
812                                         method="POST",
813                                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
814                                         headers={
815                                                         'Content-Type': "application/x-www-form-urlencoded",
816                                                         'Content-Length': str(len(parms))},
817                                         postdata=parms).addCallback(lambda x:self._okSetDect(callback,x)).addErrback(self._errorGetInfo)
818                         else:
819                                 if html.find('countDect2') != -1:
820                                         entries = re.compile('if \("1" == "1"\) countDect2\+\+;', re.S).findall(html)
821                                         dectActive = len(entries)
822                                         # debug("[FritzCallFBF] _okGetInfo dectActive: " + str(dectActive))
823
824                         found = re.match('.*var g_intFaxActive = "0";\s*if \("1" != ""\) {\s*g_intFaxActive = "1";\s*}\s*', html, re.S)
825                         if found:
826                                 faxActive = True
827                                 # debug("[FritzCallFBF] _okGetInfo faxActive")
828
829                         if html.find('cntRufumleitung') != -1:
830                                 entries = re.compile('mode = "1";\s*ziel = "[^"]+";\s*if \(mode == "1" \|\| ziel != ""\)\s*{\s*g_RufumleitungAktiv = true;', re.S).findall(html)
831                                 rufumlActive = len(entries)
832                                 entries = re.compile('if \("([^"]*)"=="([^"]*)"\) isAllIncoming\+\+;', re.S).finditer(html)
833                                 isAllIncoming = 0
834                                 for entry in entries:
835                                         # debug("[FritzCallFBF] _okGetInfo rufumlActive add isAllIncoming")
836                                         if entry.group(1) == entry.group(2):
837                                                 isAllIncoming += 1
838                                 if isAllIncoming == 2 and rufumlActive > 0:
839                                         rufumlActive -= 1
840                                 # debug("[FritzCallFBF] _okGetInfo rufumlActive: " + str(rufumlActive))
841
842                         # /cgi-bin/webcm?getpage=../html/de/home/home_dsl.txt
843                         # alternative through: fritz.box/cgi-bin/webcm?getpage=../html/de/menus/menu2.html&var:menu=internet&var:pagename=overview
844                         # { "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": "" } 
845                         if html.find('home_dsl.txt') != -1:
846                                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
847                                 parms = urlencode({
848                                         'getpage':'../html/de/home/home_dsl.txt',
849                                         'sid':self._md5Sid
850                                         })
851                                 # debug("[FritzCallFBF] get dsl state: url: '" + url + "' parms: '" + parms + "'")
852                                 getPage(url,
853                                         method="POST",
854                                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
855                                         headers={
856                                                         'Content-Type': "application/x-www-form-urlencoded",
857                                                         'Content-Length': str(len(parms))},
858                                         postdata=parms).addCallback(lambda x:self._okSetDslState(callback,x)).addErrback(self._errorGetInfo)
859                         else:
860                                 found = re.match('.*function DslStateDisplay \(state\){\s*var state = "(\d+)";', html, re.S)
861                                 if found:
862                                         # debug("[FritzCallFBF] _okGetInfo DslState: " + found.group(1))
863                                         dslState = [ found.group(1), None ] # state, speed
864                                         found = re.match('.*function DslStateDisplay \(state\){\s*var state = "\d+";.*?if \("3130" != "0"\) str = "([^"]*)";', html, re.S)
865                                         if found:
866                                                 # debug("[FritzCallFBF] _okGetInfo DslSpeed: " + found.group(1).strip())
867                                                 dslState[1] = found.group(1).strip()
868                 
869                         # /cgi-bin/webcm?getpage=../html/de/home/home_wlan.txt
870                         # { "ap_enabled": "1", "active_stations": "0", "encryption": "4", "wireless_stickandsurf_enabled": "0", "is_macfilter_active": "0", "wmm_enabled": "1", "wlan_state": [ "end" ] }
871                         if html.find('home_wlan.txt') != -1:
872                                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
873                                 parms = urlencode({
874                                         'getpage':'../html/de/home/home_wlan.txt',
875                                         'sid':self._md5Sid
876                                         })
877                                 # debug("[FritzCallFBF] get wlan state: url: '" + url + "' parms: '" + parms + "'")
878                                 getPage(url,
879                                         method="POST",
880                                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
881                                         headers={
882                                                         'Content-Type': "application/x-www-form-urlencoded",
883                                                         'Content-Length': str(len(parms))},
884                                         postdata=parms).addCallback(lambda x:self._okSetWlanState(callback,x)).addErrback(self._errorGetInfo)
885                         else:
886                                 found = re.match('.*function WlanStateLed \(state\){.*?return StateLed\("(\d+)"\);\s*}', html, re.S)
887                                 if found:
888                                         # debug("[FritzCallFBF] _okGetInfo WlanState: " + found.group(1))
889                                         wlanState = [ found.group(1), 0, 0 ] # state, encryption, number of devices
890                                         found = re.match('.*var (?:g_)?encryption = "(\d+)";', html, re.S)
891                                         if found:
892                                                 # debug("[FritzCallFBF] _okGetInfo WlanEncrypt: " + found.group(1))
893                                                 wlanState[1] = found.group(1)
894
895                         return (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive, guestAccess)
896
897                 debug("[FritzCallFBF] _okGetInfo")
898                 info = readInfo(html)
899                 debug("[FritzCallFBF] _okGetInfo info: " + str(info))
900                 self.info = info
901                 if callback:
902                         callback(info)
903
904         def _okSetDect(self, callback, html):
905                 # debug("[FritzCallFBF] _okSetDect: " + html)
906                 # found = re.match('.*"connection_status":"(\d+)".*"connection_ip":"([.\d]+)".*"connection_detail":"([^"]+)".*"connection_uptime":"([^"]+)"', html, re.S)
907                 if html.find('"dect_enabled": "1"') != -1:
908                         # debug("[FritzCallFBF] _okSetDect: dect_enabled")
909                         found = re.match('.*"dect_device_list":.*\[([^\]]*)\]', html, re.S)
910                         if found:
911                                 # debug("[FritzCallFBF] _okSetDect: dect_device_list: %s" %(found.group(1)))
912                                 entries = re.compile('"1"', re.S).findall(found.group(1))
913                                 dectActive = len(entries)
914                                 (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dummy, faxActive, rufumlActive, guestAccess) = self.info
915                                 self.info = (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive, guestAccess)
916                                 debug("[FritzCallFBF] _okSetDect info: " + str(self.info))
917                 if callback:
918                         callback(self.info)
919
920         def _okSetConInfo(self, callback, html):
921                 # debug("[FritzCallFBF] _okSetConInfo: " + html)
922                 # found = re.match('.*"connection_status":"(\d+)".*"connection_ip":"([.\d]+)".*"connection_detail":"([^"]+)".*"connection_uptime":"([^"]+)"', html, re.S)
923                 found = re.match('.*"connection_ip": "([.\d]+)".*"connection_uptime": "([^"]+)"', html, re.S)
924                 if found:
925                         # debug("[FritzCallFBF] _okSetConInfo: connection_ip: %s upTime: %s" %( found.group(1), found.group(2)))
926                         ipAddress = found.group(1)
927                         upTime = found.group(2)
928                         (boxInfo, dummy, dummy, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive, guestAccess) = self.info
929                         self.info = (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive, guestAccess)
930                         debug("[FritzCallFBF] _okSetWlanState info: " + str(self.info))
931                 else:
932                         found = re.match('.*_ip": "([.\d]+)".*"connection_uptime": "([^"]+)"', html, re.S)
933                         if found:
934                                 # debug("[FritzCallFBF] _okSetConInfo: _ip: %s upTime: %s" %( found.group(1), found.group(2)))
935                                 ipAddress = found.group(1)
936                                 upTime = found.group(2)
937                                 (boxInfo, dummy, dummy, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive, guestAccess) = self.info
938                                 self.info = (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive, guestAccess)
939                                 debug("[FritzCallFBF] _okSetWlanState info: " + str(self.info))
940                 if callback:
941                         callback(self.info)
942
943         def _okSetWlanState(self, callback, html):
944                 # debug("[FritzCallFBF] _okSetWlanState: " + html)
945                 found = re.match('.*"ap_enabled": "(\d+)"', html, re.S)
946                 if found:
947                         # debug("[FritzCallFBF] _okSetWlanState: ap_enabled: " + found.group(1))
948                         wlanState = [ found.group(1), None, None ]
949                         found = re.match('.*"encryption": "(\d+)"', html, re.S)
950                         if found:
951                                 # debug("[FritzCallFBF] _okSetWlanState: encryption: " + found.group(1))
952                                 wlanState[1] = found.group(1)
953                         found = re.match('.*"active_stations": "(\d+)"', html, re.S)
954                         if found:
955                                 # debug("[FritzCallFBF] _okSetWlanState: active_stations: " + found.group(1))
956                                 wlanState[2] = found.group(1)
957                         (boxInfo, upTime, ipAddress, dummy, dslState, tamActive, dectActive, faxActive, rufumlActive, guestAccess) = self.info
958                         self.info = (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive, guestAccess)
959                         debug("[FritzCallFBF] _okSetWlanState info: " + str(self.info))
960                 if callback:
961                         callback(self.info)
962
963         def _okSetDslState(self, callback, html):
964                 # debug("[FritzCallFBF] _okSetDslState: " + html)
965                 found = re.match('.*"dsl_carrier_state": "(\d+)"', html, re.S)
966                 if found:
967                         # debug("[FritzCallFBF] _okSetDslState: dsl_carrier_state: " + found.group(1))
968                         dslState = [ found.group(1), "" ]
969                         found = re.match('.*"dsl_ds_nrate": "(\d+)"', html, re.S)
970                         if found:
971                                 # debug("[FritzCallFBF] _okSetDslState: dsl_ds_nrate: " + found.group(1))
972                                 dslState[1] = found.group(1)
973                         found = re.match('.*"dsl_us_nrate": "(\d+)"', html, re.S)
974                         if found:
975                                 # debug("[FritzCallFBF] _okSetDslState: dsl_us_nrate: " + found.group(1))
976                                 dslState[1] = dslState[1] + '/' + found.group(1)
977                         (boxInfo, upTime, ipAddress, wlanState, dummy, tamActive, dectActive, faxActive, rufumlActive, guestAccess) = self.info
978                         self.info = (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive, guestAccess)
979                         debug("[FritzCallFBF] _okSetDslState info: " + str(self.info))
980                 if callback:
981                         callback(self.info)
982
983         def _errorGetInfo(self, error):
984                 debug("[FritzCallFBF] _errorGetInfo: %s" % (error))
985                 text = _("FRITZ!Box - Error getting status: %s") % error.getErrorMessage()
986                 self._notify(text)
987                 # linkP = open("/tmp/FritzCall_errorGetInfo.htm", "w")
988                 # linkP.write(error)
989                 # linkP.close()
990
991         def reset(self):
992                 self._login(self._reset)
993
994         def _reset(self, html):
995                 # 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
996                 if html:
997                         #===================================================================
998                         # found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
999                         # if found:
1000                         #       self._errorReset('Login: ' + found.group(1))
1001                         #       return
1002                         #===================================================================
1003                         start = html.find('<p class="errorMessage">FEHLER:&nbsp;')
1004                         if start != -1:
1005                                 start = start + len('<p class="errorMessage">FEHLER:&nbsp;')
1006                                 self._errorReset('Login: ' + html[start, html.find('</p>', start)])
1007                                 return
1008                 if self._callScreen:
1009                         self._callScreen.close()
1010                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
1011                 parms = urlencode({
1012                         'getpage':'../html/reboot.html',
1013                         'var:lang':'de',
1014                         'var:pagename':'reset',
1015                         'var:menu':'system',
1016                         'logic:command/reboot':'../gateway/commands/saveconfig.html',
1017                         'sid':self._md5Sid
1018                         })
1019                 debug("[FritzCallFBF] _reset url: '" + url + "' parms: '" + parms + "'")
1020                 getPage(url,
1021                         method="POST",
1022                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
1023                         headers={
1024                                         'Content-Type': "application/x-www-form-urlencoded",
1025                                         'Content-Length': str(len(parms))},
1026                         postdata=parms)
1027
1028         def _okReset(self, html): #@UnusedVariable # pylint: disable=W0613
1029                 debug("[FritzCallFBF] _okReset")
1030
1031         def _errorReset(self, error):
1032                 debug("[FritzCallFBF] _errorReset: %s" % (error))
1033                 text = _("FRITZ!Box - Error resetting: %s") % error.getErrorMessage()
1034                 self._notify(text)
1035
1036         def readBlacklist(self):
1037                 self._login(self._readBlacklist)
1038                 
1039         def _readBlacklist(self, html):
1040                 if html:
1041                         #===================================================================
1042                         # found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
1043                         # if found:
1044                         #       self._errorBlacklist('Login: ' + found.group(1))
1045                         #       return
1046                         #===================================================================
1047                         start = html.find('<p class="errorMessage">FEHLER:&nbsp;')
1048                         if start != -1:
1049                                 start = start + len('<p class="errorMessage">FEHLER:&nbsp;')
1050                                 self._errorBlacklist('Login: ' + html[start, html.find('</p>', start)])
1051                                 return
1052                 # http://fritz.box/cgi-bin/webcm?getpage=../html/de/menus/menu2.html&var:lang=de&var:menu=fon&var:pagename=sperre
1053                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
1054                 parms = urlencode({
1055                         'getpage':'../html/de/menus/menu2.html',
1056                         'var:lang':'de',
1057                         'var:pagename':'sperre',
1058                         'var:menu':'fon',
1059                         'sid':self._md5Sid
1060                         })
1061                 debug("[FritzCallFBF] _readBlacklist url: '" + url + "' parms: '" + parms + "'")
1062                 getPage(url,
1063                         method="POST",
1064                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
1065                         headers={
1066                                         'Content-Type': "application/x-www-form-urlencoded",
1067                                         'Content-Length': str(len(parms))},
1068                         postdata=parms).addCallback(self._okBlacklist).addErrback(self._errorBlacklist)
1069
1070         def _okBlacklist(self, html):
1071                 debug("[FritzCallFBF] _okBlacklist")
1072                 entries = re.compile('<script type="text/javascript">document.write\(Tr(Out|In)\("\d+", "(\d+)", "\w*"\)\);</script>', re.S).finditer(html)
1073                 self.blacklist = ([], [])
1074                 for entry in entries:
1075                         if entry.group(1) == "In":
1076                                 self.blacklist[0].append(entry.group(2))
1077                         else:
1078                                 self.blacklist[1].append(entry.group(2))
1079                 debug("[FritzCallFBF] _okBlacklist: %s" % repr(self.blacklist))
1080
1081         def _errorBlacklist(self, error):
1082                 debug("[FritzCallFBF] _errorBlacklist: %s" % (error))
1083                 text = _("FRITZ!Box - Error getting blacklist: %s") % error.getErrorMessage()
1084                 self._notify(text)
1085
1086 class FritzCallFBF_05_27:
1087         def __init__(self):
1088                 debug("[FritzCallFBF_05_27] __init__")
1089                 self._callScreen = None
1090                 self._md5LoginTimestamp = None
1091                 self._md5Sid = '0000000000000000'
1092                 self._callTimestamp = 0
1093                 self._callList = []
1094                 self._callType = config.plugins.FritzCall.fbfCalls.value
1095                 self._phoneBookID = '0'
1096                 self._loginCallbacks = []
1097                 self.blacklist = ([], [])
1098                 self.info = None # (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, guestAccess)
1099                 self.phonebook = None
1100                 self.getInfo(None)
1101                 # self.readBlacklist() now in getInfo
1102                 self.phonebooksFBF = []
1103
1104         def _notify(self, text):
1105                 debug("[FritzCallFBF_05_27] notify: " + text)
1106                 self._md5LoginTimestamp = None
1107                 if self._callScreen:
1108                         debug("[FritzCallFBF_05_27] notify: try to close callScreen")
1109                         self._callScreen.close()
1110                         self._callScreen = None
1111                 Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_ERROR, timeout=config.plugins.FritzCall.timeout.value)
1112                         
1113         def _login(self, callback=None):
1114                 debug("[FritzCallFBF_05_27] _login: " + time.ctime())
1115                 if callback:
1116                         debug("[FritzCallFBF_05_27] _login: add callback " + callback.__name__)
1117                         if self._loginCallbacks:
1118                                 # if login in process just add callback to _loginCallbacks
1119                                 self._loginCallbacks.append(callback)
1120                                 debug("[FritzCallFBF_05_27] _login: login in progress: leave")
1121                                 return
1122                         else:
1123                                 self._loginCallbacks.append(callback)
1124
1125                 if self._callScreen:
1126                         self._callScreen.updateStatus(_("login"))
1127                 if self._md5LoginTimestamp and ((time.time() - self._md5LoginTimestamp) < float(9.5*60)) and self._md5Sid != '0000000000000000': # new login after 9.5 minutes inactivity 
1128                         debug("[FritzCallFBF_05_27] _login: renew timestamp: " + time.ctime(self._md5LoginTimestamp) + " time: " + time.ctime())
1129                         self._md5LoginTimestamp = time.time()
1130                         for callback in self._loginCallbacks:
1131                                 debug("[FritzCallFBF_05_27] _login: calling " + callback.__name__)
1132                                 callback(None)
1133                         self._loginCallbacks = []
1134                 else:
1135                         debug("[FritzCallFBF_05_27] _login: not logged in or outdated login")
1136                         # http://fritz.box/cgi-bin/webcm?getpage=../html/login_sid.xml
1137                         parms = urlencode({'getpage':'../html/login_sid.xml'})
1138                         url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
1139                         debug("[FritzCallFBF_05_27] _login: '" + url + "?" + parms + "'")
1140                         getPage(url,
1141                                 method="POST",
1142                                 headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
1143                                                 }, postdata=parms).addCallback(self._md5Login).addErrback(self._errorLogin)
1144
1145         def _md5Login(self, sidXml):
1146                 def buildResponse(challenge, text):
1147                         debug("[FritzCallFBF_05_27] _md5Login7buildResponse: challenge: " + challenge + ' text: ' + __(text))
1148                         text = (challenge + '-' + text).decode('utf-8','ignore').encode('utf-16-le')
1149                         for i in range(len(text)):
1150                                 if ord(text[i]) > 255:
1151                                         text[i] = '.'
1152                         md5 = hashlib.md5()
1153                         md5.update(text) # pylint: disable=e1101
1154                         debug("[FritzCallFBF_05_27] md5Login/buildResponse: " + md5.hexdigest())
1155                         return challenge + '-' + md5.hexdigest()
1156
1157                 debug("[FritzCallFBF_05_27] _md5Login")
1158                 found = re.match('.*<SID>([^<]*)</SID>', sidXml, re.S)
1159                 if found:
1160                         self._md5Sid = found.group(1)
1161                         debug("[FritzCallFBF_05_27] _md5Login: SID "+ self._md5Sid)
1162                 else:
1163                         debug("[FritzCallFBF_05_27] _md5Login: no sid! That must be an old firmware.")
1164                         self._errorLogin('No sid?!?')
1165                         return
1166
1167                 debug("[FritzCallFBF_05_27] _md5Login: renew timestamp: " + time.ctime(self._md5LoginTimestamp) + " time: " + time.ctime())
1168                 self._md5LoginTimestamp = time.time()
1169                 if sidXml.find('<iswriteaccess>0</iswriteaccess>') != -1:
1170                         debug("[FritzCallFBF_05_27] _md5Login: logging in")
1171                         found = re.match('.*<Challenge>([^<]*)</Challenge>', sidXml, re.S)
1172                         if found:
1173                                 challenge = found.group(1)
1174                                 debug("[FritzCallFBF_05_27] _md5Login: challenge " + challenge)
1175                         else:
1176                                 challenge = None
1177                                 debug("[FritzCallFBF_05_27] _md5Login: login necessary and no challenge! That is terribly wrong.")
1178                         parms = urlencode({
1179                                                         'getpage':'../html/de/menus/menu2.html', # 'var:pagename':'home', 'var:menu':'home', 
1180                                                         'login:command/response': buildResponse(challenge, config.plugins.FritzCall.password.value),
1181                                                         })
1182                         url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
1183                         debug("[FritzCallFBF_05_27] _md5Login: '" + url + "?" + parms + "'")
1184                         getPage(url,
1185                                 method="POST",
1186                                 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
1187                                 headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
1188                                                 }, postdata=parms).addCallback(self._gotPageLogin).addErrback(self._errorLogin)
1189                 else:
1190                         for callback in self._loginCallbacks:
1191                                 debug("[FritzCallFBF_05_27] _md5Login: calling " + callback.__name__)
1192                                 callback(None)
1193                         self._loginCallbacks = []
1194
1195         def _gotPageLogin(self, html):
1196                 if self._callScreen:
1197                         self._callScreen.updateStatus(_("login verification"))
1198                 debug("[FritzCallFBF_05_27] _gotPageLogin: verify login")
1199                 start = html.find('<p class="errorMessage">FEHLER:&nbsp;')
1200                 if start != -1:
1201                         start = start + len('<p class="errorMessage">FEHLER:&nbsp;')
1202                         text = _("FRITZ!Box - Error logging in\n\n") + html[start : html.find('</p>', start)]
1203                         self._notify(text)
1204                 else:
1205                         if self._callScreen:
1206                                 self._callScreen.updateStatus(_("login ok"))
1207
1208                 found = re.match('.*<input type="hidden" name="sid" value="([^\"]*)"', html, re.S)
1209                 if found:
1210                         self._md5Sid = found.group(1)
1211                         debug("[FritzCallFBF_05_27] _gotPageLogin: found sid: " + self._md5Sid)
1212
1213                 for callback in self._loginCallbacks:
1214                         debug("[FritzCallFBF_05_27] _gotPageLogin: calling " + callback.__name__)
1215                         callback(None)
1216                 self._loginCallbacks = []
1217
1218         def _errorLogin(self, error):
1219                 global fritzbox
1220                 debug("[FritzCallFBF_05_27] _errorLogin: %s" % (error))
1221                 if type(error) != str:
1222                         error =  error.getErrorMessage()
1223                 text = _("FRITZ!Box - Error logging in: %s\nDisabling plugin.") % error
1224                 # config.plugins.FritzCall.enable.value = False
1225                 fritzbox = None
1226                 self._notify(text)
1227
1228         def _logout(self):
1229                 if self._md5LoginTimestamp:
1230                         self._md5LoginTimestamp = None
1231                         parms = urlencode({
1232                                                         'getpage':'../html/de/menus/menu2.html', # 'var:pagename':'home', 'var:menu':'home', 
1233                                                         'login:command/logout':'bye bye Fritz'
1234                                                         })
1235                         url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
1236                         debug("[FritzCallFBF_05_27] logout: '" + url + "' parms: '" + parms + "'")
1237                         getPage(url,
1238                                 method="POST",
1239                                 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
1240                                 headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
1241                                                 }, postdata=parms).addErrback(self._errorLogout)
1242
1243         def _errorLogout(self, error):
1244                 debug("[FritzCallFBF_05_27] _errorLogout: %s" % (error))
1245                 text = _("FRITZ!Box - Error logging out: %s") % error.getErrorMessage()
1246                 self._notify(text)
1247
1248         def loadFritzBoxPhonebook(self, phonebook):
1249                 self.phonebook = phonebook
1250                 self._login(self._selectFritzBoxPhonebook)
1251
1252         def _selectFritzBoxPhonebook(self, html):
1253                 # first check for login error
1254                 if html:
1255                         start = html.find('<p class="errorMessage">FEHLER:&nbsp;')
1256                         if start != -1:
1257                                 start = start + len('<p class="errorMessage">FEHLER:&nbsp;')
1258                                 self._errorLoad('Login: ' + html[start, html.find('</p>', start)])
1259                                 return
1260                 # look for phonebook called dreambox or Dreambox
1261                 parms = urlencode({
1262                                                 'sid':self._md5Sid,
1263                                                 })
1264                 url = "http://%s/fon_num/fonbook_select.lua" % (config.plugins.FritzCall.hostname.value)
1265                 debug("[FritzCallFBF_05_27] _selectPhonebook: '" + url + "' parms: '" + parms + "'")
1266                 getPage(url,
1267                         method="POST",
1268                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
1269                         headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
1270                                         }, postdata=parms).addCallback(self._loadFritzBoxPhonebook).addErrback(self._errorLoad)
1271
1272         def _loadFritzBoxPhonebook(self, html):
1273                 # Firmware 05.27 onwards
1274                 # look for phonebook called [dD]reambox and get bookid
1275                 found = re.match('.*<label for="uiBookid:([\d]+)">' + config.plugins.FritzCall.fritzphonebookName.value, html, re.S)
1276                 if found:
1277                         bookid = found.group(1)
1278                         debug("[FritzCallFBF_05_27] _loadFritzBoxPhonebook: found dreambox phonebook %s" % (bookid))
1279                 else:
1280                         bookid = 1
1281                 # http://192.168.178.1/fon_num/fonbook_list.lua?sid=2faec13b0000f3a2
1282                 parms = urlencode({
1283                                                 'bookid':bookid,
1284                                                 'sid':self._md5Sid,
1285                                                 })
1286                 url = "http://%s/fon_num/fonbook_list.lua" % (config.plugins.FritzCall.hostname.value)
1287                 debug("[FritzCallFBF_05_27] _loadFritzBoxPhonebookNew: '" + url + "' parms: '" + parms + "'")
1288                 getPage(url,
1289                         method="POST",
1290                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
1291                         headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
1292                                         }, postdata=parms).addCallback(self._parseFritzBoxPhonebook).addErrback(self._errorLoad)
1293
1294         def _parseFritzBoxPhonebook(self, html):
1295                 debug("[FritzCallFBF_05_27] _parseFritzBoxPhonebookNew")
1296                 found = re.match('.*<input type="hidden" name="telcfg:settings/Phonebook/Books/Name\d+" value="' + config.plugins.FritzCall.fritzphonebookName.value +'" id="uiPostPhonebookName\d+" disabled>\s*<input type="hidden" name="telcfg:settings/Phonebook/Books/Id\d+" value="(\d+)" id="uiPostPhonebookId\d+" disabled>', html, re.S)
1297                 if found:
1298                         phoneBookID = found.group(1)
1299                         debug("[FritzCallFBF_05_27] _parseFritzBoxPhonebookNew: found dreambox phonebook with id: " + phoneBookID)
1300                         if self._phoneBookID != phoneBookID:
1301                                 self._phoneBookID = phoneBookID
1302                                 debug("[FritzCallFBF_05_27] _parseFritzBoxPhonebookNew: reload phonebook")
1303                                 self._loadFritzBoxPhonebook(None) # reload with dreambox phonebook
1304                                 return
1305
1306                 # first, let us get the charset
1307                 found = re.match('.*<meta http-equiv=content-type content="text/html; charset=([^"]*)">', html, re.S)
1308                 if found:
1309                         charset = found.group(1)
1310                         debug("[FritzCallFBF_05_27] _parseFritzBoxPhonebookNew: found charset: " + charset)
1311                         html = html2unicode(html.replace(chr(0xf6),'').decode(charset)).encode('utf-8')
1312                 else: # this is kind of emergency conversion...
1313                         try:
1314                                 debug("[FritzCallFBF_05_27] _parseFritzBoxPhonebookNew: try charset utf-8")
1315                                 charset = 'utf-8'
1316                                 html = html2unicode(html.decode('utf-8')).encode('utf-8') # this looks silly, but has to be
1317                         except UnicodeDecodeError:
1318                                 debug("[FritzCallFBF_05_27] _parseFritzBoxPhonebookNew: try charset iso-8859-1")
1319                                 charset = 'iso-8859-1'
1320                                 html = html2unicode(html.decode('iso-8859-1')).encode('utf-8') # this looks silly, but has to be
1321
1322                 # cleanout hrefs
1323                 html = re.sub("<a href[^>]*>", "", html)
1324                 html = re.sub("</a>", "", html)
1325                 #=======================================================================
1326                 # linkP = open("/tmp/FritzCall_Phonebook.htm", "w")
1327                 # linkP.write(html)
1328                 # linkP.close()
1329                 #=======================================================================
1330
1331                 if html.find('class="zebra_reverse"') != -1:
1332                         debug("[FritzCallFBF_05_27] Found new 7390 firmware")
1333                         # <td class="tname">Mama</td><td class="tnum">03602191620<br>015228924783<br>03602181567</td><td class="ttype">geschäftl.<br>mobil<br>privat</td><td class="tcode"><br>**701<br></td><td class="tvanity"><br>1<br></td>
1334                         entrymask = re.compile('<td class="tname">([^<]*)</td><td class="tnum">([^<]+(?:<br>[^<]+)*)</td><td class="ttype">([^<]+(?:<br>[^<]+)*)</td><td class="tcode">([^<]*(?:<br>[^<]*)*)</td><td class="tvanity">([^<]*(?:<br>[^<]*)*)</td>', re.S)
1335                         entries = entrymask.finditer(html)
1336                         for found in entries:
1337                                 # debug("[FritzCallFBF_05_27] _parseFritzBoxPhonebookNew: processing entry for '''%s'''" % (found.group(1)))
1338                                 name = found.group(1)
1339                                 thisnumbers = found.group(2).split("<br>")
1340                                 thistypes = found.group(3).split("<br>")
1341                                 thiscodes = found.group(4).split("<br>")
1342                                 thisvanitys = found.group(5).split("<br>")
1343                                 for i in range(len(thisnumbers)):
1344                                         thisnumber = cleanNumber(thisnumbers[i])
1345                                         if self.phonebook.phonebook.has_key(thisnumber):
1346                                                 debug("[FritzCallFBF_05_27] Ignoring '''%s''' with '''%s''' from FRITZ!Box Phonebook!" % (name, __(thisnumber)))
1347                                                 continue
1348
1349                                         if not thisnumbers[i]:
1350                                                 debug("[FritzCallFBF_05_27] _parseFritzBoxPhonebookNew: Ignoring entry with empty number for '''%s'''" % (name))
1351                                                 continue
1352                                         else:
1353                                                 thisname = name
1354                                                 if config.plugins.FritzCall.showType.value and thistypes[i]:
1355                                                         thisname = thisname + " (" + thistypes[i] + ")"
1356                                                 if config.plugins.FritzCall.showShortcut.value and thiscodes[i]:
1357                                                         thisname = thisname + ", " + _("Shortcut") + ": " + thiscodes[i]
1358                                                 if config.plugins.FritzCall.showVanity.value and thisvanitys[i]:
1359                                                         thisname = thisname + ", " + _("Vanity") + ": " + thisvanitys[i]
1360         
1361                                                 # debug("[FritzCallFBF_05_27] _parseFritzBoxPhonebookNew: Adding '''%s''' with '''%s''' from FRITZ!Box Phonebook!" % (thisname.strip(), thisnumber))
1362                                                 # Beware: strings in phonebook.phonebook have to be in utf-8!
1363                                                 self.phonebook.phonebook[thisnumber] = thisname
1364                 else:
1365                         self._notify(_("Could not parse FRITZ!Box Phonebook entry"))
1366
1367         def _errorLoad(self, error):
1368                 debug("[FritzCallFBF_05_27] _errorLoad: %s" % (error))
1369                 text = _("FRITZ!Box - Could not load phonebook: %s") % error.getErrorMessage()
1370                 self._notify(text)
1371
1372         def getCalls(self, callScreen, callback, callType):
1373                 #
1374                 # FW 05.27 onwards
1375                 #
1376                 self._callScreen = callScreen
1377                 self._callType = callType
1378                 debug("[FritzCallFBF_05_27] _getCalls1New")
1379                 if self._callScreen:
1380                         self._callScreen.updateStatus(_("finishing"))
1381                 # http://192.168.178.1/fon_num/foncalls_list.lua?sid=da78ab0797197dc7
1382                 parms = urlencode({'sid':self._md5Sid})
1383                 url = "http://%s/fon_num/foncalls_list.lua?%s" % (config.plugins.FritzCall.hostname.value, parms)
1384                 getPage(url).addCallback(lambda x:self._gotPageCalls(callback, x)).addErrback(self._errorCalls)
1385
1386         def _gotPageCalls(self, callback, html=""):
1387
1388                 debug("[FritzCallFBF_05_27] _gotPageCalls")
1389                 if self._callScreen:
1390                         self._callScreen.updateStatus(_("preparing"))
1391
1392                 callListL = []
1393                 if config.plugins.FritzCall.filter.value and config.plugins.FritzCall.filterCallList.value:
1394                         filtermsns = map(lambda x: x.strip(), config.plugins.FritzCall.filtermsn.value.split(","))
1395                         # TODO: scramble filtermsns
1396                         debug("[FritzCallFBF_05_27] _gotPageCalls: filtermsns %s" % (repr(filtermsns)))
1397
1398                 #=======================================================================
1399                 # linkP = open("/tmp/FritzCall_Calllist.htm", "w")
1400                 # linkP.write(html)
1401                 # linkP.close()
1402                 #=======================================================================
1403
1404                 # 1: direct; 2: date; 3: Rufnummer; 4: Name; 5: Nebenstelle; 6: Eigene Rufnumme lang; 7: Eigene Rufnummer; 8: Dauer
1405                 entrymask = re.compile('<td class="([^"]*)" title="[^"]*"></td>\s*<td>([^<]*)</td>\s*<td(?: title="[^\d]*)?([\d]*)(?:[">]+)?(?:<a href=[^>]*>)?([^<]*)(?:</a>)?</td>\s*<td>([^<]*)</td>\s*<td title="([^"]*)">([\d]*)</td>\s*<td>([^<]*)</td>', re.S)
1406                 entries = entrymask.finditer(html)
1407                 for found in entries:
1408                         if found.group(1) == "call_in":
1409                                 direct = FBF_IN_CALLS
1410                         elif found.group(1) == "call_out":
1411                                 direct = FBF_OUT_CALLS
1412                         elif found.group(1) == "call_in_fail":
1413                                 direct = FBF_MISSED_CALLS
1414                         # debug("[FritzCallFBF_05_27] _gotPageCallsNew: direct: " + direct)
1415                         if direct != self._callType and "." != self._callType:
1416                                 continue
1417
1418                         date = found.group(2)
1419                         # debug("[FritzCallFBF_05_27] _gotPageCallsNew: date: " + date)
1420                         length = found.group(8)
1421                         # debug("[FritzCallFBF_05_27] _gotPageCallsNew: len: " + length)
1422                         remote = found.group(4)
1423                         if config.plugins.FritzCall.phonebook.value:
1424                                 if remote and not remote.isdigit():
1425                                         remote = resolveNumber(found.group(3), remote + " (FBF)", self.phonebook)
1426                                 else:
1427                                         remote = resolveNumber(found.group(3), "", self.phonebook)
1428                         # debug("[FritzCallFBF_05_27] _gotPageCallsNew: remote. " + remote)
1429                         here = found.group(7)
1430                         #===================================================================
1431                         # start = here.find('Internet: ')
1432                         # if start != -1:
1433                         #       start += len('Internet: ')
1434                         #       here = here[start:]
1435                         # else:
1436                         #       here = line[5]
1437                         #===================================================================
1438                         if config.plugins.FritzCall.filter.value and config.plugins.FritzCall.filterCallList.value:
1439                                 # debug("[FritzCallFBF_05_27] _gotPageCalls: check %s" % (here))
1440                                 if here not in filtermsns:
1441                                         # debug("[FritzCallFBF_05_27] _gotPageCalls: skip %s" % (here))
1442                                         continue
1443                         here = resolveNumber(here, found.group(6), self.phonebook)
1444                         # debug("[FritzCallFBF_05_27] _gotPageCallsNew: here: " + here)
1445
1446                         number = stripCbCPrefix(found.group(3), config.plugins.FritzCall.country.value)
1447                         if config.plugins.FritzCall.prefix.value and number and number[0] != '0':               # should only happen for outgoing
1448                                 number = config.plugins.FritzCall.prefix.value + number
1449                         # debug("[FritzCallFBF_05_27] _gotPageCallsNew: number: " + number)
1450                         debug("[FritzCallFBF_05_27] _gotPageCallsNew: append: %s" % repr((number, date, direct, remote, length, here)) )
1451                         callListL.append((number, date, direct, remote, length, here))
1452
1453                 if callback:
1454                         # debug("[FritzCallFBF_05_27] _gotPageCalls call callback with\n" + text
1455                         callback(callListL)
1456                 self._callScreen = None
1457
1458         def _errorCalls(self, error):
1459                 debug("[FritzCallFBF_05_27] _errorCalls: %s" % (error))
1460                 text = _("FRITZ!Box - Could not load calls: %s") % error.getErrorMessage()
1461                 self._notify(text)
1462
1463         def dial(self, number):
1464                 ''' initiate a call to number '''
1465                 self._login(lambda x: self._dial(number, x))
1466                 
1467         def _dial(self, number, html):
1468                 if html:
1469                         #===================================================================
1470                         # found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
1471                         # if found:
1472                         #       self._errorDial('Login: ' + found.group(1))
1473                         #       return
1474                         #===================================================================
1475                         start = html.find('<p class="errorMessage">FEHLER:&nbsp;')
1476                         if start != -1:
1477                                 start = start + len('<p class="errorMessage">FEHLER:&nbsp;')
1478                                 self._errorDial('Login: ' + html[start, html.find('</p>', start)])
1479                                 return
1480                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
1481                 parms = urlencode({
1482                         'getpage':'../html/de/menus/menu2.html',
1483                         'var:pagename':'fonbuch',
1484                         'var:menu':'home',
1485                         'telcfg:settings/UseClickToDial':'1',
1486                         'telcfg:settings/DialPort':config.plugins.FritzCall.extension.value,
1487                         'telcfg:command/Dial':number,
1488                         'sid':self._md5Sid
1489                         })
1490                 debug("[FritzCallFBF_05_27] dial url: '" + url + "' parms: '" + parms + "'")
1491                 getPage(url,
1492                         method="POST",
1493                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
1494                         headers={
1495                                         'Content-Type': "application/x-www-form-urlencoded",
1496                                         'Content-Length': str(len(parms))},
1497                         postdata=parms).addCallback(self._okDial).addErrback(self._errorDial)
1498
1499         def _okDial(self, html): #@UnusedVariable # pylint: disable=W0613
1500                 debug("[FritzCallFBF_05_27] okDial")
1501
1502         def _errorDial(self, error):
1503                 debug("[FritzCallFBF_05_27] errorDial: $s" % error)
1504                 text = _("FRITZ!Box - Dialling failed: %s") % error.getErrorMessage()
1505                 self._notify(text)
1506
1507         def changeWLAN(self, statusWLAN, callback):
1508                 ''' get status info from FBF '''
1509                 debug("[FritzCallFBF_05_27] changeWLAN start")
1510                 Notifications.AddNotification(MessageBox, _("not yet implemented"), type=MessageBox.TYPE_ERROR, timeout=config.plugins.FritzCall.timeout.value)
1511                 return
1512                 
1513         def _changeWLAN(self, statusWLAN, callback, html):
1514                 if html:
1515                         #===================================================================
1516                         # found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
1517                         # if found:
1518                         #       self._errorChangeWLAN('Login: ' + found.group(1))
1519                         #       return
1520                         #===================================================================
1521                         start = html.find('<p class="errorMessage">FEHLER:&nbsp;')
1522                         if start != -1:
1523                                 start = start + len('<p class="errorMessage">FEHLER:&nbsp;')
1524                                 self._errorChangeWLAN(callback, 'Login: ' + html[start, html.find('</p>', start)])
1525                                 return
1526
1527                 if statusWLAN == '0':
1528                         statusWLAN = 'off'
1529                 else:
1530                         statusWLAN = 'off'
1531
1532                 url = "http://%s//wlan/wlan_settings.lua" % config.plugins.FritzCall.hostname.value
1533                 parms = urlencode({
1534                         'active':str(statusWLAN),
1535                         'sid':self._md5Sid
1536                         })
1537                 debug("[FritzCallFBF] changeWLAN url: '" + url + "' parms: '" + parms + "'")
1538                 getPage(url,
1539                         method="POST",
1540                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
1541                         headers={
1542                                         'Content-Type': "application/x-www-form-urlencoded",
1543                                         'Content-Length': str(len(parms))},
1544                         postdata=parms).addCallback(self._okChangeWLAN, callback).addErrback(self._errorChangeWLAN, callback)
1545
1546         def _okChangeWLAN(self, callback, html): #@UnusedVariable # pylint: disable=W0613
1547                 debug("[FritzCallFBF] _okChangeWLAN")
1548                 callback()
1549
1550         def _errorChangeWLAN(self, callback, error):
1551                 debug("[FritzCallFBF] _errorChangeWLAN: $s" % error)
1552                 text = _("FRITZ!Box - Failed changing WLAN: %s") % error.getErrorMessage()
1553                 self._notify(text)
1554                 callback()
1555
1556         def changeMailbox(self, whichMailbox, callback):
1557                 ''' switch mailbox on/off '''
1558                 debug("[FritzCallFBF_05_27] changeMailbox start: " + str(whichMailbox))
1559                 Notifications.AddNotification(MessageBox, _("not yet implemented"), type=MessageBox.TYPE_ERROR, timeout=config.plugins.FritzCall.timeout.value)
1560
1561         def _changeMailbox(self, whichMailbox, html): # pylint: disable=W0613
1562                 return
1563
1564         def _okChangeMailbox(self, html): #@UnusedVariable # pylint: disable=W0613
1565                 debug("[FritzCallFBF_05_27] _okChangeMailbox")
1566
1567         def _errorChangeMailbox(self, error):
1568                 debug("[FritzCallFBF_05_27] _errorChangeMailbox: $s" % error)
1569                 text = _("FRITZ!Box - Failed changing Mailbox: %s") % error.getErrorMessage()
1570                 self._notify(text)
1571
1572         def getInfo(self, callback):
1573                 ''' get status info from FBF '''
1574                 debug("[FritzCallFBF_05_27] getInfo")
1575                 self._login(lambda x:self._getInfo(callback, x))
1576                 
1577         def _getInfo(self, callback, html):
1578                 debug("[FritzCallFBF_05_27] _getInfo: verify login")
1579                 if html:
1580                         start = html.find('<p class="errorMessage">FEHLER:&nbsp;')
1581                         if start != -1:
1582                                 start = start + len('<p class="errorMessage">FEHLER:&nbsp;')
1583                                 self._errorGetInfo('Login: ' + html[start, html.find('</p>', start)])
1584                                 return
1585
1586                 self._readBlacklist()
1587
1588                 url = "http://%s/home/home.lua" % config.plugins.FritzCall.hostname.value
1589                 parms = urlencode({
1590                         'sid':self._md5Sid
1591                         })
1592                 debug("[FritzCallFBF_05_27] _getInfo url: '" + url + "' parms: '" + parms + "'")
1593                 getPage(url,
1594                         method="POST",
1595                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
1596                         headers={
1597                                         'Content-Type': "application/x-www-form-urlencoded",
1598                                         'Content-Length': str(len(parms))},
1599                         postdata=parms).addCallback(lambda x:self._okGetInfo(callback,x)).addErrback(self._errorGetInfo)
1600
1601         def _okGetInfo(self, callback, html):
1602
1603                 debug("[FritzCallFBF_05_27] _okGetInfo")
1604
1605                 #=======================================================================
1606                 # linkP = open("/tmp/FritzCallInfo.htm", "w")
1607                 # linkP.write(html)
1608                 # linkP.close()
1609                 #=======================================================================
1610
1611                 if self.info:
1612                         (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive, guestAccess) = self.info
1613                 else:
1614                         (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive, guestAccess) = (None, None, None, None, None, None, None, None, None, None)
1615
1616                 found = re.match('.*<table id="tProdukt" class="tborder"> <tr> <td style="[^"]*" >([^<]*)</td> <td style="[^"]*" class="td_right">([^<]*)<a target="[^"]*" onclick="[^"]*" href="[^"]*">([^<]*)</a></td> ', html, re.S)
1617                 if found:
1618                         boxInfo = found.group(1) + ', ' + found.group(2) + found.group(3)
1619                         boxInfo = boxInfo.replace('&nbsp;',' ')
1620                         debug("[FritzCallFBF_05_27] _okGetInfo Boxinfo: " + boxInfo)
1621
1622                 found = re.match('.*<div id=\'ipv4_info\'><span class="[^"]*">verbunden seit ([^<]*)</span>', html, re.S)
1623                 if found:
1624                         upTime = found.group(1)
1625                         debug("[FritzCallFBF_05_27] _okGetInfo upTime: " + upTime)
1626
1627                 found = re.match('.*IP-Adresse: ([^<]*)</span>', html, re.S)
1628                 if found:
1629                         ipAddress = found.group(1)
1630                         debug("[FritzCallFBF_05_27] _okGetInfo ipAddress: " + ipAddress)
1631
1632                 # wlanstate = [ active, encrypted, no of devices ]
1633                 found = re.match('.*<tr id="uiTrWlan"><td class="(led_gray|led_green|led_red)"></td><td><a href="[^"]*">WLAN</a></td><td>(aus|an)(|, gesichert)</td>', html, re.S)
1634                 if found:
1635                         if found.group(1) == "led_green":
1636                                 if found.group(2):
1637                                         wlanState = [ '1', '1', '' ]
1638                                 else:
1639                                         wlanState = [ '1', '0', '' ]
1640                         else:
1641                                 wlanState = [ '0', '0', '0' ]
1642                         debug("[FritzCallFBF_05_27] _okGetInfo wlanState: " + repr(wlanState))
1643
1644                 found = re.match('.*<tr id="uiTrDsl"><td class="(led_gray|led_green|led_red)">', html, re.S)
1645                 if found:
1646                         if found.group(1) == "led_green":
1647                                 dslState = ['5', None, None]
1648                                 found = re.match('.*<a href="[^"]*">DSL</a></td><td >bereit, ([^<]*)<img src=\'[^\']*\' height=\'[^\']*\'>&nbsp;([^<]*)<img src=\'[^\']*\' height=\'[^\']*\'></td></tr>', html, re.S)
1649                                 if found:
1650                                         dslState[1] = found.group(1) + "/" + found.group(2)
1651                         else:
1652                                 dslState = ['0', None, None]
1653                 debug("[FritzCallFBF_05_27] _okGetInfo dslState: " + repr(dslState))
1654
1655                 found = re.match('.*<tr id="trTam" style=""><td><a href="[^"]*">Anrufbeantworter</a></td><td title=\'[^\']*\'>([\d]+) aktiv([^<]*)</td></tr>', html, re.S)
1656                 if found:
1657                         # found.group(2) could be ', neue Nachrichten vorhanden'; ignore for now
1658                         tamActive = [ found.group(1), False, False, False, False, False]
1659                 debug("[FritzCallFBF_05_27] _okGetInfo tamActive: " + repr(tamActive))
1660
1661                 found = re.match('.*<tr id="uiTrDect"><td class="led_green"></td><td><a href="[^"]*">DECT</a></td><td>an, (ein|\d*) Schnurlostelefon', html, re.S)
1662                 if found:
1663                         dectActive = found.group(1)
1664                 debug("[FritzCallFBF_05_27] _okGetInfo dectActive: " + repr(dectActive))
1665
1666                 found = re.match('.*<td>Integriertes Fax aktiv</td>', html, re.S)
1667                 if found:
1668                         faxActive = True
1669                 debug("[FritzCallFBF_05_27] _okGetInfo faxActive: " + repr(faxActive))
1670
1671                 found = re.match('.* <tr style=""><td><a href="[^"]*">Rufumleitung</a></td><td>deaktiviert</td></tr>', html, re.S)
1672                 if found:
1673                         rufumlActive = False
1674                 else:
1675                         rufumlActive = True
1676                 debug("[FritzCallFBF_05_27] _okGetInfo rufumlActive: " + repr(rufumlActive))
1677
1678                 info = (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive, guestAccess)
1679                 debug("[FritzCallFBF_05_27] _okGetInfo info: " + str(info))
1680                 self.info = info
1681                 if callback:
1682                         callback(info)
1683
1684         def _okSetDect(self, callback, html): # pylint: disable=W0613
1685                 return
1686         
1687         def _okSetConInfo(self, callback, html): # pylint: disable=W0613
1688                 return
1689
1690         def _okSetWlanState(self, callback, html): # pylint: disable=W0613
1691                 return
1692
1693         def _okSetDslState(self, callback, html): # pylint: disable=W0613
1694                 return
1695
1696         def _errorGetInfo(self, error):
1697                 debug("[FritzCallFBF_05_27] _errorGetInfo: %s" % (error))
1698                 text = _("FRITZ!Box - Error getting status: %s") % error.getErrorMessage()
1699                 self._notify(text)
1700                 return
1701
1702         def reset(self):
1703                 self._login(self._reset)
1704
1705         def _reset(self, html):
1706                 # 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
1707                 if html:
1708                         #===================================================================
1709                         # found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
1710                         # if found:
1711                         #       self._errorReset('Login: ' + found.group(1))
1712                         #       return
1713                         #===================================================================
1714                         start = html.find('<p class="errorMessage">FEHLER:&nbsp;')
1715                         if start != -1:
1716                                 start = start + len('<p class="errorMessage">FEHLER:&nbsp;')
1717                                 self._errorReset('Login: ' + html[start, html.find('</p>', start)])
1718                                 return
1719                 if self._callScreen:
1720                         self._callScreen.close()
1721                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
1722                 parms = urlencode({
1723                         'getpage':'../html/reboot.html',
1724                         'var:lang':'de',
1725                         'var:pagename':'reset',
1726                         'var:menu':'system',
1727                         'logic:command/reboot':'../gateway/commands/saveconfig.html',
1728                         'sid':self._md5Sid
1729                         })
1730                 debug("[FritzCallFBF_05_27] _reset url: '" + url + "' parms: '" + parms + "'")
1731                 getPage(url,
1732                         method="POST",
1733                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
1734                         headers={
1735                                         'Content-Type': "application/x-www-form-urlencoded",
1736                                         'Content-Length': str(len(parms))},
1737                         postdata=parms)
1738
1739         def _okReset(self, html): #@UnusedVariable # pylint: disable=W0613
1740                 debug("[FritzCallFBF_05_27] _okReset")
1741
1742         def _errorReset(self, error):
1743                 debug("[FritzCallFBF_05_27] _errorReset: %s" % (error))
1744                 text = _("FRITZ!Box - Error resetting: %s") % error.getErrorMessage()
1745                 self._notify(text)
1746
1747         def _readBlacklist(self):
1748                 # http://fritz.box/cgi-bin/webcm?getpage=../html/de/menus/menu2.html&var:lang=de&var:menu=fon&var:pagename=sperre
1749                 url = "http://%s/fon_num/sperre.lua" % config.plugins.FritzCall.hostname.value
1750                 parms = urlencode({
1751                         'sid':self._md5Sid
1752                         })
1753                 debug("[FritzCallFBF_05_27] _readBlacklist url: '" + url + "' parms: '" + parms + "'")
1754                 getPage(url,
1755                         method="POST",
1756                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
1757                         headers={
1758                                         'Content-Type': "application/x-www-form-urlencoded",
1759                                         'Content-Length': str(len(parms))},
1760                         postdata=parms).addCallback(self._okBlacklist).addErrback(self._errorBlacklist)
1761
1762         def _okBlacklist(self, html):
1763                 debug("[FritzCallFBF_05_27] _okBlacklist")
1764                 #=======================================================================
1765                 # linkP = open("/tmp/FritzCallBlacklist.htm", "w")
1766                 # linkP.write(html)
1767                 # linkP.close()
1768                 #=======================================================================
1769                 entries = re.compile('<span title="(?:Ankommende|Ausgehende) Rufe">(Ankommende|Ausgehende) Rufe</span></nobr></td><td><nobr><span title="[\d]+">([\d]+)</span>', re.S).finditer(html)
1770                 self.blacklist = ([], [])
1771                 for entry in entries:
1772                         if entry.group(1) == "Ankommende":
1773                                 self.blacklist[0].append(entry.group(2))
1774                         else:
1775                                 self.blacklist[1].append(entry.group(2))
1776                 debug("[FritzCallFBF_05_27] _okBlacklist: %s" % repr(self.blacklist))
1777
1778         def _errorBlacklist(self, error):
1779                 debug("[FritzCallFBF_05_27] _errorBlacklist: %s" % (error))
1780                 text = _("FRITZ!Box - Error getting blacklist: %s") % error.getErrorMessage()
1781                 self._notify(text)
1782
1783 import xml.etree.ElementTree as ET
1784 import StringIO, csv
1785
1786 class FritzCallFBF_05_50:
1787         def __init__(self):
1788                 debug("[FritzCallFBF_05_50] __init__")
1789                 self._callScreen = None
1790                 self._callType = config.plugins.FritzCall.fbfCalls.value
1791                 self._phoneBookID = '0'
1792                 self.blacklist = ([], [])
1793                 self.info = None # (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, guestAccess)
1794                 self.phonebook = None
1795                 self.getInfo(None)
1796                 # self.readBlacklist() now in getInfo
1797
1798         def _notify(self, text):
1799                 debug("[FritzCallFBF_05_50] notify: " + text)
1800                 if self._callScreen:
1801                         debug("[FritzCallFBF_05_50] notify: try to close callScreen")
1802                         self._callScreen.close()
1803                         self._callScreen = None
1804                 Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_ERROR, timeout=config.plugins.FritzCall.timeout.value)
1805                         
1806         def _login(self, callback=None):
1807                 # http://fritz.box/login_lua.xml
1808                 url = "http://%s/login_sid.lua" % (config.plugins.FritzCall.hostname.value)
1809                 debug("[FritzCallFBF_05_50] _login: " + time.ctime() + " :"+ url)
1810                 getPage(url,
1811                         method="GET",
1812                         headers={'Content-Type': "application/x-www-form-urlencoded"}
1813                         ).addCallback(self._md5Login, callback).addErrback(self._errorLogin)
1814
1815         def _md5Login(self, sidXml, callback):
1816                 def buildResponse(challenge, text):
1817                         debug("[FritzCallFBF_05_50] _md5Login7buildResponse: challenge: " + challenge + ' text: ' + __(text))
1818                         text = (challenge + '-' + text).decode('utf-8','ignore').encode('utf-16-le')
1819                         for i in range(len(text)):
1820                                 if ord(text[i]) > 255:
1821                                         text[i] = '.'
1822                         md5 = hashlib.md5()
1823                         md5.update(text) # pylint: disable=e1101
1824                         debug("[FritzCallFBF_05_50] md5Login/buildResponse: " + md5.hexdigest())
1825                         return challenge + '-' + md5.hexdigest()
1826
1827                 #=======================================================================
1828                 # linkP = open("/tmp/FritzDebug_sid.xml", "w")
1829                 # linkP.write(sidXml)
1830                 # linkP.close()
1831                 #=======================================================================
1832
1833                 debug("[FritzCallFBF_05_50] _md5Login")
1834                 sidX = ET.fromstring(sidXml)
1835         #===========================================================================
1836         #       self._md5Sid = sidX.find("SID").text
1837         #       if self._md5Sid:
1838         #               debug("[FritzCallFBF_05_50] _md5Login: SID "+ self._md5Sid)
1839         #       else:
1840         #               debug("[FritzCallFBF_05_50] _md5Login: no sid! That must be an old firmware.")
1841         #               self._notify(_("FRITZ!Box - Error logging in\n\n") + _("wrong firmware version?"))
1842         #               return
1843         # 
1844         #       if self._md5Sid != "0000000000000000":
1845         #               debug("[FritzCallFBF_05_50] _md5Login: SID "+ self._md5Sid)
1846         #               for callback in self._loginCallbacks:
1847         #                       debug("[FritzCallFBF_05_50] _md5Login: calling " + callback.__name__)
1848         #                       callback(None)
1849         #               self._loginCallbacks = []
1850         #               return
1851         #===========================================================================
1852
1853                 challenge = sidX.find("Challenge").text
1854                 if challenge:
1855                         debug("[FritzCallFBF_05_50] _md5Login: challenge " + challenge)
1856                 else:
1857                         debug("[FritzCallFBF_05_50] _md5Login: login necessary and no challenge! That is terribly wrong.")
1858
1859                 # TODO: check validity of username?
1860                 parms = urlencode({
1861                                                 'username': config.plugins.FritzCall.username.value,
1862                                                 'response': buildResponse(challenge, config.plugins.FritzCall.password.value),
1863                                                 })
1864                 url = "http://%s/login_sid.lua" % (config.plugins.FritzCall.hostname.value)
1865                 debug("[FritzCallFBF_05_50] _md5Login: " + url + "?" + parms)
1866                 getPage(url,
1867                         method="POST",
1868                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
1869                         headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
1870                                         }, postdata=parms).addCallback(self._gotPageLogin, callback).addErrback(self._errorLogin)
1871
1872         def _gotPageLogin(self, sidXml, callback):
1873                 if self._callScreen:
1874                         self._callScreen.updateStatus(_("login verification"))
1875
1876                 #=======================================================================
1877                 # linkP = open("/tmp/sid.xml", "w")
1878                 # linkP.write(sidXml)
1879                 # linkP.close()
1880                 #=======================================================================
1881
1882                 sidX = ET.fromstring(sidXml)
1883                 md5Sid = sidX.find("SID").text
1884                 if md5Sid and md5Sid != "0000000000000000":
1885                         debug("[FritzCallFBF_05_50] _gotPageLogin: found sid: " + md5Sid)
1886                 else:
1887                         debug("[FritzCallFBF_05_50] _gotPageLogin: found no sid")
1888                         self._notify(_("FRITZ!Box - Error logging in\n\n") + _("wrong user or password?"))
1889                         return
1890
1891                 if self._callScreen:
1892                         self._callScreen.updateStatus(_("login ok"))
1893
1894                 debug("[FritzCallFBF_05_50] _gotPageLogin: calling " + callback.__name__)
1895                 callback(md5Sid)
1896
1897         def _errorLogin(self, error):
1898                 global fritzbox
1899                 if type(error).__name__ == "str":
1900                         text = error
1901                 else:
1902                         text = error.getErrorMessage()
1903                 text = _("FRITZ!Box - Error logging in: %s\nDisabling plugin.") % text
1904                 # config.plugins.FritzCall.enable.value = False
1905                 fritzbox = None
1906                 debug("[FritzCallFBF_05_50] _errorLogin: %s" % (error))
1907                 self._notify(text)
1908
1909         def _logout(self, md5Sid, what):
1910                 parms = urlencode({
1911                                                 'sid':md5Sid,
1912                                                 'logout':'bye bye Fritz'
1913                                                 })
1914                 url = "http://%s/login_sid.lua" % (config.plugins.FritzCall.hostname.value)
1915                 debug("[FritzCallFBF_05_50] _logout (" + what + ") " + time.ctime() + ": " + url + "?" + parms)
1916                 getPage(url,
1917                         method="POST",
1918                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
1919                         headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
1920                                         }, postdata=parms).addErrback(self._errorLogout)
1921
1922         def _errorLogout(self, error):
1923                 debug("[FritzCallFBF_05_50] _errorLogout: %s" % (error))
1924                 text = _("FRITZ!Box - Error logging out: %s") % error.getErrorMessage()
1925                 self._notify(text)
1926
1927         def loadFritzBoxPhonebook(self, phonebook):
1928                 self.phonebook = phonebook
1929                 self._login(self._selectFritzBoxPhonebook)
1930
1931         def _selectFritzBoxPhonebook(self, md5Sid, html=None): # pylint: disable=W0613
1932                 # TODO: error check...
1933                 # look for phonebook called dreambox or Dreambox
1934                 parms = urlencode({
1935                                                 'sid':md5Sid,
1936                                                 })
1937                 url = "http://%s/fon_num/fonbook_select.lua" % (config.plugins.FritzCall.hostname.value)
1938                 debug("[FritzCallFBF_05_50] _selectPhonebook: " + url + "?" + parms)
1939                 getPage(url,
1940                         method="POST",
1941                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
1942                         headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
1943                                         }, postdata=parms).addCallback(self._loadFritzBoxPhonebook, md5Sid).addErrback(self._errorLoad, md5Sid)
1944
1945         def _loadFritzBoxPhonebook(self, html, md5Sid):
1946                 # Firmware 05.27 onwards
1947                 # look for phonebook called [dD]reambox and get bookid
1948                 found = re.match('.*<label for="uiBookid:([\d]+)">' + config.plugins.FritzCall.fritzphonebookName.value, html, re.S)
1949                 if found:
1950                         bookid = found.group(1)
1951                 else:
1952                         bookid = 1
1953                 debug("[FritzCallFBF_05_50] _loadFritzBoxPhonebook: phonebook %s" % (bookid))
1954
1955                 # http://192.168.178.1/fon_num/fonbook_list.lua?sid=2faec13b0000f3a2
1956                 parms = urlencode({
1957                                                 'bookid':bookid,
1958                                                 'sid':md5Sid,
1959                                                 'cancel':'',
1960                                                 'apply':'uiApply',
1961                                                 })
1962                 url = "http://%s/fon_num/fonbook_select.lua" % (config.plugins.FritzCall.hostname.value)
1963                 debug("[FritzCallFBF_05_50] _loadFritzBoxPhonebookNew: " + url + "?" + parms)
1964                 getPage(url,
1965                         method="POST",
1966                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
1967                         headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
1968                                         }, postdata=parms).addCallback(self._parseFritzBoxPhonebook, md5Sid).addErrback(self._errorLoad, md5Sid)
1969
1970         def _parseFritzBoxPhonebook(self, html, md5Sid):
1971                 debug("[FritzCallFBF_05_50] _parseFritzBoxPhonebook")
1972                 # first, let us get the charset
1973                 found = re.match('.*<meta http-equiv=content-type content="text/html; charset=([^"]*)">', html, re.S)
1974                 if found:
1975                         charset = found.group(1)
1976                         debug("[FritzCallFBF_05_50] _parseFritzBoxPhonebook: found charset: " + charset)
1977                         if charset != 'utf-8':
1978                                 html = html2unicode(html.replace(chr(0xf6),'').decode(charset)).encode('utf-8')
1979                 else: # this is kind of emergency conversion...
1980                         try:
1981                                 debug("[FritzCallFBF_05_50] _parseFritzBoxPhonebook: try charset utf-8")
1982                                 charset = 'utf-8'
1983                                 html = html2unicode(html.decode('utf-8')).encode('utf-8') # this looks silly, but has to be
1984                         except UnicodeDecodeError:
1985                                 debug("[FritzCallFBF_05_50] _parseFritzBoxPhonebook: try charset iso-8859-1")
1986                                 charset = 'iso-8859-1'
1987                                 html = html2unicode(html.decode('iso-8859-1')).encode('utf-8') # this looks silly, but has to be
1988
1989                 # cleanout hrefs
1990                 html = re.sub("<a href[^>]*>", "", html)
1991                 html = re.sub("</a>", "", html)
1992
1993                 #=======================================================================
1994                 # linkP = open("/tmp/FritzCall_Phonebook.htm", "w")
1995                 # linkP.write(html)
1996                 # linkP.close()
1997                 #=======================================================================
1998
1999                 if html.find('class="zebra_reverse"') != -1:
2000                         debug("[FritzCallFBF_05_50] Found new 7390 firmware")
2001                         entrymask = re.compile('<td class="tname" title="([^"]*)">[^<]*</td><td class="tnum">([^<]+(?:<br>[^<]+)*)</td><td class="ttype">([^<]+(?:<br>[^<]+)*)</td><td class="tcode">([^<]*(?:<br>[^<]*)*)</td><td class="tvanity">([^<]*(?:<br>[^<]*)*)</td>', re.S)
2002                         entries = entrymask.finditer(html)
2003                         for found in entries:
2004                                 # debug("[FritzCallFBF_05_50] _parseFritzBoxPhonebook: processing entry for '''%s'''" % repr(found.groups()))
2005                                 name = html2unicode(re.sub(",", "", found.group(1)))
2006                                 thisnumbers = found.group(2).split("<br>")
2007                                 thistypes = found.group(3).split("<br>")
2008                                 thiscodes = found.group(4).split("<br>")
2009                                 thisvanitys = found.group(5).split("<br>")
2010                                 for i in range(len(thisnumbers)):
2011                                         thisnumber = cleanNumber(thisnumbers[i])
2012                                         if self.phonebook.phonebook.has_key(thisnumber):
2013                                                 # debug("[FritzCallFBF_05_50] Ignoring '%s' ('%s') with %s' ( have: '%s')" % (name, thistypes[i], __(thisnumber), self.phonebook.phonebook[thisnumber]))
2014                                                 continue
2015
2016                                         if not thisnumbers[i]:
2017                                                 # debug("[FritzCallFBF_05_50] _parseFritzBoxPhonebook: Ignoring entry with empty number for '''%s'''" % (__(name)))
2018                                                 continue
2019                                         else:
2020                                                 thisname = name.decode('utf-8')
2021                                                 if config.plugins.FritzCall.showType.value and thistypes[i]:
2022                                                         thisname = thisname + " (" + thistypes[i].decode('utf-8') + ")"
2023                                                 if config.plugins.FritzCall.showShortcut.value and thiscodes[i]:
2024                                                         thisname = thisname + ", " + _("Shortcut") + ": " + thiscodes[i]
2025                                                 if config.plugins.FritzCall.showVanity.value and thisvanitys[i]:
2026                                                         thisname = thisname + ", " + _("Vanity") + ": " + thisvanitys[i]
2027         
2028                                                 # debug("[FritzCallFBF_05_50] _parseFritzBoxPhonebook: Adding '''%s''' with '''%s'''" % (__(thisname.strip()), __(thisnumber, False)))
2029                                                 # debug("[FritzCallFBF_05_50] _parseFritzBoxPhonebook: Adding '''%s''' with '''%s'''" % (thisname.strip(), thisnumber))
2030                                                 # Beware: strings in phonebook.phonebook have to be in utf-8!
2031                                                 self.phonebook.phonebook[thisnumber] = thisname.encode('utf-8')
2032                 else:
2033                         self._notify(_("Could not parse FRITZ!Box Phonebook entry"))
2034                 self._logout(md5Sid, "_parseFritzBoxPhonebook")
2035
2036         def _errorLoad(self, error, md5Sid):
2037                 debug("[FritzCallFBF_05_50] _errorLoad: %s" % (error))
2038                 text = _("FRITZ!Box - Could not load phonebook: %s") % error.getErrorMessage()
2039                 self._notify(text)
2040                 self._logout(md5Sid, "_errorLoad")
2041
2042         def getCalls(self, callScreen, callback, callType):
2043                 #
2044                 # FW 05.27 onwards
2045                 #
2046                 debug("[FritzCallFBF_05_50] getCalls")
2047                 self._callScreen = callScreen
2048                 self._callType = callType
2049                 self._login(lambda md5Sid:self._getCalls(callback, md5Sid))
2050
2051         def _getCalls(self, callback, md5Sid): # pylint: disable=W0613
2052                 debug("[FritzCallFBF_05_50] _getCalls")
2053                 if self._callScreen:
2054                         self._callScreen.updateStatus(_("preparing"))
2055                 # besser csv mit: https://fritz.box/fon_num/foncalls_list.lua?sid=dea373c2d0257a41&csv=
2056                 parms = urlencode({'sid':md5Sid, 'csv':''})
2057                 url = "http://%s/fon_num/foncalls_list.lua?%s" % (config.plugins.FritzCall.hostname.value, parms)
2058                 getPage(url).addCallback(lambda x:self._gotPageCalls(callback, x, md5Sid)).addErrback(self._errorCalls, md5Sid)
2059
2060         def _gotPageCalls(self, callback, csvString="", md5Sid=""):
2061
2062                 debug("[FritzCallFBF_05_50] _gotPageCalls")
2063                 if self._callScreen:
2064                         self._callScreen.updateStatus(_("finishing"))
2065
2066                 callListL = []
2067                 if config.plugins.FritzCall.filter.value and config.plugins.FritzCall.filterCallList.value:
2068                         filtermsns = map(lambda x: x.strip(), config.plugins.FritzCall.filtermsn.value.split(","))
2069                         # TODO: scramble filtermsns
2070                         debug("[FritzCallFBF_05_50] _gotPageCalls: filtermsns %s" % (repr(map(__, filtermsns))))
2071                 else:
2072                         filtermsns = None
2073
2074                 #=======================================================================
2075                 # linkP = open("/tmp/FritzCalls.csv", "w")
2076                 # linkP.write(csvString)
2077                 # linkP.close()
2078                 #=======================================================================
2079
2080                 # 0: direct; 1: date; 2: Name; 3: Nummer; 4: Nebenstelle; 5: Eigene Rufnumme; 6: Dauer
2081                 calls = csv.reader(StringIO.StringIO(csvString), delimiter=';')
2082                 calls.next() # skip sep
2083                 for call in calls:
2084                         if len(call) != 7:
2085                                 debug("[FritzCallFBF_05_50] _gotPageCalls: skip %s len: %s" %(repr(call), str(len(call))))
2086                                 continue
2087                         direct = call[0]
2088                         if direct == '1':
2089                                 direct = FBF_IN_CALLS
2090                         elif direct == '4':
2091                                 direct = FBF_OUT_CALLS
2092                         elif direct == '2':
2093                                 direct = FBF_MISSED_CALLS
2094                         if self._callType != '.' and self._callType != direct:
2095                                 continue
2096
2097                         date = call[1]
2098                         length = call[6]
2099
2100                         here = call[5]
2101                         start = here.find('Internet: ')
2102                         if start != -1:
2103                                 start += len('Internet: ')
2104                                 here = here[start:]
2105
2106                         if filtermsns and here not in filtermsns:
2107                                 # debug("[FritzCallFBF_05_50] _gotPageCalls: skip %s" % (here))
2108                                 continue
2109
2110                         if call[4]:
2111                                 here = resolveNumber(here, call[4] + " (" + here + ")", self.phonebook)
2112                         else:
2113                                 here = resolveNumber(here, "", self.phonebook)
2114                         # debug("[FritzCallFBF_05_50] _gotPageCalls: here: " + here)
2115
2116                         number = stripCbCPrefix(call[3], config.plugins.FritzCall.country.value)
2117                         if config.plugins.FritzCall.prefix.value and number and number[0] != '0':               # should only happen for outgoing
2118                                 number = config.plugins.FritzCall.prefix.value + number
2119                         # debug("[FritzCallFBF_05_50] _gotPageCalls: number: " + number)
2120
2121                         found = re.match("\d+ \((\d+)\)", call[2])
2122                         if found:
2123                                 remote = resolveNumber(number, resolveNumber(found.group(1), None, self.phonebook), self.phonebook)
2124                         else:
2125                                 remote = resolveNumber(number, re.sub(",", "", call[2]), self.phonebook)
2126                         # debug("[FritzCallFBF_05_50] _gotPageCalls: remote. " + remote)
2127
2128                         # debug("[FritzCallFBF_05_50] _gotPageCalls: append: %s" % repr((__(number, False), date, direct, __(remote), length, __(here))))
2129                         # debug("[FritzCallFBF_05_50] _gotPageCalls: append: %s" % repr((number, date, direct, remote, length, here)))
2130                         callListL.append((number, date, direct, remote, length, here))
2131
2132                 if callback:
2133                         # debug("[FritzCallFBF_05_50] _gotPageCalls call callback with\n" + text
2134                         callback(callListL)
2135                 self._callScreen = None
2136                 self._logout(md5Sid, "_gotPageCalls")
2137
2138         def _errorCalls(self, error, md5Sid):
2139                 debug("[FritzCallFBF_05_50] _errorCalls: %s" % (error))
2140                 text = _("FRITZ!Box - Could not load calls: %s") % error.getErrorMessage()
2141                 self._notify(text)
2142                 self._logout(md5Sid, "_errorCalls")
2143
2144         def dial(self, number):
2145                 ''' initiate a call to number '''
2146                 self._login(lambda x, md5Sid: self._dial(number, x, md5Sid))
2147                 
2148         def _dial(self, number, html, md5Sid):
2149                 if html:
2150                         #===================================================================
2151                         # found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
2152                         # if found:
2153                         #       self._errorDial('Login: ' + found.group(1))
2154                         #       return
2155                         #===================================================================
2156                         start = html.find('<p class="errorMessage">FEHLER:&nbsp;')
2157                         if start != -1:
2158                                 start = start + len('<p class="errorMessage">FEHLER:&nbsp;')
2159                                 self._errorDial('Login: ' + html[start, html.find('</p>', start)], md5Sid)
2160                                 return
2161                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
2162                 parms = urlencode({
2163                         'getpage':'../html/de/menus/menu2.html',
2164                         'var:pagename':'fonbuch',
2165                         'var:menu':'home',
2166                         'telcfg:settings/UseClickToDial':'1',
2167                         'telcfg:settings/DialPort':config.plugins.FritzCall.extension.value,
2168                         'telcfg:command/Dial':number,
2169                         'sid':md5Sid
2170                         })
2171                 debug("[FritzCallFBF_05_50] dial url: " + url + "?" + parms)
2172                 getPage(url,
2173                         method="POST",
2174                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
2175                         headers={
2176                                         'Content-Type': "application/x-www-form-urlencoded",
2177                                         'Content-Length': str(len(parms))},
2178                         postdata=parms).addCallback(self._okDial, md5Sid).addErrback(self._errorDial, md5Sid)
2179
2180         def _okDial(self, html, md5Sid): #@UnusedVariable # pylint: disable=W0613
2181                 debug("[FritzCallFBF_05_50] okDial")
2182                 if html:
2183                         found = re.match('.*<p class="ErrorMsg">([^<]*)</p>', html, re.S)
2184                         if found:
2185                                 self._notify(found.group(1))
2186                 self._logout(md5Sid, "_okDial")
2187
2188         def _errorDial(self, error, md5Sid):
2189                 debug("[FritzCallFBF_05_50] errorDial: $s" % error)
2190                 text = _("FRITZ!Box - Dialling failed: %s") % error.getErrorMessage()
2191                 self._notify(text)
2192                 self._logout(md5Sid, "_errorDial")
2193
2194         def changeWLAN(self, statusWLAN, callback):
2195                 ''' get status info from FBF '''
2196                 debug("[FritzCallFBF_05_50] changeWLAN start")
2197                 #=======================================================================
2198                 # Notifications.AddNotification(MessageBox, _("not yet implemented"), type=MessageBox.TYPE_ERROR, timeout=config.plugins.FritzCall.timeout.value)
2199                 # return
2200                 #=======================================================================
2201
2202                 if not statusWLAN or (statusWLAN != '1' and statusWLAN != '0'):
2203                         return
2204                 self._login(lambda md5Sid: self._changeWLAN(statusWLAN, callback, md5Sid))
2205                 
2206         def _changeWLAN(self, statusWLAN, callback, md5Sid):
2207                 if statusWLAN == '0':
2208                         parms = urlencode({
2209                                 'sid':md5Sid,
2210                                 'apply':'',
2211                                 'cancel':'',
2212                                 'btn_refresh':''
2213                                 })
2214                 else:
2215                         parms = urlencode({
2216                                 'sid':md5Sid,
2217                                 'active':'on',
2218                                 'active_24':'on',
2219                                 'active_5':'on',
2220                                 'hidden_ssid':'on',
2221                                 'apply':'',
2222                                 'cancel':'',
2223                                 'btn_refresh':''
2224                                 })
2225
2226                 url = "http://%s//wlan/wlan_settings.lua" % config.plugins.FritzCall.hostname.value
2227                 debug("[FritzCallFBF_05_50] changeWLAN url: " + url + "?" + parms)
2228                 getPage(url,
2229                         method="POST",
2230                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
2231                         headers={
2232                                         'Content-Type': "application/x-www-form-urlencoded"},
2233                         postdata=parms).addCallback(self._okChangeWLAN, callback, md5Sid).addErrback(self._errorChangeWLAN, md5Sid)
2234
2235         def _okChangeWLAN(self, html, callback, md5Sid): #@UnusedVariable # pylint: disable=W0613
2236                 debug("[FritzCallFBF_05_50] _okChangeWLAN")
2237                 if html:
2238                         found = re.match('.*<p class="ErrorMsg">([^<]*)</p>', html, re.S)
2239                         if found:
2240                                 self._notify(found.group(1))
2241                 callback()
2242                 self._logout(md5Sid, "_okChangeWLAN")
2243
2244         def _errorChangeWLAN(self, error, md5Sid):
2245                 debug("[FritzCallFBF_05_50] _errorChangeWLAN: $s" % error)
2246                 text = _("FRITZ!Box - Failed changing WLAN: %s") % error.getErrorMessage()
2247                 self._notify(text)
2248                 self._logout(md5Sid, "_errorChangeWLAN")
2249
2250         def changeGuestAccess(self, statusGuestAccess, callback):
2251                 debug("[FritzCallFBF_05_50] changeGuestAccess start")
2252                 self._login(lambda md5Sid: self._changeGuestAccessWLAN(statusGuestAccess, callback, md5Sid))
2253                 
2254         def _changeGuestAccessWLAN(self, statusGuestAccess, callback, md5Sid):
2255                 if statusGuestAccess.find('WLAN') != -1:
2256                         parms = urlencode({
2257                                 'sid':md5Sid,
2258                                 'autoupdate':'on',
2259                                 'print':'',
2260                                 'btnSave':'',
2261                                 'btnChancel':''
2262                                 })
2263                 else:
2264                         if config.plugins.FritzCall.guestSecure.value:
2265                                 parms = urlencode({
2266                                         'sid':md5Sid,
2267                                         'activate_guest_access':'on',
2268                                         'autoupdate':'on',
2269                                         'guest_ssid':config.plugins.FritzCall.guestSSID.value,
2270                                         'sec_mode':'4',
2271                                         'wpa_key': config.plugins.FritzCall.guestPassword.value,
2272                                         'down_time_activ':'on',
2273                                         'down_time_value':'30',
2274                                         'disconnect_guest_access':'on',
2275                                         'btnSave':'',
2276                                         'btnChancel':'',
2277                                         })
2278                         else:
2279                                 parms = urlencode({
2280                                         'sid':md5Sid,
2281                                         'activate_guest_access':'on',
2282                                         'autoupdate':'on',
2283                                         'guest_ssid':config.plugins.FritzCall.guestSSID.value,
2284                                         'sec_mode':'5',
2285                                         'down_time_activ':'on',
2286                                         'down_time_value':'30',
2287                                         'disconnect_guest_access':'on',
2288                                         'btnSave':'',
2289                                         'btnChancel':'',
2290                                         })
2291
2292                 url = "http://%s/wlan/guest_access.lua" % config.plugins.FritzCall.hostname.value
2293                 debug("[FritzCallFBF_05_50] _changeGuestAccessWLAN url: " + url + "?" + parms)
2294                 getPage(url,
2295                         method="POST",
2296                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
2297                         headers={
2298                                         'Content-Type': "application/x-www-form-urlencoded"},
2299                         postdata=parms).addCallback(self._okChangeGuestAccess, callback, md5Sid).addErrback(self._errorChangeGuestAccess, md5Sid)
2300
2301         def _okChangeGuestAccess(self, html, callback, md5Sid): #@UnusedVariable # pylint: disable=W0613
2302                 debug("[FritzCallFBF_05_50] _okChangeGuestAccess")
2303                 if html:
2304                         found = re.match('.*<p class="ErrorMsg">([^<]*)</p>', html, re.S)
2305                         if found:
2306                                 self._notify(found.group(1))
2307                 callback()
2308                 self._logout(md5Sid, "_okChangeGuestAccess")
2309
2310         def _errorChangeGuestAccess(self, error, md5Sid):
2311                 debug("[FritzCallFBF_05_50] _errorChangeGuestAccess: $s" % error)
2312                 text = _("FRITZ!Box - Failed changing GuestAccess: %s") % error.getErrorMessage()
2313                 self._notify(text)
2314                 self._logout(md5Sid, "_errorChangeGuestAccess")
2315
2316         def changeMailbox(self, whichMailbox, callback):
2317                 ''' switch mailbox on/off '''
2318                 debug("[FritzCallFBF_05_50] changeMailbox start: " + str(whichMailbox))
2319                 Notifications.AddNotification(MessageBox, _("not yet implemented"), type=MessageBox.TYPE_ERROR, timeout=config.plugins.FritzCall.timeout.value)
2320
2321         def getInfo(self, callback):
2322                 ''' get status info from FBF '''
2323                 debug("[FritzCallFBF_05_50] getInfo")
2324                 self._login(lambda md5Sid: self._getInfo(callback, md5Sid))
2325                 
2326         def _getInfo(self, callback, md5Sid):
2327                 debug("[FritzCallFBF_05_50] _getInfo: verify login")
2328
2329                 self._login(self._readBlacklist)
2330
2331                 url = "http://%s/home/home.lua" % config.plugins.FritzCall.hostname.value
2332                 parms = urlencode({
2333                         'sid':md5Sid
2334                         })
2335                 debug("[FritzCallFBF_05_50] _getInfo url: " + url + "?" + parms)
2336                 getPage(url,
2337                         method="POST",
2338                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
2339                         headers={
2340                                         'Content-Type': "application/x-www-form-urlencoded",
2341                                         'Content-Length': str(len(parms))},
2342                         postdata=parms).addCallback(lambda x:self._okGetInfo(callback,x, md5Sid)).addErrback(self._errorGetInfo, md5Sid)
2343
2344         def _okGetInfo(self, callback, html, md5Sid):
2345
2346                 debug("[FritzCallFBF_05_50] _okGetInfo")
2347
2348                 #=======================================================================
2349                 # linkP = open("/tmp/FritzCallInfo.htm", "w")
2350                 # linkP.write(html)
2351                 # linkP.close()
2352                 #=======================================================================
2353
2354                 (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive, guestAccess) = (None, None, None, None, None, None, None, None, None, None)
2355
2356                 found = re.match('.*<table id="tProdukt" class="tborder"> <tr> <td style="[^"]*" >([^<]*)</td> <td style="[^"]*" class="td_right">([^<]*)<a target="[^"]*" onclick="[^"]*" href="[^"]*">([^<]*)</a></td> ', html, re.S)
2357                 if found:
2358                         boxInfo = found.group(1) + ', ' + found.group(2) + found.group(3)
2359                         boxInfo = boxInfo.replace('&nbsp;',' ')
2360                         debug("[FritzCallFBF_05_50] _okGetInfo Boxinfo: " + boxInfo)
2361
2362                 found = re.match('.*<div id=\'ipv._info\'><span class="[^"]*">verbunden seit ([^<]*)</span>', html, re.S)
2363                 if found:
2364                         upTime = found.group(1)
2365                         debug("[FritzCallFBF_05_50] _okGetInfo upTime: " + upTime)
2366
2367                 ipAddress = ""
2368                 found = re.match('.*IP-Adresse: ([^<]*)</span>', html, re.S)
2369                 if found:
2370                         ipAddress = found.group(1)
2371                         debug("[FritzCallFBF_05_50] _okGetInfo ipAddress v4: " + ipAddress)
2372                 found = re.match('.*IPv6-Präfix: ([^<]*)</span>', html, re.S)
2373                 if found:
2374                         if ipAddress:
2375                                 ipAddress = ipAddress + ' / ' + found.group(1)
2376                         else:
2377                                 ipAddress = found.group(1)
2378                         debug("[FritzCallFBF_05_50] _okGetInfo ipAddress v6: " + ipAddress)
2379
2380
2381                 found = re.match('.*<tr id="uiTrDsl"><td class="(led_gray|led_green|led_red)">', html, re.S)
2382                 if found:
2383                         if found.group(1) == "led_green":
2384                                 dslState = ['5', None, None]
2385                                 found = re.match('.*<a href="[^"]*">DSL</a></td><td >bereit, ([^<]*)<img src=\'[^\']*\' height=\'[^\']*\'>&nbsp;([^<]*)<img src=\'[^\']*\' height=\'[^\']*\'></td></tr>', html, re.S)
2386                                 if found:
2387                                         dslState[1] = found.group(1) + " / " + found.group(2)
2388                         else:
2389                                 dslState = ['0', None, None]
2390                 debug("[FritzCallFBF_05_50] _okGetInfo dslState: " + repr(dslState))
2391
2392                 # wlanstate = [ active, encrypted, no of devices ]
2393                 found = re.match('.*<tr id="uiTrWlan"><td class="(led_gray|led_green|led_red)"></td><td><a href="[^"]*">WLAN</a></td><td>(aus|an)(|, gesichert)</td>', html, re.S)
2394                 if found:
2395                         if found.group(1) == "led_green":
2396                                 if found.group(3):
2397                                         wlanState = [ '1', '1', '' ]
2398                                 else:
2399                                         wlanState = [ '1', '0', '' ]
2400                         else:
2401                                 wlanState = [ '0', '0', '0' ]
2402                         debug("[FritzCallFBF_05_50] _okGetInfo wlanState: " + repr(wlanState))
2403
2404                 #=======================================================================
2405                 # found = re.match('.*<tr id="trTam" style=""><td><a href="[^"]*">Anrufbeantworter</a></td><td title=\'[^\']*\'>([\d]+) aktiv([^<]*)</td></tr>', html, re.S)
2406                 # if found:
2407                 #       # found.group(2) could be ', neue Nachrichten vorhanden'; ignore for now
2408                 #       tamActive = [ found.group(1), False, False, False, False, False]
2409                 # debug("[FritzCallFBF_05_50] _okGetInfo tamActive: " + repr(tamActive))
2410                 #=======================================================================
2411
2412                 found = re.match('.*<tr id="uiTrDect"><td class="(led_gray|led_green|led_red)"></td><td><a href="[^"]*">DECT</a></td><td>(?:aus|an, (ein|\d*) Schnurlostelefon)', html, re.S)
2413                 if found:
2414                         debug("[FritzCallFBF_05_50] _okGetInfo dectActive: " + repr(found.groups()))
2415                         if found.group(1) == "led_green":
2416                                 dectActive = found.group(2)
2417                                 debug("[FritzCallFBF_05_50] _okGetInfo dectActive: " + repr(dectActive))
2418
2419                 found = re.match('.*<tr (?:style="")?><td><a href="[^"]*">Faxfunktion</a></td><td>Integriertes Fax aktiv</td>', html, re.S)
2420                 if found:
2421                         faxActive = True
2422                         debug("[FritzCallFBF_05_50] _okGetInfo faxActive: " + repr(faxActive))
2423
2424                 found = re.match('.*Rufumleitung</a></td><td>aktiv</td>', html, re.S)
2425                 if found:
2426                         rufumlActive = -1 # means no number available
2427                         debug("[FritzCallFBF_05_50] _okGetInfo rufumlActive: " + repr(rufumlActive))
2428
2429                 guestAccess = ""
2430                 found = re.match('.*WLAN-Gastzugang</a></td><td title="[^"]*">aktiv ([^<]*)</td>', html, re.S)
2431                 if found:
2432                         # guestAccess =  "WLAN " + found.group(1)
2433                         if found.group(1).find(", gesichert"):
2434                                 guestAccess =  "WLAN (gesichert)"
2435                         else:
2436                                 guestAccess =  "WLAN (ungesichert)"
2437                         debug("[FritzCallFBF_05_50] _okGetInfo guestAccess WLAN: " + repr(guestAccess))
2438                 found = re.match('.*LAN-Gastzugang</a></td><td title="aktiv">aktiv</td>', html, re.S)
2439                 if found:
2440                         if guestAccess:
2441                                 guestAccess =  guestAccess + ", LAN"
2442                         else:
2443                                 guestAccess = "LAN"
2444                         debug("[FritzCallFBF_05_50] _okGetInfo guestAccess LAN: " + repr(guestAccess))
2445
2446                 info = (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive, guestAccess)
2447                 debug("[FritzCallFBF_05_50] _okGetInfo info: " + str(info))
2448                 self.info = info
2449                 if callback:
2450                         callback(info)
2451                 self._logout(md5Sid, "_okGetInfo")
2452
2453         def _errorGetInfo(self, error, md5Sid):
2454                 debug("[FritzCallFBF_05_50] _errorGetInfo: %s" % (error))
2455                 text = _("FRITZ!Box - Error getting status: %s") % error.getErrorMessage()
2456                 self._notify(text)
2457                 self._logout(md5Sid, "_errorGetInfo")
2458
2459         def reset(self):
2460                 self._login(self._reset)
2461
2462         def _reset(self, md5Sid):
2463                 if self._callScreen:
2464                         self._callScreen.close()
2465
2466                 url = "http://%s/system/reboot.lua" % config.plugins.FritzCall.hostname.value
2467                 parms = urlencode({
2468                         'reboot':'',
2469                         'sid':md5Sid
2470                         })
2471                 debug("[FritzCallFBF_05_50] _reset url: " + url + "?" + parms)
2472                 getPage(url,
2473                         method="POST",
2474                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
2475                         headers={
2476                                         'Content-Type': "application/x-www-form-urlencoded"},
2477                         postdata=parms).addCallback(self._okReset, md5Sid).addErrback(self._errorReset, md5Sid)
2478
2479
2480         def _okReset(self, html, md5Sid): #@UnusedVariable # pylint: disable=W0613
2481                 debug("[FritzCallFBF_05_50] _okReset")
2482                 #=======================================================================
2483                 # linkP = open("/tmp/_okReset.htm", "w")
2484                 # linkP.write(html)
2485                 # linkP.close()
2486                 #=======================================================================
2487                 if html:
2488                         found = re.match('.*<p class="ErrorMsg">([^<]*)</p>', html, re.S)
2489                         if found:
2490                                 self._notify(found.group(1))
2491                 self._logout(md5Sid, "_okReset")
2492
2493         def _errorReset(self, error, md5Sid):
2494                 debug("[FritzCallFBF_05_50] _errorReset: %s" % (error))
2495                 text = _("FRITZ!Box - Error resetting: %s") % error.getErrorMessage()
2496                 self._notify(text)
2497                 self._logout(md5Sid, "_errorReset")
2498
2499         def _readBlacklist(self, md5Sid):
2500                 # http://fritz.box/cgi-bin/webcm?getpage=../html/de/menus/menu2.html&var:lang=de&var:menu=fon&var:pagename=sperre
2501                 url = "http://%s/fon_num/sperre.lua" % config.plugins.FritzCall.hostname.value
2502                 parms = urlencode({
2503                         'sid':md5Sid
2504                         })
2505                 debug("[FritzCallFBF_05_50] _readBlacklist url: " + url + "?" + parms)
2506                 getPage(url,
2507                         method="POST",
2508                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
2509                         headers={
2510                                         'Content-Type': "application/x-www-form-urlencoded",
2511                                         'Content-Length': str(len(parms))},