#!/usr/bin/env python2.3 '''Classes implementing retrievers (message sources getmail can retrieve mail from). Currently implemented: SimplePOP3Retriever SimplePOP3SSLRetriever BrokenUIDLPOP3Retriever BrokenUIDLPOP3SSLRetriever MultidropPOP3Retriever MultidropPOP3SSLRetriever MultidropSDPSRetriever SimpleIMAPRetriever -- IMAP, as a protocol, is a FPOS, and it shows. The Python standard library module imaplib leaves much up to the user because of this. SimpleIMAPSSLRetriever - the above, for IMAP-over-SSL. MultidropIMAPRetriever MultidropIMAPSSLRetriever ''' __all__ = [ 'SimplePOP3Retriever', 'SimplePOP3SSLRetriever', 'BrokenUIDLPOP3Retriever', 'BrokenUIDLPOP3SSLRetriever', 'MultidropPOP3Retriever', 'MultidropPOP3SSLRetriever', 'MultidropSDPSRetriever', 'SimpleIMAPRetriever', 'SimpleIMAPSSLRetriever', 'MultidropIMAPRetriever', 'MultidropIMAPSSLRetriever', ] import os import poplib import imaplib import types from getmailcore.exceptions import * from getmailcore.constants import * from getmailcore.utilities import * from getmailcore.baseclasses import * from getmailcore._retrieverbases import * # # Functional classes # ####################################### class SimplePOP3Retriever(POP3RetrieverBase, POP3initMixIn): '''Retriever class for single-user POP3 mailboxes. ''' _confitems = ( ConfInstance(name='configparser', required=False), ConfDirectory(name='getmaildir', required=False, default='~/.getmail/'), ConfInt(name='timeout', required=False, default=180), ConfString(name='server'), ConfInt(name='port', required=False, default=110), ConfString(name='username'), ConfPassword(name='password', required=False, default=None), ConfBool(name='use_apop', required=False, default=False), ConfBool(name='delete_dup_msgids', required=False, default=False), ) received_from = None received_with = 'POP3' received_by = localhostname() def __str__(self): self.log.trace() return 'SimplePOP3Retriever:%s@%s:%s' % ( self.conf.get('username', 'username'), self.conf.get('server', 'server'), self.conf.get('port', 'port') ) def showconf(self): self.log.trace() self.log.info('SimplePOP3Retriever(%s)' % self._confstring() + os.linesep) ####################################### class SimplePOP3SSLRetriever(POP3RetrieverBase, POP3SSLinitMixIn): '''Retriever class for single-user POP3-over-SSL mailboxes. ''' _confitems = ( ConfInstance(name='configparser', required=False), ConfDirectory(name='getmaildir', required=False, default='~/.getmail/'), ConfInt(name='timeout', required=False, default=180), ConfString(name='server'), ConfInt(name='port', required=False, default=POP3_ssl_port), ConfString(name='username'), ConfPassword(name='password', required=False, default=None), ConfBool(name='use_apop', required=False, default=False), ConfBool(name='delete_dup_msgids', required=False, default=False), ConfFile(name='keyfile', required=False, default=None), ConfFile(name='certfile', required=False, default=None), ) received_from = None received_with = 'POP3-SSL' received_by = localhostname() def __str__(self): self.log.trace() return 'SimplePOP3SSLRetriever:%s@%s:%s' % ( self.conf.get('username', 'username'), self.conf.get('server', 'server'), self.conf.get('port', 'port') ) def showconf(self): self.log.trace() self.log.info('SimplePOP3SSLRetriever(%s)' % self._confstring() + os.linesep) ####################################### class BrokenUIDLPOP3RetrieverBase(POP3RetrieverBase): '''Retriever base class for single-user POP3 mailboxes on servers that do not properly assign unique IDs to messages. Since with these broken servers we cannot rely on UIDL, we have to use message numbers, which are unique within a POP3 session, but which change across sessions. This class therefore can not be used to leave old mail on the server and download only new mail. ''' received_from = None received_by = localhostname() def _read_oldmailfile(self): '''Force list of old messages to be empty by making this a no-op, so duplicated IDs are always treated as new messages.''' self.log.trace() def write_oldmailfile(self, **kwargs): '''Short-circuit writing the oldmail file.''' self.log.trace() def _getmsglist(self): '''Don't rely on UIDL; instead, use just the message number.''' self.log.trace() try: response, msglist, octets = self.conn.list() for line in msglist: msgnum = int(line.split()[0]) msgsize = int(line.split()[1]) self.msgnum_by_msgid[msgnum] = msgnum self.msgid_by_msgnum[msgnum] = msgnum self.msgsizes[msgnum] = msgsize except poplib.error_proto, o: raise getmailOperationError('POP error (%s)' % o) self.gotmsglist = True ####################################### class BrokenUIDLPOP3Retriever(BrokenUIDLPOP3RetrieverBase, POP3initMixIn): '''For broken POP3 servers without SSL. ''' _confitems = ( ConfInstance(name='configparser', required=False), ConfDirectory(name='getmaildir', required=False, default='~/.getmail/'), ConfInt(name='timeout', required=False, default=180), ConfString(name='server'), ConfInt(name='port', required=False, default=110), ConfString(name='username'), ConfPassword(name='password', required=False, default=None), ConfBool(name='use_apop', required=False, default=False), ) received_with = 'POP3' def __str__(self): self.log.trace() return 'BrokenUIDLPOP3Retriever:%s@%s:%s' % ( self.conf.get('username', 'username'), self.conf.get('server', 'server'), self.conf.get('port', 'port') ) def showconf(self): self.log.trace() self.log.info('BrokenUIDLPOP3Retriever(%s)' % self._confstring() + os.linesep) ####################################### class BrokenUIDLPOP3SSLRetriever(BrokenUIDLPOP3RetrieverBase, POP3SSLinitMixIn): '''For broken POP3 servers with SSL. ''' _confitems = ( ConfInstance(name='configparser', required=False), ConfDirectory(name='getmaildir', required=False, default='~/.getmail/'), ConfInt(name='timeout', required=False, default=180), ConfString(name='server'), ConfInt(name='port', required=False, default=POP3_ssl_port), ConfString(name='username'), ConfPassword(name='password', required=False, default=None), ConfBool(name='use_apop', required=False, default=False), ConfFile(name='keyfile', required=False, default=None), ConfFile(name='certfile', required=False, default=None), ) received_with = 'POP3-SSL' def __str__(self): self.log.trace() return 'BrokenUIDLPOP3SSLRetriever:%s@%s:%s' % ( self.conf.get('username', 'username'), self.conf.get('server', 'server'), self.conf.get('port', 'port') ) def showconf(self): self.log.trace() self.log.info('BrokenUIDLPOP3SSLRetriever(%s)' % self._confstring() + os.linesep) ####################################### class MultidropPOP3Retriever(MultidropPOP3RetrieverBase, POP3initMixIn): '''Retriever class for multi-drop POP3 mailboxes. ''' _confitems = ( ConfInstance(name='configparser', required=False), ConfDirectory(name='getmaildir', required=False, default='~/.getmail/'), ConfInt(name='timeout', required=False, default=180), ConfString(name='server'), ConfInt(name='port', required=False, default=110), ConfString(name='username'), ConfPassword(name='password', required=False, default=None), ConfBool(name='use_apop', required=False, default=False), ConfString(name='envelope_recipient'), ) received_from = None received_with = 'POP3' received_by = localhostname() def __str__(self): self.log.trace() return 'MultidropPOP3Retriever:%s@%s:%s' % ( self.conf.get('username', 'username'), self.conf.get('server', 'server'), self.conf.get('port', 'port') ) def showconf(self): self.log.trace() self.log.info('MultidropPOP3Retriever(%s)' % self._confstring() + os.linesep) ####################################### class MultidropPOP3SSLRetriever(MultidropPOP3RetrieverBase, POP3SSLinitMixIn): '''Retriever class for multi-drop POP3-over-SSL mailboxes. ''' _confitems = ( ConfInstance(name='configparser', required=False), ConfDirectory(name='getmaildir', required=False, default='~/.getmail/'), ConfInt(name='timeout', required=False, default=180), ConfString(name='server'), ConfInt(name='port', required=False, default=POP3_ssl_port), ConfString(name='username'), ConfPassword(name='password', required=False, default=None), ConfBool(name='use_apop', required=False, default=False), ConfString(name='envelope_recipient'), ConfFile(name='keyfile', required=False, default=None), ConfFile(name='certfile', required=False, default=None), ) received_from = None received_with = 'POP3-SSL' received_by = localhostname() def __str__(self): self.log.trace() return 'MultidropPOP3SSLRetriever:%s@%s:%s' % ( self.conf.get('username', 'username'), self.conf.get('server', 'server'), self.conf.get('port', 'port') ) def showconf(self): self.log.trace() self.log.info('MultidropPOP3SSLRetriever(%s)' % self._confstring() + os.linesep) ####################################### class MultidropSDPSRetriever(SimplePOP3Retriever, POP3initMixIn): '''Retriever class for multi-drop SDPS (demon.co.uk) mailboxes. Extend POP3 class to include support for Demon's protocol extensions, known as SDPS. A non-standard command (*ENV) is used to retrieve the message envelope. See http://www.demon.net/helpdesk/products/mail/sdps-tech.shtml for details. Support originally requested by Paul Clifford for getmail v.2/3. ''' _confitems = ( ConfInstance(name='configparser', required=False), ConfDirectory(name='getmaildir', required=False, default='~/.getmail/'), ConfInt(name='timeout', required=False, default=180), ConfString(name='server'), ConfInt(name='port', required=False, default=110), ConfString(name='username'), ConfPassword(name='password', required=False, default=None), # Demon apparently doesn't support APOP ConfBool(name='use_apop', required=False, default=False), ) received_from = None received_with = 'SDPS' received_by = localhostname() def __str__(self): self.log.trace() return 'MultidropSDPSRetriever:%s@%s:%s' % ( self.conf.get('username', 'username'), self.conf.get('server', 'server'), self.conf.get('port', 'port') ) def showconf(self): self.log.trace() self.log.info('MultidropSDPSRetriever(%s)' % self._confstring() + os.linesep) def _getmsgbyid(self, msgid): self.log.trace() msg = SimplePOP3Retriever._getmsgbyid(self, msgid) # The magic of SDPS is the "*ENV" command. Implement it: try: msgnum = self._getmsgnumbyid(msgid) resp, lines, octets = self.conn._longcmd('*ENV %i' % msgnum) except poplib.error_proto, o: raise getmailConfigurationError('server does not support *ENV (%s)' % o) if len(lines) < 4: raise getmailOperationError('short *ENV response (%s)' % lines) msg.sender = lines[2] msg.recipient = lines[3] return msg ####################################### class SimpleIMAPRetriever(IMAPRetrieverBase, IMAPinitMixIn): '''Retriever class for single-user IMAPv4 mailboxes. ''' _confitems = ( ConfInstance(name='configparser', required=False), ConfDirectory(name='getmaildir', required=False, default='~/.getmail/'), ConfInt(name='timeout', required=False, default=180), ConfString(name='server'), ConfInt(name='port', required=False, default=imaplib.IMAP4_PORT), ConfString(name='username'), ConfPassword(name='password', required=False, default=None), ConfTupleOfStrings(name='mailboxes', required=False, default="('INBOX', )"), ConfString(name='move_on_delete', required=False, default=None), # imaplib.IMAP4.login_cram_md5() requires the (unimplemented) # .authenticate(), so we can't do this yet (?). ConfBool(name='use_cram_md5', required=False, default=False), ) received_from = None received_with = 'IMAP4' received_by = localhostname() def __str__(self): self.log.trace() return 'SimpleIMAPRetriever:%s@%s:%s' % ( self.conf.get('username', 'username'), self.conf.get('server', 'server'), self.conf.get('port', 'port') ) def showconf(self): self.log.trace() self.log.info('SimpleIMAPRetriever(%s)' % self._confstring() + os.linesep) ####################################### class SimpleIMAPSSLRetriever(IMAPRetrieverBase, IMAPSSLinitMixIn): '''Retriever class for single-user IMAPv4-over-SSL mailboxes. ''' _confitems = ( ConfInstance(name='configparser', required=False), ConfDirectory(name='getmaildir', required=False, default='~/.getmail/'), # socket.ssl() and socket timeouts are incompatible in Python 2.3 #ConfInt(name='timeout', required=False, default=180), ConfString(name='server'), ConfInt(name='port', required=False, default=imaplib.IMAP4_SSL_PORT), ConfString(name='username'), ConfPassword(name='password', required=False, default=None), ConfTupleOfStrings(name='mailboxes', required=False, default="('INBOX', )"), ConfString(name='move_on_delete', required=False, default=None), ConfFile(name='keyfile', required=False, default=None), ConfFile(name='certfile', required=False, default=None), # imaplib.IMAP4.login_cram_md5() requires the (unimplemented) # .authenticate(), so we can't do this yet (?). ConfBool(name='use_cram_md5', required=False, default=False), ) received_from = None received_with = 'IMAP4-SSL' received_by = localhostname() def __str__(self): self.log.trace() return 'SimpleIMAPSSLRetriever:%s@%s:%s' % ( self.conf.get('username', 'username'), self.conf.get('server', 'server'), self.conf.get('port', 'port') ) def showconf(self): self.log.trace() self.log.info('SimpleIMAPSSLRetriever(%s)' % self._confstring() + os.linesep) ####################################### class MultidropIMAPRetriever(MultidropIMAPRetrieverBase, IMAPinitMixIn): '''Retriever class for multi-drop IMAPv4 mailboxes. ''' _confitems = ( ConfInstance(name='configparser', required=False), ConfDirectory(name='getmaildir', required=False, default='~/.getmail/'), ConfInt(name='timeout', required=False, default=180), ConfString(name='server'), ConfInt(name='port', required=False, default=imaplib.IMAP4_PORT), ConfString(name='username'), ConfPassword(name='password', required=False, default=None), ConfTupleOfStrings(name='mailboxes', required=False, default="('INBOX', )"), ConfString(name='move_on_delete', required=False, default=None), # imaplib.IMAP4.login_cram_md5() requires the (unimplemented) # .authenticate(), so we can't do this yet (?). ConfBool(name='use_cram_md5', required=False, default=False), ConfString(name='envelope_recipient'), ) received_from = None received_with = 'IMAP4' received_by = localhostname() def __str__(self): self.log.trace() return 'MultidropIMAPRetriever:%s@%s:%s' % ( self.conf.get('username', 'username'), self.conf.get('server', 'server'), self.conf.get('port', 'port') ) def showconf(self): self.log.trace() self.log.info('MultidropIMAPRetriever(%s)' % self._confstring() + os.linesep) ####################################### class MultidropIMAPSSLRetriever(MultidropIMAPRetrieverBase, IMAPSSLinitMixIn): '''Retriever class for multi-drop IMAPv4-over-SSL mailboxes. ''' _confitems = ( ConfInstance(name='configparser', required=False), ConfDirectory(name='getmaildir', required=False, default='~/.getmail/'), # socket.ssl() and socket timeouts are incompatible in Python 2.3 #ConfInt(name='timeout', required=False, default=180), ConfString(name='server'), ConfInt(name='port', required=False, default=110), ConfString(name='username'), ConfPassword(name='password', required=False, default=None), ConfTupleOfStrings(name='mailboxes', required=False, default="('INBOX', )"), ConfString(name='move_on_delete', required=False, default=None), ConfFile(name='keyfile', required=False, default=None), ConfFile(name='certfile', required=False, default=None), # imaplib.IMAP4.login_cram_md5() requires the (unimplemented) # .authenticate(), so we can't do this yet (?). ConfBool(name='use_cram_md5', required=False, default=False), ConfString(name='envelope_recipient'), ) received_from = None received_with = 'IMAP4-SSL' received_by = localhostname() def __str__(self): self.log.trace() return 'MultidropIMAPSSLRetriever:%s@%s:%s' % ( self.conf.get('username', 'username'), self.conf.get('server', 'server'), self.conf.get('port', 'port') ) def showconf(self): self.log.trace() self.log.info('MultidropIMAPSSLRetriever(%s)' % self._confstring() + os.linesep)