Beispiel #1
0
def add_fake_message(db_session, namespace_id, thread=None, from_addr=None,
                     to_addr=None, cc_addr=None, bcc_addr=None,
                     received_date=None, subject='',
                     body='', snippet='', add_sent_category=False):
    from inbox.models import Message, Category
    from inbox.contacts.process_mail import update_contacts_from_message
    m = Message()
    m.namespace_id = namespace_id
    m.from_addr = from_addr or []
    m.to_addr = to_addr or []
    m.cc_addr = cc_addr or []
    m.bcc_addr = bcc_addr or []
    m.received_date = received_date or datetime.utcnow()
    m.size = 0
    m.is_read = False
    m.is_starred = False
    m.body = body
    m.snippet = snippet
    m.subject = subject

    if thread:
        thread.messages.append(m)
        update_contacts_from_message(db_session, m, thread.namespace)

        db_session.add(m)
        db_session.commit()

    if add_sent_category:
        category = Category.find_or_create(
            db_session, namespace_id, 'sent', 'sent', type_='folder')
        if category not in m.categories:
            m.categories.add(category)
        db_session.commit()

    return m
Beispiel #2
0
def add_fake_message(db_session, namespace_id, thread=None, from_addr=None,
                     to_addr=None, cc_addr=None, bcc_addr=None,
                     received_date=None, subject='',
                     body='', snippet=''):
    from inbox.models import Message
    from inbox.contacts.process_mail import update_contacts_from_message
    m = Message()
    m.namespace_id = namespace_id
    m.from_addr = from_addr or []
    m.to_addr = to_addr or []
    m.cc_addr = cc_addr or []
    m.bcc_addr = bcc_addr or []
    m.received_date = received_date or datetime.utcnow()
    m.size = 0
    m.is_read = False
    m.is_starred = False
    m.body = body
    m.snippet = snippet
    m.subject = subject

    if thread:
        thread.messages.append(m)
        update_contacts_from_message(db_session, m, thread.namespace)

        db_session.add(m)
        db_session.commit()
    return m
Beispiel #3
0
def test_threading_limit(db, folder_sync_engine, monkeypatch):
    """Test that custom threading doesn't produce arbitrarily long threads,
    which eventually break things."""
    from inbox.models import Message, Thread
    # Shorten bound to make test faster
    MAX_THREAD_LENGTH = 10
    monkeypatch.setattr(
        'inbox.mailsync.backends.imap.generic.MAX_THREAD_LENGTH',
        MAX_THREAD_LENGTH)
    namespace_id = folder_sync_engine.namespace_id

    msg = MockRawMessage([])
    for i in range(3 * MAX_THREAD_LENGTH):
        m = Message()
        m.namespace_id = namespace_id
        m.received_date = datetime.datetime.utcnow()
        m.references = []
        m.size = 0
        m.body = ''
        m.from_addr = [("Karim Hamidou", "*****@*****.**")]
        m.to_addr = [("Eben Freeman", "*****@*****.**")]
        m.snippet = ''
        m.subject = 'unique subject'
        db.session.add(m)
        folder_sync_engine.add_message_to_thread(db.session, m, msg)
        db.session.commit()
    new_threads = db.session.query(Thread). \
        filter(Thread.subject == 'unique subject').all()
    assert len(new_threads) == 3
    assert all(len(thread.messages) == MAX_THREAD_LENGTH for thread in
               new_threads)
Beispiel #4
0
def add_fake_message(db_session,
                     namespace_id,
                     thread=None,
                     from_addr=None,
                     to_addr=None,
                     cc_addr=None,
                     bcc_addr=None,
                     received_date=None,
                     subject=None):
    from inbox.models import Message
    from inbox.contacts.process_mail import update_contacts_from_message
    m = Message()
    m.namespace_id = namespace_id
    m.from_addr = from_addr or []
    m.to_addr = to_addr or []
    m.cc_addr = cc_addr or []
    m.bcc_addr = bcc_addr or []
    m.received_date = received_date or datetime.utcnow()
    m.size = 0
    m.sanitized_body = ''
    m.snippet = ''
    m.subject = subject or ''

    if thread:
        thread.messages.append(m)
        update_contacts_from_message(db_session, m, thread.namespace)

        db_session.add(m)
        db_session.commit()
    return m
