"Fossies" - the Fresh Open Source Software Archive

Member "getmail-5.16/getmailcore/_pop3ssl.py" (31 Oct 2021, 5940 Bytes) of package /linux/misc/getmail-5.16.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "_pop3ssl.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 5.15_vs_5.16.

    1 #!/usr/bin/env python2
    2 '''Provide an SSL-capable POP3 class.
    3 
    4 '''
    5 
    6 __all__ = [
    7     'POP3_ssl_port',
    8     'sslsocket',
    9     'POP3SSL',
   10 ]
   11 
   12 import socket
   13 from poplib import POP3, CR, LF, CRLF, error_proto
   14 
   15 from getmailcore.exceptions import *
   16 import getmailcore.logging
   17 log = getmailcore.logging.Logger()
   18 
   19 POP3_ssl_port = 995
   20 
   21 class sslsocket(object):
   22     '''The Python poplib.POP3() class mixes socket-like .sendall() and
   23     file-like .readline() for communications.  That would be okay, except that
   24     the new socket.ssl objects provide only read() and write(), so they
   25     don't act like a socket /or/ like a file.  Argh.
   26 
   27     This class takes a standard, connected socket.socket() object, sets it
   28     to blocking mode (required for socket.ssl() to work correctly, though
   29     apparently not documented), wraps .write() for .sendall() and implements
   30     .readline().
   31 
   32     The modified POP3 class below can then use this to provide POP3-over-SSL.
   33 
   34     Thanks to Frank Benkstein for the inspiration.
   35     '''
   36     def __init__(self, sock, keyfile=None, certfile=None):
   37         log.trace()
   38         self.sock = sock
   39         #self.sock.setblocking(1)
   40         if keyfile and certfile:
   41             self.ssl = socket.ssl(self.sock, keyfile, certfile)
   42         else:
   43             self.ssl = socket.ssl(self.sock)
   44         self.buf = ''
   45         self.bufsize = 128
   46 
   47     def _fillbuf(self):
   48         '''Fill an internal buffer for .readline() to use.
   49         '''
   50         log.trace()
   51         want = self.bufsize - len(self.buf)
   52         log.trace('want %i bytes\n' % want)
   53         if want <= 0:
   54             return
   55         s = self.ssl.read(want)
   56         got = len(s)
   57         log.trace('got %i bytes\n' % got)
   58         self.buf += s
   59 
   60     def close(self):
   61         self.sock.close()
   62         self.ssl = None
   63 
   64     # self.sock.sendall
   65     def sendall(self, s):
   66         # Maybe only set blocking around this call?
   67         self.ssl.write(s)
   68 
   69     # self.file.readline
   70     def readline(self):
   71         '''Simple hack to implement .readline() on a non-file object that
   72         only supports .read().
   73         '''
   74         log.trace()
   75         line = ''
   76         try:
   77             if not self.buf:
   78                 self._fillbuf()
   79             log.trace('checking self.buf\n')
   80             if self.buf:
   81                 log.trace('self.buf = "%r", len %i\n'
   82                           % (self.buf, len(self.buf)))
   83                 while True:
   84                     log.trace('looking for EOL\n')
   85                     i = self.buf.find('\n')
   86                     if i != -1:
   87                         log.trace('EOL found at %d\n' % i)
   88                         line += self.buf[:i + 1]
   89                         self.buf = self.buf[i + 1:]
   90                         break
   91                     # else
   92                     log.trace('EOL not found, trying to fill self.buf\n')
   93                     line += self.buf
   94                     self.buf = ''
   95                     self._fillbuf()
   96                     if not self.buf:
   97                         log.trace('nothing read, exiting\n')
   98                         break
   99                     log.trace('end of loop\n')
  100             log.trace('returning line "%r"\n' % line)
  101             return line
  102         except (socket.sslerror, socket.error), o:
  103             raise getmailOperationError(
  104                 'socket/ssl error while reading from server (%s)' % o
  105             )
  106 
  107 class POP3SSL(POP3):
  108     '''Thin subclass to add SSL functionality to the built-in POP3 class.
  109     Note that Python's socket module does not do certificate verification
  110     for SSL connections.
  111 
  112     This gets rid of the .file attribute from os.makefile(rawsock) and relies on
  113     sslsocket() above to provide .readline() instead.
  114     '''
  115     def __init__(self, host, port=POP3_ssl_port, keyfile=None, certfile=None):
  116         if not ((certfile and keyfile) or (keyfile == certfile == None)):
  117             raise getmailConfigurationError('certfile requires keyfile')
  118         self.host = host
  119         self.port = port
  120         msg = "getaddrinfo returns an empty list"
  121         self.rawsock = None
  122         self.sock = None
  123         for res in socket.getaddrinfo(self.host, self.port, 0,
  124                                       socket.SOCK_STREAM):
  125             (af, socktype, proto, canonname, sa) = res
  126             try:
  127                 self.rawsock = socket.socket(af, socktype, proto)
  128                 self.rawsock.connect(sa)
  129                 if certfile and keyfile:
  130                     self.sock = sslsocket(self.rawsock, keyfile, certfile)
  131                 else:
  132                     self.sock = sslsocket(self.rawsock)
  133             except socket.error, msg:
  134                 if self.rawsock:
  135                     self.rawsock.close()
  136                 self.rawsock = None
  137                 continue
  138             break
  139         if not self.sock:
  140             raise socket.error, msg
  141         self._debugging = 0
  142         self.welcome = self._getresp()
  143 
  144     # Internal: return one line from the server, stripping CRLF.
  145     # This is where all the CPU time of this module is consumed.
  146     # Raise error_proto('-ERR EOF') if the connection is closed.
  147     def _getline(self):
  148         line = self.sock.readline()
  149         if self._debugging > 1:
  150             print '*get*', `line`
  151         if not line:
  152             raise error_proto('-ERR EOF')
  153         octets = len(line)
  154         # server can send any combination of CR & LF
  155         # however, 'readline()' returns lines ending in LF
  156         # so only possibilities are ...LF, ...CRLF, CR...LF
  157         if line[-2:] == CRLF:
  158             return line[:-2], octets
  159         if line[0] == CR:
  160             return line[1:-1], octets
  161         return line[:-1], octets
  162 
  163     def quit(self):
  164         """Signoff: commit changes on server, unlock mailbox, close connection.
  165         """
  166         try:
  167             resp = self._shortcmd('QUIT')
  168         except (error_proto, socket.error), val:
  169             resp = val
  170         self.sock.close()
  171         del self.sock
  172         return resp