PyXR

c:\python24\lib \ poplib.py



0001 """A POP3 client class.
0002 
0003 Based on the J. Myers POP3 draft, Jan. 96
0004 """
0005 
0006 # Author: David Ascher <david_ascher@brown.edu>
0007 #         [heavily stealing from nntplib.py]
0008 # Updated: Piers Lauder <piers@cs.su.oz.au> [Jul '97]
0009 # String method conversion and test jig improvements by ESR, February 2001.
0010 # Added the POP3_SSL class. Methods loosely based on IMAP_SSL. Hector Urtubia <urtubia@mrbook.org> Aug 2003
0011 
0012 # Example (see the test function at the end of this file)
0013 
0014 # Imports
0015 
0016 import re, socket
0017 
0018 __all__ = ["POP3","error_proto","POP3_SSL"]
0019 
0020 # Exception raised when an error or invalid response is received:
0021 
0022 class error_proto(Exception): pass
0023 
0024 # Standard Port
0025 POP3_PORT = 110
0026 
0027 # POP SSL PORT
0028 POP3_SSL_PORT = 995
0029 
0030 # Line terminators (we always output CRLF, but accept any of CRLF, LFCR, LF)
0031 CR = '\r'
0032 LF = '\n'
0033 CRLF = CR+LF
0034 
0035 
0036 class POP3:
0037 
0038     """This class supports both the minimal and optional command sets.
0039     Arguments can be strings or integers (where appropriate)
0040     (e.g.: retr(1) and retr('1') both work equally well.
0041 
0042     Minimal Command Set:
0043             USER name               user(name)
0044             PASS string             pass_(string)
0045             STAT                    stat()
0046             LIST [msg]              list(msg = None)
0047             RETR msg                retr(msg)
0048             DELE msg                dele(msg)
0049             NOOP                    noop()
0050             RSET                    rset()
0051             QUIT                    quit()
0052 
0053     Optional Commands (some servers support these):
0054             RPOP name               rpop(name)
0055             APOP name digest        apop(name, digest)
0056             TOP msg n               top(msg, n)
0057             UIDL [msg]              uidl(msg = None)
0058 
0059     Raises one exception: 'error_proto'.
0060 
0061     Instantiate with:
0062             POP3(hostname, port=110)
0063 
0064     NB:     the POP protocol locks the mailbox from user
0065             authorization until QUIT, so be sure to get in, suck
0066             the messages, and quit, each time you access the
0067             mailbox.
0068 
0069             POP is a line-based protocol, which means large mail
0070             messages consume lots of python cycles reading them
0071             line-by-line.
0072 
0073             If it's available on your mail server, use IMAP4
0074             instead, it doesn't suffer from the two problems
0075             above.
0076     """
0077 
0078 
0079     def __init__(self, host, port = POP3_PORT):
0080         self.host = host
0081         self.port = port
0082         msg = "getaddrinfo returns an empty list"
0083         self.sock = None
0084         for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM):
0085             af, socktype, proto, canonname, sa = res
0086             try:
0087                 self.sock = socket.socket(af, socktype, proto)
0088                 self.sock.connect(sa)
0089             except socket.error, msg:
0090                 if self.sock:
0091                     self.sock.close()
0092                 self.sock = None
0093                 continue
0094             break
0095         if not self.sock:
0096             raise socket.error, msg
0097         self.file = self.sock.makefile('rb')
0098         self._debugging = 0
0099         self.welcome = self._getresp()
0100 
0101 
0102     def _putline(self, line):
0103         if self._debugging > 1: print '*put*', repr(line)
0104         self.sock.sendall('%s%s' % (line, CRLF))
0105 
0106 
0107     # Internal: send one command to the server (through _putline())
0108 
0109     def _putcmd(self, line):
0110         if self._debugging: print '*cmd*', repr(line)
0111         self._putline(line)
0112 
0113 
0114     # Internal: return one line from the server, stripping CRLF.
0115     # This is where all the CPU time of this module is consumed.
0116     # Raise error_proto('-ERR EOF') if the connection is closed.
0117 
0118     def _getline(self):
0119         line = self.file.readline()
0120         if self._debugging > 1: print '*get*', repr(line)
0121         if not line: raise error_proto('-ERR EOF')
0122         octets = len(line)
0123         # server can send any combination of CR & LF
0124         # however, 'readline()' returns lines ending in LF
0125         # so only possibilities are ...LF, ...CRLF, CR...LF
0126         if line[-2:] == CRLF:
0127             return line[:-2], octets
0128         if line[0] == CR:
0129             return line[1:-1], octets
0130         return line[:-1], octets
0131 
0132 
0133     # Internal: get a response from the server.
0134     # Raise 'error_proto' if the response doesn't start with '+'.
0135 
0136     def _getresp(self):
0137         resp, o = self._getline()
0138         if self._debugging > 1: print '*resp*', repr(resp)
0139         c = resp[:1]
0140         if c != '+':
0141             raise error_proto(resp)
0142         return resp
0143 
0144 
0145     # Internal: get a response plus following text from the server.
0146 
0147     def _getlongresp(self):
0148         resp = self._getresp()
0149         list = []; octets = 0
0150         line, o = self._getline()
0151         while line != '.':
0152             if line[:2] == '..':
0153                 o = o-1
0154                 line = line[1:]
0155             octets = octets + o
0156             list.append(line)
0157             line, o = self._getline()
0158         return resp, list, octets
0159 
0160 
0161     # Internal: send a command and get the response
0162 
0163     def _shortcmd(self, line):
0164         self._putcmd(line)
0165         return self._getresp()
0166 
0167 
0168     # Internal: send a command and get the response plus following text
0169 
0170     def _longcmd(self, line):
0171         self._putcmd(line)
0172         return self._getlongresp()
0173 
0174 
0175     # These can be useful:
0176 
0177     def getwelcome(self):
0178         return self.welcome
0179 
0180 
0181     def set_debuglevel(self, level):
0182         self._debugging = level
0183 
0184 
0185     # Here are all the POP commands:
0186 
0187     def user(self, user):
0188         """Send user name, return response
0189 
0190         (should indicate password required).
0191         """
0192         return self._shortcmd('USER %s' % user)
0193 
0194 
0195     def pass_(self, pswd):
0196         """Send password, return response
0197 
0198         (response includes message count, mailbox size).
0199 
0200         NB: mailbox is locked by server from here to 'quit()'
0201         """
0202         return self._shortcmd('PASS %s' % pswd)
0203 
0204 
0205     def stat(self):
0206         """Get mailbox status.
0207 
0208         Result is tuple of 2 ints (message count, mailbox size)
0209         """
0210         retval = self._shortcmd('STAT')
0211         rets = retval.split()
0212         if self._debugging: print '*stat*', repr(rets)
0213         numMessages = int(rets[1])
0214         sizeMessages = int(rets[2])
0215         return (numMessages, sizeMessages)
0216 
0217 
0218     def list(self, which=None):
0219         """Request listing, return result.
0220 
0221         Result without a message number argument is in form
0222         ['response', ['mesg_num octets', ...]].
0223 
0224         Result when a message number argument is given is a
0225         single response: the "scan listing" for that message.
0226         """
0227         if which is not None:
0228             return self._shortcmd('LIST %s' % which)
0229         return self._longcmd('LIST')
0230 
0231 
0232     def retr(self, which):
0233         """Retrieve whole message number 'which'.
0234 
0235         Result is in form ['response', ['line', ...], octets].
0236         """
0237         return self._longcmd('RETR %s' % which)
0238 
0239 
0240     def dele(self, which):
0241         """Delete message number 'which'.
0242 
0243         Result is 'response'.
0244         """
0245         return self._shortcmd('DELE %s' % which)
0246 
0247 
0248     def noop(self):
0249         """Does nothing.
0250 
0251         One supposes the response indicates the server is alive.
0252         """
0253         return self._shortcmd('NOOP')
0254 
0255 
0256     def rset(self):
0257         """Not sure what this does."""
0258         return self._shortcmd('RSET')
0259 
0260 
0261     def quit(self):
0262         """Signoff: commit changes on server, unlock mailbox, close connection."""
0263         try:
0264             resp = self._shortcmd('QUIT')
0265         except error_proto, val:
0266             resp = val
0267         self.file.close()
0268         self.sock.close()
0269         del self.file, self.sock
0270         return resp
0271 
0272     #__del__ = quit
0273 
0274 
0275     # optional commands:
0276 
0277     def rpop(self, user):
0278         """Not sure what this does."""
0279         return self._shortcmd('RPOP %s' % user)
0280 
0281 
0282     timestamp = re.compile(r'\+OK.*(<[^>]+>)')
0283 
0284     def apop(self, user, secret):
0285         """Authorisation
0286 
0287         - only possible if server has supplied a timestamp in initial greeting.
0288 
0289         Args:
0290                 user    - mailbox user;
0291                 secret  - secret shared between client and server.
0292 
0293         NB: mailbox is locked by server from here to 'quit()'
0294         """
0295         m = self.timestamp.match(self.welcome)
0296         if not m:
0297             raise error_proto('-ERR APOP not supported by server')
0298         import md5
0299         digest = md5.new(m.group(1)+secret).digest()
0300         digest = ''.join(map(lambda x:'%02x'%ord(x), digest))
0301         return self._shortcmd('APOP %s %s' % (user, digest))
0302 
0303 
0304     def top(self, which, howmuch):
0305         """Retrieve message header of message number 'which'
0306         and first 'howmuch' lines of message body.
0307 
0308         Result is in form ['response', ['line', ...], octets].
0309         """
0310         return self._longcmd('TOP %s %s' % (which, howmuch))
0311 
0312 
0313     def uidl(self, which=None):
0314         """Return message digest (unique id) list.
0315 
0316         If 'which', result contains unique id for that message
0317         in the form 'response mesgnum uid', otherwise result is
0318         the list ['response', ['mesgnum uid', ...], octets]
0319         """
0320         if which is not None:
0321             return self._shortcmd('UIDL %s' % which)
0322         return self._longcmd('UIDL')
0323 
0324 class POP3_SSL(POP3):
0325     """POP3 client class over SSL connection
0326 
0327     Instantiate with: POP3_SSL(hostname, port=995, keyfile=None, certfile=None)
0328 
0329            hostname - the hostname of the pop3 over ssl server
0330            port - port number
0331            keyfile - PEM formatted file that countains your private key
0332            certfile - PEM formatted certificate chain file
0333 
0334         See the methods of the parent class POP3 for more documentation.
0335     """
0336 
0337     def __init__(self, host, port = POP3_SSL_PORT, keyfile = None, certfile = None):
0338         self.host = host
0339         self.port = port
0340         self.keyfile = keyfile
0341         self.certfile = certfile
0342         self.buffer = ""
0343         msg = "getaddrinfo returns an empty list"
0344         self.sock = None
0345         for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM):
0346             af, socktype, proto, canonname, sa = res
0347             try:
0348                 self.sock = socket.socket(af, socktype, proto)
0349                 self.sock.connect(sa)
0350             except socket.error, msg:
0351                 if self.sock:
0352                     self.sock.close()
0353                 self.sock = None
0354                 continue
0355             break
0356         if not self.sock:
0357             raise socket.error, msg
0358         self.file = self.sock.makefile('rb')
0359         self.sslobj = socket.ssl(self.sock, self.keyfile, self.certfile)
0360         self._debugging = 0
0361         self.welcome = self._getresp()
0362 
0363     def _fillBuffer(self):
0364         localbuf = self.sslobj.read()
0365         if len(localbuf) == 0:
0366             raise error_proto('-ERR EOF')
0367         self.buffer += localbuf
0368 
0369     def _getline(self):
0370         line = ""
0371         renewline = re.compile(r'.*?\n')
0372         match = renewline.match(self.buffer)
0373         while not match:
0374             self._fillBuffer()
0375             match = renewline.match(self.buffer)
0376         line = match.group(0)
0377         self.buffer = renewline.sub('' ,self.buffer, 1)
0378         if self._debugging > 1: print '*get*', repr(line)
0379 
0380         octets = len(line)
0381         if line[-2:] == CRLF:
0382             return line[:-2], octets
0383         if line[0] == CR:
0384             return line[1:-1], octets
0385         return line[:-1], octets
0386 
0387     def _putline(self, line):
0388         if self._debugging > 1: print '*put*', repr(line)
0389         line += CRLF
0390         bytes = len(line)
0391         while bytes > 0:
0392             sent = self.sslobj.write(line)
0393             if sent == bytes:
0394                 break    # avoid copy
0395             line = line[sent:]
0396             bytes = bytes - sent
0397 
0398     def quit(self):
0399         """Signoff: commit changes on server, unlock mailbox, close connection."""
0400         try:
0401             resp = self._shortcmd('QUIT')
0402         except error_proto, val:
0403             resp = val
0404         self.sock.close()
0405         del self.sslobj, self.sock
0406         return resp
0407 
0408 
0409 if __name__ == "__main__":
0410     import sys
0411     a = POP3(sys.argv[1])
0412     print a.getwelcome()
0413     a.user(sys.argv[2])
0414     a.pass_(sys.argv[3])
0415     a.list()
0416     (numMsgs, totalSize) = a.stat()
0417     for i in range(1, numMsgs + 1):
0418         (header, msg, octets) = a.retr(i)
0419         print "Message %d:" % i
0420         for line in msg:
0421             print '   ' + line
0422         print '-----------------------'
0423     a.quit()
0424 

Generated by PyXR 0.9.4
SourceForge.net Logo