Beispiel #5
0
def add_fake_message(account_id, thread, to_email, received_date,
                     db_session):
    """ One-off helper function to add 'fake' messages to the datastore."""
    m = Message()
    m.from_addr = [('', to_email)]
    m.received_date = received_date
    m.size = 0
    m.sanitized_body = ''
    m.snippet = ''
    m.thread = thread
    update_contacts_from_message(db_session, m, account_id)
    db_session.add(m)
    db_session.commit()
Beispiel #6
0
def add_fake_message(account_id, thread, to_email, received_date,
                     db_session):
    """ One-off helper function to add 'fake' messages to the datastore."""
    m = Message()
    m.from_addr = [('', to_email)]
    m.received_date = received_date
    m.size = 0
    m.sanitized_body = ''
    m.snippet = ''
    m.thread = thread
    update_contacts_from_message(db_session, m, account_id)
    db_session.add(m)
    db_session.commit()
Beispiel #7
0
def add_fake_message(
    db_session,
    namespace_id,
    thread=None,
    from_addr=None,
    to_addr=None,
    cc_addr=None,
    bcc_addr=None,
    received_date=None,
    subject="",
    body="",
    snippet="",
    g_msgid=None,
    add_sent_category=False,
):
    from inbox.contacts.processing import update_contacts_from_message
    from inbox.models import Category, Message

    m = Message()
    m.namespace_id = namespace_id
    m.from_addr = from_addr or []
    m.to_addr = to_addr or []
    m.cc_addr = cc_addr or []
    m.bcc_addr = bcc_addr or []
    m.received_date = received_date or datetime.utcnow()
    m.size = 0
    m.is_read = False
    m.is_starred = False
    m.body = body
    m.snippet = snippet
    m.subject = subject
    m.g_msgid = g_msgid

    if thread:
        thread.messages.append(m)
        update_contacts_from_message(db_session, m, thread.namespace.id)

        db_session.add(m)
        db_session.commit()

    if add_sent_category:
        category = Category.find_or_create(db_session,
                                           namespace_id,
                                           "sent",
                                           "sent",
                                           type_="folder")
        if category not in m.categories:
            m.categories.add(category)
        db_session.commit()

    return m
def add_fake_message(db_session, thread, from_addr=None, to_addr=None,
                     cc_addr=None, bcc_addr=None):
    m = Message()
    m.namespace_id = NAMESPACE_ID
    m.from_addr = from_addr or []
    m.to_addr = to_addr or []
    m.cc_addr = cc_addr or []
    m.bcc_addr = bcc_addr or []
    m.received_date = datetime.utcnow()
    m.size = 0
    m.sanitized_body = ''
    m.snippet = ''
    m.thread = thread
    update_contacts_from_message(db_session, m, thread.namespace)
    db_session.add(m)
    db_session.commit()
    return m
Beispiel #9
0
def message():
    received_date = datetime.datetime.utcfromtimestamp(10**9 + 1)
    new_msg = Message()
    new_msg.from_addr = (('Some Dude', '*****@*****.**'),)
    new_msg.to_addr = (('Somebody Else',
                        '*****@*****.**'),)
    new_msg.cc_addr = (('A Bystander',
                        '*****@*****.**'),)
    new_msg.bcc_addr = (('The NSA', '*****@*****.**'),)
    new_msg.thread_id = 1
    new_msg.size = 22
    new_msg.is_draft = False
    new_msg.decode_error = False
    new_msg.sanitized_body = 'Are you there?'
    new_msg.snippet = 'Are you there?'
    new_msg.received_date = received_date
    return new_msg
Beispiel #10
0
def add_fake_message(db_session,
                     namespace_id,
                     thread=None,
                     from_addr=None,
                     to_addr=None,
                     cc_addr=None,
                     bcc_addr=None,
                     received_date=None,
                     subject='',
                     body='',
                     snippet='',
                     add_sent_category=False):
    from inbox.models import Message, Category
    from inbox.contacts.process_mail import update_contacts_from_message
    m = Message()
    m.namespace_id = namespace_id
    m.from_addr = from_addr or []
    m.to_addr = to_addr or []
    m.cc_addr = cc_addr or []
    m.bcc_addr = bcc_addr or []
    m.received_date = received_date or datetime.utcnow()
    m.size = 0
    m.is_read = False
    m.is_starred = False
    m.body = body
    m.snippet = snippet
    m.subject = subject

    if thread:
        thread.messages.append(m)
        update_contacts_from_message(db_session, m, thread.namespace)

        db_session.add(m)
        db_session.commit()

    if add_sent_category:
        category = Category.find_or_create(db_session,
                                           namespace_id,
                                           'sent',
                                           'sent',
                                           type_='folder')
        if category not in m.categories:
            m.categories.add(category)
        db_session.commit()

    return m
