def parse_mail(self): # We'll just use some encoding that handles all byte values # without bailing out. Non-ASCII characters should not exist # in the headers according to email standards, but if they do # anyway, we mustn't crash. encoding = "iso-8859-1" self._processor.log("") self._processor.log("New mail detected at {0}:".format(iso_8601_now())) self._processor.log("Path: {0}".format(ascii(self.path))) try: fp = open(self.path, encoding=encoding) except IOError as e: # The file was probably (re)moved by some other process. self._processor.log_mail_opening_error(self.path, e) return False headers = email_parser.Parser().parse(fp, headersonly=True) fp.close() for name in headers.keys(): value_parts = [] for header in headers.get_all(name, []): try: for (s, c) in email_header.decode_header(header): # email.header.decode_header in Python 3.x may # return either [(str, None)] or [(bytes, # None), ..., (bytes, encoding)]. We must # compensate for this. if not isinstance(s, str): s = s.decode(c if c else "ascii") value_parts.append(s) except (email_errors.HeaderParseError, LookupError, ValueError): self._processor.log_error( "Error: Could not decode header {0}".format( ascii(header))) value_parts.append(header) self._headers[name.lower()] = " ".join(value_parts) return True
def main(): imapproc_directory = "~/.mailprocessing" default_rcfile_location = os.path.join(imapproc_directory, "imap.rc") default_logfile_location = os.path.join(imapproc_directory, "log-imap") if not os.path.isdir(os.path.expanduser(imapproc_directory)): os.mkdir(os.path.expanduser(imapproc_directory)) parser = OptionParser( version="1.0.1", description=( "imapproc is a program that scans one or more folders in an" " IMAP mailbox and processes found mail as defined by an rc file." " See http://mailprocessing.github.io/mailprocessing for more information.")) parser.add_option( "--auto-reload-rcfile", action="store_true", default=False, help=( "turn on automatic reloading of the rc file when it has been" " modified")) parser.add_option( "--dry-run", action="store_true", default=False, help=( "just log what should have been done; implies --once")) parser.add_option( "-l", "--logfile", type="string", dest="logfile", metavar="FILE", help="send log to FILE instead of the default ({0})".format( default_logfile_location), default=default_logfile_location) parser.add_option( "--log-level", type="int", metavar="INTEGER", help=( "only include log messages with this log level or lower; defaults" " to 1"), default=1) parser.add_option( "-f", "--folder", action="append", type="string", default=[], dest="folders", metavar="FOLDER", help=( "add FOLDER to the set of IMAP folders to process (can" " be passed multiple times). Using `.` as a folder should" " work for most IMAP servers.")) parser.add_option( "--once", action="store_true", default=False, help=( "only process the maildirs once and then exit; without this flag," " mailprocessing will scan the folders in regular intervals (set these" " with the --interval option)")) parser.add_option( "-r", "--rcfile", type="string", dest="rcfile", metavar="FILE", help=( "use the given rc file instead of the default ({0})".format( default_rcfile_location)), default=default_rcfile_location) parser.add_option( "--test", action="store_true", default=False, help=( "test mode; implies --dry-run, --once, --logfile=- and" " --verbose")) parser.add_option( "-v", "--verbose", action="count", default=0, dest="verbosity", help="increase log level one step") # IMAP specific options parser.add_option( "--cache-file", type="string", help=("File to store header cache in. By default " "~/.mailprocessing/<HOST>.cache will be used, " "where <HOST> is the IMAP server's host name.")) parser.add_option( "-C", "--cache-headers", action="store_true", help="Cache mail headers retrieved from imap_server") parser.add_option( "-c", "--certfile", type="string", help="Certificate file to verify server's certificate against (only relevant for IMAPS)") parser.add_option( "-H", "--host", type="string", help="IMAP server to log in to.") parser.add_option( "-i", "--interval", type="int", default=300, metavar="INTERVAL", help=( "Scan IMAP folders every INTERVAL seconds for new mail. Will be" " ignored if --once is specified as well.")) parser.add_option( "-p", "--password", type="string", help="Password to log into IMAP server with.") parser.add_option( "--password-command", type="string", metavar="COMMAND", help="Execute COMMAND and read the password to log in to IMAP " "server with from its standard output.") parser.add_option( "-P", "--port", type="int", help="IMAP port to use. Defaults to 143 if --use-ssl is not specified " "and 993 if it is.") parser.add_option( "--folder-prefix", type="string", metavar="PREFIX", help="Name space prefix for IMAP folder names") parser.add_option( "--folder-separator", type="string", metavar="SEP", help="Name space separator for IMAP folder names") parser.add_option( "-u", "--user", type="string", help="IMAP User to log into IMAP server as.") parser.add_option( "-s", "--use-ssl", action="store_true", default=False, help="Use SSL to connect to IMAP server.") (options, _) = parser.parse_args(sys.argv[1:]) bad_options = False for opt in ("host", "user"): if not options.__dict__[opt]: print("Please specify --%s option." % opt, file=sys.stderr) bad_options = True if not ( options.password or options.password_command ): print("Please specify either --password or --password-command.", file=sys.stderr) bad_options = True if options.password and options.password_command: print("Please specify only one of --password or --password-command.", file=sys.stderr) bad_options = True if bad_options: sys.exit(1) processor_kwargs = {} if options.password: processor_kwargs['password'] = options.password if options.password_command: try: p = subprocess.check_output(options.password_command, shell=True) p = p.decode(locale.getpreferredencoding()).rstrip("\n") except subprocess.CalledProcessError as e: print("Password command failed with exit status %d, " "output follows." % e.returncode, file=sys.stderr) sys.stderr.buffer.write(e.output) sys.exit(1) except Exception as e: print ("Could not execute command %s: %s" % (options.password_command, e)) sys.exit(1) processor_kwargs['password'] = p if options.test: options.dry_run = True options.logfile = "-" options.verbosity = max(1, options.verbosity) if options.dry_run: options.once = True if options.logfile == "-": log_fp = sys.stdout else: log_fp = open( os.path.expanduser(options.logfile), "a", encoding=locale.getpreferredencoding(), errors="backslashreplace") processor_kwargs["log_level"] = options.log_level + options.verbosity rcfile = os.path.expanduser(options.rcfile) processor_kwargs["run_once"] = options.once for opt in ("auto_reload_rcfile", "certfile", "dry_run", "folders", "folder_prefix", "folder_separator", "host", "interval", "port", "user", "use_ssl", "verbosity"): processor_kwargs[opt] = options.__dict__[opt] if options.cache_headers: if options.cache_file: cache_file = options.cache_file else: cache_file = os.path.join(imapproc_directory, options.host + '.cache') processor_kwargs['cache_file'] = os.path.expanduser(cache_file) # Try to open rc file early on. Otherwise we'll process headers until we # get to the point where we open the rc-file, possibly wasting a lot of # time if it is missing. try: open(rcfile).close() except IOError as e: print("Error: Could not open RC file: {0}".format(e), file=sys.stderr) sys.exit(1) processor = ImapProcessor(rcfile, log_fp, **processor_kwargs) processor.log("") processor.log( "Starting imapproc {0} at {1}".format( parser.version, iso_8601_now())) if options.folders: processor.folders = options.folders if "SENDMAIL" in os.environ: processor.sendmail = os.environ["SENDMAIL"] if "SENDMAILFLAGS" in os.environ: processor.sendmail_flags = os.environ["SENDMAILFLAGS"] environment = {"processor": processor} if rcfile == "-": processor.log("RC file: <standard input>") rc = sys.stdin.read() exec(rc, environment) else: processor.log("RC file: {0}".format(ascii(rcfile))) while True: try: rc = open(rcfile).read() except IOError as e: processor.fatal_error( "Error: Could not open RC file: {0}".format(e)) else: exec(rc, environment) if not processor.rcfile_modified: # Normal exit. break
def main(): maildirproc_directory = "~/.mailprocessing" default_rcfile_location = os.path.join(maildirproc_directory, "maildir.rc") default_logfile_location = os.path.join(maildirproc_directory, "log-maildir") if not os.path.isdir(os.path.expanduser(maildirproc_directory)): os.mkdir(os.path.expanduser(maildirproc_directory)) parser = OptionParser( version="1.0.1", description=( "maildirproc is a program that scans a number of maildir mail" " boxes and processes found mail as defined by an rc file. See" " http://mailprocessing.github.io/mailprocessing for more" " information.")) parser.add_option( "--auto-reload-rcfile", action="store_true", default=False, help=( "turn on automatic reloading of the rc file when it has been" " modified")) parser.add_option( "--dry-run", action="store_true", default=False, help=( "just log what should have been done; implies --once")) parser.add_option( "-l", "--logfile", type="string", dest="logfile", metavar="FILE", help="send log to FILE instead of the default ({0})".format( default_logfile_location), default=default_logfile_location) parser.add_option( "--log-level", type="int", metavar="INTEGER", help=( "only include log messages with this log level or lower; defaults" " to 1"), default=1) parser.add_option( "-m", "--maildir", action="append", type="string", default=[], dest="maildirs", metavar="DIRECTORY", help=( "add DIRECTORY to the set of maildir directories to process (can" " be passed multiple times); if DIRECTORY is relative, it is" " relative to the maildir base directory")) parser.add_option( "-b", "--maildir-base", type="string", default=".", dest="maildir_base", metavar="DIRECTORY", help="set maildir base directory; defaults to the current working" " directory") parser.add_option( "-s", "--folder-separator", type="string", default=".", metavar="SEP", help="Separate maildir folder names by SEP") parser.add_option( "-p", "--folder-prefix", type="string", default=".", metavar="PREFIX", help="Prefix maildir directory names by PREFIX") parser.add_option( "--once", action="store_true", default=False, help=( "only process the maildirs once and then exit; without this flag," " maildirproc will scan the maildirs continuously")) parser.add_option( "-r", "--rcfile", type="string", dest="rcfile", metavar="FILE", help=( "use the given rc file instead of the default ({0})".format( default_rcfile_location)), default=default_rcfile_location) parser.add_option( "--test", action="store_true", default=False, help=( "test mode; implies --dry-run, --once, --logfile=- and" " --verbose")) parser.add_option( "-v", "--verbose", action="count", default=0, dest="verbosity", help="increase log level one step") (options, _) = parser.parse_args(sys.argv[1:]) if options.test: options.dry_run = True options.logfile = "-" options.verbosity = max(1, options.verbosity) if options.dry_run: options.once = True if options.logfile == "-": log_fp = sys.stdout else: log_fp = open( os.path.expanduser(options.logfile), "a", encoding=locale.getpreferredencoding(), errors="backslashreplace") log_level = options.log_level + options.verbosity rcfile = os.path.expanduser(options.rcfile) processor = MaildirProcessor( rcfile=rcfile, log_fp=log_fp, log_level=log_level, dry_run=options.dry_run, run_once=options.once, auto_reload_rcfile=options.auto_reload_rcfile, folder_prefix=options.folder_prefix, folder_separator=options.folder_separator) processor.log("") processor.log( "Starting maildirproc {0} at {1}".format( parser.version, iso_8601_now())) processor.maildir_base = options.maildir_base if options.maildirs: processor.maildirs = options.maildirs if "SENDMAIL" in os.environ: processor.sendmail = os.environ["SENDMAIL"] if "SENDMAILFLAGS" in os.environ: processor.sendmail_flags = os.environ["SENDMAILFLAGS"] environment = {"processor": processor} if rcfile == "-": processor.log("RC file: <standard input>") rc = sys.stdin.read() exec(rc, environment) else: processor.log("RC file: {0}".format(ascii(rcfile))) while True: try: rc = open(rcfile).read() except IOError as e: processor.fatal_error( "Error: Could not open RC file: {0}".format(e)) else: exec(rc, environment) if not processor.rcfile_modified: # Normal exit. break
def main(): imapproc_directory = "~/.mailprocessing" default_rcfile_location = os.path.join(imapproc_directory, "imap.rc") default_logfile_location = os.path.join(imapproc_directory, "log-imap") if not os.path.isdir(os.path.expanduser(imapproc_directory)): os.mkdir(os.path.expanduser(imapproc_directory)) parser = OptionParser( version="1.0.1", description=( "imapproc is a program that scans one or more folders in an" " IMAP mailbox and processes found mail as defined by an rc file." " See http://mailprocessing.github.io/mailprocessing for more information.")) parser.add_option( "--auto-reload-rcfile", action="store_true", default=False, help=( "turn on automatic reloading of the rc file when it has been" " modified")) parser.add_option( "--dry-run", action="store_true", default=False, help=( "just log what should have been done; implies --once")) parser.add_option( "-l", "--logfile", type="string", dest="logfile", metavar="FILE", help="send log to FILE instead of the default ({0})".format( default_logfile_location), default=default_logfile_location) parser.add_option( "--log-level", type="int", metavar="INTEGER", help=( "only include log messages with this log level or lower; defaults" " to 1"), default=1) parser.add_option( "-f", "--folder", action="append", type="string", default=[], dest="folders", metavar="FOLDER", help=( "add FOLDER to the set of IMAP folders to process (can" " be passed multiple times). Using `.` as a folder should" " work for most IMAP servers.")) parser.add_option( "--once", action="store_true", default=False, help=( "only process the maildirs once and then exit; without this flag," " mailprocessing will scan the folders in regular intervals (set these" " with the --interval option)")) parser.add_option( "-r", "--rcfile", type="string", dest="rcfile", metavar="FILE", help=( "use the given rc file instead of the default ({0})".format( default_rcfile_location)), default=default_rcfile_location) parser.add_option( "--test", action="store_true", default=False, help=( "test mode; implies --dry-run, --once, --logfile=- and" " --verbose")) parser.add_option( "-v", "--verbose", action="count", default=0, dest="verbosity", help="increase log level one step") # IMAP specific options parser.add_option( "--cache-file", type="string", help=("File to store header cache in. By default " "~/.mailprocessing/<HOST>.cache will be used, " "where <HOST> is the IMAP server's host name.")) parser.add_option( "-C", "--cache-headers", action="store_true", help="Cache mail headers retrieved from imap_server") parser.add_option( "-c", "--certfile", type="string", help="Certificate file to verify server's certificate against (only relevant for IMAPS)") parser.add_option( "-H", "--host", type="string", help="IMAP server to log in to.") parser.add_option( "-i", "--interval", type="int", default=300, metavar="INTERVAL", help=( "Scan IMAP folders every INTERVAL seconds for new mail. Will be" " ignored if --once is specified as well.")) parser.add_option( "-p", "--password", type="string", help="Password to log into IMAP server with.") parser.add_option( "--password-command", type="string", metavar="COMMAND", help="Execute COMMAND and read the password to log in to IMAP " "server with from its standard output.") parser.add_option( "-P", "--port", type="int", help="IMAP port to use. Defaults to 143 if --use-ssl is not specified " "and 993 if it is.") parser.add_option( "--folder-prefix", type="string", metavar="PREFIX", help="Name space prefix for IMAP folder names") parser.add_option( "--folder-separator", type="string", metavar="SEP", help="Name space separator for IMAP folder names") parser.add_option( "-u", "--user", type="string", help="IMAP User to log into IMAP server as.") parser.add_option( "-s", "--use-ssl", action="store_true", default=False, help="Use SSL to connect to IMAP server.") parser.add_option( "--insecure", action="store_true", default=False, help="Skip SSL certificate validation when connecting to IMAP server (unsafe).") (options, _) = parser.parse_args(sys.argv[1:]) bad_options = False for opt in ("host", "user"): if not options.__dict__[opt]: print("Please specify --%s option." % opt, file=sys.stderr) bad_options = True if not ( options.password or options.password_command ): print("Please specify either --password or --password-command.", file=sys.stderr) bad_options = True if options.password and options.password_command: print("Please specify only one of --password or --password-command.", file=sys.stderr) bad_options = True if options.insecure and options.certfile: print("Please specify only one of --insecure or --certfile.", file=sys.stderr) bad_options = True if bad_options: sys.exit(1) processor_kwargs = {} if options.password: processor_kwargs['password'] = options.password if options.password_command: try: p = subprocess.check_output(options.password_command, shell=True) p = p.decode(locale.getpreferredencoding()).rstrip("\n") except subprocess.CalledProcessError as e: print("Password command failed with exit status %d, " "output follows." % e.returncode, file=sys.stderr) sys.stderr.buffer.write(e.output) sys.exit(1) except Exception as e: print ("Could not execute command %s: %s" % (options.password_command, e)) sys.exit(1) processor_kwargs['password'] = p if options.test: options.dry_run = True options.logfile = "-" options.verbosity = max(1, options.verbosity) if options.dry_run: options.once = True if options.logfile == "-": log_fp = sys.stdout else: log_fp = open( os.path.expanduser(options.logfile), "a", encoding=locale.getpreferredencoding(), errors="backslashreplace") processor_kwargs["log_level"] = options.log_level + options.verbosity rcfile = os.path.expanduser(options.rcfile) processor_kwargs["run_once"] = options.once for opt in ("auto_reload_rcfile", "certfile", "dry_run", "folders", "folder_prefix", "folder_separator", "host", "interval", "port", "user", "use_ssl", "insecure", "verbosity"): processor_kwargs[opt] = options.__dict__[opt] if options.cache_headers: if options.cache_file: cache_file = options.cache_file else: cache_file = os.path.join(imapproc_directory, options.host + '.cache') processor_kwargs['cache_file'] = os.path.expanduser(cache_file) # Try to open rc file early on. Otherwise we'll process headers until we # get to the point where we open the rc-file, possibly wasting a lot of # time if it is missing. try: open(rcfile).close() except IOError as e: print("Error: Could not open RC file: {0}".format(e), file=sys.stderr) sys.exit(1) processor = ImapProcessor(rcfile, log_fp, **processor_kwargs) processor.log("") processor.log( "Starting imapproc {0} at {1}".format( parser.version, iso_8601_now())) if options.folders: processor.folders = options.folders if "SENDMAIL" in os.environ: processor.sendmail = os.environ["SENDMAIL"] if "SENDMAILFLAGS" in os.environ: processor.sendmail_flags = os.environ["SENDMAILFLAGS"] environment = {"processor": processor} if rcfile == "-": processor.log("RC file: <standard input>") rc = sys.stdin.read() exec(rc, environment) else: processor.log("RC file: {0}".format(ascii(rcfile))) while True: try: rc = open(rcfile).read() except IOError as e: processor.fatal_error( "Error: Could not open RC file: {0}".format(e)) else: exec(rc, environment) if not processor.rcfile_modified: # Normal exit. break