"Fossies" - the Fresh Open Source Software Archive

Member "getmail-5.16/getmail" (31 Oct 2021, 42103 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. See also the latest Fossies "Diffs" side-by-side code changes report for "getmail": 5.15_vs_5.16.

    1 #!/usr/bin/env python2
    2 
    3 import sys
    4 
    5 if sys.hexversion < 0x2030300:
    6     raise ImportError('getmail version 5 requires Python version 2.3.3 '
    7                       'or later')
    8 
    9 import os.path
   10 import time
   11 import ConfigParser
   12 import netrc
   13 import poplib
   14 import imaplib
   15 import pprint
   16 from optparse import OptionParser, OptionGroup
   17 import socket
   18 import signal
   19 import errno
   20 
   21 # Optional gnome-keyring integration
   22 try:
   23     import gnomekeyring
   24     import glib
   25     glib.set_application_name('getmail')
   26     # And test to see if it's actually available
   27     if not gnomekeyring.is_available():
   28         gnomekeyring = None
   29 except ImportError:
   30     gnomekeyring = None
   31 
   32 options_bool = (
   33     'read_all',
   34     'delete',
   35     'delivered_to',
   36     'received',
   37     'message_log_verbose',
   38     'message_log_syslog',
   39     'fingerprint',
   40     'use_netrc',
   41 )
   42 options_int = (
   43     'delete_after',
   44     'delete_bigger_than',
   45     'max_message_size',
   46     'max_messages_per_session',
   47     'max_bytes_per_session',
   48     'verbose',
   49 )
   50 options_str = (
   51     'message_log',
   52     'netrc_file',
   53 )
   54 
   55 # Unix only
   56 try:
   57     import syslog
   58 except ImportError:
   59     pass
   60 
   61 try:
   62     from getmailcore import __version__, retrievers, destinations, filters, \
   63         logging
   64     from getmailcore.exceptions import *
   65     from getmailcore.utilities import eval_bool, logfile, format_params, \
   66         address_no_brackets, expand_user_vars, get_password, run_command
   67 except ImportError, o:
   68     sys.stderr.write('ImportError:  %s\n' % o)
   69     sys.exit(127)
   70 
   71 log = logging.Logger()
   72 log.addhandler(sys.stdout, logging.INFO, maxlevel=logging.INFO)
   73 log.addhandler(sys.stderr, logging.WARNING)
   74 
   75 defaults = {
   76     'getmaildir' : '~/.getmail/',
   77     'rcfile' : 'getmailrc',
   78 
   79     'verbose' : 1,
   80     'read_all' : True,
   81     'delete' : False,
   82     'delete_after' : 0,
   83     'delete_bigger_than' : 0,
   84     'max_message_size' : 0,
   85     'max_messages_per_session' : 0,
   86     'max_bytes_per_session' : 0,
   87     'delivered_to' : True,
   88     'received' : True,
   89     'message_log' : None,
   90     'message_log_verbose' : False,
   91     'message_log_syslog' : False,
   92     'logfile' : None,
   93     'fingerprint' : False,
   94     'use_netrc' : False,
   95     'netrc_file' : None,
   96 }
   97 
   98 
   99 
  100 
  101 #######################################
  102 def convert_to_sigint(unused1, unused2):
  103     """Catch a SIGTERM and raise a SIGINT so getmail exits normally and does
  104     cleanup if killed with default signal.
  105     """
  106     raise KeyboardInterrupt('from signal')
  107 
  108 signal.signal(signal.SIGTERM, convert_to_sigint)
  109 
  110 #######################################
  111 def blurb():
  112     log.info('getmail version %s\n' % __version__)
  113     log.info('Copyright (C) 1998-2021 Charles Cazabon.  Licensed under the '
  114              'GNU GPL version 2.\n')
  115 
  116 #######################################
  117 def go(configs, idle):
  118     """Main code.
  119 
  120     Returns True if all goes well, False if any error condition occurs.
  121     """
  122     blurb()
  123     summary = []
  124     errorexit = False
  125     idling = False
  126 
  127     if len(configs) > 1 and idle:
  128         log.info('more than one config file given with --idle, ignoring\n')
  129         idle = False
  130 
  131     for (configfile, retriever, _filters, destination, options) in configs:
  132         if options['read_all'] and not options['delete']:
  133             if idle:
  134                 # This is a nonsense combination of options; every time the
  135                 # server returns from IDLE, all messages will be re-retrieved.
  136                 log.error('%s: IDLE, read_all, and not delete - bad '
  137                           'combination, skipping\n' 
  138                           % retriever)
  139                 continue
  140             else:
  141                 # Slightly less nonsensical, but still weird.
  142                 log.warning('%s: read_all and not delete -- all messages will '
  143                             'be retrieved each time getmail is run\n' 
  144                             % retriever)
  145             
  146         oplevel = options['verbose']
  147         logverbose = options['message_log_verbose']
  148         now = int(time.time())
  149         msgs_retrieved = 0
  150         bytes_retrieved = 0
  151         msgs_skipped = 0
  152         if options['message_log_syslog']:
  153             syslog.openlog('getmail', 0, syslog.LOG_MAIL)
  154         try:
  155             if not idling:
  156                 log.info('%s:\n' % retriever)
  157                 logline = 'Initializing %s:' % retriever
  158                 if options['logfile'] and logverbose:
  159                     options['logfile'].write(logline)
  160                 if options['message_log_syslog'] and logverbose:
  161                     syslog.syslog(syslog.LOG_INFO, logline)
  162                 retriever.initialize(options)
  163                 destination.retriever_info(retriever)
  164 
  165             for mailbox in retriever.mailboxes:
  166                 if mailbox:
  167                     # For POP this is None and uninteresting
  168                     log.debug('  checking mailbox %s ...\n' 
  169                               % mailbox.encode('utf-8'))
  170                 try:
  171                     retriever.select_mailbox(mailbox)
  172                 except getmailMailboxSelectError, o:
  173                     errorexit = True
  174                     log.info('  mailbox %s not selectable (%s) - verify the '
  175                                 'mailbox exists and you have sufficient '
  176                                 'permissions\n' % (mailbox.encode('utf-8'), o))
  177                     continue
  178                 nummsgs = len(retriever)
  179                 fmtlen = len(str(nummsgs))
  180                 for (msgnum, msgid) in enumerate(retriever):
  181                     log.debug('  message %s ...\n' % msgid)
  182                     msgnum += 1
  183                     retrieve = False
  184                     reason = 'seen'
  185                     delete = False
  186                     timestamp = retriever.oldmail.get(msgid, None)
  187                     size = retriever.getmsgsize(msgid)
  188                     info = ('msg %*d/%*d (%d bytes)'
  189                             % (fmtlen, msgnum, fmtlen, nummsgs, size))
  190                     logline = '%s msgid %s' % (info, msgid)
  191                     if options['read_all'] or timestamp is None:
  192                         retrieve = True
  193                     if (options['max_message_size']
  194                             and size > options['max_message_size']):
  195                         retrieve = False
  196                         reason = 'oversized'
  197                     if (options['max_bytes_per_session']
  198                             and (bytes_retrieved + size)
  199                                 > options['max_bytes_per_session']):
  200                         retrieve = False
  201                         reason = 'would surpass max_bytes_per_session'
  202                     try:
  203                         if retrieve:
  204                             try:
  205                                 msg = retriever.getmsg(msgid)
  206                             except getmailRetrievalError, o:
  207                                 errorexit = True
  208                                 log.error(
  209                                     'Retrieval error: server for %s is broken; '
  210                                     'offered message %s but failed to provide it.  '
  211                                     'Please notify the administrator of the '
  212                                     'server.  Skipping message...\n'
  213                                     % (retriever, msgid)
  214                                 )
  215                                 continue
  216                             msgs_retrieved += 1
  217                             bytes_retrieved += size
  218                             if oplevel > 1:
  219                                 info += (' from <%s>'
  220                                          % address_no_brackets(msg.sender))
  221                                 if msg.recipient is not None:
  222                                     info += (' to <%s>'
  223                                              % address_no_brackets(msg.recipient))
  224                             logline += (' from <%s>'
  225                                         % address_no_brackets(msg.sender))
  226                             if msg.recipient is not None:
  227                                 logline += (' to <%s>'
  228                                             % address_no_brackets(msg.recipient))
  229 
  230                             for mail_filter in _filters:
  231                                 log.debug('    passing to filter %s\n'
  232                                           % mail_filter)
  233                                 msg = mail_filter.filter_message(msg, retriever)
  234                                 if msg is None:
  235                                     log.debug('    dropped by filter %s\n'
  236                                               % mail_filter)
  237                                     info += (' dropped by filter %s'
  238                                              % mail_filter)
  239                                     logline += (' dropped by filter %s'
  240                                                 % mail_filter)
  241                                     retriever.delivered(msgid)
  242                                     break
  243 
  244                             if msg is not None:
  245                                 r = destination.deliver_message(msg,
  246                                     options['delivered_to'], options['received'])
  247                                 log.debug('    delivered to %s\n' % r)
  248                                 info += ' delivered'
  249                                 if oplevel > 1:
  250                                     info += (' to %s' % r)
  251                                 logline += (' delivered to %s' % r)
  252                                 retriever.delivered(msgid)
  253                             if options['delete']:
  254                                 delete = True
  255                         else:
  256                             logline += ' not retrieved (%s)' % reason
  257                             msgs_skipped += 1
  258                             log.debug('    not retrieving (timestamp %s)\n'
  259                                       % timestamp)
  260                             if oplevel > 1:
  261                                 info += ' not retrieved (%s)' % reason
  262 
  263                         if (options['delete_after'] and timestamp
  264                                 and (now - timestamp) / 86400
  265                                     >= options['delete_after']):
  266                             log.debug(
  267                                 '    older than %d days (%s seconds), will delete\n'
  268                                 % (options['delete_after'], (now - timestamp))
  269                             )
  270                             delete = True
  271 
  272                         if options['delete'] and timestamp:
  273                             log.debug('    will delete\n')
  274                             delete = True
  275                         
  276                         if (options['delete_bigger_than'] 
  277                                 and size > options['delete_bigger_than']):
  278                             log.debug('    bigger than %d, will delete\n'
  279                                       % options['delete_bigger_than'])
  280                             delete = True
  281 
  282                         if not retrieve and timestamp is None:
  283                             # We haven't retrieved this message.  Don't delete it.
  284                             log.debug('    not yet retrieved, not deleting\n')
  285                             delete = False
  286 
  287                         if delete:
  288                             retriever.delmsg(msgid)
  289                             log.debug('    deleted\n')
  290                             info += ', deleted'
  291                             logline += ', deleted'
  292 
  293                     except getmailDeliveryError, o:
  294                         errorexit = True
  295                         log.error('Delivery error (%s)\n' % o)
  296                         info += ', delivery error (%s)' % o
  297                         if options['logfile']:
  298                             options['logfile'].write('Delivery error (%s)' % o)
  299                         if options['message_log_syslog']:
  300                             syslog.syslog(syslog.LOG_ERR,
  301                                           'Delivery error (%s)' % o)
  302 
  303                     except getmailFilterError, o:
  304                         errorexit = True
  305                         log.error('Filter error (%s)\n' % o)
  306                         info += ', filter error (%s)' % o
  307                         if options['logfile']:
  308                             options['logfile'].write('Filter error (%s)' % o)
  309                         if options['message_log_syslog']:
  310                             syslog.syslog(syslog.LOG_ERR,
  311                                           'Filter error (%s)' % o)
  312 
  313                     if (retrieve or delete or oplevel > 1):
  314                         log.info('  %s\n' % info)
  315                     if options['logfile'] and (retrieve or delete or logverbose):
  316                         options['logfile'].write(logline)
  317                     if options['message_log_syslog'] and (retrieve or delete
  318                                                           or logverbose):
  319                         syslog.syslog(syslog.LOG_INFO, logline)
  320 
  321                     if (options['max_messages_per_session']
  322                             and msgs_retrieved >=
  323                             options['max_messages_per_session']):
  324                         log.debug('hit max_messages_per_session (%d), breaking\n'
  325                             % options['max_messages_per_session'])
  326                         if oplevel > 1:
  327                             log.info('  max messages per session (%d)\n'
  328                                      % options['max_messages_per_session'])
  329                         raise StopIteration('max_messages_per_session %d'
  330                                             % options['max_messages_per_session'])
  331 
  332         except StopIteration:
  333             pass
  334 
  335         except KeyboardInterrupt, o:
  336             log.warning('%s: user aborted\n' % configfile)
  337             if options['logfile']:
  338                 options['logfile'].write('user aborted')
  339 
  340         except socket.timeout, o:
  341             errorexit = True
  342             retriever.abort()
  343             if type(o) == tuple and len(o) > 1:
  344                 o = o[1]
  345             log.error('%s: timeout (%s)\n' % (configfile, o))
  346             if options['logfile']:
  347                 options['logfile'].write('timeout error (%s)' % o)
  348 
  349         except (poplib.error_proto, imaplib.IMAP4.abort), o:
  350             errorexit = True
  351             retriever.abort()
  352             log.error('%s: protocol error (%s)\n' % (configfile, o))
  353             if options['logfile']:
  354                 options['logfile'].write('protocol error (%s)' % o)
  355 
  356         except socket.gaierror, o:
  357             errorexit = True
  358             retriever.abort()
  359             if type(o) == tuple and len(o) > 1:
  360                 o = o[1]
  361             log.error('%s: error resolving name (%s)\n' % (configfile, o))
  362             if options['logfile']:
  363                 options['logfile'].write('gaierror error (%s)' % o)
  364 
  365         except socket.error, o:
  366             errorexit = True
  367             retriever.abort()
  368             if type(o) == tuple and len(o) > 1:
  369                 o = o[1]
  370             log.error('%s: socket error (%s)\n' % (configfile, o))
  371             if options['logfile']:
  372                 options['logfile'].write('socket error (%s)' % o)
  373 
  374         except getmailCredentialError, o:
  375             errorexit = True
  376             retriever.abort()
  377             log.error('%s: credential/login error (%s)\n' % (configfile, o))
  378             if options['logfile']:
  379                 options['logfile'].write('credential/login error (%s)' % o)
  380 
  381         except getmailLoginRefusedError, o:
  382             retriever.abort()
  383             log.error('%s: login refused error (%s)\n' % (configfile, o))
  384             if options['logfile']:
  385                 options['logfile'].write('login refused error (%s)' % o)
  386 
  387         except getmailOperationError, o:
  388             errorexit = True
  389             retriever.abort()
  390             log.error('%s: operation error (%s)\n' % (configfile, o))
  391             if options['logfile']:
  392                 options['logfile'].write('getmailOperationError error (%s)' % o)
  393             if options['message_log_syslog']:
  394                 syslog.syslog(syslog.LOG_ERR,
  395                               'getmailOperationError error (%s)' % o)
  396 
  397         summary.append(
  398             (retriever, msgs_retrieved, bytes_retrieved, msgs_skipped)
  399         )
  400 
  401         log.info('  %d messages (%d bytes) retrieved, %d skipped\n'
  402                  % (msgs_retrieved, bytes_retrieved, msgs_skipped))
  403         if options['logfile'] and logverbose:
  404             options['logfile'].write(
  405                 '  %d messages (%d bytes) retrieved, %d skipped\n'
  406                 % (msgs_retrieved, bytes_retrieved, msgs_skipped)
  407             )
  408         log.debug('retriever %s finished\n' % retriever)
  409         try:
  410             if idle and not retriever.supports_idle:
  411                 log.info('--idle given, but retriever does not support IDLE\n')
  412                 idle = False
  413             if idle and sys.version_info < (2, 5, 0):
  414                 log.info('--idle requires Python 2.5 or higher\n')
  415                 idle = False
  416 
  417             if idle and not errorexit:
  418                 # TODO
  419                 # Okay, so what should really happen here is that when go_idle
  420                 # returns, getmail should use the *existing* connection to check
  421                 # for new messages and then call go_idle again once that is
  422                 # done. The current code layout doesn't lend itself very well to
  423                 # that since the message download code is coupled with the
  424                 # connection setup/teardown code.
  425                 #
  426                 # Therefore, we do a bit of a hack.
  427                 # We add the current config back into configs, so that when the
  428                 # main for loop over configs runs again, it will find the same
  429                 # config again, and thus download the new messages and then go
  430                 # back to IDLEing. Since the return value of go_idle changes the
  431                 # value of idling, a failed connection will cause it to become
  432                 # False, which will make the main go() loop reconnect, which is
  433                 # what we want.
  434                 # Expunge and close the mailbox to  prevent the same messages
  435                 # being pulled again in some configurations.
  436                 retriever.close_mailbox()
  437                 try:
  438                     idling = retriever.go_idle(idle)
  439                     # Returned from idle
  440                     retriever.set_new_timestamp()
  441                     configs.append(configs[0])
  442                     continue
  443                 except KeyboardInterrupt, o:
  444                     # Because configs isn't appended to, this just means we'll
  445                     # quit, which is presumably what the user wanted
  446                     # The newline is to clear the ^C shown in terminal
  447                     log.info('\n')
  448                     pass
  449                 except socket.error, o:
  450                     if o.errno != errno.ECONNRESET:
  451                         # Something unexpected happened
  452                         raise
  453                     #pass
  454                     # Just exit after a reset connection.
  455 
  456             retriever.quit()
  457         except getmailOperationError, o:
  458             errorexit = True
  459             log.debug('%s: operation error during quit (%s)\n'
  460                       % (configfile, o))
  461             if options['logfile']:
  462                 options['logfile'].write('%s: operation error during quit (%s)'
  463                                          % (configfile, o))
  464 
  465     if sum([i for (unused, i, unused, unused) in summary]) and oplevel > 1:
  466         log.info('Summary:\n')
  467         for (retriever, msgs_retrieved, bytes_retrieved, unused) in summary:
  468             log.info('Retrieved %d messages (%s bytes) from %s\n'
  469                      % (msgs_retrieved, bytes_retrieved, retriever))
  470 
  471     return (not errorexit)
  472 
  473 
  474 #######################################
  475 def main():
  476     try:
  477         parser = OptionParser(version='%%prog %s' % __version__)
  478         parser.add_option(
  479             '-g', '--getmaildir',
  480             dest='getmaildir', action='store', default=defaults['getmaildir'],
  481             help='look in DIR for config/data files', metavar='DIR'
  482         )
  483         parser.add_option(
  484             '-r', '--rcfile',
  485             dest='rcfile', action='append', default=[],
  486             help='load configuration from FILE (may be given multiple times)',
  487             metavar='FILE'
  488         )
  489         parser.add_option(
  490             '--dump',
  491             dest='dump_config', action='store_true', default=False,
  492             help='dump configuration and exit (debugging)'
  493         )
  494         parser.add_option(
  495             '--trace',
  496             dest='trace', action='store_true', default=False,
  497             help='print extended trace information (extremely verbose)'
  498         )
  499         parser.add_option(
  500             '-i', '--idle',
  501             dest='idle', action='store', default='',
  502             help='maintain connection and listen for new messages in FOLDER. '
  503                  'Only applies if a single rc file is given with a connection '
  504                  'to an IMAP server that supports the IDLE command',
  505             metavar='FOLDER'
  506         )
  507         if gnomekeyring:
  508             parser.add_option(
  509                 '--store-password-in-gnome-keyring',
  510                 dest='store_gnome_keyring', action='store_true', default=False,
  511                 help='store the POP/IMAP password in the Gnome keyring'
  512             )
  513         overrides = OptionGroup(
  514             parser, 'Overrides',
  515             'The following options override those specified in any '
  516                 'getmailrc file.'
  517         )
  518         overrides.add_option(
  519             '-v', '--verbose',
  520             dest='override_verbose', action='count',
  521             help='operate more verbosely (may be given multiple times)'
  522         )
  523         overrides.add_option(
  524             '--fingerprint',
  525             dest='override_fingerprint', action='store_true',
  526             help='show SSL/TLS fingerprint and connection information'
  527         )
  528         overrides.add_option(
  529             '-q', '--quiet',
  530             dest='override_verbose', action='store_const',
  531             const=0,
  532             help='operate quietly (only report errors)'
  533         )
  534         overrides.add_option(
  535             '-d', '--delete',
  536             dest='override_delete', action='store_true',
  537             help='delete messages from server after retrieving'
  538         )
  539         overrides.add_option(
  540             '-l', '--dont-delete',
  541             dest='override_delete', action='store_false',
  542             help='do not delete messages from server after retrieving'
  543         )
  544         overrides.add_option(
  545             '-a', '--all',
  546             dest='override_read_all', action='store_true',
  547             help='retrieve all messages'
  548         )
  549         overrides.add_option(
  550             '-n', '--new',
  551             dest='override_read_all', action='store_false',
  552             help='retrieve only unread messages'
  553         )
  554         parser.add_option_group(overrides)
  555 
  556         (options, args) = parser.parse_args(sys.argv[1:])
  557         if args:
  558             raise getmailOperationError('unknown argument(s) %s ; try --help'
  559                                         % args)
  560 
  561         if options.trace:
  562             log.clearhandlers()
  563 
  564         if not options.rcfile:
  565             options.rcfile.append(defaults['rcfile'])
  566 
  567         s = ''
  568         for attr in dir(options):
  569             if attr.startswith('_'):
  570                 continue
  571             if s:
  572                 s += ','
  573             s += '%s="%s"' % (attr, pprint.pformat(getattr(options, attr)))
  574         log.debug('parsed options:  %s\n' % s)
  575 
  576         getmaildir_type = 'Default'
  577         if options.getmaildir != defaults['getmaildir']:
  578             getmaildir_type = 'Specified'
  579         getmaildir = expand_user_vars(options.getmaildir)
  580         if not os.path.exists(getmaildir):
  581             raise getmailOperationError(
  582                 '%s config/data dir "%s" does not exist - create '
  583                 'or specify alternate directory with --getmaildir option'
  584                 % (getmaildir_type, getmaildir)
  585             )
  586         if not os.path.isdir(getmaildir):
  587             raise getmailOperationError(
  588                 '%s config/data dir "%s" is not a directory - fix '
  589                 'or specify alternate directory with --getmaildir option'
  590                 % (getmaildir_type, getmaildir)
  591             )
  592         if not os.access(getmaildir, os.W_OK):
  593             raise getmailOperationError(
  594                 '%s config/data dir "%s" is not writable - fix permissions '
  595                 'or specify alternate directory with --getmaildir option'
  596                 % (getmaildir_type, getmaildir)
  597             )
  598 
  599         configs = []
  600         for filename in options.rcfile:
  601             path = os.path.join(os.path.expanduser(options.getmaildir),
  602                                 filename)
  603             log.debug('processing rcfile %s\n' % path)
  604             if not os.path.exists(path):
  605                 raise getmailOperationError('configuration file %s does '
  606                                             'not exist' % path)
  607             elif not os.path.isfile(path):
  608                 raise getmailOperationError('%s is not a file' % path)
  609             f = open(path, 'rb')
  610             config = {
  611                 'verbose' : defaults['verbose'],
  612                 'read_all' : defaults['read_all'],
  613                 'delete' : defaults['delete'],
  614                 'delete_after' : defaults['delete_after'],
  615                 'delete_bigger_than' : defaults['delete_bigger_than'],
  616                 'max_message_size' : defaults['max_message_size'],
  617                 'max_messages_per_session' :
  618                     defaults['max_messages_per_session'],
  619                 'max_bytes_per_session' :
  620                     defaults['max_bytes_per_session'],
  621                 'delivered_to' : defaults['delivered_to'],
  622                 'received' : defaults['received'],
  623                 'logfile' : defaults['logfile'],
  624                 'message_log' : defaults['message_log'],
  625                 'message_log_verbose' : defaults['message_log_verbose'],
  626                 'message_log_syslog' : defaults['message_log_syslog'],
  627                 'fingerprint' : defaults['fingerprint'],
  628                 'use_netrc' : defaults['use_netrc'],
  629                 'netrc_file' : defaults['netrc_file'],
  630             }
  631             # Python's ConfigParser .getboolean() couldn't handle booleans in
  632             # the defaults. Submitted a patch; they fixed it a different way.
  633             # But for the extant, unfixed versions, an ugly hack....
  634             parserdefaults = config.copy()
  635             for (key, value) in parserdefaults.items():
  636                 if type(value) == bool:
  637                     parserdefaults[key] = str(value)
  638 
  639             try:
  640                 configparser = ConfigParser.RawConfigParser(parserdefaults)
  641                 configparser.readfp(f, path)
  642                 for option in options_bool:
  643                     log.debug('  looking for option %s ... ' % option)
  644                     if configparser.has_option('options', option):
  645                         log.debug('got "%s"'
  646                                   % configparser.get('options', option))
  647                         try:
  648                             config[option] = configparser.getboolean(
  649                                 'options', option
  650                             )
  651                             log.debug('-> %s' % config[option])
  652                         except ValueError:
  653                             raise getmailConfigurationError(
  654                                 'configuration file %s incorrect (option %s '
  655                                 'must be boolean, not %s)'
  656                                 % (path, option,
  657                                    configparser.get('options', option))
  658                             )
  659                     else:
  660                         log.debug('not found')
  661                     log.debug('\n')
  662 
  663                 for option in options_int:
  664                     log.debug('  looking for option %s ... ' % option)
  665                     if configparser.has_option('options', option):
  666                         log.debug(
  667                             'got "%s"' % configparser.get('options', option)
  668                         )
  669                         try:
  670                             config[option] = configparser.getint('options',
  671                                                                  option)
  672                             log.debug('-> %s' % config[option])
  673                         except ValueError:
  674                             raise getmailConfigurationError(
  675                                 'configuration file %s incorrect (option %s '
  676                                 'must be integer, not %s)'
  677                                 % (path, option,
  678                                    configparser.get('options', option))
  679                             )
  680                     else:
  681                         log.debug('not found')
  682                     log.debug('\n')
  683 
  684                 # Message log file
  685                 for option in options_str:
  686                     log.debug('  looking for option %s ... ' % option)
  687                     if configparser.has_option('options', option):
  688                         log.debug('got "%s"'
  689                                   % configparser.get('options', option))
  690                         config[option] = configparser.get('options', option)
  691                         log.debug('-> %s' % config[option])
  692                     else:
  693                         log.debug('not found')
  694                     log.debug('\n')
  695                 if config['message_log']:
  696                     try:
  697                         config['logfile'] = logfile(config['message_log'])
  698                     except IOError, o:
  699                         raise getmailConfigurationError(
  700                             'error opening message_log file %s (%s)'
  701                             % (config['message_log'], o)
  702                         )
  703 
  704                 # see if a netrc file is configured
  705                 netrc_object = None
  706                 if config['use_netrc']:
  707                     netrc_object = netrc.netrc(
  708                         config['netrc_file']
  709                         and expand_user_vars(config['netrc_file']))
  710 
  711                 # Clear out the ConfigParser defaults before processing further
  712                 # sections
  713                 configparser._defaults = {}
  714 
  715                 # Retriever
  716                 log.debug('  getting retriever\n')
  717                 retriever_type = configparser.get('retriever', 'type')
  718                 log.debug('    type="%s"\n' % retriever_type)
  719                 retriever_func = getattr(retrievers, retriever_type)
  720                 if not callable(retriever_func):
  721                     raise getmailConfigurationError(
  722                         'configuration file %s specifies incorrect '
  723                         'retriever type (%s)'
  724                         % (path, retriever_type)
  725                     )
  726                 retriever_args = {
  727                     'getmaildir' : options.getmaildir,
  728                     'configparser' : configparser,
  729                 }
  730                 for (name, value) in configparser.items('retriever'):
  731                     if name in ('type', 'configparser'):
  732                         continue
  733                     if name == 'password':
  734                         log.debug('    parameter %s=*\n' % name)
  735                     else:
  736                         log.debug('    parameter %s="%s"\n' % (name, value))
  737                     retriever_args[name] = value
  738                 if netrc_object:
  739                     # add username and password from netrc, as read above
  740                     netrc_auth = netrc_object.authenticators(
  741                         retriever_args['server'])
  742                     if netrc_auth and netrc_auth[0]:
  743                         retriever_args['username'] = netrc_auth[0]
  744                     if netrc_auth and netrc_auth[2]:
  745                         retriever_args['password'] = netrc_auth[2]
  746                 log.debug('    instantiating retriever %s with args %s\n'
  747                           % (retriever_type, format_params(retriever_args)))
  748                 try:
  749                     retriever = retriever_func(**retriever_args)
  750                     log.debug('    checking retriever configuration for %s\n'
  751                               % retriever)
  752                     retriever.checkconf()
  753                 except getmailOperationError, o:
  754                     log.error('Error initializing retriever: %s\n' % o)
  755                     continue
  756                 
  757                 # Retriever is okay.  Check if user wants us to store the
  758                 # password in a Gnome keyring for future use.
  759                 if gnomekeyring and options.store_gnome_keyring:
  760                     # Need to get the password first, if the user hasn't put
  761                     # it in the rc file.
  762                     if retriever.conf.get('password', None) is not None:
  763                         password = retriever.conf['password']
  764                     elif retriever.conf.get('password_command', None):
  765                         # Retrieve from an arbitrary external command
  766                         command = retriever.conf['password_command'][0]
  767                         args = retriever.conf['password_command'][1:]
  768                         (rc, stdout, stderr) = run_command(command, args)
  769                         if stderr:
  770                             log.warn(
  771                                 'External password program "%s" wrote to stderr: %s',
  772                                 command, stderr
  773                             )
  774                         if rc:
  775                             # program exited nonzero
  776                             raise getmailOperationError(
  777                                 'External password program error (exited %d)' % rc
  778                             )
  779                         else:
  780                             password = stdout
  781                     else:
  782                         password = get_password(
  783                             str(retriever), retriever.conf['username'], 
  784                             retriever.conf['server'], retriever.received_with, 
  785                             log
  786                         )
  787 
  788                     gnomekeyring.set_network_password_sync(
  789                         # keyring=None, user, domain=None, server, object=None, 
  790                         # protocol, authtype=None, port=0
  791                         None, retriever.conf['username'], None, 
  792                         retriever.conf['server'], None, retriever.received_with,
  793                         None, 0, password
  794                     )
  795                     log.info('Stored password in Gnome keyring.  Exiting.\n')
  796                     raise SystemExit()
  797 
  798                 # Destination
  799                 log.debug('  getting destination\n')
  800                 destination_type = configparser.get('destination', 'type')
  801                 log.debug('    type="%s"\n' % destination_type)
  802                 destination_func = getattr(destinations, destination_type)
  803                 if not callable(destination_func):
  804                     raise getmailConfigurationError(
  805                         'configuration file %s specifies incorrect destination '
  806                         'type (%s)'
  807                         % (path, destination_type)
  808                     )
  809                 destination_args = {'configparser' : configparser}
  810                 for (name, value) in configparser.items('destination'):
  811                     if name in ('type', 'configparser'):
  812                         continue
  813                     if name == 'password':
  814                         log.debug('    parameter %s=*\n' % name)
  815                     else:
  816                         log.debug('    parameter %s="%s"\n' % (name, value))
  817                     destination_args[name] = value
  818                 log.debug('    instantiating destination %s with args %s\n'
  819                           % (destination_type, format_params(destination_args)))
  820                 destination = destination_func(**destination_args)
  821 
  822                 # Filters
  823                 log.debug('  getting filters\n')
  824                 _filters = []
  825                 filtersections =  [
  826                     section.lower() for section in configparser.sections()
  827                     if section.lower().startswith('filter')
  828                 ]
  829                 filtersections.sort()
  830                 for section in filtersections:
  831                     log.debug('    processing filter section %s\n' % section)
  832                     filter_type = configparser.get(section, 'type')
  833                     log.debug('      type="%s"\n' % filter_type)
  834                     filter_func = getattr(filters, filter_type)
  835                     if not callable(filter_func):
  836                         raise getmailConfigurationError(
  837                             'configuration file %s specifies incorrect filter '
  838                             'type (%s)'
  839                             % (path, filter_type)
  840                         )
  841                     filter_args = {'configparser' : configparser}
  842                     for (name, value) in configparser.items(section):
  843                         if name in ('type', 'configparser'):
  844                             continue
  845                         if name == 'password':
  846                             log.debug('    parameter %s=*\n' % name)
  847                         else:
  848                             log.debug('    parameter %s="%s"\n' % (name, value))
  849                         filter_args[name] = value
  850                     log.debug('      instantiating filter %s with args %s\n'
  851                               % (filter_type, format_params(filter_args)))
  852                     mail_filter = filter_func(**filter_args)
  853                     _filters.append(mail_filter)
  854 
  855             except ConfigParser.NoSectionError, o:
  856                 raise getmailConfigurationError(
  857                     'configuration file %s missing section (%s)' % (path, o)
  858                 )
  859             except ConfigParser.NoOptionError, o:
  860                 raise getmailConfigurationError(
  861                     'configuration file %s missing option (%s)' % (path, o)
  862                 )
  863             except (ConfigParser.DuplicateSectionError,
  864                     ConfigParser.InterpolationError,
  865                     ConfigParser.MissingSectionHeaderError,
  866                     ConfigParser.ParsingError), o:
  867                 raise getmailConfigurationError(
  868                     'configuration file %s incorrect (%s)' % (path, o)
  869                 )
  870             except getmailConfigurationError, o:
  871                 raise getmailConfigurationError(
  872                     'configuration file %s incorrect (%s)' % (path, o)
  873                 )
  874 
  875             # Apply overrides from commandline
  876             for option in ('read_all', 'delete', 'verbose', 'fingerprint'):
  877                 val = getattr(options, 'override_%s' % option)
  878                 if val is not None:
  879                     log.debug('overriding option %s from commandline %s\n'
  880                               % (option, val))
  881                     config[option] = val
  882 
  883             if config['verbose'] > 2:
  884                 config['verbose'] = 2
  885 
  886             if not options.trace and config['verbose'] == 0:
  887                 log.clearhandlers()
  888                 log.addhandler(sys.stderr, logging.WARNING)
  889 
  890             configs.append((os.path.basename(filename), retriever, _filters,
  891                             destination, config.copy()))
  892 
  893         if options.dump_config:
  894             # Override any "verbose = 0" in the config file
  895             log.clearhandlers()
  896             log.addhandler(sys.stdout, logging.INFO, maxlevel=logging.INFO)
  897             log.addhandler(sys.stderr, logging.WARNING)
  898             blurb()
  899             for (filename, retriever, _filters, destination, config) in configs:
  900                 log.info('getmail configuration:\n')
  901                 log.info('  getmail version %s\n' % __version__)
  902                 log.info('  Python version %s\n' % sys.version)
  903                 log.info('  retriever:  ')
  904                 retriever.showconf()
  905                 if _filters:
  906                     for _filter in _filters:
  907                         log.info('  filter:  ')
  908                         _filter.showconf()
  909                 log.info('  destination:  ')
  910                 destination.showconf()
  911                 log.info('  options:\n')
  912                 names = config.keys()
  913                 names.sort()
  914                 for name in names:
  915                     log.info('    %s : %s\n' % (name, config[name]))
  916                 log.info('\n')
  917             sys.exit()
  918 
  919         # Go!
  920         success = go(configs, options.idle)
  921         if not success:
  922             raise SystemExit(127)
  923 
  924     except KeyboardInterrupt:
  925         log.warning('Operation aborted by user (keyboard interrupt)\n')
  926         sys.exit(0)
  927     except getmailConfigurationError, o:
  928         log.error('Configuration error: %s\n' % o)
  929         sys.exit(2)
  930     except getmailOperationError, o:
  931         log.error('Error: %s\n' % o)
  932         sys.exit(3)
  933     except StandardError, o:
  934         log.critical(
  935             '\nException: please read docs/BUGS and include the '
  936             'following information in any bug report:\n\n'
  937         )
  938         log.critical('  getmail version %s\n' % __version__)
  939         log.critical('  Python version %s\n\n' % sys.version)
  940         log.critical('Unhandled exception follows:\n')
  941         (exc_type, value, tb) = sys.exc_info()
  942         import traceback
  943         tblist = (traceback.format_tb(tb, None)
  944                   + traceback.format_exception_only(exc_type, value))
  945         if type(tblist) != list:
  946             tblist = [tblist]
  947         for line in tblist:
  948             log.critical('  %s\n' % line.rstrip())
  949         log.critical('\nPlease also include configuration information '
  950                      'from running getmail\n')
  951         log.critical('with your normal options plus "--dump".\n')
  952         sys.exit(4)
  953 
  954 #######################################
  955 if __name__ == '__main__':
  956     main()