Beispiel #11
0
def add_fake_message(db_session, thread, from_addr=None, to_addr=None,
                     cc_addr=None, bcc_addr=None):
    m = Message()
    m.from_addr = from_addr or []
    m.to_addr = to_addr or []
    m.cc_addr = cc_addr or []
    m.bcc_addr = bcc_addr or []
    m.received_date = datetime.utcnow()
    m.size = 0
    m.sanitized_body = ''
    m.snippet = ''
    m.thread = thread
    account_id = thread.namespace.account_id
    update_contacts_from_message(db_session, m, account_id)
    db_session.add(m)
    db_session.commit()
    return m
Beispiel #12
0
def add_fake_message(db_session, namespace_id, thread, from_addr=None,
                     to_addr=None, cc_addr=None, bcc_addr=None,
                     received_date=None, subject=None):
    from inbox.models import Message
    from inbox.contacts.process_mail import update_contacts_from_message
    m = Message()
    m.namespace_id = namespace_id
    m.from_addr = from_addr or []
    m.to_addr = to_addr or []
    m.cc_addr = cc_addr or []
    m.bcc_addr = bcc_addr or []
    m.received_date = received_date or datetime.utcnow()
    m.size = 0
    m.sanitized_body = ''
    m.snippet = ''
    m.subject = subject or ''
    m.thread = thread
    update_contacts_from_message(db_session, m, thread.namespace)
    db_session.add(m)
    db_session.commit()
    return m
def test_threading_limit(db, folder_sync_engine, monkeypatch):
    """Test that custom threading doesn't produce arbitrarily long threads,
    which eventually break things."""
    from inbox.models import Message, Thread, Account
    # Shorten bound to make test faster
    MAX_THREAD_LENGTH = 10
    monkeypatch.setattr(
        'inbox.mailsync.backends.imap.generic.MAX_THREAD_LENGTH',
        MAX_THREAD_LENGTH)
    namespace_id = folder_sync_engine.namespace_id
    account = db.session.query(Account).get(folder_sync_engine.account_id)
    account.namespace.create_canonical_tags()

    account.inbox_folder = Folder(account=account,
                                  name='Inbox',
                                  canonical_name='inbox')
    folder = account.inbox_folder

    msg = MockRawMessage([])
    for i in range(3 * MAX_THREAD_LENGTH):
        m = Message()
        m.namespace_id = namespace_id
        m.received_date = datetime.datetime.utcnow()
        m.references = []
        m.size = 0
        m.sanitized_body = ''
        m.from_addr = [("Karim Hamidou", "*****@*****.**")]
        m.to_addr = [("Eben Freeman", "*****@*****.**")]
        m.snippet = ''
        m.subject = 'unique subject'
        uid = ImapUid(message=m, account=account, msg_uid=2222 + i,
                      folder=folder)
        folder_sync_engine.add_message_attrs(db.session, uid, msg)
        db.session.add(m)
        db.session.commit()
    new_threads = db.session.query(Thread). \
        filter(Thread.subject == 'unique subject').all()
    assert len(new_threads) == 3
    assert all(len(thread.messages) == MAX_THREAD_LENGTH for thread in
               new_threads)
