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