Exemplo n.º 1
0
def test_remote_save_draft(db, config, message):
    """ Tests the save_draft function, which saves the draft to the remote. """
    from inbox.actions.backends.gmail import remote_save_draft
    from inbox.sendmail.base import _parse_recipients
    from inbox.sendmail.message import create_email, Recipients
    from inbox.models import Account

    account = db.session.query(Account).get(ACCOUNT_ID)
    to, subject, body = message
    to_addr = _parse_recipients(to)
    recipients = Recipients(to_addr, [], [])
    email = create_email(account.sender_name, account.email_address, None,
                         recipients, subject, body, None)
    date = datetime.utcnow()

    remote_save_draft(account, account.drafts_folder.name, email.to_string(),
                      db.session, date)

    with crispin_client(account.id, account.provider) as c:
        criteria = ['NOT DELETED', 'SUBJECT "{0}"'.format(subject)]

        c.conn.select_folder(account.drafts_folder.name, readonly=False)

        draft_uids = c.conn.search(criteria)
        assert draft_uids, 'Message missing from Drafts folder'

        flags = c.conn.get_flags(draft_uids)
        for uid in draft_uids:
            f = flags.get(uid)
            assert f and '\\Draft' in f, "Message missing '\\Draft' flag"

        c.conn.delete_messages(draft_uids)
        c.conn.expunge()
Exemplo n.º 2
0
    def send(self, draft):
        """
        Turn a draft object into a MIME message and send it.

        Parameters
        ----------
        draft : models.message.Message object
            the draft message to send.
        """
        blocks = [p.block for p in draft.attachments]
        attachments = generate_attachments(blocks)

        # Note that we intentionally don't set the Bcc header in the message we
        # construct.
        msg = create_email(sender_name=self.sender_name,
                           sender_email=self.email_address,
                           inbox_uid=draft.inbox_uid,
                           to_addr=draft.to_addr,
                           cc_addr=draft.cc_addr,
                           bcc_addr=None,
                           subject=draft.subject,
                           html=draft.sanitized_body,
                           in_reply_to=draft.in_reply_to,
                           references=draft.references,
                           attachments=attachments)

        recipient_emails = [
            email for name, email in itertools.chain(
                draft.to_addr, draft.cc_addr, draft.bcc_addr)
        ]
        return self._send(recipient_emails, msg)
Exemplo n.º 3
0
def save_draft(account_id, message_id, db_session):
    """ Sync a new/updated draft back to the remote backend. """
    account = db_session.query(Account).get(account_id)
    message = db_session.query(Message).get(message_id)
    if message is None:
        log.info('tried to save nonexistent message as draft',
                 message_id=message_id, account_id=account_id)
        return
    if not message.is_draft:
        log.warning('tried to save non-draft message as draft',
                    message_id=message_id,
                    account_id=account_id)
        return

    recipients = Recipients(message.to_addr, message.cc_addr,
                            message.bcc_addr)
    blocks = [p.block for p in message.attachments]
    attachments = generate_attachments(blocks)
    mimemsg = create_email(account.name, account.email_address,
                           message.inbox_uid, recipients, message.subject,
                           message.sanitized_body, attachments)

    if account.drafts_folder is None:
        # account has no detected drafts folder - create one.
        drafts_folder = Folder.find_or_create(db_session, account,
                                              'Drafts', 'drafts')
        account.drafts_folder = drafts_folder

    remote_save_draft = module_registry[account.provider].remote_save_draft
    remote_save_draft(account, account.drafts_folder.name,
                      mimemsg.to_string(), message.created_at)