def test_threading_limit(db, folder_sync_engine, monkeypatch):
    """Test that custom threading doesn't produce arbitrarily long threads,
    which eventually break things."""
    from inbox.models import Message, Thread, Account
    # Shorten bound to make test faster
    MAX_THREAD_LENGTH = 10
    monkeypatch.setattr(
        'inbox.mailsync.backends.imap.generic.MAX_THREAD_LENGTH',
        MAX_THREAD_LENGTH)
    namespace_id = folder_sync_engine.namespace_id
    account = db.session.query(Account).get(folder_sync_engine.account_id)
    account.namespace.create_canonical_tags()

    account.inbox_folder = Folder(account=account,
                                  name='Inbox',
                                  canonical_name='inbox')
    folder = account.inbox_folder

    msg = MockRawMessage([])
    for i in range(3 * MAX_THREAD_LENGTH):
        m = Message()
        m.namespace_id = namespace_id
        m.received_date = datetime.datetime.utcnow()
        m.references = []
        m.size = 0
        m.sanitized_body = ''
        m.from_addr = [("Karim Hamidou", "*****@*****.**")]
        m.to_addr = [("Eben Freeman", "*****@*****.**")]
        m.snippet = ''
        m.subject = 'unique subject'
        uid = ImapUid(message=m, account=account, msg_uid=2222 + i,
                      folder=folder)
        folder_sync_engine.add_message_attrs(db.session, uid, msg)
        db.session.add(m)
        db.session.commit()
    new_threads = db.session.query(Thread). \
        filter(Thread.subject == 'unique subject').all()
    assert len(new_threads) == 3
    assert all(len(thread.messages) == MAX_THREAD_LENGTH for thread in
               new_threads)
Beispiel #15
0
def create_draft(data, namespace, db_session, syncback):
    """ Construct a draft object (a Message instance) from `data`, a dictionary
    representing the POST body of an API request. All new objects are added to
    the session, but not committed."""

    # Validate the input and get referenced objects (thread, attachments)
    # as necessary.
    to_addr = get_recipients(data.get('to'), 'to')
    cc_addr = get_recipients(data.get('cc'), 'cc')
    bcc_addr = get_recipients(data.get('bcc'), 'bcc')
    from_addr = get_recipients(data.get('from'), 'from')
    reply_to = get_recipients(data.get('reply_to'), 'reply_to')

    if from_addr and len(from_addr) > 1:
        raise InputError("from_addr field can have at most one item")
    if reply_to and len(reply_to) > 1:
        raise InputError("reply_to field can have at most one item")

    subject = data.get('subject')
    if subject is not None and not isinstance(subject, basestring):
        raise InputError('"subject" should be a string')
    body = data.get('body', '')
    if not isinstance(body, basestring):
        raise InputError('"body" should be a string')
    blocks = get_attachments(data.get('file_ids'), namespace.id, db_session)
    reply_to_thread = get_thread(data.get('thread_id'), namespace.id,
                                 db_session)
    reply_to_message = get_message(data.get('reply_to_message_id'),
                                   namespace.id, db_session)
    if reply_to_message is not None and reply_to_thread is not None:
        if reply_to_message not in reply_to_thread.messages:
            raise InputError('Message {} is not in thread {}'.
                             format(reply_to_message.public_id,
                                    reply_to_thread.public_id))

    with db_session.no_autoflush:
        account = namespace.account
        dt = datetime.utcnow()
        uid = generate_public_id()
        to_addr = to_addr or []
        cc_addr = cc_addr or []
        bcc_addr = bcc_addr or []
        blocks = blocks or []
        if subject is None:
            # If this is a reply with no explicitly specified subject, set the
            # subject from the prior message/thread by default.
            # TODO(emfree): Do we want to allow changing the subject on a reply
            # at all?
            if reply_to_message is not None:
                subject = reply_to_message.subject
            elif reply_to_thread is not None:
                subject = reply_to_thread.subject
        subject = subject or ''

        message = Message()
        message.namespace = namespace
        message.is_created = True
        message.is_draft = True
        message.from_addr = from_addr if from_addr else \
            [(account.name, account.email_address)]
        # TODO(emfree): we should maybe make received_date nullable, so its
        # value doesn't change in the case of a drafted-and-later-reconciled
        # message.
        message.received_date = dt
        message.subject = subject
        message.body = body
        message.to_addr = to_addr
        message.cc_addr = cc_addr
        message.bcc_addr = bcc_addr
        message.reply_to = reply_to
        # TODO(emfree): this is different from the normal 'size' value of a
        # message, which is the size of the entire MIME message.
        message.size = len(body)
        message.is_read = True
        message.is_sent = False
        message.public_id = uid
        message.version = 0
        message.regenerate_inbox_uid()

        # Set the snippet
        message.snippet = message.calculate_html_snippet(body)

        # Associate attachments to the draft message
        for block in blocks:
            # Create a new Part object to associate to the message object.
            # (You can't just set block.message, because if block is an
            # attachment on an existing message, that would dissociate it from
            # the existing message.)
            part = Part(block=block)
            part.namespace_id = namespace.id
            part.content_disposition = 'attachment'
            part.is_inboxapp_attachment = True
            message.parts.append(part)

        update_contacts_from_message(db_session, message, namespace)

        if reply_to_message is not None:
            message.is_reply = True
            _set_reply_headers(message, reply_to_message)
            thread = reply_to_message.thread
            message.reply_to_message = reply_to_message
        elif reply_to_thread is not None:
            message.is_reply = True
            thread = reply_to_thread
            # Construct the in-reply-to and references headers from the last
            # message currently in the thread.
            previous_messages = [m for m in thread.messages if not m.is_draft]
            if previous_messages:
                last_message = previous_messages[-1]
                message.reply_to_message = last_message
                _set_reply_headers(message, last_message)
        else:
            # If this isn't a reply to anything, create a new thread object for
            # the draft.  We specialize the thread class so that we can, for
            # example, add the g_thrid for Gmail later if we reconcile a synced
            # message with this one. This is a huge hack, but works.
            message.is_reply = False
            thread_cls = account.thread_cls
            thread = thread_cls(
                subject=message.subject,
                recentdate=message.received_date,
                namespace=namespace,
                subjectdate=message.received_date)

        message.thread = thread

    db_session.add(message)
    if syncback:
        schedule_action('save_draft', message, namespace.id, db_session,
                        version=message.version)
    db_session.flush()
    return message
