X-Git-Url: http://git.maemo.org/git/?a=blobdiff_plain;f=src%2Fgv_backend.py;h=0d05d41ab55901d9495050cd8d8af8a4180b5fb3;hb=e05a73034ce93f0ac08ed94b40ed67cb2181b9bb;hp=9d6d4fb5be03e4d00929153776dc995ace104a57;hpb=2b053a403ff9763a8dac9ff460af8e27e6fd7ffd;p=gc-dialer diff --git a/src/gv_backend.py b/src/gv_backend.py index 9d6d4fb..0d05d41 100644 --- a/src/gv_backend.py +++ b/src/gv_backend.py @@ -1,3 +1,15 @@ +#!/usr/bin/python + +# DialCentral - Front end for Google's Grand Central service. +# Copyright (C) 2008 Eric Warnke ericew AT gmail DOT com +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # @@ -20,20 +32,17 @@ import urllib import urllib2 import time import warnings +import traceback from xml.etree import ElementTree from browser_emu import MozillaEmulator -import socket - try: import simplejson except ImportError: simplejson = None -socket.setdefaulttimeout(5) - _TRUE_REGEX = re.compile("true") _FALSE_REGEX = re.compile("false") @@ -50,7 +59,7 @@ if simplejson is None: return safe_eval(flattened) else: def parse_json(flattened): - return simplejson.loads(json) + return simplejson.loads(flattened) class GVDialer(object): @@ -59,11 +68,6 @@ class GVDialer(object): the functions include login, setting up a callback number, and initalting a callback """ - _contactsRe = re.compile(r"""(.*?)""", re.S) - _contactsNextRe = re.compile(r""".*Next""", re.S) - _contactDetailGroupRe = re.compile(r"""Group:\s*(\w*)""", re.S) - _contactDetailPhoneRe = re.compile(r"""(\w+):[0-9\-\(\) \t]*?call""", re.S) - _isNotLoginPageRe = re.compile(r"""I cannot access my account""") _tokenRe = re.compile(r"""""") _accountNumRe = re.compile(r"""(.{14})""") @@ -71,13 +75,18 @@ class GVDialer(object): _validateRe = re.compile("^[0-9]{10,}$") _gvDialingStrRe = re.compile("This may take a few seconds", re.M) - _clicktocallURL = "http://www.google.com/voice/m/sendcall" - _contactsURL = "http://www.google.com/voice/m/contacts" - _contactDetailURL = "http://www.google.com/voice/m/contact" + _contactsRe = re.compile(r"""(.*?)""", re.S) + _contactsNextRe = re.compile(r""".*Next.*?""", re.S) + _contactDetailPhoneRe = re.compile(r"""([0-9\-\(\) \t]+?)\((\w+)\)""", re.S) + + _clicktocallURL = "https://www.google.com/voice/m/sendcall" + _contactsURL = "https://www.google.com/voice/mobile/contacts" + _contactDetailURL = "https://www.google.com/voice/mobile/contact" _loginURL = "https://www.google.com/accounts/ServiceLoginAuth" + _setforwardURL = "https://www.google.com//voice/m/setphone" _accountNumberURL = "https://www.google.com/voice/mobile" - _forwardURL = "https://www.google.com/voice/m/phones" + _forwardURL = "https://www.google.com/voice/mobile/phones" _inboxURL = "https://www.google.com/voice/inbox/" _recentCallsURL = "https://www.google.com/voice/inbox/recent/" @@ -115,12 +124,14 @@ class GVDialer(object): try: inboxPage = self._browser.download(self._inboxURL) except urllib2.URLError, e: + warnings.warn(traceback.format_exc()) raise RuntimeError("%s is not accesible" % self._inboxURL) self._browser.cookies.save() if self._isNotLoginPageRe.search(inboxPage) is not None: return False + self._grab_account_info() self._lastAuthed = time.time() return True @@ -129,8 +140,8 @@ class GVDialer(object): Attempt to login to grandcentral @returns Whether login was successful or not """ - #if self.is_authed(): - # return True + if self.is_authed(): + return True loginPostData = urllib.urlencode({ 'Email' : username, @@ -141,10 +152,9 @@ class GVDialer(object): try: loginSuccessOrFailurePage = self._browser.download(self._loginURL, loginPostData) except urllib2.URLError, e: + warnings.warn(traceback.format_exc()) raise RuntimeError("%s is not accesible" % self._loginURL) - #self._grab_account_info(loginSuccessOrFailurePage) - self._grab_account_info() return self.is_authed() def logout(self): @@ -172,17 +182,17 @@ class GVDialer(object): "number": number, "phone": self._callbackNumber, "_rnr_se": self._token, - "submit": "Call", }) otherData = { - 'Referer': 'https://www.google.com/voice/m/callsms', + 'Referer' : 'https://google.com/voice/m/callsms', } - callSuccessPage = self._browser.download(self._clicktocallURL, clickToCallData, otherData) + callSuccessPage = self._browser.download(self._clicktocallURL, clickToCallData, None, otherData) except urllib2.URLError, e: + warnings.warn(traceback.format_exc()) raise RuntimeError("%s is not accesible" % self._clicktocallURL) if self._gvDialingStrRe.search(callSuccessPage) is None: - raise RuntimeError("Grand Central returned an error") + raise RuntimeError("Google Voice returned an error") return True @@ -212,17 +222,17 @@ class GVDialer(object): numbers = self.get_callback_numbers() for number, description in numbers.iteritems(): - if not re.compile(r"""1747""").match(number) is None: + if re.compile(r"""1747""").match(number) is not None: self.set_callback_number(number) return for number, description in numbers.iteritems(): - if not re.compile(r"""gizmo""", re.I).search(description) is None: + if re.compile(r"""gizmo""", re.I).search(description) is not None: self.set_callback_number(number) return for number, description in numbers.iteritems(): - if not re.compile(r"""computer""", re.I).search(description) is None: + if re.compile(r"""computer""", re.I).search(description) is not None: self.set_callback_number(number) return @@ -246,6 +256,17 @@ class GVDialer(object): @param callbacknumber should be a proper 10 digit number """ self._callbackNumber = callbacknumber + callbackPostData = urllib.urlencode({ + '_rnr_se': self._token, + 'phone': callbacknumber + }) + try: + callbackSetPage = self._browser.download(self._setforwardURL, callbackPostData) + except urllib2.URLError, e: + warnings.warn(traceback.format_exc()) + raise RuntimeError("%s is not accesible" % self._setforwardURL) + + self._browser.cookies.save() return True def get_callback_number(self): @@ -266,6 +287,7 @@ class GVDialer(object): try: allRecentData = self._grab_json(url) except urllib2.URLError, e: + warnings.warn(traceback.format_exc()) raise RuntimeError("%s is not accesible" % self._clicktocallURL) for recentCallData in allRecentData["messages"].itervalues(): @@ -307,6 +329,7 @@ class GVDialer(object): try: contactsPage = self._browser.download(contactsPageUrl) except urllib2.URLError, e: + warnings.warn(traceback.format_exc()) raise RuntimeError("%s is not accesible" % self._clicktocallURL) for contact_match in self._contactsRe.finditer(contactsPage): contactId = contact_match.group(1) @@ -330,11 +353,12 @@ class GVDialer(object): try: detailPage = self._browser.download(self._contactDetailURL + '/' + contactId) except urllib2.URLError, e: + warnings.warn(traceback.format_exc()) raise RuntimeError("%s is not accesible" % self._clicktocallURL) for detail_match in self._contactDetailPhoneRe.finditer(detailPage): - phoneType = detail_match.group(1) - phoneNumber = detail_match.group(2) + phoneNumber = detail_match.group(1) + phoneType = detail_match.group(2) yield (phoneType, phoneNumber) def _grab_json(self, url): @@ -345,22 +369,25 @@ class GVDialer(object): jsonTree = parse_json(flatJson) return jsonTree - def _grab_account_info(self, loginPage = None): - if loginPage is None: + def _grab_account_info(self, accountNumberPage = None): + if accountNumberPage is None: accountNumberPage = self._browser.download(self._accountNumberURL) - else: - accountNumberPage = loginPage + tokenGroup = self._tokenRe.search(accountNumberPage) - if tokenGroup is not None: - self._token = tokenGroup.group(1) + if tokenGroup is None: + raise RuntimeError("Could not extract authentication token from GrandCentral") + self._token = tokenGroup.group(1) + anGroup = self._accountNumRe.search(accountNumberPage) - if anGroup is not None: - self._accountNum = anGroup.group(1) + if atGroup is None: + raise RuntimeError("Could not extract account number from GrandCentral") + self._accountNum = anGroup.group(1) callbackPage = self._browser.download(self._forwardURL) self._callbackNumbers = {} for match in self._callbackRe.finditer(callbackPage): self._callbackNumbers[match.group(2)] = match.group(1) + if len(self._callbackNumber) == 0: self.set_sane_callback() @@ -374,9 +401,13 @@ def test_backend(username, password): print "Token: ", backend._token print "Account: ", backend.get_account_number() print "Callback: ", backend.get_callback_number() - print "All Callback: ", - pprint.pprint(backend.get_callback_numbers()) - print "Recent: ", - pprint.pprint(list(backend.get_recent())) + # print "All Callback: ", + # pprint.pprint(backend.get_callback_numbers()) + # print "Recent: ", + # pprint.pprint(list(backend.get_recent())) + # print "Contacts: ", + # for contact in backend.get_contacts(): + # print contact + # pprint.pprint(list(backend.get_contact_details(contact[0]))) return backend