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