Beispiel #16
0
def create_and_save_draft(db_session,
                          account,
                          to_addr=None,
                          subject=None,
                          body=None,
                          blocks=None,
                          cc_addr=None,
                          bcc_addr=None,
                          new_tags=None,
                          thread=None,
                          is_reply=False,
                          syncback=True):
    """Create a draft object and commit it to the database."""
    with db_session.no_autoflush:
        dt = datetime.utcnow()
        uid = generate_public_id()
        version = generate_public_id()
        to_addr = to_addr or []
        cc_addr = cc_addr or []
        bcc_addr = bcc_addr or []
        blocks = blocks or []
        body = body or ''
        if subject is None and thread is not None:
            # Set subject from thread by default.
            subject = thread.subject
        subject = subject or ''

        message = Message()
        message.namespace = account.namespace
        message.is_created = True
        message.is_draft = True
        message.state = 'draft'
        message.from_addr = [(account.name, account.email_address)]
        # TODO(emfree): we should maybe make received_date nullable, so its
        # value doesn't change in the case of a drafted-and-later-reconciled
        # message.
        message.received_date = dt
        message.subject = subject
        message.sanitized_body = body
        message.to_addr = to_addr
        message.cc_addr = cc_addr
        message.bcc_addr = bcc_addr
        # TODO(emfree): this is different from the normal 'size' value of a
        # message, which is the size of the entire MIME message.
        message.size = len(body)
        message.is_read = True
        message.is_sent = False
        message.is_reply = is_reply
        message.public_id = uid
        message.version = version
        message.inbox_uid = version

        # Set the snippet
        message.snippet = message.calculate_html_snippet(body)

        # Associate attachments to the draft message
        for block in blocks:
            # Create a new Part object to associate to the message object.
            # (You can't just set block.message, because if block is an
            # attachment on an existing message, that would dissociate it from
            # the existing message.)
            part = Part(block=block)
            part.namespace_id = account.namespace.id
            part.content_disposition = 'attachment'
            part.is_inboxapp_attachment = True
            message.parts.append(part)
            db_session.add(part)

        update_contacts_from_message(db_session, message, account.namespace)

        if is_reply:
            message.is_reply = True
            # Construct the in-reply-to and references headers from the last
            # message currently in the thread.
            _set_reply_headers(message, thread)
        if thread is None:
            # Create a new thread object for the draft.
            # We specialize the thread class so that we can, for example, add
            # the g_thrid for Gmail later if we reconcile a synced message with
            # this one. This is a huge hack, but works.
            thread_cls = account.thread_cls
            thread = thread_cls(subject=message.subject,
                                recentdate=message.received_date,
                                namespace=account.namespace,
                                subjectdate=message.received_date)
            db_session.add(thread)

        message.thread = thread
        thread.apply_tag(account.namespace.tags['drafts'])

        if new_tags:
            tags_to_keep = {tag for tag in thread.tags if not tag.user_created}
            thread.tags = new_tags | tags_to_keep

        if syncback:
            schedule_action('save_draft', message, message.namespace.id,
                            db_session)

    db_session.add(message)
    db_session.commit()
    return message