Exemplo n.º 4
0
def test_remote_save_draft(db, config, message):
    """ Tests the save_draft function, which saves the draft to the remote. """
    from inbox.actions.backends.gmail import remote_save_draft
    from inbox.sendmail.base import _parse_recipients
    from inbox.sendmail.message import create_email, Recipients
    from inbox.models import Account

    account = db.session.query(Account).get(ACCOUNT_ID)
    to, subject, body = message
    to_addr = _parse_recipients(to)
    recipients = Recipients(to_addr, [], [])
    email = create_email(account.sender_name, account.email_address, None,
                         recipients, subject, body, None)
    date = datetime.utcnow()

    remote_save_draft(account, account.drafts_folder.name, email.to_string(),
                      db.session, date)

    with crispin_client(account.id, account.provider) as c:
        criteria = ['NOT DELETED', 'SUBJECT "{0}"'.format(subject)]

        c.conn.select_folder(account.drafts_folder.name, readonly=False)

        draft_uids = c.conn.search(criteria)
        assert draft_uids, 'Message missing from Drafts folder'

        flags = c.conn.get_flags(draft_uids)
        for uid in draft_uids:
            f = flags.get(uid)
            assert f and '\\Draft' in f, "Message missing '\\Draft' flag"

        c.conn.delete_messages(draft_uids)
        c.conn.expunge()
Exemplo n.º 5
0
def create_gmail_reply(sender_name,
                       sender_email,
                       in_reply_to,
                       references,
                       inbox_uid,
                       recipients,
                       subject,
                       body,
                       attachments=None):
    """ Create a Gmail email reply. """
    mimemsg = create_email(sender_name, sender_email, inbox_uid, recipients,
                           subject, body, attachments)

    # Add general reply headers:
    if in_reply_to:
        mimemsg.headers['In-Reply-To'] = in_reply_to
    if references:
        mimemsg.headers['References'] = references

    # Set the 'Subject' header of the reply, required for Gmail.
    # Gmail requires the same subject as the original (adding Re:/Fwd: is fine
    # though) to group messages in the same conversation,
    # See: https://support.google.com/mail/answer/5900?hl=en
    mimemsg.headers['Subject'] = REPLYSTR + subject

    return smtp_attrs(mimemsg)
Exemplo n.º 6
0
def save_draft(account_id, message_id, db_session):
    """ Sync a new/updated draft back to the remote backend. """
    account = db_session.query(Account).get(account_id)
    message = db_session.query(Message).get(message_id)
    if message is None:
        log.info('tried to save nonexistent message as draft',
                 message_id=message_id,
                 account_id=account_id)
        return
    if not message.is_draft:
        log.warning('tried to save non-draft message as draft',
                    message_id=message_id,
                    account_id=account_id)
        return

    recipients = Recipients(message.to_addr, message.cc_addr, message.bcc_addr)
    blocks = [p.block for p in message.attachments]
    attachments = generate_attachments(blocks)
    mimemsg = create_email(account.sender_name, account.email_address,
                           message.inbox_uid, recipients, message.subject,
                           message.sanitized_body, attachments)

    if account.drafts_folder is None:
        # account has no detected drafts folder - create one.
        drafts_folder = Folder.find_or_create(db_session, account, 'Drafts',
                                              'drafts')
        account.drafts_folder = drafts_folder

    remote_save_draft = module_registry[account.provider].remote_save_draft
    remote_save_draft(account, account.drafts_folder.name, mimemsg.to_string(),
                      message.created_at)
Exemplo n.º 7
0
    def send(self, draft):
        """
        Turn a draft object into a MIME message and send it.

        Parameters
        ----------
        draft : models.message.Message object
            the draft message to send.
        """
        blocks = [p.block for p in draft.attachments]
        attachments = generate_attachments(blocks)

        # Note that we intentionally don't set the Bcc header in the message we
        # construct.
        msg = create_email(sender_name=self.sender_name,
                           sender_email=self.email_address,
                           inbox_uid=draft.inbox_uid,
                           to_addr=draft.to_addr,
                           cc_addr=draft.cc_addr,
                           bcc_addr=None,
                           subject=draft.subject,
                           html=draft.sanitized_body,
                           in_reply_to=draft.in_reply_to,
                           references=draft.references,
                           attachments=attachments)

        recipient_emails = [email for name, email in itertools.chain(
            draft.to_addr, draft.cc_addr, draft.bcc_addr)]
        return self._send(recipient_emails, msg)
Exemplo n.º 8
0
def create_gmail_email(sender_name, sender_email, inbox_uid, recipients,
                       subject, body, attachments=None):
    """ Create a Gmail email. """
    mimemsg = create_email(sender_name, sender_email, inbox_uid, recipients,
                           subject, body, attachments)

    return smtp_attrs(mimemsg)
