#50 libgmail.py (for domains)

Waseem Daher
Dennis Schafroth

I am trying to use libgmail on my domain account, but it fails.

[root@john libgmail]# python libgmail.py
Gmail account name: dennis
Domain? [leave blank for Gmail]: XXXXXXX.com

Please wait, logging in...
Login successful.

Traceback (most recent call last):
File "libgmail.py", line 1615, in <module>
quotaInfo = ga.getQuotaInfo()
File "libgmail.py", line 507, in getQuotaInfo
return self._cachedQuotaInfo[0][:3]
TypeError: 'NoneType' object is unsubscriptable

Any ideas?


  • Kenneth Duda
    Kenneth Duda

    Logged In: YES
    Originator: NO

    I am having the same problem.

  • Logged In: YES
    Originator: NO

    The login procedure for Google Apps For Your Domain Accounts seems to have changed. It is now key to get the GMAIL_AT cookie.

    I created a patch do get the GMAIL_AT cookie and the page content with the e-mails for the domain accounts. However, this is a dead end, since the format of the page content is different, and needs to be parsed differently.

    It seems that it is much better to use IMAP to interact with Google Mail. One of the libgmail maintainers seems to be working on the new library called gmailimap, which even provides the same API as libgmail (though you don't need this if you were not using libgmail heavily in the first place). The project is here:
    It includes a link to the SourceForge project page. Also, here is a relevant discussion on the subject:

    This library works just as well for the domain accounts. You just need to specify the full e-mail, and keep the same servers and ports ('imap_server' : 'imap.gmail.com', 'imap_port' : 993, 'smtp_server' : 'smtp.gmail.com', 'smtp_port' : 587). Yes, gmail.com servers work for the Google Apps For Your Domain accounts. In either case, the users might need to enable IMAP in their account settings.

    Feel free to take a look at
    for our local modifications of the gmailimap and for how we use it in the Mail widget for the GNOME Online Desktop:

    Below is the patch for getting the GMAIL_AT cookie and the page content for the domain accounts, in case someone will decide to try to parse it:

    --- trunk/bigboard/stocks/mail/libgmail_patched.py (original)
    +++ trunk/bigboard/stocks/mail/libgmail_patched.py Wed May 7 19:51:55 2008
    @@ -84,6 +84,10 @@
    the embedded Javascript.

    + if LG_DEBUG:
    + # This will print the page content we are about to parse to get the e-mails.
    + # The current parsing does not work for what is returned for Google Apps For Your Domain accounts.
    + print "pageContent %s" % pageContent
    lines = pageContent.splitlines()
    data = '\n'.join([x for x in lines if x and x[0] in ['D', ')', ',', ']']])
    #data = data.replace(',,',',').replace(',,',',')
    @@ -141,8 +145,9 @@
    return result

    class SmartRedirectHandler(urllib2.HTTPRedirectHandler):
    - def __init__(self, cookiejar):
    + def __init__(self, cookiejar, extractCookies = False):
    self.cookiejar = cookiejar
    + self.extractCookies = extractCookies

    def http_error_302(self, req, fp, code, msg, headers):
    # The location redirect doesn't seem to change
    @@ -152,6 +157,8 @@
    if new_host:
    req.add_header("Host", new_host.groups()[0])
    + if self.extractCookies:
    + self.cookiejar.extractCookies(headers)
    result = urllib2.HTTPRedirectHandler.http_error_302(
    self, req, fp, code, msg, headers)
    return result
    @@ -179,10 +186,15 @@
    # TODO: Do this all more nicely?
    for cookie in headers.getheaders('Set-Cookie'):
    name, value = (cookie.split("=", 1) + [""])[:2]
    - if LG_DEBUG: print "Extracted cookie `%s`" % (name)
    + if LG_DEBUG:
    + print "Extracted cookie `%s`" % (name)
    if not nameFilter or name in nameFilter:
    - self._cookies[name] = value.split(";")[0]
    - if LG_DEBUG: print "Stored cookie `%s` value `%s`" % (name, self._cookies[name])
    + cookie_value = value.split(";")[0]
    + # do not overwrite the HID cookie when we get information that it is expired
    + if not (name == "HID" and cookie_value == "EXPIRED"):
    + self._cookies[name] = cookie_value
    + if LG_DEBUG:
    + print "Stored cookie `%s` value `%s`" % (name, self._cookies[name])
    if self._cookies[name] == "EXPIRED":
    if LG_DEBUG:
    print "We got an expired cookie: %s:%s, deleting." % (name, self._cookies[name])
    @@ -289,7 +301,7 @@
    self.domain = domain
    if self.domain:
    - URL_LOGIN = "https://www.google.com/a/" + self.domain + "/LoginAction"
    + URL_LOGIN = "https://www.google.com/a/" + self.domain + "/LoginAction2"
    URL_GMAIL = "http://mail.google.com/a/" + self.domain + "/?"
    @@ -306,10 +318,13 @@
    gmail_transport.ConnectHTTPSHandler(proxy = PROXY_URL),
    + # processing of the Google Apps For Your Domain login requires extracting cookies
    + # before redirects, so we pass self.domain for the extractCookies flag to the
    + # SmartRedirectHandler
    self.opener = urllib2.build_opener(
    - SmartRedirectHandler(self._cookieJar))
    + SmartRedirectHandler(self._cookieJar, self.domain))
    elif state:
    # TODO: Check for stale state cookies?
    self.name, self._cookieJar = state.state
    @@ -331,8 +346,8 @@
    data = urllib.urlencode({'continue': URL_GMAIL,
    'at' : 'null',
    'service' : 'mail',
    - 'userName': self.name,
    - 'password': self.password,
    + 'Email': self.name,
    + 'Passwd': self.password,
    data = urllib.urlencode({'continue': URL_GMAIL,
    @@ -363,6 +378,14 @@
    # We aren't concerned with the actual content of this page,
    # just the cookie that is returned with it.
    pageData = self._retrievePage(redirectURL)
    + else:
    + if LG_DEBUG:
    + print "cookie jar has HID cookie: %s" % (self._cookieJar._cookies.has_key('HID'))
    + if self._cookieJar._cookies.has_key('HID'):
    + req2 = urllib2.Request("https://mail.google.com/a/" + self.domain + "/?auth=" + self._cookieJar._cookies['HID'] + "husr=" + self.name + "@" + self.domain)
    + pageData2 = self._retrievePage(req2)
    + else:
    + raise GmailLoginFailure("Login failed. (Wrong username/password?)")

    def _retrievePage(self, urlOrRequest):

  • Waseem Daher
    Waseem Daher

    • assigned_to: nobody --> wdaher
    • status: open --> closed-fixed
  • Waseem Daher
    Waseem Daher

    Logged In: YES
    Originator: NO

    Fixed in CVS. Please check out the latest version; if it still doesn't work for you there, let us know.

  • Logged In: YES
    Originator: NO

    Tested the latest version with a Google Apps For Your Domain account and it works. Thanks!