Beispiel #17
0
def create_message_from_json(data, namespace, db_session, is_draft):
    """ Construct a Message instance from `data`, a dictionary representing the
    POST body of an API request. All new objects are added to the session, but
    not committed."""

    # Validate the input and get referenced objects (thread, attachments)
    # as necessary.
    to_addr = get_recipients(data.get('to'), 'to')
    cc_addr = get_recipients(data.get('cc'), 'cc')
    bcc_addr = get_recipients(data.get('bcc'), 'bcc')
    from_addr = get_recipients(data.get('from'), 'from')
    reply_to = get_recipients(data.get('reply_to'), 'reply_to')

    if from_addr and len(from_addr) > 1:
        raise InputError("from_addr field can have at most one item")
    if reply_to and len(reply_to) > 1:
        raise InputError("reply_to field can have at most one item")

    subject = data.get('subject')
    if subject is not None and not isinstance(subject, basestring):
        raise InputError('"subject" should be a string')
    body = data.get('body', '')
    if not isinstance(body, basestring):
        raise InputError('"body" should be a string')
    blocks = get_attachments(data.get('file_ids'), namespace.id, db_session)
    reply_to_thread = get_thread(data.get('thread_id'), namespace.id,
                                 db_session)
    reply_to_message = get_message(data.get('reply_to_message_id'),
                                   namespace.id, db_session)
    if reply_to_message is not None and reply_to_thread is not None:
        if reply_to_message not in reply_to_thread.messages:
            raise InputError('Message {} is not in thread {}'.format(
                reply_to_message.public_id, reply_to_thread.public_id))

    with db_session.no_autoflush:
        account = namespace.account
        dt = datetime.utcnow()
        uid = generate_public_id()
        to_addr = to_addr or []
        cc_addr = cc_addr or []
        bcc_addr = bcc_addr or []
        blocks = blocks or []
        if subject is None:
            # If this is a reply with no explicitly specified subject, set the
            # subject from the prior message/thread by default.
            # TODO(emfree): Do we want to allow changing the subject on a reply
            # at all?
            if reply_to_message is not None:
                subject = reply_to_message.subject
            elif reply_to_thread is not None:
                subject = reply_to_thread.subject
        subject = subject or ''

        message = Message()
        message.namespace = namespace
        message.is_created = True
        message.is_draft = is_draft
        message.from_addr = from_addr if from_addr else \
            [(account.name, account.email_address)]
        # TODO(emfree): we should maybe make received_date nullable, so its
        # value doesn't change in the case of a drafted-and-later-reconciled
        # message.
        message.received_date = dt
        message.subject = subject
        message.body = body
        message.to_addr = to_addr
        message.cc_addr = cc_addr
        message.bcc_addr = bcc_addr
        message.reply_to = reply_to
        # TODO(emfree): this is different from the normal 'size' value of a
        # message, which is the size of the entire MIME message.
        message.size = len(body)
        message.is_read = True
        message.is_sent = False
        message.public_id = uid
        message.version = 0
        message.regenerate_inbox_uid()

        # Set the snippet
        message.snippet = message.calculate_html_snippet(body)

        # Associate attachments to the draft message
        for block in blocks:
            # Create a new Part object to associate to the message object.
            # (You can't just set block.message, because if block is an
            # attachment on an existing message, that would dissociate it from
            # the existing message.)
            part = Part(block=block)
            part.namespace_id = namespace.id
            part.content_disposition = 'attachment'
            part.is_inboxapp_attachment = True
            message.parts.append(part)

        update_contacts_from_message(db_session, message, namespace)

        if reply_to_message is not None:
            message.is_reply = True
            _set_reply_headers(message, reply_to_message)
            thread = reply_to_message.thread
            message.reply_to_message = reply_to_message
        elif reply_to_thread is not None:
            message.is_reply = True
            thread = reply_to_thread
            # Construct the in-reply-to and references headers from the last
            # message currently in the thread.
            previous_messages = [m for m in thread.messages if not m.is_draft]
            if previous_messages:
                last_message = previous_messages[-1]
                message.reply_to_message = last_message
                _set_reply_headers(message, last_message)
        else:
            # If this isn't a reply to anything, create a new thread object for
            # the draft.  We specialize the thread class so that we can, for
            # example, add the g_thrid for Gmail later if we reconcile a synced
            # message with this one. This is a huge hack, but works.
            message.is_reply = False
            thread_cls = account.thread_cls
            thread = thread_cls(subject=message.subject,
                                recentdate=message.received_date,
                                namespace=namespace,
                                subjectdate=message.received_date)

        message.thread = thread

    db_session.add(message)
    if is_draft:
        schedule_action('save_draft',
                        message,
                        namespace.id,
                        db_session,
                        version=message.version)
    db_session.flush()
    return message