Exemplo n.º 9
0
    def send(self, draft):
        """
        Turn a draft object into a MIME message and send it.

        Parameters
        ----------
        draft : models.message.Message object
            the draft message to send.
        """
        blocks = [p.block for p in draft.attachments]
        attachments = generate_attachments(draft, blocks)
        # @emfree - 3/19/2015
        #
        # Note that we intentionally don't set the Bcc header in the message we
        # construct, because this would would result in a MIME message being
        # generated with a Bcc header which all recipients can see.
        #
        # Arguably we should send each Bcc'ed recipient a MIME message that has
        # a Bcc: <only them> header. This is what the Gmail web UI appears to
        # do. However, this would need to be carefully implemented and tested.
        # The current approach was chosen for its comparative simplicity. I'm
        # pretty sure that other clients do it this way as well. It is the
        # first of the three implementations described here:
        # http://tools.ietf.org/html/rfc2822#section-3.6.3
        #
        # Note that we ensure in our SMTP code BCCed recipients still actually
        # get the message.

        # from_addr is only ever a list with one element
        from_addr = draft.from_addr[0]
        msg = create_email(
            from_name=from_addr[0],
            from_email=from_addr[1],
            reply_to=draft.reply_to,
            nylas_uid=draft.nylas_uid,
            to_addr=draft.to_addr,
            cc_addr=draft.cc_addr,
            bcc_addr=None,
            subject=draft.subject,
            html=draft.body,
            in_reply_to=draft.in_reply_to,
            references=draft.references,
            attachments=attachments,
        )

        recipient_emails = [
            email
            for name, email in itertools.chain(
                draft.to_addr, draft.cc_addr, draft.bcc_addr
            )
        ]
        self._send(recipient_emails, msg)

        # Sent to all successfully
        self.log.info("Sending successful", draft_id=draft.id)
Exemplo n.º 10
0
def create_gmail_email(sender_name,
                       sender_email,
                       inbox_uid,
                       recipients,
                       subject,
                       body,
                       attachments=None):
    """ Create a Gmail email. """
    mimemsg = create_email(sender_name, sender_email, inbox_uid, recipients,
                           subject, body, attachments)

    return smtp_attrs(mimemsg)
Exemplo n.º 11
0
def _create_email(account, message, generate_new_uid=False):
    recipients = Recipients(message.to_addr, message.cc_addr,
                            message.bcc_addr)
    blocks = [p.block for p in message.attachments]
    attachments = generate_attachments(blocks)

    inbox_uid = message.inbox_uid
    if generate_new_uid:
        inbox_uid = None

    return create_email(account.name, account.email_address,
                        inbox_uid, recipients, message.subject,
                        message.sanitized_body, attachments)
Exemplo n.º 12
0
    def send(self, draft):
        """
        Turn a draft object into a MIME message and send it.

        Parameters
        ----------
        draft : models.message.Message object
            the draft message to send.
        """
        blocks = [p.block for p in draft.attachments]
        attachments = generate_attachments(blocks)
        # @emfree - 3/19/2015
        #
        # Note that we intentionally don't set the Bcc header in the message we
        # construct, because this would would result in a MIME message being
        # generated with a Bcc header which all recipients can see.
        #
        # Arguably we should send each Bcc'ed recipient a MIME message that has
        # a Bcc: <only them> header. This is what the Gmail web UI appears to
        # do. However, this would need to be carefully implemented and tested.
        # The current approach was chosen for its comparative simplicity. I'm
        # pretty sure that other clients do it this way as well. It is the
        # first of the three implementations described here:
        # http://tools.ietf.org/html/rfc2822#section-3.6.3
        #
        # Note that we ensure in our SMTP code BCCed recipients still actually
        # get the message.

        # from_addr is only ever a list with one element
        from_addr = draft.from_addr[0]
        msg = create_email(from_name=from_addr[0],
                           from_email=from_addr[1],
                           reply_to=draft.reply_to,
                           nylas_uid=draft.nylas_uid,
                           to_addr=draft.to_addr,
                           cc_addr=draft.cc_addr,
                           bcc_addr=None,
                           subject=draft.subject,
                           html=draft.body,
                           in_reply_to=draft.in_reply_to,
                           references=draft.references,
                           attachments=attachments)

        recipient_emails = [email for name, email in itertools.chain(
            draft.to_addr, draft.cc_addr, draft.bcc_addr)]
        self._send(recipient_emails, msg)

        # Sent to all successfully
        self.log.info('Sending successful', sender=from_addr[1],
                      recipients=recipient_emails)
