Example #1
0
    def delete(self):
        """
        Deletes the message.
        """

        if signals.terminate():
            self.processor.clean_exit()

        # Make sure we have this message's folder selected (UIDs should be
        # globally unique but may only unique in folder scope in sufficiently
        # broken IMAP implementations).
        if self.processor.selected != self.folder:
            self.processor.select(self.folder)

        try:
            self._processor.log("==> Deleting %s" % self.uid)
            self._processor.imap.uid('store', self.uid, '+FLAGS', '\\Deleted')
            self._processor.imap.expunge()
        except self._processor.imap.error as e:
            # Fail hard because repeated failures here can leave a mess of
            # messages with `Deleted` flags.
            self._processor.fatal_imap_error("Deleting message %s'" % self.uid,
                                             e)
            raise

        # make sure this gets purged from cache later
        self.processor.cache_delete[self.folder].append(self.uid)
Example #2
0
    def copy(self, folder, create=False):
        """
        Copies the message to folder. Optionally that folder can be created
        if it does not exist. In that case, specify create=True.

        folder may either be a string or a list of path components (a list will
        be joined by the IMAP server's separator character).
        """

        if signals.terminate():
            self.processor.clean_exit()

        # Make sure the folder name is prepended with the IMAP server's prefix
        # where applicable.
        folder = self.processor.path_ensure_prefix(folder)

        # Make sure we have this message's folder selected (UIDs should be
        # globally unique but may only unique in folder scope in sufficiently
        # broken IMAP implementations).
        if self.processor.selected != self.folder:
            self.processor.select(self.folder)

        folder = self._processor.list_path(folder,
                                           sep=self._processor.separator)

        try:
            self._processor.log("==> Copying {0} to {1}".format(
                self.uid, folder))
            status, data = self._processor.imap.uid('copy', self.uid, folder)
        except self._processor.imap.error as e:
            self._processor.fatal_imap_error(
                "Copying message UID %s to %s" % (self.uid, folder), e)
        if status == 'NO':
            if create and 'TRYCREATE' in data[0].decode('ascii'):
                self._processor.log(
                    "==> Destination folder %s does not exist, "
                    "creating." % folder)
                self._processor.create_folder(folder)
                try:
                    self._processor.log("==> Copying {0} to {1}".format(
                        self.uid, folder))
                    status, data = self._processor.imap.uid(
                        'copy', self.uid, folder)
                except self._processor.imap.error as e:
                    self._processor.fatal_imap_error(
                        "Copying message UID %s to %s" % (self.uid, folder), e)
                if status == 'NO':
                    self._processor.fatal_imap_error("Copying message UID %s "
                                                     " to %s failed with NO, "
                                                     " aborting." %
                                                     (self.uid, folder))
            else:
                self._processor.fatal_error("Destination folder %s does not "
                                            "exist and I am not supposed to "
                                            "create folders. Please use "
                                            "move() or copy() with "
                                            "create=True to automatically "
                                            "create nonexistent "
                                            "folders." % folder)
Example #3
0
    def forward(self, addresses, env_sender, delete=True):
        """
        Forwards the message to one or more addresses. The original message
        will be deleted.
        """

        if signals.terminate():
            self.processor.clean_exit()

        # Make sure we have this message's folder selected (UIDs should be
        # globally unique but may be on a per folder basis in sufficiently
        # broken IMAP implementations).
        if self.processor.selected != self.folder:
            self._processor.select(self.folder)

        if isinstance(addresses, str):
            addresses = [addresses]
        else:
            addresses = list(addresses)
        if delete:
            copy = ""
        else:
            copy = " copy"
        flags = self._processor.sendmail_flags
        if env_sender is not None:
            flags += " -f {0}".format(env_sender)

        self._processor.log(
            "==> Forwarding{0} to {1!r}".format(copy, addresses))
        try:
            ret, msg = self._processor.imap.uid('fetch', self.uid, "RFC822")
        except self._processor.imap.error as e:
            # Fail soft, since we haven't changed any mailbox state or forwarded
            # anything, yet. Hence we might as well retry later.
            self._processor.log_imap_error(
                "Error forwarding: Could not retrieve message UID {0}: "
                "{1}".format(self.uid, e))
            return

        p = subprocess.Popen(
            "{0} {1} -- {2}".format(self._processor.sendmail,
                                    flags,
                                    " ".join(addresses)
                                    ),
            shell=True,
            stdin=subprocess.PIPE)

        p.stdin.write(msg)
        p.stdin.close()
        sendmail_status = p.wait()

        if sendmail_status != 0:
            self._processor.log_error("Forwarding message failed: %s "
                                      "exited %d" % (self._processor.sendmail,
                                                     sendmail_status))
            return

        if delete:
            self.delete()
