bigboard r7336 - in trunk/bigboard: gmailimap stocks/files stocks/mail



Author: marinaz
Date: Wed May  7 20:52:52 2008
New Revision: 7336
URL: http://svn.gnome.org/viewvc/bigboard?rev=7336&view=rev

Log:
Use the modified gmailimap library to get Google e-mail in the MailStock. This works for both Gmail and GAFYD accounts.

MailStock no longer depends on libgmail.


Added:
   trunk/bigboard/gmailimap/__init__.py
Modified:
   trunk/bigboard/gmailimap/gmailimap.py
   trunk/bigboard/gmailimap/libgmail-imap.py
   trunk/bigboard/stocks/files/FilesStock.py
   trunk/bigboard/stocks/mail/MailStock.py

Added: trunk/bigboard/gmailimap/__init__.py
==============================================================================
--- (empty file)
+++ trunk/bigboard/gmailimap/__init__.py	Wed May  7 20:52:52 2008
@@ -0,0 +1 @@
+# This file needs to be present for the gmailimap module to be found.

Modified: trunk/bigboard/gmailimap/gmailimap.py
==============================================================================
--- trunk/bigboard/gmailimap/gmailimap.py	(original)
+++ trunk/bigboard/gmailimap/gmailimap.py	Wed May  7 20:52:52 2008
@@ -61,7 +61,7 @@
             for option,val in imap_options.items():
                 if option in ('imap_port','smtp_port'):
                     val = int(val)
-                    setattr(self,option,val)
+                setattr(self,option,val)                
         else:
             self.logger.debug("Parsing imap file")
             try:
@@ -95,13 +95,20 @@
         #self.logger.debug("Parsing message list: %s" % mlist)
         msg_objects_list = []
         proper_msg_list = []
-        for i in range(0,len(mlist),2):# jump over crap returnt by imap
-            try:
+        for i in range(0,len(mlist),2): # even parts of the list are flags which we process together with the corresponding messages
+            try: 
                 id = int(mlist[i][0].split('(RFC822')[0])
             except (ValueError,AttributeError),e:
                 EmailParseError,"Message # %s parse error: %s" % (i,e) 
-            proper_msg_list.append((id,mlist[i][1]))
-        
+            seenFlag = False
+            if len(mlist) > i and mlist[i+1].find("Seen") >= 0:
+                seenFlag = True    
+            proper_msg_list.append((id, mlist[i][1], seenFlag))
+     
+            #self.logger.debug("message %s" % mlist[i][1])
+            #if len(mlist[i]) > 2:
+            #    self.logger.debug("flags %s" % mlist[i][2])
+
         proper_msg_list.sort(reverse=True)# we use tuples with the gmail id as the first member    
         for item in proper_msg_list:
             try:
@@ -109,7 +116,7 @@
             except email.Errors.MessageError, e:
                 self.logger.error("Message %s parse error: %s" % (item[1],e))
                 raise EmailParseError,"Message %s parse error: %s" % (item[1],e) 
-            msg_objects_list.append((item[0],msgs))
+            msg_objects_list.append((item[0],msgs,item[2]))
         return msg_objects_list
 
     def _make_message_threads(self,mlist):
@@ -135,7 +142,7 @@
         except imaplib.IMAP4.error,e:
             self.logger.error("imap server login raised an exception, check your ~/.imap file")
             self.logger.error("reason for the exception: %s" % e)
-            sys.exit(1)
+            raise NoLoginError, e
         else:
             self.logger.debug("Logged into Imap_ssl server")
             return True
@@ -150,7 +157,7 @@
         except imaplib.IMAP4.error,e:
             self.logger.error("imap server login raised an exception, check your ~/.imap file")
             self.logger.error("reason for the exception: %s" % e)
-            sys.exit(1)
+            raise GmailError, 'Failed to parse data returned from gmail.'
         else:
             self.logger.debug("Logged into Imap server")
             return True
