SOURCES: python-libgmail-cvs.patch (NEW) - fix for "no key 'v' error"

charles charles at pld-linux.org
Mon Jul 11 01:52:59 CEST 2005


Author: charles                      Date: Sun Jul 10 23:52:59 2005 GMT
Module: SOURCES                       Tag: HEAD
---- Log message:
- fix for "no key 'v' error"

---- Files affected:
SOURCES:
   python-libgmail-cvs.patch (NONE -> 1.1)  (NEW)

---- Diffs:

================================================================
Index: SOURCES/python-libgmail-cvs.patch
diff -u /dev/null SOURCES/python-libgmail-cvs.patch:1.1
--- /dev/null	Mon Jul 11 01:52:59 2005
+++ SOURCES/python-libgmail-cvs.patch	Mon Jul 11 01:52:54 2005
@@ -0,0 +1,529 @@
+diff -U 3 -H -d -r -N -- libgmail-0.0.8.orig/constants.py libgmail-0.0.8/constants.py
+--- libgmail-0.0.8.orig/constants.py	2004-08-10 15:18:43.000000000 +0200
++++ libgmail-0.0.8/constants.py	2005-07-11 00:56:08.000000000 +0200
+@@ -1,6 +1,8 @@
+ #
+ # Generated file -- DO NOT EDIT
+ #
++# Note: This file is now edited! 2005-04-25
++#
+ # constants.py -- Useful constants extracted from Gmail Javascript code
+ #
+ # Source version: 44f09303f2d4f76f
+@@ -46,8 +48,8 @@
+ TS_TOTAL = 2
+ TS_ESTIMATES = 3
+ TS_TITLE = 4
+-TS_TIMESTAMP = 5
+-TS_TOTAL_MSGS = 6
++TS_TIMESTAMP = 5 + 1
++TS_TOTAL_MSGS = 6 + 1
+ T_THREADID = 0
+ T_UNREAD = 1
+ T_STAR = 2
+@@ -76,19 +78,20 @@
+ MI_STAR = 3
+ MI_REFMSG = 4
+ MI_AUTHORNAME = 5
+-MI_AUTHOREMAIL = 6
+-MI_MINIHDRHTML = 7
+-MI_DATEHTML = 8
+-MI_TO = 9
+-MI_CC = 10
+-MI_BCC = 11
+-MI_REPLYTO = 12
+-MI_DATE = 13
+-MI_SUBJECT = 14
+-MI_SNIPPETHTML = 15
+-MI_ATTACHINFO = 16
+-MI_KNOWNAUTHOR = 17
+-MI_PHISHWARNING = 18
++MI_AUTHORFIRSTNAME = 6 # ? -- Name supplied by rj
++MI_AUTHOREMAIL = 6 + 1
++MI_MINIHDRHTML = 7 + 1
++MI_DATEHTML = 8 + 1
++MI_TO = 9 + 1
++MI_CC = 10 + 1
++MI_BCC = 11 + 1
++MI_REPLYTO = 12 + 1
++MI_DATE = 13 + 1
++MI_SUBJECT = 14 + 1
++MI_SNIPPETHTML = 15 + 1
++MI_ATTACHINFO = 16 + 1
++MI_KNOWNAUTHOR = 17 + 1
++MI_PHISHWARNING = 18 + 1
+ A_ID = 0
+ A_FILENAME = 1
+ A_MIMETYPE = 2
+diff -U 3 -H -d -r -N -- libgmail-0.0.8.orig/libgmail.py libgmail-0.0.8/libgmail.py
+--- libgmail-0.0.8.orig/libgmail.py	2005-07-11 01:39:40.000000000 +0200
++++ libgmail-0.0.8/libgmail.py	2005-07-09 16:06:27.000000000 +0200
+@@ -1,8 +1,8 @@
+-#!/usr/bin/python
++#!/usr/bin/python2.3
+ #
+ # libgmail -- Gmail access via Python
+ #
+-# Version: 0.0.8 (23 August 2004)
++# Version: 0.0.9 (XX October 2004)
+ #
+ # Author: follower at myrealbox.com
+ #
+@@ -30,6 +30,7 @@
+ 
+ from constants import *
+ 
++import os
+ import re
+ import urllib
+ import urllib2
+@@ -50,6 +51,14 @@
+                     U_ALL_SEARCH, U_DRAFTS_SEARCH,
+                     U_SENT_SEARCH, U_SPAM_SEARCH]
+ 
++# Constants with names not from the Gmail Javascript:
++# TODO: Move to `constants.py`?
++U_SAVEDRAFT_VIEW = "sd"
++
++D_DRAFTINFO = "di"
++# NOTE: All other DI_* field offsets seem to match the MI_* field offsets
++DI_BODY = 19
++
+ versionWarned = False # If the Javascript version is different have we
+                       # warned about it?
+ 
+@@ -83,7 +92,7 @@
+             # TODO: Parse this better/safer?
+             # TODO: Handle "mb" mail bodies better as they can be anything.
+             if value != "": # Empty strings aren't parsed successfully.
+-                parsedValue = eval(value.replace("\n",""))
++                parsedValue = eval(value.replace("\n","").replace(",,",",None,").replace(",,",",None,")) # Yuck! Need two ",," replaces to handle ",,," overlap. TODO: Tidy this up... TODO: It appears there may have been a change in the number & order of at least the CS_* values, investigate.
+             else:
+                 parsedValue = value
+         except SyntaxError:
+@@ -105,7 +114,7 @@
+ 
+     global versionWarned
+     if itemsDict[D_VERSION] != js_version and not versionWarned:
+-        logging.warning("Live Javascript and constants file versions differ.")
++        logging.debug("Live Javascript and constants file versions differ.")
+         versionWarned = True
+ 
+     return itemsDict
+@@ -227,21 +236,30 @@
+     """
+     """
+ 
+-    def __init__(self, name, pw):
++    def __init__(self, name = "", pw = "", state = None):
+         """
+         """
+-        self.name = name
+-        self._pw = pw
++        # TODO: Change how all this is handled?
++        if name and pw:
++            self.name = name
++            self._pw = pw
++            self._cookieJar = CookieJar()
++        elif state:
++            # TODO: Check for stale state cookies?
++            self.name, self._cookieJar = state.state
++        else:
++            raise ValueError("GmailAccount must be instantiated with " \
++                             "either GmailSessionState object or name " \
++                             "and password.")
+ 
+         self._cachedQuotaInfo = None
+         self._cachedLabelNames = None
+         
+-        self._cookieJar = CookieJar()
+-
+ 
+     def login(self):
+         """
+         """
++        # TODO: Throw exception if we were instantiated with state?
+         data = urllib.urlencode({'continue': URL_GMAIL,
+                                  'service': 'mail',
+                                  'Email': self.name,
+@@ -282,10 +300,7 @@
+         pageData = resp.read()
+ 
+         # Extract cookies here
+-        # NOTE: "SID" & "GV" really only need to be extracted once
+-        #       as they don't seem to change during session.
+-        self._cookieJar.extractCookies(resp, [ACTION_TOKEN_COOKIE,
+-                                              "SID", "GV"])
++        self._cookieJar.extractCookies(resp)
+ 
+         # TODO: Enable logging of page data for debugging purposes?
+ 
+@@ -460,13 +475,22 @@
+         return at
+ 
+ 
+-    def sendMessage(self, msg):
++    def sendMessage(self, msg, asDraft = False, _extraParams = None):
+         """
+ 
+           `msg` -- `GmailComposedMessage` instance.
++
++          `_extraParams` -- Dictionary containing additional parameters
++                            to put into POST message. (Not officially
++                            for external use, more to make feature
++                            additional a little easier to play with.)
+         
++        Note: Now returns `GmailMessageStub` instance with populated
++              `id` (and `_account`) fields on success or None on failure.
++
+         """
+-        params = {U_VIEW: U_SENDMAIL_VIEW,
++        # TODO: Handle drafts separately?
++        params = {U_VIEW: [U_SENDMAIL_VIEW, U_SAVEDRAFT_VIEW][asDraft],
+                   U_REFERENCED_MSG: "",
+                   U_THREAD: "",
+                   U_DRAFT_MSG: "",
+@@ -479,6 +503,9 @@
+                   "msgbody": msg.body,
+                   }
+ 
++        if _extraParams:
++            params.update(_extraParams)
++
+         # Amongst other things, I used the following post to work out this:
+         # <http://groups.google.com/groups?
+         #  selm=mailman.1047080233.20095.python-list%40python.org>
+@@ -525,8 +552,15 @@
+ 
+         items = self._parsePage(req)
+ 
+-        # TODO: Check composeid & store new thread id?
+-        return (items[D_SENDMAIL_RESULT][SM_SUCCESS] == 1)
++        # TODO: Check composeid?
++        result = None
++        resultInfo = items[D_SENDMAIL_RESULT]
++        
++        if resultInfo[SM_SUCCESS]:
++            result = GmailMessageStub(id = resultInfo[SM_NEWTHREADID],
++                                      _account = self)
++            
++        return result
+ 
+ 
+     def trashMessage(self, msg):
+@@ -545,8 +579,8 @@
+         # TODO: Mark as trashed on success?
+         return (items[D_ACTION_RESULT][AR_SUCCESS] == 1)
+ 
+-        
+-    def trashThread(self, thread):
++
++    def _doThreadAction(self, actionId, thread):
+         """
+         """
+         # TODO: Decide if we should make this a method of `GmailThread`.
+@@ -554,18 +588,111 @@
+         params = {
+             U_SEARCH: U_ALL_SEARCH, #TODO:Check this search value always works.
+             U_VIEW: U_UPDATE_VIEW,
+-            U_ACTION: U_MARKTRASH_ACTION,
++            U_ACTION: actionId,
+             U_ACTION_THREAD: thread.id,
+             U_ACTION_TOKEN: self._getActionToken(),
+             }
+ 
+         items = self._parsePage(_buildURL(**params))
+ 
+-        # TODO: Mark as trashed on success?
+         return (items[D_ACTION_RESULT][AR_SUCCESS] == 1)
++        
++        
++    def trashThread(self, thread):
++        """
++        """
++        # TODO: Decide if we should make this a method of `GmailThread`.
++        # TODO: Should we check we have been given a `GmailThread` instance?
+ 
++        result = self._doThreadAction(U_MARKTRASH_ACTION, thread)
+         
++        # TODO: Mark as trashed on success?
++        return result
++
++
++    def _createUpdateRequest(self, actionId): #extraData):
++        """
++        Helper method to create a Request instance for an update (view)
++        action.
++
++        Returns populated `Request` instance.
++        """
++        params = {
++            U_VIEW: U_UPDATE_VIEW,
++            }
++
++        data = {
++            U_ACTION: actionId,
++            U_ACTION_TOKEN: self._getActionToken(),
++            }
++
++        #data.update(extraData)
++
++        req = urllib2.Request(_buildURL(**params),
++                              data = urllib.urlencode(data))
++
++        return req
++
++
++    # TODO: Extract additional common code from handling of labels?
++    def createLabel(self, labelName):
++        """
++        """
++        req = self._createUpdateRequest(U_CREATECATEGORY_ACTION + labelName)
++
++        # Note: Label name cache is updated by this call as well. (Handy!)
++        items = self._parsePage(req)
++
++        return (items[D_ACTION_RESULT][AR_SUCCESS] == 1)
++
++
++    def deleteLabel(self, labelName):
++        """
++        """
++        # TODO: Check labelName exits?
++        req = self._createUpdateRequest(U_DELETECATEGORY_ACTION + labelName)
++
++        # Note: Label name cache is updated by this call as well. (Handy!)
++        items = self._parsePage(req)
++
++        return (items[D_ACTION_RESULT][AR_SUCCESS] == 1)
++
++
++    def renameLabel(self, oldLabelName, newLabelName):
++        """
++        """
++        # TODO: Check oldLabelName exits?
++        req = self._createUpdateRequest("%s%s^%s" % (U_RENAMECATEGORY_ACTION,
++                                                   oldLabelName, newLabelName))
++
++        # Note: Label name cache is updated by this call as well. (Handy!)
++        items = self._parsePage(req)
++
++        return (items[D_ACTION_RESULT][AR_SUCCESS] == 1)
++
++
++    def storeFile(self, filename, label = None):
++        """
++        """
++        # TODO: Handle files larger than single attachment size.
++        # TODO: Allow file data objects to be supplied?
++        FILE_STORE_VERSION = "FSV_01"
++        FILE_STORE_SUBJECT_TEMPLATE = "%s %s" % (FILE_STORE_VERSION, "%s")
++
++        subject = FILE_STORE_SUBJECT_TEMPLATE % os.path.basename(filename)
+ 
++        msg = GmailComposedMessage(to="", subject=subject, body="",
++                                   filenames=[filename])
++
++        draftMsg = self.sendMessage(msg, asDraft = True)
++
++        if draftMsg and label:
++            draftMsg.addLabel(label)
++
++        return draftMsg
++
++        
++        
+ def _splitBunches(infoItems):
+     """
+ 
+@@ -613,8 +740,61 @@
+         return len(self._threads)
+ 
+ 
++from cPickle import load, dump
+ 
+-class GmailThread:
++class GmailSessionState:
++    """
++    """
++
++    def __init__(self, account = None, filename = ""):
++        """
++        """
++        if account:
++            self.state = (account.name, account._cookieJar)
++        elif filename:
++            self.state = load(open(filename, "rb"))
++        else:
++            raise ValueError("GmailSessionState must be instantiated with " \
++                             "either GmailAccount object or filename.")
++
++
++    def save(self, filename):
++        """
++        """
++        dump(self.state, open(filename, "wb"), -1)
++
++
++class _LabelHandlerMixin(object):
++    """
++
++    Note: Because a message id can be used as a thread id this works for
++          messages as well as threads.
++    """
++
++    def addLabel(self, labelName):
++        """
++        """
++        # Note: It appears this also automatically creates new labels.
++        result = self._account._doThreadAction(U_ADDCATEGORY_ACTION+labelName,
++                                               self)
++        # TODO: Update list of attached labels?
++        return result
++
++
++    def removeLabel(self, labelName):
++        """
++        """
++        # TODO: Check label is already attached?
++        # Note: An error is not generated if the label is not already attached.
++        result = \
++               self._account._doThreadAction(U_REMOVECATEGORY_ACTION+labelName,
++                                             self)
++        # TODO: Update list of attached labels?
++        return result
++    
++
++
++class GmailThread(_LabelHandlerMixin):
+     """
+ 
+ 
+@@ -636,6 +816,9 @@
+         self.id = threadInfo[T_THREADID] # TODO: Change when canonical updated?
+         self.subject = threadInfo[T_SUBJECT_HTML]
+ 
++        self.snippet = threadInfo[T_SNIPPET_HTML]
++        #self.extraSummary = threadInfo[T_EXTRA_SNIPPET] #TODO: What is this?
++
+         # TODO: Store other info?
+         # Extract number of messages in thread/conversation.
+ 
+@@ -654,7 +837,6 @@
+         self._messages = []
+ 
+         
+-
+     def __len__(self):
+         """
+         """
+@@ -680,25 +862,59 @@
+                                                  th = thread.id,
+                                                  q = "in:anywhere")
+ 
++        result = []
+         # TODO: Handle this better?
+-        msgsInfo = items[D_MSGINFO]
++        # Note: This handles both draft & non-draft messages in a thread...
++        for key, isDraft in [(D_MSGINFO, False), (D_DRAFTINFO, True)]:
++            try:
++                msgsInfo = items[key]
++            except KeyError:
++                # No messages of this type (e.g. draft or non-draft)
++                continue
++            else:
++                # TODO: Handle special case of only 1 message in thread better?
++                if type(msgsInfo) != type([]):
++                    msgsInfo = [msgsInfo]
+ 
+-        # TODO: Handle special case of only one message in thread better?
+-        if type(msgsInfo) != type([]):
+-            msgsInfo = [msgsInfo]
++                result += [GmailMessage(thread, msg, isDraft = isDraft)
++                           for msg in msgsInfo]
+ 
+-        return [GmailMessage(thread, msg)
+-                for msg in msgsInfo]
++        return result
++
++
++    # TODO: Add property to retrieve list of labels for this message.
++    
++
++
++class GmailMessageStub(_LabelHandlerMixin):
++    """
++
++    Intended to be used where not all message information is known/required.
++
++    NOTE: This may go away.
++    """
+ 
++    # TODO: Provide way to convert this to a full `GmailMessage` instance
++    #       or allow `GmailMessage` to be created without all info?
++
++    def __init__(self, id = None, _account = None):
++        """
++        """
++        self.id = id
++        self._account = _account
++    
+ 
+         
+ class GmailMessage(object):
+     """
+     """
+     
+-    def __init__(self, parent, msgData):
++    def __init__(self, parent, msgData, isDraft = False):
+         """
++
++        Note: `msgData` can be from either D_MSGINFO or D_DRAFTINFO.
+         """
++        # TODO: Automatically detect if it's a draft or not?
+         # TODO Handle this better?
+         self._parent = parent
+         self._account = self._parent._account
+@@ -712,6 +928,9 @@
+ 
+         # TODO: Populate additional fields & cache...(?)
+ 
++        # TODO: Handle body differently if it's from a draft?
++        self.isDraft = isDraft
++        
+         self._source = None
+ 
+ 
+@@ -720,6 +939,8 @@
+         """
+         if not self._source:
+             # TODO: Do this more nicely...?
++            # TODO: Strip initial white space & fix up last line ending
++            #       to make it legal as per RFC?
+             self._source = self._account.getRawMessage(self.id)
+ 
+         return self._source
+@@ -761,6 +982,23 @@
+     content = property(_getContent, doc = "")
+ 
+ 
++    def _getFullId(self):
++        """
++
++        Returns the "full path"/"full id" of the attachment. (Used
++        to refer to the file when forwarding.)
++
++        The id is of the form: "<thread_id>_<msg_id>_<attachment_id>"
++        
++        """
++        return "%s_%s_%s" % (self._parent._parent.id,
++                             self._parent.id,
++                             self.id)
++
++    _fullId = property(_getFullId, doc = "")
++
++
++
+ class GmailComposedMessage:
+     """
+     """
================================================================



More information about the pld-cvs-commit mailing list