symbian-qemu-0.9.1-12/python-win32-2.6.1/lib/poplib.py
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 """A POP3 client class.
       
     2 
       
     3 Based on the J. Myers POP3 draft, Jan. 96
       
     4 """
       
     5 
       
     6 # Author: David Ascher <david_ascher@brown.edu>
       
     7 #         [heavily stealing from nntplib.py]
       
     8 # Updated: Piers Lauder <piers@cs.su.oz.au> [Jul '97]
       
     9 # String method conversion and test jig improvements by ESR, February 2001.
       
    10 # Added the POP3_SSL class. Methods loosely based on IMAP_SSL. Hector Urtubia <urtubia@mrbook.org> Aug 2003
       
    11 
       
    12 # Example (see the test function at the end of this file)
       
    13 
       
    14 # Imports
       
    15 
       
    16 import re, socket
       
    17 
       
    18 __all__ = ["POP3","error_proto"]
       
    19 
       
    20 # Exception raised when an error or invalid response is received:
       
    21 
       
    22 class error_proto(Exception): pass
       
    23 
       
    24 # Standard Port
       
    25 POP3_PORT = 110
       
    26 
       
    27 # POP SSL PORT
       
    28 POP3_SSL_PORT = 995
       
    29 
       
    30 # Line terminators (we always output CRLF, but accept any of CRLF, LFCR, LF)
       
    31 CR = '\r'
       
    32 LF = '\n'
       
    33 CRLF = CR+LF
       
    34 
       
    35 
       
    36 class POP3:
       
    37 
       
    38     """This class supports both the minimal and optional command sets.
       
    39     Arguments can be strings or integers (where appropriate)
       
    40     (e.g.: retr(1) and retr('1') both work equally well.
       
    41 
       
    42     Minimal Command Set:
       
    43             USER name               user(name)
       
    44             PASS string             pass_(string)
       
    45             STAT                    stat()
       
    46             LIST [msg]              list(msg = None)
       
    47             RETR msg                retr(msg)
       
    48             DELE msg                dele(msg)
       
    49             NOOP                    noop()
       
    50             RSET                    rset()
       
    51             QUIT                    quit()
       
    52 
       
    53     Optional Commands (some servers support these):
       
    54             RPOP name               rpop(name)
       
    55             APOP name digest        apop(name, digest)
       
    56             TOP msg n               top(msg, n)
       
    57             UIDL [msg]              uidl(msg = None)
       
    58 
       
    59     Raises one exception: 'error_proto'.
       
    60 
       
    61     Instantiate with:
       
    62             POP3(hostname, port=110)
       
    63 
       
    64     NB:     the POP protocol locks the mailbox from user
       
    65             authorization until QUIT, so be sure to get in, suck
       
    66             the messages, and quit, each time you access the
       
    67             mailbox.
       
    68 
       
    69             POP is a line-based protocol, which means large mail
       
    70             messages consume lots of python cycles reading them
       
    71             line-by-line.
       
    72 
       
    73             If it's available on your mail server, use IMAP4
       
    74             instead, it doesn't suffer from the two problems
       
    75             above.
       
    76     """
       
    77 
       
    78 
       
    79     def __init__(self, host, port=POP3_PORT,
       
    80                  timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
       
    81         self.host = host
       
    82         self.port = port
       
    83         self.sock = socket.create_connection((host, port), timeout)
       
    84         self.file = self.sock.makefile('rb')
       
    85         self._debugging = 0
       
    86         self.welcome = self._getresp()
       
    87 
       
    88 
       
    89     def _putline(self, line):
       
    90         if self._debugging > 1: print '*put*', repr(line)
       
    91         self.sock.sendall('%s%s' % (line, CRLF))
       
    92 
       
    93 
       
    94     # Internal: send one command to the server (through _putline())
       
    95 
       
    96     def _putcmd(self, line):
       
    97         if self._debugging: print '*cmd*', repr(line)
       
    98         self._putline(line)
       
    99 
       
   100 
       
   101     # Internal: return one line from the server, stripping CRLF.
       
   102     # This is where all the CPU time of this module is consumed.
       
   103     # Raise error_proto('-ERR EOF') if the connection is closed.
       
   104 
       
   105     def _getline(self):
       
   106         line = self.file.readline()
       
   107         if self._debugging > 1: print '*get*', repr(line)
       
   108         if not line: raise error_proto('-ERR EOF')
       
   109         octets = len(line)
       
   110         # server can send any combination of CR & LF
       
   111         # however, 'readline()' returns lines ending in LF
       
   112         # so only possibilities are ...LF, ...CRLF, CR...LF
       
   113         if line[-2:] == CRLF:
       
   114             return line[:-2], octets
       
   115         if line[0] == CR:
       
   116             return line[1:-1], octets
       
   117         return line[:-1], octets
       
   118 
       
   119 
       
   120     # Internal: get a response from the server.
       
   121     # Raise 'error_proto' if the response doesn't start with '+'.
       
   122 
       
   123     def _getresp(self):
       
   124         resp, o = self._getline()
       
   125         if self._debugging > 1: print '*resp*', repr(resp)
       
   126         c = resp[:1]
       
   127         if c != '+':
       
   128             raise error_proto(resp)
       
   129         return resp
       
   130 
       
   131 
       
   132     # Internal: get a response plus following text from the server.
       
   133 
       
   134     def _getlongresp(self):
       
   135         resp = self._getresp()
       
   136         list = []; octets = 0
       
   137         line, o = self._getline()
       
   138         while line != '.':
       
   139             if line[:2] == '..':
       
   140                 o = o-1
       
   141                 line = line[1:]
       
   142             octets = octets + o
       
   143             list.append(line)
       
   144             line, o = self._getline()
       
   145         return resp, list, octets
       
   146 
       
   147 
       
   148     # Internal: send a command and get the response
       
   149 
       
   150     def _shortcmd(self, line):
       
   151         self._putcmd(line)
       
   152         return self._getresp()
       
   153 
       
   154 
       
   155     # Internal: send a command and get the response plus following text
       
   156 
       
   157     def _longcmd(self, line):
       
   158         self._putcmd(line)
       
   159         return self._getlongresp()
       
   160 
       
   161 
       
   162     # These can be useful:
       
   163 
       
   164     def getwelcome(self):
       
   165         return self.welcome
       
   166 
       
   167 
       
   168     def set_debuglevel(self, level):
       
   169         self._debugging = level
       
   170 
       
   171 
       
   172     # Here are all the POP commands:
       
   173 
       
   174     def user(self, user):
       
   175         """Send user name, return response
       
   176 
       
   177         (should indicate password required).
       
   178         """
       
   179         return self._shortcmd('USER %s' % user)
       
   180 
       
   181 
       
   182     def pass_(self, pswd):
       
   183         """Send password, return response
       
   184 
       
   185         (response includes message count, mailbox size).
       
   186 
       
   187         NB: mailbox is locked by server from here to 'quit()'
       
   188         """
       
   189         return self._shortcmd('PASS %s' % pswd)
       
   190 
       
   191 
       
   192     def stat(self):
       
   193         """Get mailbox status.
       
   194 
       
   195         Result is tuple of 2 ints (message count, mailbox size)
       
   196         """
       
   197         retval = self._shortcmd('STAT')
       
   198         rets = retval.split()
       
   199         if self._debugging: print '*stat*', repr(rets)
       
   200         numMessages = int(rets[1])
       
   201         sizeMessages = int(rets[2])
       
   202         return (numMessages, sizeMessages)
       
   203 
       
   204 
       
   205     def list(self, which=None):
       
   206         """Request listing, return result.
       
   207 
       
   208         Result without a message number argument is in form
       
   209         ['response', ['mesg_num octets', ...], octets].
       
   210 
       
   211         Result when a message number argument is given is a
       
   212         single response: the "scan listing" for that message.
       
   213         """
       
   214         if which is not None:
       
   215             return self._shortcmd('LIST %s' % which)
       
   216         return self._longcmd('LIST')
       
   217 
       
   218 
       
   219     def retr(self, which):
       
   220         """Retrieve whole message number 'which'.
       
   221 
       
   222         Result is in form ['response', ['line', ...], octets].
       
   223         """
       
   224         return self._longcmd('RETR %s' % which)
       
   225 
       
   226 
       
   227     def dele(self, which):
       
   228         """Delete message number 'which'.
       
   229 
       
   230         Result is 'response'.
       
   231         """
       
   232         return self._shortcmd('DELE %s' % which)
       
   233 
       
   234 
       
   235     def noop(self):
       
   236         """Does nothing.
       
   237 
       
   238         One supposes the response indicates the server is alive.
       
   239         """
       
   240         return self._shortcmd('NOOP')
       
   241 
       
   242 
       
   243     def rset(self):
       
   244         """Unmark all messages marked for deletion."""
       
   245         return self._shortcmd('RSET')
       
   246 
       
   247 
       
   248     def quit(self):
       
   249         """Signoff: commit changes on server, unlock mailbox, close connection."""
       
   250         try:
       
   251             resp = self._shortcmd('QUIT')
       
   252         except error_proto, val:
       
   253             resp = val
       
   254         self.file.close()
       
   255         self.sock.close()
       
   256         del self.file, self.sock
       
   257         return resp
       
   258 
       
   259     #__del__ = quit
       
   260 
       
   261 
       
   262     # optional commands:
       
   263 
       
   264     def rpop(self, user):
       
   265         """Not sure what this does."""
       
   266         return self._shortcmd('RPOP %s' % user)
       
   267 
       
   268 
       
   269     timestamp = re.compile(r'\+OK.*(<[^>]+>)')
       
   270 
       
   271     def apop(self, user, secret):
       
   272         """Authorisation
       
   273 
       
   274         - only possible if server has supplied a timestamp in initial greeting.
       
   275 
       
   276         Args:
       
   277                 user    - mailbox user;
       
   278                 secret  - secret shared between client and server.
       
   279 
       
   280         NB: mailbox is locked by server from here to 'quit()'
       
   281         """
       
   282         m = self.timestamp.match(self.welcome)
       
   283         if not m:
       
   284             raise error_proto('-ERR APOP not supported by server')
       
   285         import hashlib
       
   286         digest = hashlib.md5(m.group(1)+secret).digest()
       
   287         digest = ''.join(map(lambda x:'%02x'%ord(x), digest))
       
   288         return self._shortcmd('APOP %s %s' % (user, digest))
       
   289 
       
   290 
       
   291     def top(self, which, howmuch):
       
   292         """Retrieve message header of message number 'which'
       
   293         and first 'howmuch' lines of message body.
       
   294 
       
   295         Result is in form ['response', ['line', ...], octets].
       
   296         """
       
   297         return self._longcmd('TOP %s %s' % (which, howmuch))
       
   298 
       
   299 
       
   300     def uidl(self, which=None):
       
   301         """Return message digest (unique id) list.
       
   302 
       
   303         If 'which', result contains unique id for that message
       
   304         in the form 'response mesgnum uid', otherwise result is
       
   305         the list ['response', ['mesgnum uid', ...], octets]
       
   306         """
       
   307         if which is not None:
       
   308             return self._shortcmd('UIDL %s' % which)
       
   309         return self._longcmd('UIDL')
       
   310 
       
   311 try:
       
   312     import ssl
       
   313 except ImportError:
       
   314     pass
       
   315 else:
       
   316 
       
   317     class POP3_SSL(POP3):
       
   318         """POP3 client class over SSL connection
       
   319 
       
   320         Instantiate with: POP3_SSL(hostname, port=995, keyfile=None, certfile=None)
       
   321 
       
   322                hostname - the hostname of the pop3 over ssl server
       
   323                port - port number
       
   324                keyfile - PEM formatted file that countains your private key
       
   325                certfile - PEM formatted certificate chain file
       
   326 
       
   327             See the methods of the parent class POP3 for more documentation.
       
   328         """
       
   329 
       
   330         def __init__(self, host, port = POP3_SSL_PORT, keyfile = None, certfile = None):
       
   331             self.host = host
       
   332             self.port = port
       
   333             self.keyfile = keyfile
       
   334             self.certfile = certfile
       
   335             self.buffer = ""
       
   336             msg = "getaddrinfo returns an empty list"
       
   337             self.sock = None
       
   338             for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM):
       
   339                 af, socktype, proto, canonname, sa = res
       
   340                 try:
       
   341                     self.sock = socket.socket(af, socktype, proto)
       
   342                     self.sock.connect(sa)
       
   343                 except socket.error, msg:
       
   344                     if self.sock:
       
   345                         self.sock.close()
       
   346                     self.sock = None
       
   347                     continue
       
   348                 break
       
   349             if not self.sock:
       
   350                 raise socket.error, msg
       
   351             self.file = self.sock.makefile('rb')
       
   352             self.sslobj = ssl.wrap_socket(self.sock, self.keyfile, self.certfile)
       
   353             self._debugging = 0
       
   354             self.welcome = self._getresp()
       
   355 
       
   356         def _fillBuffer(self):
       
   357             localbuf = self.sslobj.read()
       
   358             if len(localbuf) == 0:
       
   359                 raise error_proto('-ERR EOF')
       
   360             self.buffer += localbuf
       
   361 
       
   362         def _getline(self):
       
   363             line = ""
       
   364             renewline = re.compile(r'.*?\n')
       
   365             match = renewline.match(self.buffer)
       
   366             while not match:
       
   367                 self._fillBuffer()
       
   368                 match = renewline.match(self.buffer)
       
   369             line = match.group(0)
       
   370             self.buffer = renewline.sub('' ,self.buffer, 1)
       
   371             if self._debugging > 1: print '*get*', repr(line)
       
   372 
       
   373             octets = len(line)
       
   374             if line[-2:] == CRLF:
       
   375                 return line[:-2], octets
       
   376             if line[0] == CR:
       
   377                 return line[1:-1], octets
       
   378             return line[:-1], octets
       
   379 
       
   380         def _putline(self, line):
       
   381             if self._debugging > 1: print '*put*', repr(line)
       
   382             line += CRLF
       
   383             bytes = len(line)
       
   384             while bytes > 0:
       
   385                 sent = self.sslobj.write(line)
       
   386                 if sent == bytes:
       
   387                     break    # avoid copy
       
   388                 line = line[sent:]
       
   389                 bytes = bytes - sent
       
   390 
       
   391         def quit(self):
       
   392             """Signoff: commit changes on server, unlock mailbox, close connection."""
       
   393             try:
       
   394                 resp = self._shortcmd('QUIT')
       
   395             except error_proto, val:
       
   396                 resp = val
       
   397             self.sock.close()
       
   398             del self.sslobj, self.sock
       
   399             return resp
       
   400 
       
   401     __all__.append("POP3_SSL")
       
   402 
       
   403 if __name__ == "__main__":
       
   404     import sys
       
   405     a = POP3(sys.argv[1])
       
   406     print a.getwelcome()
       
   407     a.user(sys.argv[2])
       
   408     a.pass_(sys.argv[3])
       
   409     a.list()
       
   410     (numMsgs, totalSize) = a.stat()
       
   411     for i in range(1, numMsgs + 1):
       
   412         (header, msg, octets) = a.retr(i)
       
   413         print "Message %d:" % i
       
   414         for line in msg:
       
   415             print '   ' + line
       
   416         print '-----------------------'
       
   417     a.quit()