@@ -168,25 +175,11 @@
             except Exception:
                 self.logger.exception("Error while closing connection")
     
-    def get_unread_message_count(self):
+    def get_unread_message_id_list(self):
         """Get a list of unread message ids from the inbox.
         Return a list with ids (integers)
         """
-        result, message = self.AC.select(readonly=1)
-        if result != 'OK':
-            raise ServerError,message
-        # fetch message ids from server
-        self.logger.debug("Fetching ids from unread messages, if any")
-        try:
-            typ, data = self.AC.search(None, '(UNSEEN UNDELETED)')
-            msg_id_list = [m for m in data[0].split()]
-        except KeyError,e:
-            print "get_unread_message_count raised unkown error", e
-            # TODO: XXX Can't remember what this except catches :-(
-        if not msg_id_list:
-            self.logger.debug("No new messages")
-            return []
-        return msg_id_list
+        return self.get_message_ids_by_folder('INBOX', '(UNSEEN UNDELETED)')
     
     def get_all_messages(self):
         """Get all messages from inbox.
@@ -194,7 +187,7 @@
         there are no unread messages. The message objects are message objects from the 
         Python email module.
         Message objects can have multiple messages representing message threads."""
-        return self.get_messages_by_folder('INBOX',all=True)
+        return self.get_messages_by_folder('INBOX')
 
     def get_unread_messages(self):
         """Get unread messages from the inbox.
@@ -203,37 +196,35 @@
         Python email module.
         Message objects can have multiple messages representing message threads.
         """
-        result, message = self.AC.select(readonly=1)
-        if result != 'OK':
-            raise ServerError,message
-            
-        msg_id_list = self.get_unread_message_count()
-        if not msg_id_list:
-            return []
-           
-        # we have unread message ids
-        msg_list = []
-        self.logger.debug("Getting new messages")
-        msg_list = self.AC.fetch(','.join(msg_id_list),'(RFC822)')[1]
-        return self._parse_message_list(msg_list)
+        return self.get_messages_by_folder("INBOX", '(UNSEEN UNDELETED)')
 
-    def get_messages_by_folder(self,folder,all=None):
+    def get_messages_by_folder(self,folder,criterion=None,max_count=None):
         """Get contents of the @folder.
         Returns a list of email.message.Message objects.
         """
+        msg_id_list = self.get_message_ids_by_folder(folder, criterion)
+        if max_count and max_count < len(msg_id_list):
+            msg_id_list = msg_id_list[-max_count:]
+        comma_separated_msg_id_list = ','.join([m for m in msg_id_list])
+        msg_list = self.AC.fetch(comma_separated_msg_id_list,'(RFC822 FLAGS)')[1]
+        return self._parse_message_list(msg_list)
+
+    def get_message_ids_by_folder(self,folder,criterion=None):
+        """Get the ids of messages in the @folder.
+        Suitable for getting the count of messages of a certain type in the folder.
+        """
         result, message = self.AC.select(folder,readonly=1)
         if result != 'OK':
             raise ServerError,message
-        self.logger.debug("Getting messages from %s" % folder)
-        if all:
-            result, data = self.AC.search(None,'ALL')
+        self.logger.debug("Getting message ids from %s" % folder)
+        if criterion:
+            result, data = self.AC.search(None, criterion)
         else:
-            result, data = self.AC.search(None, '(UNDELETED)')
+            result, data = self.AC.search(None,'ALL')
         if result != 'OK':
             raise ServerError,message