Beispiel #18
0
def create_and_save_draft(db_session, account, to_addr=None, subject=None,
                          body=None, blocks=None, cc_addr=None, bcc_addr=None,
                          new_tags=None, thread=None, is_reply=False,
                          syncback=True):
    """Create a draft object and commit it to the database."""
    with db_session.no_autoflush:
        dt = datetime.utcnow()
        uid = generate_public_id()
        version = generate_public_id()
        to_addr = to_addr or []
        cc_addr = cc_addr or []
        bcc_addr = bcc_addr or []
        blocks = blocks or []
        body = body or ''
        if subject is None and thread is not None:
            # Set subject from thread by default.
            subject = thread.subject
        subject = subject or ''

        message = Message()
        message.namespace = account.namespace
        message.is_created = True
        message.is_draft = True
        message.state = 'draft'
        message.from_addr = [(account.name, account.email_address)]
        # TODO(emfree): we should maybe make received_date nullable, so its
        # value doesn't change in the case of a drafted-and-later-reconciled
        # message.
        message.received_date = dt
        message.subject = subject
        message.sanitized_body = body
        message.to_addr = to_addr
        message.cc_addr = cc_addr
        message.bcc_addr = bcc_addr
        # TODO(emfree): this is different from the normal 'size' value of a
        # message, which is the size of the entire MIME message.
        message.size = len(body)
        message.is_read = True
        message.is_sent = False
        message.is_reply = is_reply
        message.public_id = uid
        message.version = version
        message.inbox_uid = version

        # Set the snippet
        message.snippet = message.calculate_html_snippet(body)

        # Associate attachments to the draft message
        for block in blocks:
            # Create a new Part object to associate to the message object.
            # (You can't just set block.message, because if block is an
            # attachment on an existing message, that would dissociate it from
            # the existing message.)
            part = Part(block=block)
            part.namespace_id = account.namespace.id
            part.content_disposition = 'attachment'
            part.is_inboxapp_attachment = True
            message.parts.append(part)
            db_session.add(part)

        update_contacts_from_message(db_session, message, account.namespace)

        if is_reply:
            message.is_reply = True
            # Construct the in-reply-to and references headers from the last
            # message currently in the thread.
            _set_reply_headers(message, thread)
        if thread is None:
            # Create a new thread object for the draft.
            # We specialize the thread class so that we can, for example, add
            # the g_thrid for Gmail later if we reconcile a synced message with
            # this one. This is a huge hack, but works.
            thread_cls = account.thread_cls
            thread = thread_cls(
                subject=message.subject,
                recentdate=message.received_date,
                namespace=account.namespace,
                subjectdate=message.received_date)
            db_session.add(thread)

        message.thread = thread
        thread.apply_tag(account.namespace.tags['drafts'])

        if new_tags:
            tags_to_keep = {tag for tag in thread.tags if not tag.user_created}
            thread.tags = new_tags | tags_to_keep

        if syncback:
            schedule_action('save_draft', message, message.namespace.id,
                            db_session)

    db_session.add(message)
    db_session.commit()
    return message