Example #4
0
    def _update_cache(self, folder, cache):
        """
        This method updates an existing header cache data structure for a given
        folder. Message UIDs that do not exist in the cache, yet, will be
        added.
        """

        self.log_debug("Updating cache")

        message_list = self.list_messages(folder)
        uids_download = []

        # Remove stale cache entries
        stale = []
        for message in cache:
            if message not in message_list:
                stale.append(message)
        for message in stale:
            cache.pop(message)

        for message in message_list:
            if signals.terminate():
                self.clean_exit()
            if message in cache:
                cached_headers = cache[message]['headers']
                cached_flags = cache[message]['flags']
                msg_obj = self._mail_class(self,
                                           folder=folder,
                                           uid=message,
                                           headers=cached_headers,
                                           flags=cached_flags)
                cache[message]['obj'] = msg_obj

            else:
                uids_download.append(message)

        self.log_debug("Cache miss for the following UIDs: %s" %
                       ",".join(uids_download))

        if len(uids_download) != 0:
            messages = self._download_headers_batched(folder, uids_download)
            for uid in uids_download:
                msg_obj = self._mail_class(self,
                                           folder=folder,
                                           uid=uid,
                                           headers=messages[uid]['headers'],
                                           flags=messages[uid]['flags'])
                cache[uid] = {}
                cache[uid]['headers'] = msg_obj._headers
                cache[uid]['flags'] = msg_obj.message_flags
                cache[uid]['obj'] = msg_obj
        return cache
Example #5
0
    def _initialize_cache(self, folder):
        """
        This method initializes a cache data structure for a given folder.
        """
        cache = {}
        uids = self.list_messages(folder)
        messages = self._download_headers_batched(folder, uids)

        for message_uid in uids:
            if signals.terminate():
                self.clean_exit()
            msg_obj = self._mail_class(self, folder=folder,
                                       uid=message_uid,
                                       headers=messages[message_uid]['headers'],
                                       flags=messages[message_uid]['flags'])
            cache[message_uid] = {}
            cache[message_uid]['headers'] = msg_obj._headers
            cache[message_uid]['flags'] = msg_obj.message_flags
            cache[message_uid]['obj'] = msg_obj
        return cache
Example #6
0
    def move(self, folder, create=False):
        """
        Moves the message to folder. Optionally that folder can be created if
        it does not exist. In that case, specify create=True.

        folder may either be a string or a list of path components (a list will
        be joined by the IMAP server's separator character).
        """

        if signals.terminate():
            self.processor.clean_exit()

        # Make sure the folder name is prepended with the IMAP server's prefix
        # where applicable.
        folder = self.processor.path_ensure_prefix(folder)

        folder = self._processor.list_path(folder, sep=self._processor.separator)
        self._processor.log("==> Moving UID {0} to {1}".format(self.uid, folder))
        self.copy(folder, create)
        self.delete()
Example #7
0
    def __iter__(self):
        """
        Iterator method used to invoke the processor from the filter
        configuration file.
        """
        if not self._folders:
            self.fatal_error("Error: No folders to process")

        self.rcfile_modified = False

        while not signals.signal_event.is_set():
            if self.auto_reload_rcfile:
                current_rcfile_mtime = self._get_previous_rcfile_mtime()
                if current_rcfile_mtime != self._previous_rcfile_mtime:
                    self._previous_rcfile_mtime = current_rcfile_mtime
                    self.rcfile_modified = True
                    self.log_info("Detected modified RC file; reloading")
                    break

            for folder in self.folders:
                for message in self.header_cache[folder]['uids']:
                    yield self.header_cache[folder]['uids'][message]['obj']

            if self._run_once:
                self.clean_exit()
            else:
                self.clean_sleep()

            signals.signal_event.wait(self.interval)

            if signals.terminate():
                # Simply exit, since clean_sleep() will already have performed
                # all exit rites if get here.
                sys.exit(0)

            self.reconnect()
            self._cache_headers()