-        msg_id_list = ','.join([m for m in data[0].split()])
-        msg_list = self.AC.fetch(msg_id_list,'(RFC822)')[1]
-        return self._parse_message_list(msg_list)
+        msg_id_list = data[0].split()
+        return msg_id_list
 
     def get_folder_names(self):
         """List all the Gmail mailfolders.
@@ -246,6 +237,25 @@
             raise ServerError,data 
         return data
 
+    def get_folder_unread_counts(self):
+        """ this includes INBOX """
+        data = self.get_folder_names()
+        labels = {}
+        # parse the list with strings to get the names
+        for item in data:
+            # self.logger.debug("item is %s" % (item))
+            line = item.split('"/"')[1].split('/')
+            if '[Gmail]' in item and len(line) > 1:
+                # since Gmail specific names such as "Spam" or "Trash" are not allowed as Gmail labels,
+                # there should not be any danger of duplicate keys here        
+                labels["[Gmail]/" + line[1][0:-1]] = len(self.get_message_ids_by_folder("[Gmail]/" + line[1][0:-1],'(UNSEEN UNDELETED)'))   
+            elif '[Gmail]' not in item:
+                labels[line[0][2:-1]] = len(self.get_message_ids_by_folder(line[0][2:-1],'(UNSEEN UNDELETED)'))
+        return labels
+
+    def get_folder_unread_count(self, folder):
+        return len(self.get_message_ids_by_folder(folder,'(UNSEEN UNDELETED)'))
+
     def search(self,folder,pattern):
         """Search for messages in @folder which match @pattern.
         Returns a list with email.message.Message objects or an empty list
@@ -260,7 +270,7 @@
         if result != 'OK':
             raise ServerError,data
         msg_id_list = ','.join([m for m in data[0].split()])
-        msg_list = self.AC.fetch(msg_id_list,'(RFC822)')[1]
+        msg_list = self.AC.fetch(msg_id_list,'(RFC822 FLAGS)')[1]
         return self._parse_message_list(msg_list)    
 
 ##      THIS DON'T WORK ON GMAIL IMAP, seems that THREAD is a unkown command ???
@@ -344,7 +354,7 @@
             server.ehlo()
             server.starttls()
             server.ehlo()
