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