Exemplo n.º 13
0
def save_draft(account_id, message_id, db_session):
    """ Sync a new/updated draft back to the remote backend. """
    account = db_session.query(Account).get(account_id)
    message = db_session.query(Message).get(message_id)
    assert message.is_draft

    recipients = Recipients(message.to_addr, message.cc_addr, message.bcc_addr)
    attachments = generate_attachments(message.attachments)
    mimemsg = create_email(account.sender_name, account.email_address,
                           message.inbox_uid, recipients, message.subject,
                           message.sanitized_body, attachments)

    remote_save_draft = module_registry[account.provider].remote_save_draft
    remote_save_draft(account, account.drafts_folder.name, mimemsg.to_string(),
                      message.created_at)
Exemplo n.º 14
0
def _create_email(account, message):
    blocks = [p.block for p in message.attachments]
    attachments = generate_attachments(blocks)
    msg = create_email(sender_name=account.name,
                       sender_email=account.email_address,
                       inbox_uid=message.inbox_uid,
                       to_addr=message.to_addr,
                       cc_addr=message.cc_addr,
                       bcc_addr=message.bcc_addr,
                       subject=message.subject,
                       html=message.sanitized_body,
                       in_reply_to=message.in_reply_to,
                       references=message.references,
                       attachments=attachments)
    return msg
Exemplo n.º 15
0
def _create_email(account, message):
    blocks = [p.block for p in message.attachments]
    attachments = generate_attachments(blocks)
    msg = create_email(sender_name=account.name,
                       sender_email=account.email_address,
                       inbox_uid=message.inbox_uid,
                       to_addr=message.to_addr,
                       cc_addr=message.cc_addr,
                       bcc_addr=message.bcc_addr,
                       subject=message.subject,
                       html=message.sanitized_body,
                       in_reply_to=message.in_reply_to,
                       references=message.references,
                       attachments=attachments)
    return msg
Exemplo n.º 16
0
def test_remote_delete_draft(db, config, message):
    """
    Tests the delete_draft function, which deletes the draft from the
    remote.

    """
    from inbox.actions.backends.gmail import remote_delete_draft, remote_save_draft
    from inbox.models import Account
    from inbox.sendmail.base import _parse_recipients
    from inbox.sendmail.message import Recipients, create_email

    account = db.session.query(Account).get(ACCOUNT_ID)
    to, subject, body = message
    to_addr = _parse_recipients(to)
    recipients = Recipients(to_addr, [], [])
    email = create_email(
        account.sender_name,
        account.email_address,
        None,
        recipients,
        subject,
        body,
        None,
    )
    date = datetime.utcnow()

    # Save on remote
    remote_save_draft(account, account.drafts_folder.name, email.to_string(),
                      db.session, date)

    inbox_uid = email.headers["X-INBOX-ID"]

    with crispin_client(account.id, account.provider) as c:
        criteria = [
            "DRAFT", "NOT DELETED", "HEADER X-INBOX-ID {0}".format(inbox_uid)
        ]

        c.conn.select_folder(account.drafts_folder.name, readonly=False)
        uids = c.conn.search(criteria)
        assert uids, "Message missing from Drafts folder"

        # Delete on remote
        remote_delete_draft(account, account.drafts_folder.name, inbox_uid,
                            db.session)

        c.conn.select_folder(account.drafts_folder.name, readonly=False)
        uids = c.conn.search(criteria)
        assert not uids, "Message still in Drafts folder"
Exemplo n.º 17
0
def save_draft(account_id, message_id, db_session):
    """ Sync a new/updated draft back to the remote backend. """
    account = db_session.query(Account).get(account_id)
    message = db_session.query(Message).get(message_id)
    assert message.is_draft

    recipients = Recipients(message.to_addr, message.cc_addr,
                            message.bcc_addr)
    attachments = generate_attachments(message.attachments)
    mimemsg = create_email(account.sender_name, account.email_address,
                           message.inbox_uid, recipients, message.subject,
                           message.sanitized_body, attachments)

    remote_save_draft = module_registry[account.provider].remote_save_draft
    remote_save_draft(account, account.drafts_folder.name,
                      mimemsg.to_string(), message.created_at)
