示例#1
0
class OnlineDistributor(Distributor):
    '''
    Distributes mails in "real-time".

    There is one public method, update. When it is called, the server is polled. If new mails have
    arrived, they are processed and resent to the members of the list. Afterward, the mails
    are deleted, but only if the resend process finished successfully.

    If the subject is in a special format, instead of resending the
    mail, a DownloadMessage is generated and sent back.
    '''

    def __init__(self, config):
        super(OnlineDistributor,self).__init__(config)
        self._reader = Reader(config)

    def update(self):
        '''
        Update the distribution list. Every new message in the server is processed and resent to the
        members of the list. If the resend is successful the new messages are deleted.
        '''
        logger.debug('update is called')

        try:
            self._reader.connect()
        except Exception as e:
            logger.info('connect failed with the exception: %s', e)
            return False

        ids = self._reader.new_messages()
        for id in ids:
            msg = self._reader.get(id)
            if self._isvalid(msg):
                self._process(msg)
                self._reader.delete(id)
        self._reader.disconnect()
        logger.debug('update is finished')
        return len(ids) != 0

    def _process(self, msg):
        '''
        Redirects to the correct action based on the subject of the
        message.
        '''
        subject = msg['Subject']

        if subject.lower().startswith('get'):
            logger.debug('calling _download_and_send')
            self._download_and_send(subject, msg)
        else:
            logger.debug('calling _resend')
            self._resend(msg)

    def _download_and_send(self, subject, msg):
        '''
        Creates a new DownloadMessage based on the subject and sends
        it back to the sender.

        The format of the subject must be: GET 'url'.
        '''
        id = self._store.archive(msg)
        sender = self._find_sender_email(msg)
        url = self._get_download_url(subject)

        if url is not None:
            logger.info('Downloading message for %s with url %s', sender, url)
            self._sender.send(DownloadMessage(url), sender)
            self._store.mark_as_sent(id)

    def _get_download_url(self, subject):
        '''
        Returns the url to download from the subject of the message,
        or None if no url could be found.
        '''
        subject = subject.lower().strip(' ')
        parts = re.split(r'\s+', subject)
        if len(parts) != 2:
            logger.error('_get_download_url, %s has no valid url', subject)
            return None
        return parts[1]

    def _resend(self, msg):
        '''
        Sends a message to the appropriate members of the list after processing it.
        '''
        self._edit_msg(msg)
        id = self._store.archive(msg)
        sender = self._find_sender_email(msg)
        self._sender.send(msg, *self._mgr.active_members(sender))
        self._store.digest(id, *self._mgr.digest_members(sender))
        self._store.mark_as_sent(id)

    def _edit_msg(self, msg):
        '''
        Processes a message and returns it. The following steps are taken for each part of the
        message that can be interpreted as text:

        - A header and a footer are added, both using the encoding of the payload.
        - The payload has all the email hosts removed.

        The parts are separated with newlines, which depend on whether the message is plain text or
        other (like HTML).
        '''
        header = self._create_header(msg)
        footer = self._create_footer(msg)
        for editable in self._find_actual_text(msg):
            nl = u'\n' if editable.get_content_subtype() == 'plain' else u'<br>'
            editable.set_payload((nl * 2).join([
                        nl.join(header),
                        EMAIL.sub(anonymize_email, editable.get_clean_payload(self._cfg['forbidden_words'])),
                        nl.join(footer)]))

    def _choose_intro(self):
        '''Randomly chooses an introduction text from the configuration.'''
        return random.choice(self._cfg['introductions'])

    def _create_footer(self, msg):
        '''
        Creates a footer for the message, returned as a list of strings. The footer contains the
        name of the list, a randomly chosen quote and the program id.
        '''
        return [FOOTER_DIVIDER,
                self._cfg['real_name'],
                random.choice(self._cfg['quotes']),
                self._powered_by()]

    def _powered_by(self):
        '''
        Returns the program id, which consists of the name, version, and description of this sw.
        '''
        name = self._manifest['name']
        version = self._manifest['version']
        description = self._manifest['description']
        return u'Powered by %s %s, %s' % (name, version, description)