예제 #1
0
    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
예제 #2
0
    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
예제 #3
0
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
예제 #4
0
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
예제 #5
0
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