screenlets.plugins.Mail

Package screenlets :: Package plugins :: Module Mail |
[hide private]
[frames] | no frames]

# Source Code for Module screenlets.plugins.Mail

      1  # This program is free software: you can redistribute it and/or modify
      2  # it under the terms of the GNU General Public License as published by
      3  # the Free Software Foundation, either version 3 of the License, or
      4  # (at your option) any later version.
      5  #
      6  # This program is distributed in the hope that it will be useful,
      7  # but WITHOUT ANY WARRANTY; without even the implied warranty of
      8  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      9  # GNU General Public License for more details.
     10  #
     11  # You should have received a copy of the GNU General Public License
     12  # along with this program.  If not, see <http://www.gnu.org/licenses/>.
     13
     14  #  mail module (c) Whise (Helder Fraga) 2008 <helder.fraga@hotmail.com>
     15
     16  import screenlets
     17  import dbus
     18  import os
     19  import sys
     20  import stat
     21  import gettext
     22  import re
     23  import urllib
     24  gettext.textdomain('screenlets')
     25  gettext.bindtextdomain('screenlets', screenlets.INSTALL_PREFIX +  '/share/locale')
     26  import gobject
     27  import socket
     28  import threading
     29  try:
     30          import poplib
     31  except ImportError, err:
     32          print " !!!Please install python poplib :", err
     33  try:
     34          import imaplib
     35  except ImportError, err:
     36          print " !!!Please install python imaplib :", err
     37
     38  try:
     39          import gnomevfs
     40  except ImportError, err:
     41          print " !!!Please install python gnomevfs :", err
     42
     43
     44 -def get_KMail_num():
     45          """This gets the unread mail number of kmail"""
     46          kmail = commands.getoutput("dcop kmail default checkMail; sleep 5; echo ' ' | tr -d '\n'; dcop kmail KMailIface getFolder /Krealia/Inbox > /dev/null; dcop 'DCOPRef(kmail,FolderIface)' unreadMessages | tr -d '\n'; echo ' '")
     47          if kmail.find("ERROR: Couldn't attach to DCOP server!") != -1:
     48                  return None
     49          else:
     50                  return kmail
     51
     52 -def get_GMail_Num(login, password):
     53          """This output the number of messages of gmail box"""
     54          f = os.popen("wget --no-check-certificate -qO - https://%s:%s@mail.google.com/mail/feed/atom" % (urllib.pathname2url(login), urllib.pathname2url(password)))
     55          a = f.read()
     56          f.close()
     57          match = re.search("<fullcount>([0-9]+)</fullcount>", a)
     58          if match == None:
     59                  return None
     60          else:
     61                  return match.group(1)
     62
     63
     64
     65 -def get_Mail_Num(server, login, passwd):
     66          """This output the number of messages of mail box"""
     67          try:
     68                  m = poplib.POP3_SSL(server)
     69          except:
     70                  try:
     71                          m = poplib.POP3(server)
     72                  except:
     73                          return None
     74
     75          m.user(login)
     76          m.pass_(passwd)
     77          out = m.stat()
     78          m.quit()
     79          num = out[0]
     80          return num
     81
     82
     83 -def send_mail(smtp_server,fromaddr,toaddrs, subject,msg):
     84          """Send mail via SMTP"""
     85          import smtplib
     86          server = smtplib.SMTP(smtp_server)
     87          server.sendmail(fromaddr, toaddrs, subject + msg)
     88          server.quit()
     89
     90  #------CLASSES----------
     91  #-----------------------
     92
     93  # error messages
     94  MSG_CONNECTION_FAILED   = "Error while connecting to server."
     95  MSG_FETCH_MAILS_FAILED  = "Unable to retrieve mails from server."
     96  MSG_AUTH_FAILED = """Error on login - invalid login data given? Some hosts
     97  may block connections for a certain interval before allowing reconnects."""
     98
     99  # the current operational status of the mailcheck
    100 -class MailboxStatus(object):
    101          UNKNOWN                 = 0
    102          ALL_READ                = 1
    103          UNREAD_MAIL             = 2
    104          NEW_MAIL                = 3
    105
    106  # the mailcheck status
    107 -class MailCheckStatus(object):
    108          REFRESH         = 1
    109          GOT_MAIL        = 2
    110          ERROR           = 3
    111          IDLE            = 100
    112
    113 -class MailCheckBackend (gobject.GObject):
    114          """The backend class which performs checking for mail and offers access
    115          to the current mail-backend. By subclassing this class you can add multiple
    116          mail-backends to the MailCheckScreenlet (e.g. pop3, maildir, imap,
    117          gmail, ...)."""
    118
    119
    120          __gsignals__ = {
    121                  'check_finished' : (gobject.SIGNAL_RUN_LAST,    gobject.TYPE_NONE,(gobject.TYPE_INT, gobject.TYPE_INT,))
    122                  }
    123
    124 -        def __init__ (self, name, screenlet):
    125                  gobject.GObject.__init__(self)
    126                  # properties
    127                  self.name               = name                  # name of backend
    128                  self.screenlet          = screenlet             # assigned MailCheckScreenlet
    129                  self.refreshing         = False                 # not refreshing yet
    130                  self.unseen_count       = 0                     # number of unread messages on the server
    131                  self.status             = MailCheckStatus.IDLE  # status of the mailcheck backend
    132                  self.mailbox_status     = MailboxStatus.UNKNOWN # status of the mailbox
    133                  self.error                                      = ''            # human-readable error message
    134                  self.options            = []                            # ???additonal ptions for backend
    135                  self.thread             = None
    136                  self.mailcount          = 0                            #
    137
    138 -        def check_mail (self):
    139                  """This handler should be overridden by subclasses to add new types
    140                  of checking mails in a backend. This handler has to set self.mailcount
    141                  to the number of mails found in the backend. The return value is
    142                  ignored, set self.error and self.status to return results."""
    143
    144 -        def stop (self):
    145            """Stop receiving mails from the backend.  This should be overridden
    146            by subclasses."""
    147            self.thread = None
    148
    149 -        def start (self):
    150                  """Start receiving mails from the backend. Runs self.__execute as
    151                  a separate thread."""
    152                  self.thread = threading.Thread(target=self.__execute).start()
    153
    154 -        def __execute (self):
    155                  """Execute the thread and call the check-mail function."""
    156                  # set status to REFRESH and call check_mail-handler to fetch mails
    157                  self.refreshing = True
    158                  self.check_mail()
    159                  self.emit('check_finished', self.status, self.mailbox_status)
    160                  # not refreshing anymore
    161                  self.refreshing = False
    162
    163
    164  # IMAPBackend was contributed by Robert Gartler - thanks :)
    165 -class IMAPBackend(MailCheckBackend):
    166          """A backend for retrieving the mailcount from an IMAP server."""
    167
    168 -        def __init__ (self, screenlet):
    169                  # call super
    170                  MailCheckBackend.__init__(self, 'IMAP', screenlet)
    171                  self.server = None
    172
    173 -        def check_mail(self):
    174                  # set default timeout for all socket connections to 30 secs
    175                  socket.setdefaulttimeout(30000)
    176                  print "IMAPBackend: Connecting to IMAP-server ... please wait."
    177                  self.status = MailCheckStatus.REFRESH
    178                  try:
    179                          self.server = imaplib.IMAP4_SSL(self.screenlet.imap_host)
    180                  except:
    181                          try:
    182                                  self.server = imaplib.IMAP4(self.screenlet.imap_host)
    183                          except:
    184                                  self.error      = MSG_CONNECTION_FAILED
    185                                  self.status     = MailCheckStatus.ERROR
    186                                  return False
    187                  user, passwd = self.screenlet.imap_account
    188                  try:
    189                          self.server.login(user, passwd)
    190                  except:
    191                          self.error      = MSG_AUTH_FAILED
    192                          self.status     = MailCheckStatus.ERROR
    193                          self.server.logout()
    194                          return False
    195
    196                  self.server.select()
    197                  typ, data = self.server.search(None, 'UNSEEN')
    198                  if typ == 'OK':
    199                          self.unseen_count = len(data[0].split())
    200                          if self.unseen_count > 0:
    201                                  typ, data = self.server.search(None, 'NEW')
    202                                  if typ == 'OK':
    203                                          if len(data[0].split()) > 0:
    204                                                  self.mailbox_status = MailboxStatus.NEW_MAIL
    205                                                  print "NEW_MAIL"
    206                                          else:
    207                                                  self.mailbox_status = MailboxStatus.UNREAD_MAIL
    208                                                  print "UNREAD_MAIL"
    209                                  else:
    210                                          print "IMAP error (checking new count): " + typ
    211                          else:
    212                                  self.mailbox_status = MailboxStatus.ALL_READ
    213                          self.status = MailCheckStatus.IDLE
    214                  else:
    215                          print "IMAP error (checking unseen count): " + typ
    216                          self.error      = MSG_FETCH_MAILS_FAILED
    217                          self.status     = MailCheckStatus.ERROR
    218                          self.mailbox_status = MailboxStatus.UNKNOWN
    219                  self.server.close()
    220                  self.server.logout()
    221                  return False
    222
    223 -        def stop(self):
    224                  if self.server:
    225                          self.server.close()
    226                          self.server.logout()
    227                          self.thread.join()
    228                          self.thread = None
    229
    230 -class Mailer(object):
    231      """
    232      Class that retrieve the information from an Imap, Pop or mbox account
    233
    234      All the email-related operation lies in this few lines
    235      """
    236      import imaplib
    237      import poplib
    238      import mailbox
    239      from sys import exc_info
    240      from os import stat, utime, path, listdir
    241
    242
    243 -    def __init__(self, config):
    244          self.config=config
    245          self.last_size=-1
    246          self.size=-1
    247          self.mbox_size = 0
    248          self.mbox_mtime = 0
    249
    250 -    def __call__(self):
    251          self.last_size=self.size
    252
    253          try:
    254              # IMAP4
    255              #
    256              if self.config['method']=='imap4':
    257                  s = self.imaplib.__dict__['IMAP4'+['','_SSL']
    258                                            [self.config['ssl']]]\
    259                                            (self.config['host'])
    260                  s.login(self.config['user_name'],self.config['user_password'])
    261                  s.select()
    262                  size = len(s.search(None, 'UNSEEN')[1][0].split())
    263                  s.logout()
    264
    265              # POP3
    266              #
    267              elif self.config['method']=='pop3':
    268                  s = self.poplib.__dict__['POP3'+['','_SSL']
    269                                           [self.config['ssl']]]\
    270                                           (self.config['host'])
    271                  s.user(self.config['user_name'])
    272                  s.pass_(self.config['user_password'])
    273                  size = len(s.list()[1])
    274
    275              # Maildir
    276              #
    277              # This was reported to work with qmail, but it is untested with
    278              # other mail servers -- for maximum portability, one could
    279              # still rewrite next four lines using the mailbox Python module
    280              # (in core libraries).
    281              #
    282              elif self.config['method'] == 'maildir':
    283                  mdir_path = getenv('MAILDIR', self.config['mailspool'])
    284                  mdir_new = self.path.join(self.path.expanduser(mdir_path), 'new')
    285
    286                  size = len([f for f in self.listdir(mdir_new) if f[0] != '.'])
    287
    288              # Unix mbox
    289              #
    290              elif self.config['method'] == 'mbox':
    291                  mbox_path = getenv('MAIL',self.config['mailspool'])
    292                  # Get mbox inode properties
    293                  #
    294                  s = self.stat(mbox_path)
    295                  if (s.st_size == self.mbox_size and
    296                      s.st_mtime == self.mbox_mtime):
    297                      size = self.last_size       # mbox has not changed on disk
    298                  else:
    299                      size = 0                    # mbox has changed
    300                      for m in self.mailbox.PortableUnixMailbox(file(mbox_path)):
    301                          if m.get('status','N').find('N') != -1:
    302                              size += 1
    303
    304                      # Trick the system into thinking the mbox inode was not
    305                      # accessed since last modification. From 'manual.txt'
    306                      # of mutt 1.5.8:
    307                      #
    308                      # [ ... new mail is detected by comparing the last
    309                      # modification time to the last access time.
    310                      # Utilities like biff or frm or any other program
    311                      # which accesses the mailbox might cause Mutt to
    312                      # never detect new mail for that mailbox if they
    313                      # do not properly reset the access time.
    314                      # Backup tools are another common reason for updated
    315                      # access times. ]
    316                      #
    317                      self.utime(mbox_path, (s.st_atime, s.st_mtime))
    318
    319                      # Remember size and time
    320                      #
    321                      self.mbox_size = s.st_size
    322                      self.mbox_mtime = s.st_mtime
    323
    324              # Uknown access method
    325              #
    326              else:
    327                  raise RuntimeError('unknown access method `%s\'' %
    328                                     self.config['method'])
    329          except:
    330              # Exception handling: output a significant printout
    331              #
    332              size = -1
    333              print '='*80
    334              print traceback.print_exception(*self.exc_info())
    335              print '='*80
    336              print self.config
    337              print '='*80
    338
    339          self.size = size
    340          return size
    341
    342
    343 -class POP3Backend (MailCheckBackend):
    344          """A backend for retrieving the mailcount from a POP3 server."""
    345
    346 -        def __init__ (self, screenlet):
    347                  # call super
    348                  MailCheckBackend.__init__(self, 'POP3', screenlet)
    349                  self.server = None
    350                  # init additional attributes for this backend-type
    351                  # TODO: add POP3-specific options to the backend instead of having them
    352                  # defined in the screenlet by default (ideally they should be only shown
    353                  # when the POP3-backend is active
    354
    355 -        def check_mail (self):
    356                  # set default timeout for all socket connections to 30 secs
    357                  socket.setdefaulttimeout(30000)
    358                  print "POP3Backend: Connecting to POP3-server ... please wait."
    359                  #self.screenlet.redraw_canvas()
    360                  try:
    361                          self.server = poplib.POP3_SSL(self.screenlet.pop3_server)
    362                  except:
    363                          try:
    364                                  self.server = poplib.POP3(self.screenlet.pop3_server)
    365                          except:
    366                                  self.error      = MSG_CONNECTION_FAILED
    367                                  self.status = MailCheckStatus.ERROR
    368                                  return False
    369                  # authenticate
    370                  user, pw = self.screenlet.pop3_account
    371                  #print "ACCOUNT IS %s/%s!!" % (o[0], o[1])
    372                  try:
    373                          self.server.user(user)
    374                          self.server.pass_(pw)
    375                  except:
    376                          self.error      = MSG_AUTH_FAILED
    377                          self.status = MailCheckStatus.ERROR
    378                          self.server.quit()
    379                          return False
    380                  # get list with mails (response, list-of-mails)
    381                  resp = self.server.list()
    382                  if resp[0].startswith('+OK'):
    383                          messages = resp[1]
    384                          #print messages
    385                          msgnum = len(messages)
    386                          if msgnum > self.mailcount:
    387                                  diff = msgnum - self.mailcount
    388                                  self.mailcount = msgnum
    389                                  self.mailbox_status = MailboxStatus.NEW_MAIL
    390                                  self.status = MailCheckStatus.GOT_MAIL
    391                                  print "GOT_MAIL"
    392                          elif msgnum <= self.mailcount:
    393                                  print "set status to IDLE (POP3Backend.check_mail)"
    394                                  self.mailbox_status = MailboxStatus.ALL_READ
    395                                  self.mailcount = msgnum
    396                                  self.status = MailCheckStatus.IDLE
    397                                  print "IDLE"
    398                  else:
    399                          self.error      = MSG_FETCH_MAILS_FAILED
    400                          self.status     = MailCheckStatus.ERROR
    401                          #server.quit()
    402                          #return False
    403                  # close connection
    404                  self.server.quit()
    405                  return False
    406
    407 -        def stop(self):
    408                  if self.server:
    409                          self.server.quit()
    410                          self.thread.join()
    411                          self.thread = None
    412