-            server.login(self.imap_user,self.imap_password)
+            server.login(self.imap_user,self.imap_pass)
             server.sendmail(mesg.sender, [mesg.recipient], mesg.msg.as_string())
         except (smtplib.SMTPHeloError,\
                 smtplib.SMTPAuthenticationError,\
@@ -392,7 +402,7 @@
     ac.login_ssl()
     
     print "number of unread messages"
-    print ac.get_unread_message_count()
+    print len(ac.get_unread_message_id_list())
     
     print "unread messages from inbox"
     mgs = ac.get_unread_messages()

Modified: trunk/bigboard/gmailimap/libgmail-imap.py
==============================================================================
--- trunk/bigboard/gmailimap/libgmail-imap.py	(original)
+++ trunk/bigboard/gmailimap/libgmail-imap.py	Wed May  7 20:52:52 2008
@@ -35,7 +35,8 @@
           `folderName` -- As set in Gmail interface.
 
         libgmail: Returns a `GmailSearchResult` instance."""
-        result = self.get_messages_by_folder(self,folderName,allPages)
+        # TODO: convert allPages to criterion
+        result = self.get_messages_by_folder(self,folderName)
         
         
     def getMessagesByQuery(self, query,  allPages = False):
@@ -101,7 +102,8 @@
         # There are issues with Gmail labels
         # When messages are deleted, moved to Trash, with there label attached
         # they still returnt by GM.search(None, '(UNDELETED)')
-        return self.get_messages_by_folder(label,all)    
+        # TODO: convert all to criterion
+        return self.get_messages_by_folder(label)    
     
     def getLabelNames(self,foo):
         """List all the Gmail labels.
@@ -151,14 +153,16 @@
     ac.login_ssl()
     
 ##    print "number of unread messages"
-##    print ac.get_unread_message_count()
+##    print len(ac.get_unread_message_id_list())
 ##    
 ##    print "unread messages from inbox"
 ##    pprint.pprint(ac.get_unread_messages())
     
     print "all INBOX folder messages:"
     # same as get_unread_messages but now also the read messages
-    pprint.pprint(ac.get_all_messages())
+    messages = ac.get_all_messages()
+    for message in messages:
+        print message[1].as_string()
     
     print "folders:",ac.get_folder_names()
     

Modified: trunk/bigboard/stocks/files/FilesStock.py
==============================================================================
--- trunk/bigboard/stocks/files/FilesStock.py	(original)
+++ trunk/bigboard/stocks/files/FilesStock.py	Wed May  7 20:52:52 2008
@@ -189,7 +189,6 @@
 
     def get_type(self):
         if self._mimetype:
-            _logger.debug("mimetype is %s class is %s dir %s" % (self._mimetype, self._mimetype.__class__, dir(self._mimetype)))  
             return gnomevfs.mime_get_description(self._mimetype)
         return None
 
@@ -365,7 +364,6 @@
                 _launch_vfsmimeapp(app, fobj.get_url())      
             directory = apps_directory.get_app_directory()
             for app in apps:
-                _logger.debug("mime type: %s got app %s", mime, app)
                 button = hippo.CanvasButton()
                 labelhbox = gtk.HBox()
                 labelhbox.set_border_width(1)

Modified: trunk/bigboard/stocks/mail/MailStock.py
==============================================================================
--- trunk/bigboard/stocks/mail/MailStock.py	(original)
+++ trunk/bigboard/stocks/mail/MailStock.py	Wed May  7 20:52:52 2008
@@ -14,11 +14,10 @@
 import bigboard.google_stock as google_stock  
 from bigboard.big_widgets import CanvasHBox, CanvasVBox, Button, Header, PrelightingCanvasBox
 import bigboard.libbig as libbig
+import bigboard.gmailimap.gmailimap as gmailimap
 #TODO: add a scrollable view for emails
 #import bigboard.scroll_ribbon as scroll_ribbon
 
-import libgmail_patched as libgmail
-
 _logger = logging.getLogger('bigboard.stocks.MailStock')
 
 def replace_chr(m):
@@ -55,6 +54,11 @@
     # Return the sanely filtered content
     return textContent.getvalue()
 
+def make_folder_name_pretty(folder_name):
+    if folder_name.find("[Gmail]/") == 0:
+        return folder_name[len("[Gmail]/"):]
+    return folder_name
+
 class LabelSlideout(ThemedSlideout):
     __gsignals__ = {
                     'changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_STRING, )),
@@ -64,12 +68,11 @@
         vbox = CanvasVBox(border_color=0x0000000ff, spacing=4)
         self.get_root().append(vbox)
         header = Header(topborder=False)
-        account_text = hippo.CanvasText(classes='header', text=ga.name)
+        account_text = hippo.CanvasText(classes='header', text=ga.imap_user)
         header.append(account_text, hippo.PACK_EXPAND)        
         vbox.append(header)
         try:
-            folderCounts = ga.getFolderCounts()
-            folderCounts["unread"] = ga.getUnreadMsgCount()
+            folderCounts = ga.get_folder_unread_counts()
         except urllib2.URLError:
             error = hippo.CanvasText(text=google_stock.FAILED_TO_CONNECT_STRING)
             vbox.append(error)    
@@ -80,14 +83,15 @@
             box.connect('button-release-event', self.on_button_release_event, label)
             vbox.append(box)
             hbox = CanvasHBox(spacing=4, padding=4)
-            text= hippo.CanvasText(text=label, xalign=hippo.ALIGNMENT_START)
+            text= hippo.CanvasText(text=make_folder_name_pretty(label), xalign=hippo.ALIGNMENT_START)
             hbox.append(text)
             text= hippo.CanvasText(text=number, xalign=hippo.ALIGNMENT_START)
             hbox.append(text, flags=hippo.PACK_END)
             box.append(hbox)
     
-    def on_button_release_event (self, hippo_item, event, label_text):
-        self.emit('changed', label_text)
+    def on_button_release_event (self, hippo_item, event, label):
+        self.emit('changed', label)
+        self.emit('close', True)
 
 class EmailSlideout(ThemedSlideout):
     def __init__(self, thread):
@@ -95,25 +99,31 @@
         vbox = CanvasVBox(border_color=0x0000000ff, spacing=4)
         self.get_root().append(vbox)
         self.__header = Header(topborder=False)
-        self.id = thread.id        
+        self.id = thread[0]
 
-        subject = gmail_jshtml_str_parse(thread.subject)
+        subject = gmail_jshtml_str_parse(thread[1]['subject'])
         
         subject_box = hippo.CanvasText(classes='header', text=subject)
         self.__header.append(subject_box, hippo.PACK_EXPAND)
         vbox.append(self.__header)
         
-        for key in ("date", "categories", "snippet"):
-            value = getattr(thread, key, None)
-            if value:
-                if type(value) is list:
-                    s = ", ".join(value)
-                if type(value) is str:
-                    _logger.debug("passing in %s" % value) 
-                    s = gmail_jshtml_str_parse(value)
-
-                box = hippo.CanvasText(text=s, xalign=hippo.ALIGNMENT_START)
-                vbox.append(box)
+        # for key in thread[1].keys():
+        #     _logger.debug("key %s" % key)
+               
+        value = thread[1]["date"]
+        s = gmail_jshtml_str_parse(value)
+        box = hippo.CanvasText(text=s, xalign=hippo.ALIGNMENT_START)
+        vbox.append(box)
+        message = thread[1]
+        while message.is_multipart():
+            message = message.get_payload(0)
+        
+        if len(message.get_payload()) <= 200:
+            value = message.get_payload()
+        else:
+            value = message.get_payload()[:200] + "..."
+        box = hippo.CanvasText(text=value, xalign=hippo.ALIGNMENT_START)
+        vbox.append(box)        
         
         #todo: nicify email, strip out junk, and show actual email
         #email_source = thread[len(thread)-1].source
@@ -153,10 +163,9 @@
     
     def update_google_data(self, gobj):
         self.__current_gobj = gobj
-        username = gobj.get_account().get_username()
+        username = gobj.get_account().get_username_as_google_email()
         password = gobj.get_account().get_password()
-        domain = gobj.get_account().get_url() 
-        self.__update_email_box(username, password, domain)
+        self.__update_email_box(username, password)
    
     def remove_google_data(self, gobj):
         # sometimes we don't even get the self.__current_gobj because the polling task didn't start, so 
@@ -168,61 +177,65 @@
             self.__google_account = None
             self.__logged_in = False
 
-    def __update_email_box (self, username, password, domain):       
+    def __update_email_box (self, username, password):       
         _logger.debug("will update mailbox")
         try:
-            if self.__google_account is None or username != self.__google_account.name or \
-               password != self.__google_account.password:
-                if domain and (len(domain) == 0 or domain == "gmail.com"):
-                    domain = None 
-                _logger.debug("username %s domain %s" % (username, domain))
-                self.__google_account = libgmail.GmailAccount(username, password, domain = domain)
-                self.__google_account.login()
+            if self.__google_account is None or username != self.__google_account.imap_user or \
+               password != self.__google_account.imap_pass:
+                self.__google_account = gmailimap.ImapAccount({'imap_server' : 'imap.gmail.com', 'imap_port' : 993, 'imap_user' : username, 'imap_pass' : password, 'smtp_server' : 'smtp.gmail.com', 'smtp_port' : 587})
+                self.__google_account.login_ssl()
             elif not self.__logged_in:
-                self.__google_account.login()                
+                self.__google_account.login_ssl()             
 
             self.__logged_in = True
 
-            if self.__folder == 'inbox':
-                threads = self.__google_account.getMessagesByFolder(self.__folder)
-            elif self.__folder == 'unread':
-                threads = self.__google_account.getUnreadMessages()
-            else:
-                threads = self.__google_account.getMessagesByLabel(self.__folder)
-
+            threads = self.__google_account.get_messages_by_folder(self.__folder, max_count=self.__display_limit)
+            
             self._box.remove_all()
-            account = hippo.CanvasText(classes='header', text=self.__google_account.name)
+            account = hippo.CanvasText(classes='header', text=self.__google_account.imap_user)
             self._box.append(account)
             
             box = PrelightingCanvasBox()
             box.connect("button-press-event", self.create_label_slideout, self.__google_account)
             self._box.append(box)
-            label = hippo.CanvasText(text=self.__folder, font="14px Bold Italic")
+            label = hippo.CanvasText(text=make_folder_name_pretty(self.__folder), font="14px Bold Italic")
             box.append(label)
             
             i = 0
             for thread in threads:
                 if i >= self.__display_limit: break
                 
-                subject = gmail_jshtml_str_parse(thread.subject, True)
+                subject = gmail_jshtml_str_parse(thread[1]['subject'], True)
                 
                 box = PrelightingCanvasBox()
                 box.connect("button-press-event", self.create_email_slideout, thread)
                 self._box.append(box)
-                email = hippo.CanvasText(markup=subject, xalign=hippo.ALIGNMENT_START)
+                seenFlag = thread[2]
+                if seenFlag:
+                    email = hippo.CanvasText(markup=subject, xalign=hippo.ALIGNMENT_START)
+                else:
+                    email = hippo.CanvasText(markup=subject, xalign=hippo.ALIGNMENT_START, font="Bold")
                 box.append(email)
                 i += 1
-            labelsDict = self.__google_account.getFolderCounts()
-            footer = hippo.CanvasText(classes='footer', text="%s unread" % labelsDict['inbox'], font="14px Bold Italic")
+            folder_unread_count = self.__google_account.get_folder_unread_count(self.__folder)
+            footer = hippo.CanvasText(classes='footer', text="%s unread" % folder_unread_count, font="14px Bold Italic")
             self._box.append(footer)
             print "updated mailbox"
             
-        except libgmail.GmailLoginFailure:
+        except gmailimap.NoLoginError, e:
             self._box.remove_all()
             self.__logged_in = False
             if self._login_button.get_property("text") == google_stock.CHECKING_LOGIN_STRING:
-                error = hippo.CanvasText(text=google_stock.FAILED_TO_LOGIN_STRING, size_mode=hippo.CANVAS_SIZE_WRAP_WORD)
-                self._box.append(error)
+                error_text = google_stock.FAILED_TO_LOGIN_STRING
+            elif str(e.message).find("not enabled for IMAP") >=0:
+                # TODO: provide a link to the settings page once the mail stock design is finalized
+                error_text = "Enable IMAP in your Google mail settings for " + username
+                # this will force us to check the account again during the next polling task
+                self.__google_account = None
+            else:
+                error_text = e.message
+            error = hippo.CanvasText(text=error_text, size_mode=hippo.CANVAS_SIZE_WRAP_WORD)
+            self._box.append(error)  
             self._login_button.set_property('text', google_stock.LOGIN_TO_GOOGLE_STRING) 
             self._box.append(self._login_button)
         except urllib2.URLError:
@@ -257,7 +270,7 @@
         self.show_slideout(widget)
     
     def create_email_slideout(self, widget, hippo_event, data):
-        if type(self.__slideout) is EmailSlideout and self.__slideout.id == data.id and \
+        if type(self.__slideout) is EmailSlideout and self.__slideout.id == data[0] and \
            self.__last_slideout_event_time == gtk.get_current_event_time():
             self.__slideout = None
             return 
@@ -266,7 +279,7 @@
     
     def on_label_changed (self, slideout, label):
         self.__folder = label
-        self.__update_email_box(self.__google_account.name, None)
+        self.__update_email_box(self.__google_account.imap_user, self.__google_account.imap_pass)
     
     def __on_more_button(self):
         libbig.show_url("http://mail.google.com/mail";)



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]