Exemplo n.º 18
0
def create_gmail_reply(sender_name, sender_email, in_reply_to, references,
                       inbox_uid, recipients, subject, body, attachments=None):
    """ Create a Gmail email reply. """
    mimemsg = create_email(sender_name, sender_email, inbox_uid, recipients,
                           subject, body, attachments)

    # Add general reply headers:
    mimemsg.headers['In-Reply-To'] = in_reply_to
    mimemsg.headers['References'] = references

    # Set the 'Subject' header of the reply, required for Gmail.
    # Gmail requires the same subject as the original (adding Re:/Fwd: is fine
    # though) to group messages in the same conversation,
    # See: https://support.google.com/mail/answer/5900?hl=en
    mimemsg.headers['Subject'] = REPLYSTR + subject

    return smtp_attrs(mimemsg)
Exemplo n.º 19
0
def _create_email(account, message):
    blocks = [p.block for p in message.attachments]
    attachments = generate_attachments(blocks)
    from_name, from_email = message.from_addr[0]
    msg = create_email(from_name=from_name,
                       from_email=from_email,
                       reply_to=message.reply_to,
                       inbox_uid=message.inbox_uid,
                       to_addr=message.to_addr,
                       cc_addr=message.cc_addr,
                       bcc_addr=message.bcc_addr,
                       subject=message.subject,
                       html=message.body,
                       in_reply_to=message.in_reply_to,
                       references=message.references,
                       attachments=attachments)
    return msg
Exemplo n.º 20
0
def _create_email(account, message):
    blocks = [p.block for p in message.attachments]
    attachments = generate_attachments(blocks)
    from_name, from_email = message.from_addr[0]
    msg = create_email(from_name=from_name,
                       from_email=from_email,
                       reply_to=message.reply_to,
                       inbox_uid=message.inbox_uid,
                       to_addr=message.to_addr,
                       cc_addr=message.cc_addr,
                       bcc_addr=message.bcc_addr,
                       subject=message.subject,
                       html=message.body,
                       in_reply_to=message.in_reply_to,
                       references=message.references,
                       attachments=attachments)
    return msg
Exemplo n.º 21
0
def test_remote_delete_draft(db, config, message):
    """
    Tests the delete_draft function, which deletes the draft from the
    remote.

    """
    from inbox.actions.backends.gmail import (remote_save_draft,
                                              remote_delete_draft)
    from inbox.sendmail.base import _parse_recipients
    from inbox.sendmail.message import create_email, Recipients
    from inbox.models import Account

    account = db.session.query(Account).get(ACCOUNT_ID)
    to, subject, body = message
    to_addr = _parse_recipients(to)
    recipients = Recipients(to_addr, [], [])
    email = create_email(account.sender_name, account.email_address, None,
                         recipients, subject, body, None)
    date = datetime.utcnow()

    # Save on remote
    remote_save_draft(account, account.drafts_folder.name, email.to_string(),
                      db.session, date)

    inbox_uid = email.headers['X-INBOX-ID']

    with crispin_client(account.id, account.provider) as c:
        criteria = ['DRAFT', 'NOT DELETED',
                    'HEADER X-INBOX-ID {0}'.format(inbox_uid)]

        c.conn.select_folder(account.drafts_folder.name, readonly=False)
        uids = c.conn.search(criteria)
        assert uids, 'Message missing from Drafts folder'

        # Delete on remote
        remote_delete_draft(account, account.drafts_folder.name, inbox_uid,
                            db.session)

        c.conn.select_folder(account.drafts_folder.name, readonly=False)
        uids = c.conn.search(criteria)
        assert not uids, 'Message still in Drafts folder'
