python-2.5.2/win32/Lib/poplib.py
changeset 0 ae805ac0140d
equal deleted inserted replaced
-1:000000000000 0:ae805ac0140d
       
     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","POP3_SSL"]
       
    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         self.host = host
       
    81         self.port = port
       
    82         msg = "getaddrinfo returns an empty list"
       
    83         self.sock = None
       
    84         for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM):
       
    85             af, socktype, proto, canonname, sa = res
       
    86             try:
       
    87                 self.sock = socket.socket(af, socktype, proto)
       
    88                 self.sock.connect(sa)
       
    89             except socket.error, msg:
       
    90                 if self.sock:
       
    91                     self.sock.close()
       
    92                 self.sock = None
       
    93                 continue
       
    94             break
       
    95         if not self.sock:
       
    96             raise socket.error, msg
       
    97         self.file = self.sock.makefile('rb')
       
    98         self._debugging = 0
       
    99         self.welcome = self._getresp()
       
   100 
       
   101 
       
   102     def _putline(self, line):
       
   103         if self._debugging > 1: print '*put*', repr(line)
       
   104         self.sock.sendall('%s%s' % (line, CRLF))
       
   105 
       
   106 
       
   107     # Internal: send one command to the server (through _putline())
       
   108 
       
   109     def _putcmd(self, line):
       
   110         if self._debugging: print '*cmd*', repr(line)
       
   111         self._putline(line)
       
   112 
       
   113 
       
   114     # Internal: return one line from the server, stripping CRLF.
       
   115     # This is where all the CPU time of this module is consumed.
       
   116     # Raise error_proto('-ERR EOF') if the connection is closed.
       
   117 
       
   118     def _getline(self):
       
   119         line = self.file.readline()
       
   120         if self._debugging > 1: print '*get*', repr(line)
       
   121         if not line: raise error_proto('-ERR EOF')
       
   122         octets = len(line)
       
   123         # server can send any combination of CR & LF
       
   124         # however, 'readline()' returns lines ending in LF
       
   125         # so only possibilities are ...LF, ...CRLF, CR...LF
       
   126         if line[-2:] == CRLF:
       
   127             return line[:-2], octets
       
   128         if line[0] == CR:
       
   129             return line[1:-1], octets
       
   130         return line[:-1], octets
       
   131 
       
   132 
       
   133     # Internal: get a response from the server.
       
   134     # Raise 'error_proto' if the response doesn't start with '+'.
       
   135 
       
   136     def _getresp(self):
       
   137         resp, o = self._getline()
       
   138         if self._debugging > 1: print '*resp*', repr(resp)
       
   139         c = resp[:1]
       
   140         if c != '+':
       
   141             raise error_proto(resp)
       
   142         return resp
       
   143 
       
   144 
       
   145     # Internal: get a response plus following text from the server.
       
   146 
       
   147     def _getlongresp(self):
       
   148         resp = self._getresp()
       
   149         list = []; octets = 0
       
   150         line, o = self._getline()
       
   151         while line != '.':
       
   152             if line[:2] == '..':
       
   153                 o = o-1
       
   154                 line = line[1:]
       
   155             octets = octets + o
       
   156             list.append(line)
       
   157             line, o = self._getline()
       
   158         return resp, list, octets
       
   159 
       
   160 
       
   161     # Internal: send a command and get the response
       
   162 
       
   163     def _shortcmd(self, line):
       
   164         self._putcmd(line)
       
   165         return self._getresp()
       
   166 
       
   167 
       
   168     # Internal: send a command and get the response plus following text
       
   169 
       
   170     def _longcmd(self, line):
       
   171         self._putcmd(line)
       
   172         return self._getlongresp()
       
   173 
       
   174 
       
   175     # These can be useful:
       
   176 
       
   177     def getwelcome(self):
       
   178         return self.welcome
       
   179 
       
   180 
       
   181     def set_debuglevel(self, level):
       
   182         self._debugging = level
       
   183 
       
   184 
       
   185     # Here are all the POP commands:
       
   186 
       
   187     def user(self, user):
       
   188         """Send user name, return response
       
   189 
       
   190         (should indicate password required).
       
   191         """
       
   192         return self._shortcmd('USER %s' % user)
       
   193 
       
   194 
       
   195     def pass_(self, pswd):
       
   196         """Send password, return response
       
   197 
       
   198         (response includes message count, mailbox size).
       
   199 
       
   200         NB: mailbox is locked by server from here to 'quit()'
       
   201         """
       
   202         return self._shortcmd('PASS %s' % pswd)
       
   203 
       
   204 
       
   205     def stat(self):
       
   206         """Get mailbox status.
       
   207 
       
   208         Result is tuple of 2 ints (message count, mailbox size)
       
   209         """
       
   210         retval = self._shortcmd('STAT')
       
   211         rets = retval.split()
       
   212         if self._debugging: print '*stat*', repr(rets)
       
   213         numMessages = int(rets[1])
       
   214         sizeMessages = int(rets[2])
       
   215         return (numMessages, sizeMessages)
       
   216 
       
   217 
       
   218     def list(self, which=None):
       
   219         """Request listing, return result.
       
   220 
       
   221         Result without a message number argument is in form
       
   222         ['response', ['mesg_num octets', ...], octets].
       
   223 
       
   224         Result when a message number argument is given is a
       
   225         single response: the "scan listing" for that message.
       
   226         """
       
   227         if which is not None:
       
   228             return self._shortcmd('LIST %s' % which)
       
   229         return self._longcmd('LIST')
       
   230 
       
   231 
       
   232     def retr(self, which):
       
   233         """Retrieve whole message number 'which'.
       
   234 
       
   235         Result is in form ['response', ['line', ...], octets].
       
   236         """
       
   237         return self._longcmd('RETR %s' % which)
       
   238 
       
   239 
       
   240     def dele(self, which):
       
   241         """Delete message number 'which'.
       
   242 
       
   243         Result is 'response'.
       
   244         """
       
   245         return self._shortcmd('DELE %s' % which)
       
   246 
       
   247 
       
   248     def noop(self):
       
   249         """Does nothing.
       
   250 
       
   251         One supposes the response indicates the server is alive.
       
   252         """
       
   253         return self._shortcmd('NOOP')
       
   254 
       
   255 
       
   256     def rset(self):
       
   257         """Not sure what this does."""
       
   258         return self._shortcmd('RSET')
       
   259 
       
   260 
       
   261     def quit(self):
       
   262         """Signoff: commit changes on server, unlock mailbox, close connection."""
       
   263         try:
       
   264             resp = self._shortcmd('QUIT')
       
   265         except error_proto, val:
       
   266             resp = val
       
   267         self.file.close()
       
   268         self.sock.close()
       
   269         del self.file, self.sock
       
   270         return resp
       
   271 
       
   272     #__del__ = quit
       
   273 
       
   274 
       
   275     # optional commands:
       
   276 
       
   277     def rpop(self, user):
       
   278         """Not sure what this does."""
       
   279         return self._shortcmd('RPOP %s' % user)
       
   280 
       
   281 
       
   282     timestamp = re.compile(r'\+OK.*(<[^>]+>)')
       
   283 
       
   284     def apop(self, user, secret):
       
   285         """Authorisation
       
   286 
       
   287         - only possible if server has supplied a timestamp in initial greeting.
       
   288 
       
   289         Args:
       
   290                 user    - mailbox user;
       
   291                 secret  - secret shared between client and server.
       
   292 
       
   293         NB: mailbox is locked by server from here to 'quit()'
       
   294         """
       
   295         m = self.timestamp.match(self.welcome)
       
   296         if not m:
       
   297             raise error_proto('-ERR APOP not supported by server')
       
   298         import hashlib
       
   299         digest = hashlib.md5(m.group(1)+secret).digest()
       
   300         digest = ''.join(map(lambda x:'%02x'%ord(x), digest))
       
   301         return self._shortcmd('APOP %s %s' % (user, digest))
       
   302 
       
   303 
       
   304     def top(self, which, howmuch):
       
   305         """Retrieve message header of message number 'which'
       
   306         and first 'howmuch' lines of message body.
       
   307 
       
   308         Result is in form ['response', ['line', ...], octets].
       
   309         """
       
   310         return self._longcmd('TOP %s %s' % (which, howmuch))
       
   311 
       
   312 
       
   313     def uidl(self, which=None):
       
   314         """Return message digest (unique id) list.
       
   315 
       
   316         If 'which', result contains unique id for that message
       
   317         in the form 'response mesgnum uid', otherwise result is
       
   318         the list ['response', ['mesgnum uid', ...], octets]
       
   319         """
       
   320         if which is not None:
       
   321             return self._shortcmd('UIDL %s' % which)
       
   322         return self._longcmd('UIDL')
       
   323 
       
   324 class POP3_SSL(POP3):
       
   325     """POP3 client class over SSL connection
       
   326 
       
   327     Instantiate with: POP3_SSL(hostname, port=995, keyfile=None, certfile=None)
       
   328 
       
   329            hostname - the hostname of the pop3 over ssl server
       
   330            port - port number
       
   331            keyfile - PEM formatted file that countains your private key
       
   332            certfile - PEM formatted certificate chain file
       
   333 
       
   334         See the methods of the parent class POP3 for more documentation.
       
   335     """
       
   336 
       
   337     def __init__(self, host, port = POP3_SSL_PORT, keyfile = None, certfile = None):
       
   338         self.host = host
       
   339         self.port = port
       
   340         self.keyfile = keyfile
       
   341         self.certfile = certfile
       
   342         self.buffer = ""
       
   343         msg = "getaddrinfo returns an empty list"
       
   344         self.sock = None
       
   345         for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM):
       
   346             af, socktype, proto, canonname, sa = res
       
   347             try:
       
   348                 self.sock = socket.socket(af, socktype, proto)
       
   349                 self.sock.connect(sa)
       
   350             except socket.error, msg:
       
   351                 if self.sock:
       
   352                     self.sock.close()
       
   353                 self.sock = None
       
   354                 continue
       
   355             break
       
   356         if not self.sock:
       
   357             raise socket.error, msg
       
   358         self.file = self.sock.makefile('rb')
       
   359         self.sslobj = socket.ssl(self.sock, self.keyfile, self.certfile)
       
   360         self._debugging = 0
       
   361         self.welcome = self._getresp()
       
   362 
       
   363     def _fillBuffer(self):
       
   364         localbuf = self.sslobj.read()
       
   365         if len(localbuf) == 0:
       
   366             raise error_proto('-ERR EOF')
       
   367         self.buffer += localbuf
       
   368 
       
   369     def _getline(self):
       
   370         line = ""
       
   371         renewline = re.compile(r'.*?\n')
       
   372         match = renewline.match(self.buffer)
       
   373         while not match:
       
   374             self._fillBuffer()
       
   375             match = renewline.match(self.buffer)
       
   376         line = match.group(0)
       
   377         self.buffer = renewline.sub('' ,self.buffer, 1)
       
   378         if self._debugging > 1: print '*get*', repr(line)
       
   379 
       
   380         octets = len(line)
       
   381         if line[-2:] == CRLF:
       
   382             return line[:-2], octets
       
   383         if line[0] == CR:
       
   384             return line[1:-1], octets
       
   385         return line[:-1], octets
       
   386 
       
   387     def _putline(self, line):
       
   388         if self._debugging > 1: print '*put*', repr(line)
       
   389         line += CRLF
       
   390         bytes = len(line)
       
   391         while bytes > 0:
       
   392             sent = self.sslobj.write(line)
       
   393             if sent == bytes:
       
   394                 break    # avoid copy
       
   395             line = line[sent:]
       
   396             bytes = bytes - sent
       
   397 
       
   398     def quit(self):
       
   399         """Signoff: commit changes on server, unlock mailbox, close connection."""
       
   400         try:
       
   401             resp = self._shortcmd('QUIT')
       
   402         except error_proto, val:
       
   403             resp = val
       
   404         self.sock.close()
       
   405         del self.sslobj, self.sock
       
   406         return resp
       
   407 
       
   408 
       
   409 if __name__ == "__main__":
       
   410     import sys
       
   411     a = POP3(sys.argv[1])
       
   412     print a.getwelcome()
       
   413     a.user(sys.argv[2])
       
   414     a.pass_(sys.argv[3])
       
   415     a.list()
       
   416     (numMsgs, totalSize) = a.stat()
       
   417     for i in range(1, numMsgs + 1):
       
   418         (header, msg, octets) = a.retr(i)
       
   419         print "Message %d:" % i
       
   420         for line in msg:
       
   421             print '   ' + line
       
   422         print '-----------------------'
       
   423     a.quit()