#!/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.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
"""
+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.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
Grandcentral backend code
"""
import time
import warnings
import traceback
+from xml.sax import saxutils
-from browser_emu import MozillaEmulator
+import browser_emu
class GCDialer(object):
_accessTokenRe = re.compile(r"""<input type="hidden" name="a_t" [^>]*value="(.*)"/>""")
_isLoginPageRe = re.compile(r"""<form method="post" action="https://www.grandcentral.com/mobile/account/login">""")
_callbackRe = re.compile(r"""name="default_number" value="(\d+)" />\s+(.*)\s$""", re.M)
- _accountNumRe = re.compile(r"""<img src="/images/mobile/inbox_logo.gif" alt="GrandCentral" />\s*(.{14})\s* """, re.M)
+ _accountNumRe = re.compile(r"""<input type="hidden" name="gcentral_num" [^>]*value="(.*)"/>""")
_inboxRe = re.compile(r"""<td>.*?(voicemail|received|missed|call return).*?</td>\s+<td>\s+<font size="2">\s+(.*?)\s+ \| \s+<a href="/mobile/contacts/.*?">(.*?)\s?</a>\s+<br/>\s+(.*?)\s?<a href=""", re.S)
_contactsRe = re.compile(r"""<a href="/mobile/contacts/detail/(\d+)">(.*?)</a>""", re.S)
_contactsNextRe = re.compile(r""".*<a href="/mobile/contacts(\?page=\d+)">Next</a>""", re.S)
def __init__(self, cookieFile = None):
# Important items in this function are the setup of the browser emulation and cookie file
- self._browser = MozillaEmulator(None, 0)
+ self._browser = browser_emu.MozillaEmulator(1)
if cookieFile is None:
cookieFile = os.path.join(os.path.expanduser("~"), ".gc_cookies.txt")
self._browser.cookies.filename = cookieFile
self._browser.cookies.load()
self._accessToken = None
- self._accountNum = None
+ self._accountNum = ""
self._lastAuthed = 0.0
self._callbackNumbers = {}
return True
+ def send_sms(self, number, message):
+ raise NotImplementedError("SMS Is Not Supported by GrandCentral")
+
def clear_caches(self):
self.__contacts = None
for c in self._browser.cookies:
if c.name == "pda_forwarding_number":
return c.value
- return None
+ return ""
def get_recent(self):
"""
for match in self._inboxRe.finditer(recentCallsPage):
phoneNumber = match.group(4)
- action = match.group(1)
- date = match.group(2)
- personsName = match.group(3)
+ action = saxutils.unescape(match.group(1))
+ date = saxutils.unescape(match.group(2))
+ personsName = saxutils.unescape(match.group(3))
yield personsName, phoneNumber, date, action
def get_addressbooks(self):
for contact_match in self._contactsRe.finditer(contactsPage):
contactId = contact_match.group(1)
contactName = contact_match.group(2)
- contact = contactId, contactName
+ contact = contactId, saxutils.unescape(contactName)
self.__contacts.append(contact)
yield contact
raise RuntimeError("%s is not accesible" % self._contactDetailURL)
for detail_match in self._contactDetailPhoneRe.finditer(detailPage):
- phoneType = detail_match.group(1)
+ phoneType = saxutils.unescape(detail_match.group(1))
phoneNumber = detail_match.group(2)
yield (phoneType, phoneNumber)
+ def get_messages(self):
+ return ()
+
def _grab_token(self, data):
"Pull the magic cookie from the datastream"
atGroup = self._accessTokenRe.search(data)
self._accessToken = atGroup.group(1)
anGroup = self._accountNumRe.search(data)
- if anGroup is None:
- raise RuntimeError("Could not extract account number from GrandCentral")
- self._accountNum = anGroup.group(1)
+ if anGroup is not None:
+ self._accountNum = anGroup.group(1)
+ else:
+ warnings.warn("Could not extract account number from GrandCentral", UserWarning, 2)
self._callbackNumbers = {}
for match in self._callbackRe.finditer(data):
print "Authenticated: ", backend.is_authed()
print "Login?: ", backend.login(username, password)
print "Authenticated: ", backend.is_authed()
- print "Token: ", backend._accessToken
+ # print "Token: ", backend._accessToken
print "Account: ", backend.get_account_number()
print "Callback: ", backend.get_callback_number()
# print "All Callback: ",