Exemplo n.º 22
0
    def send_custom(self, draft, body, recipients):
        """
        Turn a draft object into a MIME message, replacing the body with
        the provided body, and send it only to the provided recipients.

        Parameters
        ----------
        draft: models.message.Message object
            the draft message to send.
        body: string
            message body to send in place of the existing body attribute in
            the draft.
        recipient_emails: email addresses to send copies of this message to.
        """
        blocks = [p.block for p in draft.attachments]
        attachments = generate_attachments(blocks)
        from_addr = draft.from_addr[0]
        msg = create_email(from_name=from_addr[0],
                           from_email=from_addr[1],
                           reply_to=draft.reply_to,
                           inbox_uid=draft.inbox_uid,
                           to_addr=draft.to_addr,
                           cc_addr=draft.cc_addr,
                           bcc_addr=None,
                           subject=draft.subject,
                           html=body,
                           in_reply_to=draft.in_reply_to,
                           references=draft.references,
                           attachments=attachments)

        recipient_emails = [email for name, email in recipients]

        self._send(recipient_emails, msg)

        # Sent successfully
        self.log.info('Sending successful',
                      sender=from_addr[1],
                      recipients=recipient_emails)
Exemplo n.º 23
0
def save_draft(account_id, message_id, db_session):
    """ Sync a new/updated draft back to the remote backend. """
    account = db_session.query(Account).get(account_id)
    message = db_session.query(Message).get(message_id)
    assert message.is_draft

    recipients = Recipients(message.to_addr, message.cc_addr, message.bcc_addr)
    attachments = generate_attachments(message.attachments)
    mimemsg = create_email(account.sender_name, account.email_address,
                           message.inbox_uid, recipients, message.subject,
                           message.sanitized_body, attachments)

    remote_save_draft = module_registry[account.provider].remote_save_draft
    remote_save_draft(account, account.drafts_folder.name, mimemsg.to_string(),
                      message.created_at)

    # If this draft is created by an update_draft() call,
    # delete the one it supercedes on the remote.
    # Needed because our update_draft() creates a new draft
    # message but the user expects to see the one
    # updated draft only.
    if message.parent_draft:
        return delete_draft(account_id, message.parent_draft.id, db_session)
Exemplo n.º 24
0
    def send_custom(self, draft, body, recipients):
        """
        Turn a draft object into a MIME message, replacing the body with
        the provided body, and send it only to the provided recipients.

        Parameters
        ----------
        draft: models.message.Message object
            the draft message to send.
        body: string
            message body to send in place of the existing body attribute in
            the draft.
        recipient_emails: email addresses to send copies of this message to.
        """
        blocks = [p.block for p in draft.attachments]
        attachments = generate_attachments(blocks)
        from_addr = draft.from_addr[0]
        msg = create_email(from_name=from_addr[0],
                           from_email=from_addr[1],
                           reply_to=draft.reply_to,
                           nylas_uid=draft.nylas_uid,
                           to_addr=draft.to_addr,
                           cc_addr=draft.cc_addr,
                           bcc_addr=None,
                           subject=draft.subject,
                           html=body,
                           in_reply_to=draft.in_reply_to,
                           references=draft.references,
                           attachments=attachments)

        recipient_emails = [email for name, email in recipients]

        self._send(recipient_emails, msg)

        # Sent successfully
        self.log.info('Sending successful', sender=from_addr[1],
                      recipients=recipient_emails)
Exemplo n.º 25
0
def save_draft(account_id, message_id):
    """ Sync a new/updated draft back to the remote backend. """
    with session_scope() as db_session:
        account = db_session.query(Account).get(account_id)
        message = db_session.query(SpoolMessage).get(message_id)

        recipients = Recipients(message.to_addr, message.cc_addr,
                                message.bcc_addr)
        attachments = generate_attachments(message.attachments)
        mimemsg = create_email(account.sender_name, account.email_address,
                               message.inbox_uid, recipients, message.subject,
                               message.sanitized_body, attachments)

        remote_save_draft = module_registry[account.provider].remote_save_draft
        remote_save_draft(account, account.drafts_folder.name,
                          mimemsg.to_string(), message.created_date)

        # If this draft is created by an update_draft() call,
        # delete the one it supercedes on the remote.
        # Needed because our update_draft() creates a new draft
        # message but the user expects to see the one
        # updated draft only.
        if message.parent_draft:
            return delete_draft(account_id, message.parent_draft.id)