Example #1
0
def test_folder_crud(db, default_account, mock_imapclient, obj_type):
    mock_imapclient.create_folder = mock.Mock()
    mock_imapclient.rename_folder = mock.Mock()
    mock_imapclient.delete_folder = mock.Mock()
    cat = add_fake_category(db.session, default_account.namespace.id,
                            'MyFolder')
    with writable_connection_pool(default_account.id).get() as crispin_client:
        if obj_type == 'folder':
            create_folder(crispin_client, default_account.id, cat.id)
        else:
            create_label(crispin_client, default_account.id, cat.id)
        mock_imapclient.create_folder.assert_called_with('MyFolder')

        cat.display_name = 'MyRenamedFolder'
        db.session.commit()
        if obj_type == 'folder':
            update_folder(crispin_client, default_account.id, cat.id,
                          {'old_name': 'MyFolder',
                           'new_name': 'MyRenamedFolder'})
        else:
            update_label(crispin_client, default_account.id, cat.id,
                         {'old_name': 'MyFolder',
                          'new_name': 'MyRenamedFolder'})
        mock_imapclient.rename_folder.assert_called_with('MyFolder',
                                                         'MyRenamedFolder')

        category_id = cat.id
        if obj_type == 'folder':
            delete_folder(crispin_client, default_account.id, cat.id)
        else:
            delete_label(crispin_client, default_account.id, cat.id)
    mock_imapclient.delete_folder.assert_called_with('MyRenamedFolder')
    db.session.commit()
    assert db.session.query(Category).get(category_id) is None
Example #2
0
def user_console(user_email_address):
    with global_session_scope() as db_session:
        account = db_session.query(Account).filter_by(
            email_address=user_email_address).one()

        if account.provider == 'eas':
            banner = """
        You can access the account instance with the 'account' variable.
        """
        else:
            with writable_connection_pool(account.id, pool_size=1).get()\
                    as crispin_client:
                if account.provider == 'gmail' \
                        and 'all' in crispin_client.folder_names():
                    crispin_client.select_folder(
                        crispin_client.folder_names()['all'][0],
                        uidvalidity_cb)

                banner = """
        You can access the crispin instance with the 'crispin_client' variable,
        and the account instance with the 'account' variable.

        IMAPClient docs are at:

            http://imapclient.readthedocs.org/en/latest/#imapclient-class-reference
        """

        IPython.embed(banner1=banner)
Example #3
0
def remote_delete_folder(account_id, category_id):
    with session_scope(account_id) as db_session:
        account_provider = db_session.query(Account).get(account_id).provider
        category = db_session.query(Category).get(category_id)
        display_name = category.display_name

    with writable_connection_pool(account_id).get() as crispin_client:
        try:
            if account_provider not in ['gmail', 'eas']:
                # Translate a Unix-style path to the actual folder path.
                display_name = imap_folder_path(
                    display_name,
                    separator=crispin_client.folder_separator,
                    prefix=crispin_client.folder_prefix)

            crispin_client.conn.delete_folder(display_name)
        except IMAP4.error:
            # Folder has already been deleted on remote. Treat delete as
            # no-op.
            pass

    with session_scope(account_id) as db_session:
        category = db_session.query(Category).get(category_id)
        db_session.delete(category)
        db_session.commit()
Example #4
0
def test_change_flags(db, default_account, message, folder, mock_imapclient):
    mock_imapclient.add_folder_data(folder.name, {})
    mock_imapclient.add_flags = mock.Mock()
    mock_imapclient.remove_flags = mock.Mock()
    add_fake_imapuid(db.session, default_account.id, message, folder, 22)
    with writable_connection_pool(default_account.id).get() as crispin_client:
        mark_unread(crispin_client, default_account.id, message.id,
                    {'unread': False})
        mock_imapclient.add_flags.assert_called_with([22], ['\\Seen'],
                                                     silent=True)

        mark_unread(crispin_client, default_account.id, message.id,
                    {'unread': True})
        mock_imapclient.remove_flags.assert_called_with([22], ['\\Seen'],
                                                        silent=True)

        mark_starred(crispin_client, default_account.id, message.id,
                     {'starred': True})
        mock_imapclient.add_flags.assert_called_with([22], ['\\Flagged'],
                                                     silent=True)

        mark_starred(crispin_client, default_account.id, message.id,
                     {'starred': False})
        mock_imapclient.remove_flags.assert_called_with([22], ['\\Flagged'],
                                                        silent=True)
Example #5
0
def remote_create_folder(account_id, category_id):
    with session_scope(account_id) as db_session:
        account_provider = db_session.query(Account).get(account_id).provider
        category = db_session.query(Category).get(category_id)
        display_name = category.display_name

    with writable_connection_pool(account_id).get() as crispin_client:
        # Some generic IMAP providers have different conventions
        # regarding folder names. For example, Fastmail wants paths
        # to be of the form "INBOX.A". The API abstracts this.
        if account_provider not in ['gmail', 'eas']:
            # Translate the name of the folder to an actual IMAP name
            # (e.g: "Accounting/Taxes" becomes "Accounting.Taxes")
            new_display_name = imap_folder_path(
                display_name,
                separator=crispin_client.folder_separator,
                prefix=crispin_client.folder_prefix)
        else:
            new_display_name = display_name
        crispin_client.conn.create_folder(new_display_name)

    if new_display_name != display_name:
        with session_scope(account_id) as db_session:
            category = db_session.query(Category).get(category_id)
            category.display_name = new_display_name
def remote_copy(account, thread_id, from_folder, to_folder, db_session):
    if from_folder == to_folder:
        return

    uids = []
    thread = get_thread_uids(db_session, thread_id, account.namespace.id)
    for msg in thread.messages:
        uids.extend([uid.msg_uid for uid in msg.imapuids])

    if not uids:
        return

    with writable_connection_pool(account.id).get() as crispin_client:
        crispin_client.select_folder(from_folder, uidvalidity_cb)

        folders = crispin_client.folder_names()

        if from_folder not in folders.values() and \
           from_folder not in folders['extra']:
            raise Exception("Unknown from_folder '{}'".format(from_folder))

        if to_folder not in folders.values() and \
           to_folder not in folders['extra']:
            raise Exception("Unknown to_folder '{}'".format(to_folder))

        crispin_client.copy_uids(uids, to_folder)
Example #7
0
def remote_copy(account, thread_id, from_folder, to_folder, db_session):
    if from_folder == to_folder:
        return

    uids = []
    thread = get_thread_uids(db_session, thread_id, account.namespace.id)
    for msg in thread.messages:
        uids.extend([uid.msg_uid for uid in msg.imapuids])

    if not uids:
        return

    with writable_connection_pool(account.id).get() as crispin_client:
        crispin_client.select_folder(from_folder, uidvalidity_cb)

        folders = crispin_client.folder_names()

        if from_folder not in folders.values() and \
           from_folder not in folders['extra']:
                raise Exception("Unknown from_folder '{}'".format(from_folder))

        if to_folder not in folders.values() and \
           to_folder not in folders['extra']:
                raise Exception("Unknown to_folder '{}'".format(to_folder))

        crispin_client.copy_uids(uids, to_folder)
Example #8
0
def remote_change_labels(account, message_id, db_session, removed_labels, added_labels):
    uids_for_message = uids_by_folder(message_id, db_session)
    with writable_connection_pool(account.id).get() as crispin_client:
        for folder_name, uids in uids_for_message.items():
            crispin_client.select_folder(folder_name, uidvalidity_cb)
            crispin_client.conn.add_gmail_labels(uids, _encode_labels(added_labels))
            crispin_client.conn.remove_gmail_labels(uids, _encode_labels(removed_labels))
Example #9
0
def remote_update_draft(account_id, message_id):
    with session_scope(account_id) as db_session:
        account = db_session.query(Account).get(account_id)
        message = db_session.query(Message).get(message_id)
        message_id_header = message.message_id_header
        message_public_id = message.public_id
        version = message.version
        mimemsg = _create_email(account, message)

    with writable_connection_pool(account_id).get() as crispin_client:
        if 'drafts' not in crispin_client.folder_names():
            log.info('Account has no detected drafts folder; not saving draft',
                     account_id=account_id)
            return
        folder_name = crispin_client.folder_names()['drafts'][0]
        crispin_client.select_folder(folder_name, uidvalidity_cb)
        existing_copy = crispin_client.find_by_header(
            'Message-Id', message_id_header)
        if not existing_copy:
            crispin_client.save_draft(mimemsg)
        else:
            log.info('Not saving draft; copy already exists on remote',
                     message_id_header=message_id_header)

        # Check for an older version and delete it. (We can stop once we find
        # one, to reduce the latency of this operation.)
        for old_version in reversed(range(0, version)):
            message_id_header = '<{}-{}@mailer.nylas.com>'.format(
                message_public_id, old_version)
            old_version_deleted = crispin_client.delete_draft(
                message_id_header)
            if old_version_deleted:
                break
Example #10
0
def remote_delete_draft(account, inbox_uid, message_id_header, db_session):
    with writable_connection_pool(account.id).get() as crispin_client:
        if 'drafts' not in crispin_client.folder_names():
            log.info('Account has no detected drafts folder; not deleting draft',
                     account_id=account.id)
            return
        crispin_client.delete_draft(inbox_uid, message_id_header)
Example #11
0
def _syncback_action(fn, account, folder_name, db_session):
    """ `folder_name` is a Gmail folder name. """
    assert folder_name, "folder '{}' is not selectable".format(folder_name)

    with writable_connection_pool(account.id).get() as crispin_client:
        crispin_client.select_folder(folder_name, uidvalidity_cb)
        fn(account, db_session, crispin_client)
Example #12
0
def user_console(user_email_address):
    with session_scope() as db_session:
        account = db_session.query(Account).filter_by(
            email_address=user_email_address).one()

        if account.provider == 'eas':
            banner = """
        You can access the account instance with the 'account' variable.
        """
        else:
            with writable_connection_pool(account.id, pool_size=1).get()\
                    as crispin_client:
                if account.provider == 'gmail' \
                        and 'all' in crispin_client.folder_names():
                    crispin_client.select_folder(
                        crispin_client.folder_names()['all'][0],
                        uidvalidity_cb)

                banner = """
        You can access the crispin instance with the 'crispin_client' variable,
        and the account instance with the 'account' variable.

        IMAPClient docs are at:

            http://imapclient.readthedocs.org/en/latest/#imapclient-class-reference
        """

        IPython.embed(banner1=banner)
Example #13
0
def test_gmail_labels(client):
    # test case: create a label on the gmail account
    # apply it to a thread. Check that it gets picked up.
    # Remove it. Check that it gets picked up.
    thread = random.choice(client.threads.all())

    account = None
    with session_scope() as db_session:
        account = db_session.query(Account).filter_by(
            email_address=client.email_address).one()

        with writable_connection_pool(account.id, pool_size=1).get() as crispin_client:
            labelname = "custom-label" + datetime.now().strftime("%s.%f")
            print "Label: %s" % labelname

            folder_name = crispin_client.folder_names()['all']
            crispin_client.select_folder(folder_name, uidvalidity_cb)

            print "Subject : %s" % thread.subject
            uids = crispin_client.search_uids(['SUBJECT', thread.subject])
            g_thrid = crispin_client.g_metadata(uids).items()[0][1].thrid

            crispin_client.add_label(g_thrid, labelname)
            wait_for_tag(client, thread.id, labelname)

            draft = client.drafts.create(to=[{'name': 'Inbox SelfSend',
                                          'email': client.email_address}],
                                          body="Blah, replying to message",
                                          subject=thread.subject)
            draft.send()

            crispin_client.remove_label(g_thrid, labelname)
            wait_for_tag_removal(client, thread.id, labelname)
Example #14
0
File: imap.py Project: 0xcd03/inbox
def syncback_action(fn, account, folder_name, db_session):
    """ `folder_name` is a provider folder name, not a local tag

    `folder_name` is the folder which is selected before `fn` is called.

    """
    assert folder_name, "folder '{}' is not selectable".format(folder_name)

    # NOTE: This starts a *new* IMAP session every time---we will want
    # to optimize this at some point. But for now, it's most correct.
    for i in range(2):
        with writable_connection_pool(account.id).get() as crispin_client:
            try:
                crispin_client.select_folder(folder_name, uidvalidity_cb)
                fn(account, db_session, crispin_client)
                return
            except TransientConnectionError:
                # this was probably a transient server error --
                # back off and retry once:
                if i == 2:
                    # retry only once
                    logger.error("Error syncing back - second error in a row",
                                 account_id=account.id)
                    account.sync_state = 'connerror'
                    raise

                # wait a random delay because
                # we don't want to be hammering the server all at once.
                gevent.sleep(random.uniform(1, 10))
                continue
            except ConnectionError:
                logger.error("Error syncing back", account_id=account.id)
                account.sync_state = 'connerror'
                raise
Example #15
0
def remote_update_draft(account_id, message_id):
    with session_scope(account_id) as db_session:
        account = db_session.query(Account).get(account_id)
        message = db_session.query(Message).get(message_id)
        message_id_header = message.message_id_header
        message_public_id = message.public_id
        version = message.version
        mimemsg = _create_email(account, message)

    with writable_connection_pool(account_id).get() as crispin_client:
        if 'drafts' not in crispin_client.folder_names():
            log.info('Account has no detected drafts folder; not saving draft',
                     account_id=account_id)
            return
        folder_name = crispin_client.folder_names()['drafts'][0]
        crispin_client.select_folder(folder_name, uidvalidity_cb)
        existing_copy = crispin_client.find_by_header('Message-Id',
                                                      message_id_header)
        if not existing_copy:
            crispin_client.save_draft(mimemsg)
        else:
            log.info('Not saving draft; copy already exists on remote',
                     message_id_header=message_id_header)

        # Check for an older version and delete it. (We can stop once we find
        # one, to reduce the latency of this operation.)
        for old_version in reversed(range(0, version)):
            message_id_header = '<{}-{}@mailer.nylas.com>'.format(
                message_public_id, old_version)
            old_version_deleted = crispin_client.delete_draft(
                message_id_header)
            if old_version_deleted:
                break
Example #16
0
def syncback_action(fn, account, folder_name, db_session):
    """ `folder_name` is a provider folder name, not a local tag

    `folder_name` is the folder which is selected before `fn` is called.

    """
    assert folder_name, "folder '{}' is not selectable".format(folder_name)

    # NOTE: This starts a *new* IMAP session every time---we will want
    # to optimize this at some point. But for now, it's most correct.
    for i in range(2):
        with writable_connection_pool(account.id).get() as crispin_client:
            try:
                crispin_client.select_folder(folder_name, uidvalidity_cb)
                fn(account, db_session, crispin_client)
                return
            except TransientConnectionError:
                # this was probably a transient server error --
                # back off and retry once:
                if i == 2:
                    # retry only once
                    logger.error("Error syncing back - second error in a row",
                                 account_id=account.id)
                    account.sync_state = 'connerror'
                    raise

                # wait a random delay because
                # we don't want to be hammering the server all at once.
                gevent.sleep(random.uniform(1, 10))
                continue
            except ConnectionError:
                logger.error("Error syncing back", account_id=account.id)
                account.sync_state = 'connerror'
                raise
Example #17
0
def test_draft_updates(db, default_account, mock_imapclient):
    # Set up folder list
    mock_imapclient._data['Drafts'] = {}
    mock_imapclient._data['Trash'] = {}
    mock_imapclient.list_folders = lambda: [
        (('\\HasNoChildren', '\\Drafts'), '/', 'Drafts'),
        (('\\HasNoChildren', '\\Trash'), '/', 'Trash')
    ]

    pool = writable_connection_pool(default_account.id)

    draft = create_message_from_json({'subject': 'Test draft'},
                                     default_account.namespace, db.session,
                                     True)

    draft.is_draft = True
    draft.version = 0
    db.session.commit()
    with pool.get() as conn:
        save_draft(conn, default_account.id, draft.id, {'version': 0})
        conn.select_folder('Drafts', lambda *args: True)
        assert len(conn.all_uids()) == 1

        # Check that draft is not resaved if already synced.
        update_draft(conn, default_account.id, draft.id, {'version': 0})
        conn.select_folder('Drafts', lambda *args: True)
        assert len(conn.all_uids()) == 1

        # Check that an older version is deleted
        draft.version = 4
        sendmail_update_draft(db.session,
                              default_account,
                              draft,
                              from_addr=draft.from_addr,
                              subject='New subject',
                              blocks=[])
        db.session.commit()

        update_draft(conn, default_account.id, draft.id, {'version': 5})

        conn.select_folder('Drafts', lambda *args: True)
        all_uids = conn.all_uids()
        assert len(all_uids) == 1
        data = conn.uids(all_uids)[0]
        parsed = mime.from_string(data.body)
        expected_message_id = '<{}-{}@mailer.nylas.com>'.format(
            draft.public_id, draft.version)
        assert parsed.headers.get('Message-Id') == expected_message_id

        delete_draft(
            conn, default_account.id, draft.id, {
                'message_id_header': draft.message_id_header,
                'nylas_uid': draft.nylas_uid,
                'version': 5
            })

        conn.select_folder('Drafts', lambda *args: True)
        all_uids = conn.all_uids()
        assert len(all_uids) == 0
Example #18
0
def remote_delete_sent(account_id, message_id_header):
    with writable_connection_pool(account_id).get() as crispin_client:
        if 'sent' not in crispin_client.folder_names():
            log.info(
                'Account has no detected sent folder; not deleting message',
                account_id=account_id)
            return
        crispin_client.delete_sent_message(message_id_header)
Example #19
0
def remote_change_labels(account, message_id, db_session, removed_labels,
                         added_labels):
    uids_for_message = uids_by_folder(message_id, db_session)
    with writable_connection_pool(account.id).get() as crispin_client:
        for folder_name, uids in uids_for_message.items():
            crispin_client.select_folder(folder_name, uidvalidity_cb)
            crispin_client.conn.add_gmail_labels(uids, added_labels)
            crispin_client.conn.remove_gmail_labels(uids, removed_labels)
Example #20
0
def remote_delete_sent(account_id, message_id_header):
    with writable_connection_pool(account_id).get() as crispin_client:
        if 'sent' not in crispin_client.folder_names():
            log.info(
                'Account has no detected sent folder; not deleting message',
                account_id=account_id)
            return
        crispin_client.delete_sent_message(message_id_header)
Example #21
0
def remote_save_draft(account, folder_name, message, db_session, date=None):
    with writable_connection_pool(account.id).get() as crispin_client:
        # Create drafts folder on the backend if it doesn't exist.
        if 'drafts' not in crispin_client.folder_names():
            crispin_client.create_folder('Drafts')

        assert folder_name == crispin_client.folder_names()['drafts']
        crispin_client.select_folder(folder_name, uidvalidity_cb)
        crispin_client.save_draft(message, date)
Example #22
0
def remote_save_draft(account, message, db_session, date=None):
    with writable_connection_pool(account.id).get() as crispin_client:
        if 'drafts' not in crispin_client.folder_names():
            log.info('Account has no detected drafts folder; not saving draft',
                     account_id=account.id)
            return
        folder_name = crispin_client.folder_names()['drafts'][0]
        crispin_client.select_folder(folder_name, uidvalidity_cb)
        crispin_client.save_draft(message, date)
Example #23
0
def remote_save_draft(account, folder_name, message, db_session, date=None):
    with writable_connection_pool(account.id).get() as crispin_client:
        # Create drafts folder on the backend if it doesn't exist.
        if 'drafts' not in crispin_client.folder_names():
            crispin_client.create_folder('Drafts')

        assert folder_name == crispin_client.folder_names()['drafts']
        crispin_client.select_folder(folder_name, uidvalidity_cb)
        crispin_client.save_draft(message, date)
Example #24
0
def remote_save_sent(account, message, date=None):
    with writable_connection_pool(account.id).get() as crispin_client:
        if 'sent' not in crispin_client.folder_names():
            log.info('Account has no detected sent folder; not saving message',
                     account_id=account.id)
            return

        folder_name = crispin_client.folder_names()['sent'][0]
        crispin_client.select_folder(folder_name, uidvalidity_cb)
        crispin_client.create_message(message, date)
Example #25
0
def remote_save_sent(account, message, date=None):
    with writable_connection_pool(account.id).get() as crispin_client:
        if 'sent' not in crispin_client.folder_names():
            log.info('Account has no detected sent folder; not saving message',
                     account_id=account.id)
            return

        folder_name = crispin_client.folder_names()['sent'][0]
        crispin_client.select_folder(folder_name, uidvalidity_cb)
        crispin_client.create_message(message, date)
Example #26
0
def remote_move(account, message_id, db_session, destination):
    uids_for_message = uids_by_folder(message_id, db_session)
    if not uids_for_message:
        log.warning('No UIDs found for message', message_id=message_id)
        return

    with writable_connection_pool(account.id).get() as crispin_client:
        for folder_name, uids in uids_for_message.items():
            crispin_client.select_folder(folder_name, uidvalidity_cb)
            crispin_client.conn.copy(uids, destination)
            crispin_client.delete_uids(uids)
Example #27
0
def remote_delete_label(account, category_id, db_session):
    category = db_session.query(Category).get(category_id)
    with writable_connection_pool(account.id).get() as crispin_client:
        try:
            crispin_client.conn.delete_folder(category.display_name)
        except IMAP4.error:
            # Label has already been deleted on remote. Treat delete as
            # no-op.
            pass
    db_session.delete(category)
    db_session.commit()
def test_draft_updates(db, default_account, mock_imapclient):
    # Set up folder list
    mock_imapclient._data['Drafts'] = {}
    mock_imapclient._data['Trash'] = {}
    mock_imapclient.list_folders = lambda: [
        (('\\HasNoChildren', '\\Drafts'), '/', 'Drafts'),
        (('\\HasNoChildren', '\\Trash'), '/', 'Trash')
    ]

    pool = writable_connection_pool(default_account.id)

    draft = create_message_from_json({'subject': 'Test draft'},
                                     default_account.namespace, db.session,
                                     True)

    draft.is_draft = True
    draft.version = 0
    db.session.commit()
    save_draft(default_account.id, draft.id, {'version': 0})
    with pool.get() as conn:
        conn.select_folder('Drafts', lambda *args: True)
        assert len(conn.all_uids()) == 1

    # Check that draft is not resaved if already synced.
    update_draft(default_account.id, draft.id, {'version': 0})
    with pool.get() as conn:
        conn.select_folder('Drafts', lambda *args: True)
        assert len(conn.all_uids()) == 1

    # Check that an older version is deleted
    draft.version = 4
    sendmail_update_draft(db.session, default_account, draft,
                          from_addr=draft.from_addr, subject='New subject',
                          blocks=[])
    db.session.commit()

    update_draft(default_account.id, draft.id, {'version': 5})
    with pool.get() as conn:
        conn.select_folder('Drafts', lambda *args: True)
        all_uids = conn.all_uids()
        assert len(all_uids) == 1
        data = conn.uids(all_uids)[0]
        parsed = mime.from_string(data.body)
        expected_message_id = '<{}-{}@mailer.nylas.com>'.format(
            draft.public_id, draft.version)
        assert parsed.headers.get('Message-Id') == expected_message_id

    delete_draft(default_account.id, draft.id,
                 {'message_id_header': draft.message_id_header,
                  'inbox_uid': draft.inbox_uid, 'version': 5})
    with pool.get() as conn:
        conn.select_folder('Drafts', lambda *args: True)
        all_uids = conn.all_uids()
        assert len(all_uids) == 0
Example #29
0
def remote_move(account, message_id, db_session, destination):
    uids_for_message = uids_by_folder(message_id, db_session)
    if not uids_for_message:
        log.warning('No UIDs found for message', message_id=message_id)
        return

    with writable_connection_pool(account.id).get() as crispin_client:
        for folder_name, uids in uids_for_message.items():
            crispin_client.select_folder(folder_name, uidvalidity_cb)
            crispin_client.conn.copy(uids, destination)
            crispin_client.delete_uids(uids)
Example #30
0
def remote_delete_folder(account, category_id, db_session):
    category = db_session.query(Category).get(category_id)
    with writable_connection_pool(account.id).get() as crispin_client:
        try:
            crispin_client.conn.delete_folder(category.display_name)
        except IMAP4.error:
            # Folder has already been deleted on remote. Treat delete as
            # no-op.
            pass
    db_session.delete(category)
    db_session.commit()
Example #31
0
def remote_update_folder(account, category_id, db_session, old_name):
    category = db_session.query(Category).get(category_id)
    with writable_connection_pool(account.id).get() as crispin_client:
        if account.provider in ['generic', 'fastmail']:
            # Update the name of the folder to 'INBOX.whatever'.
            # We need to do this to keep track of the folder name
            # on the backend. The API abstracts this anyway.
            category.display_name = imap_folder_path(
                category.display_name,
                separator=crispin_client.folder_delimiter)

        crispin_client.conn.rename_folder(old_name, category.display_name)
Example #32
0
def test_change_labels(db, default_account, message, folder, mock_imapclient):
    mock_imapclient.add_folder_data(folder.name, {})
    mock_imapclient.add_gmail_labels = mock.Mock()
    mock_imapclient.remove_gmail_labels = mock.Mock()
    add_fake_imapuid(db.session, default_account.id, message, folder, 22)

    with writable_connection_pool(default_account.id).get() as crispin_client:
        change_labels(crispin_client, default_account.id, message.id,
                      {'removed_labels': ['\\Inbox'],
                       'added_labels': [u'motörhead', u'μετάνοια']})
        mock_imapclient.add_gmail_labels.assert_called_with(
            [22], ['mot&APY-rhead', '&A7wDtQPEA6wDvQO,A7kDsQ-'])
        mock_imapclient.remove_gmail_labels.assert_called_with([22], ['\\Inbox'])
Example #33
0
def _set_flag(account, message_id, flag_name, db_session, is_add):
    uids_for_message = uids_by_folder(message_id, db_session)
    if not uids_for_message:
        log.warning('No UIDs found for message', message_id=message_id)
        return

    with writable_connection_pool(account.id).get() as crispin_client:
        for folder_name, uids in uids_for_message.items():
            crispin_client.select_folder(folder_name, uidvalidity_cb)
            if is_add:
                crispin_client.conn.add_flags(uids, [flag_name])
            else:
                crispin_client.conn.remove_flags(uids, [flag_name])
Example #34
0
def remote_save_draft(account_id, message_id):
    with session_scope(account_id) as db_session:
        account = db_session.query(Account).get(account_id)
        message = db_session.query(Message).get(message_id)
        mimemsg = _create_email(account, message)

    with writable_connection_pool(account_id).get() as crispin_client:
        if "drafts" not in crispin_client.folder_names():
            log.info("Account has no detected drafts folder; not saving draft", account_id=account_id)
            return
        folder_name = crispin_client.folder_names()["drafts"][0]
        crispin_client.select_folder(folder_name, uidvalidity_cb)
        crispin_client.save_draft(mimemsg)
Example #35
0
def syncback_action(fn, account, folder_name, db_session):
    """ `folder_name` is a provider folder name, not a local tag

    `folder_name` is the folder which is selected before `fn` is called.

    """
    assert folder_name, "folder '{}' is not selectable".format(folder_name)

    with writable_connection_pool(account.id).get() as crispin_client:
        # NOTE: This starts a *new* IMAP session every time---we will want
        # to optimize this at some point. But for now, it's most correct.
        crispin_client.select_folder(folder_name, uidvalidity_cb)
        fn(account, db_session, crispin_client)
Example #36
0
def _set_flag(account, message_id, flag_name, db_session, is_add):
    uids_for_message = uids_by_folder(message_id, db_session)
    if not uids_for_message:
        log.warning('No UIDs found for message', message_id=message_id)
        return

    with writable_connection_pool(account.id).get() as crispin_client:
        for folder_name, uids in uids_for_message.items():
            crispin_client.select_folder(folder_name, uidvalidity_cb)
            if is_add:
                crispin_client.conn.add_flags(uids, [flag_name])
            else:
                crispin_client.conn.remove_flags(uids, [flag_name])
Example #37
0
def user_console(user_email_address):
    with global_session_scope() as db_session:
        result = (db_session.query(Account).filter_by(
            email_address=user_email_address).all())

        account = None

        if len(result) == 1:
            account = result[0]
        elif len(result) > 1:
            print("\n{} accounts found for that email.\n".format(len(result)))
            for idx, acc in enumerate(result):
                print("[{}] - {} {} {}".format(
                    idx,
                    acc.provider,
                    acc.namespace.email_address,
                    acc.namespace.public_id,
                ))
            choice = int(input("\nWhich # do you want to select? "))
            account = result[choice]

        if account is None:
            print(
                "No account found with email '{}'".format(user_email_address))
            return

        if account.provider == "eas":
            banner = """
        You can access the account instance with the 'account' variable.
        """
            IPython.embed(banner1=banner)
        else:
            with writable_connection_pool(account.id,
                                          pool_size=1).get() as crispin_client:
                if (account.provider == "gmail"
                        and "all" in crispin_client.folder_names()):
                    crispin_client.select_folder(
                        crispin_client.folder_names()["all"][0],
                        uidvalidity_cb)

                banner = """
        You can access the crispin instance with the 'crispin_client' variable,
        and the account instance with the 'account' variable.

        IMAPClient docs are at:

            http://imapclient.readthedocs.org/en/latest/#imapclient-class-reference
        """

                IPython.embed(banner1=banner)
Example #38
0
def remote_save_draft(account_id, message_id):
    with session_scope(account_id) as db_session:
        account = db_session.query(Account).get(account_id)
        message = db_session.query(Message).get(message_id)
        mimemsg = _create_email(account, message)

    with writable_connection_pool(account_id).get() as crispin_client:
        if 'drafts' not in crispin_client.folder_names():
            log.info('Account has no detected drafts folder; not saving draft',
                     account_id=account_id)
            return
        folder_name = crispin_client.folder_names()['drafts'][0]
        crispin_client.select_folder(folder_name, uidvalidity_cb)
        crispin_client.save_draft(mimemsg)
Example #39
0
def test_change_labels(db, default_account, message, folder, mock_imapclient):
    mock_imapclient.add_folder_data(folder.name, {})
    mock_imapclient.add_gmail_labels = mock.Mock()
    mock_imapclient.remove_gmail_labels = mock.Mock()
    add_fake_imapuid(db.session, default_account.id, message, folder, 22)

    with writable_connection_pool(default_account.id).get() as crispin_client:
        change_labels(crispin_client, default_account.id, message.id,
                      {'removed_labels': ['\\Inbox'],
                       'added_labels': [u'motörhead', u'μετάνοια']})
        mock_imapclient.add_gmail_labels.assert_called_with(
            [22], ['mot&APY-rhead', '&A7wDtQPEA6wDvQO,A7kDsQ-'], silent=True)
        mock_imapclient.remove_gmail_labels.assert_called_with([22], ['\\Inbox'],
                                                               silent=True)
Example #40
0
def set_remote_trash(account, thread_id, trash, db_session):
    with writable_connection_pool(account.id).get() as crispin_client:
        thread = db_session.query(ImapThread).filter_by(
            namespace_id=account.namespace.id,
            id=thread_id).options(load_only('g_thrid')).one()
        g_thrid = thread.g_thrid
        if trash:
            crispin_client.select_folder(account.all_folder.name,
                                         uidvalidity_cb)
            crispin_client.add_label(g_thrid, '\\Trash')
        else:
            crispin_client.select_folder(account.trash_folder.name,
                                         uidvalidity_cb)
            crispin_client.add_label(g_thrid, '\\Inbox')
Example #41
0
def set_remote_trash(account, thread_id, trash, db_session):
    with writable_connection_pool(account.id).get() as crispin_client:
        thread = db_session.query(ImapThread).filter_by(
            namespace_id=account.namespace.id,
            id=thread_id).options(load_only('g_thrid')).one()
        g_thrid = thread.g_thrid
        if trash:
            crispin_client.select_folder(account.all_folder.name,
                                         uidvalidity_cb)
            crispin_client.add_label(g_thrid, '\\Trash')
        else:
            crispin_client.select_folder(account.trash_folder.name,
                                         uidvalidity_cb)
            crispin_client.add_label(g_thrid, '\\Inbox')
Example #42
0
def syncback_action(fn, account, folder_name, db_session, select_folder=True):
    """ `folder_name` is a provider folder name, not a local tag

    `folder_name` is the folder which is selected before `fn` is called.

    """
    assert folder_name, "folder '{}' is not selectable".format(folder_name)

    # NOTE: This starts a *new* IMAP session every time---we will want
    # to optimize this at some point. But for now, it's most correct.
    with writable_connection_pool(account.id).get() as crispin_client:
        if select_folder:
            crispin_client.select_folder(folder_name, uidvalidity_cb)

        return fn(account, db_session, crispin_client)
Example #43
0
def remote_delete_folder(account_id, category_id):
    with session_scope(account_id) as db_session:
        category = db_session.query(Category).get(category_id)
        display_name = category.display_name
    with writable_connection_pool(account_id).get() as crispin_client:
        try:
            crispin_client.conn.delete_folder(display_name)
        except IMAP4.error:
            # Folder has already been deleted on remote. Treat delete as
            # no-op.
            pass

    with session_scope(account_id) as db_session:
        category = db_session.query(Category).get(category_id)
        db_session.delete(category)
        db_session.commit()
Example #44
0
def remote_create_folder(account, category_id, db_session):
    category = db_session.query(Category).get(category_id)

    with writable_connection_pool(account.id).get() as crispin_client:
        # Some generic IMAP providers have different conventions
        # regarding folder names. For example, Fastmail wants paths
        # to be of the form "INBOX.A". The API abstracts this.
        if account.provider in ['generic', 'fastmail']:
            # Update the name of the folder to 'INBOX.whatever'.
            # We need to do this to keep track of the folder name
            # on the backend. The API abstracts this anyway.
            category.display_name = imap_folder_path(
                category.display_name,
                separator=crispin_client.folder_delimiter)

        crispin_client.conn.create_folder(category.display_name)
Example #45
0
def user_console(user_email_address):
    with global_session_scope() as db_session:
        result = db_session.query(Account).filter_by(
            email_address=user_email_address).all()

        account = None

        if len(result) == 1:
            account = result[0]
        elif len(result) > 1:
            print "\n{} accounts found for that email.\n".format(len(result))
            for idx, acc in enumerate(result):
                print "[{}] - {} {} {}".format(idx, acc.provider,
                                               acc.namespace.email_address,
                                               acc.namespace.public_id)
            choice = int(raw_input("\nWhich # do you want to select? "))
            account = result[choice]

        if account is None:
            print "No account found with email '{}'".format(user_email_address)
            return

        if account.provider == 'eas':
            banner = """
        You can access the account instance with the 'account' variable.
        """
            IPython.embed(banner1=banner)
        else:
            with writable_connection_pool(account.id, pool_size=1).get()\
                    as crispin_client:
                if account.provider == 'gmail' \
                        and 'all' in crispin_client.folder_names():
                    crispin_client.select_folder(
                        crispin_client.folder_names()['all'][0],
                        uidvalidity_cb)

                banner = """
        You can access the crispin instance with the 'crispin_client' variable,
        and the account instance with the 'account' variable.

        IMAPClient docs are at:

            http://imapclient.readthedocs.org/en/latest/#imapclient-class-reference
        """

                IPython.embed(banner1=banner)
Example #46
0
def remote_save_sent(account_id, message_id):
    with session_scope(account_id) as db_session:
        account = db_session.query(Account).get(account_id)
        message = db_session.query(Message).get(message_id)
        if message is None:
            log.info("tried to create nonexistent message", message_id=message_id, account_id=account_id)
            return
        mimemsg = _create_email(account, message)

    with writable_connection_pool(account_id).get() as crispin_client:
        if "sent" not in crispin_client.folder_names():
            log.info("Account has no detected sent folder; not saving message", account_id=account_id)
            return

        folder_name = crispin_client.folder_names()["sent"][0]
        crispin_client.select_folder(folder_name, uidvalidity_cb)
        crispin_client.create_message(mimemsg)
Example #47
0
def test_folder_crud(db, default_account, mock_imapclient, obj_type):
    mock_imapclient.create_folder = mock.Mock()
    mock_imapclient.rename_folder = mock.Mock()
    mock_imapclient.delete_folder = mock.Mock()
    cat = add_fake_category(db.session, default_account.namespace.id,
                            "MyFolder")
    with writable_connection_pool(default_account.id).get() as crispin_client:
        if obj_type == "folder":
            create_folder(crispin_client, default_account.id, cat.id)
        else:
            create_label(crispin_client, default_account.id, cat.id)
        mock_imapclient.create_folder.assert_called_with("MyFolder")

        cat.display_name = "MyRenamedFolder"
        db.session.commit()
        if obj_type == "folder":
            update_folder(
                crispin_client,
                default_account.id,
                cat.id,
                {
                    "old_name": "MyFolder",
                    "new_name": "MyRenamedFolder"
                },
            )
        else:
            update_label(
                crispin_client,
                default_account.id,
                cat.id,
                {
                    "old_name": "MyFolder",
                    "new_name": "MyRenamedFolder"
                },
            )
        mock_imapclient.rename_folder.assert_called_with(
            "MyFolder", "MyRenamedFolder")

        category_id = cat.id
        if obj_type == "folder":
            delete_folder(crispin_client, default_account.id, cat.id)
        else:
            delete_label(crispin_client, default_account.id, cat.id)
    mock_imapclient.delete_folder.assert_called_with("MyRenamedFolder")
    db.session.commit()
    assert db.session.query(Category).get(category_id) is None
Example #48
0
def remote_delete_label(account_id, category_id):
    with session_scope(account_id) as db_session:
        category = db_session.query(Category).get(category_id)
        display_name = category.display_name

    with writable_connection_pool(account_id).get() as crispin_client:
        try:
            crispin_client.conn.delete_folder(display_name)
        except IMAP4.error:
            # Label has already been deleted on remote. Treat delete as
            # no-op.
            pass

    with session_scope(account_id) as db_session:
        category = db_session.query(Category).get(category_id)
        db_session.delete(category)
        db_session.commit()
Example #49
0
def remote_delete(account, thread_id, folder, db_session):
    """ We currently only allow this for Drafts. """

    uids = []

    thread = get_thread_uids(db_session, thread_id,
                             account.namespace.id)
    for msg in thread.messages:
        uids.extend([uid.msg_uid for uid in msg.imapuids])

    if not uids:
        return

    with writable_connection_pool(account.id).get() as crispin_client:
        crispin_client.select_folder(folder, uidvalidity_cb)

        if folder == crispin_client.folder_names()['drafts']:
            crispin_client.delete_uids(uids)
Example #50
0
def remote_delete(account, thread_id, folder, db_session):
    """ We currently only allow this for Drafts. """

    uids = []

    thread = get_thread_uids(db_session, thread_id,
                             account.namespace.id)
    for msg in thread.messages:
        uids.extend([uid.msg_uid for uid in msg.imapuids])

    if not uids:
        return

    with writable_connection_pool(account.id).get() as crispin_client:
        crispin_client.select_folder(folder, uidvalidity_cb)

        if folder == crispin_client.folder_names()['drafts']:
            crispin_client.delete_uids(uids)
Example #51
0
def remote_update_folder(account_id, category_id, old_name):
    with session_scope(account_id) as db_session:
        account_provider = db_session.query(Account).get(account_id).provider
        category = db_session.query(Category).get(category_id)
        display_name = category.display_name

    with writable_connection_pool(account_id).get() as crispin_client:
        if account_provider not in ['gmail', 'eas']:
            new_display_name = imap_folder_path(
                display_name, separator=crispin_client.folder_separator,
                prefix=crispin_client.folder_prefix)
        else:
            new_display_name = display_name
        crispin_client.conn.rename_folder(old_name, new_display_name)

    if new_display_name != display_name:
        with session_scope(account_id) as db_session:
            category = db_session.query(Category).get(category_id)
            category.display_name = new_display_name
Example #52
0
def remote_save_sent(account_id, message_id):
    with session_scope(account_id) as db_session:
        account = db_session.query(Account).get(account_id)
        message = db_session.query(Message).get(message_id)
        if message is None:
            log.info('tried to create nonexistent message',
                     message_id=message_id,
                     account_id=account_id)
            return
        mimemsg = _create_email(account, message)

    with writable_connection_pool(account_id).get() as crispin_client:
        if 'sent' not in crispin_client.folder_names():
            log.info('Account has no detected sent folder; not saving message',
                     account_id=account_id)
            return

        folder_name = crispin_client.folder_names()['sent'][0]
        crispin_client.select_folder(folder_name, uidvalidity_cb)
        crispin_client.create_message(mimemsg)
Example #53
0
def remote_update_folder(account_id, category_id, old_name):
    with session_scope(account_id) as db_session:
        account_provider = db_session.query(Account).get(account_id).provider
        category = db_session.query(Category).get(category_id)
        display_name = category.display_name

    with writable_connection_pool(account_id).get() as crispin_client:
        if account_provider in ["generic", "fastmail"]:
            # Update the name of the folder to 'INBOX.whatever'.
            # We need to do this to keep track of the folder name
            # on the backend. The API abstracts this anyway.
            new_display_name = imap_folder_path(display_name, separator=crispin_client.folder_delimiter)
        else:
            new_display_name = display_name
        crispin_client.conn.rename_folder(old_name, new_display_name)

    if new_display_name != display_name:
        with session_scope(account_id) as db_session:
            category = db_session.query(Category).get(category_id)
            category.display_name = new_display_name
Example #54
0
def remote_update_folder(account_id, category_id, old_name):
    with session_scope(account_id) as db_session:
        account_provider = db_session.query(Account).get(account_id).provider
        category = db_session.query(Category).get(category_id)
        display_name = category.display_name

    with writable_connection_pool(account_id).get() as crispin_client:
        if account_provider not in ['gmail', 'eas']:
            new_display_name = imap_folder_path(
                display_name,
                separator=crispin_client.folder_separator,
                prefix=crispin_client.folder_prefix)
        else:
            new_display_name = display_name
        crispin_client.conn.rename_folder(old_name, new_display_name)

    if new_display_name != display_name:
        with session_scope(account_id) as db_session:
            category = db_session.query(Category).get(category_id)
            category.display_name = new_display_name
Example #55
0
def test_change_flags(db, default_account, message, folder, mock_imapclient):
    mock_imapclient.add_folder_data(folder.name, {})
    mock_imapclient.add_flags = mock.Mock()
    mock_imapclient.remove_flags = mock.Mock()
    add_fake_imapuid(db.session, default_account.id, message, folder, 22)
    with writable_connection_pool(default_account.id).get() as crispin_client:
        mark_unread(crispin_client, default_account.id, message.id,
                    {'unread': False})
        mock_imapclient.add_flags.assert_called_with([22], ['\\Seen'], silent=True)

        mark_unread(crispin_client, default_account.id, message.id,
                    {'unread': True})
        mock_imapclient.remove_flags.assert_called_with([22], ['\\Seen'], silent=True)

        mark_starred(crispin_client, default_account.id, message.id,
                     {'starred': True})
        mock_imapclient.add_flags.assert_called_with([22], ['\\Flagged'], silent=True)

        mark_starred(crispin_client, default_account.id, message.id,
                     {'starred': False})
        mock_imapclient.remove_flags.assert_called_with([22], ['\\Flagged'], silent=True)
Example #56
0
def remote_update_folder(account_id, category_id, old_name):
    with session_scope(account_id) as db_session:
        account_provider = db_session.query(Account).get(account_id).provider
        category = db_session.query(Category).get(category_id)
        display_name = category.display_name

    with writable_connection_pool(account_id).get() as crispin_client:
        if account_provider in ['generic', 'fastmail']:
            # Update the name of the folder to 'INBOX.whatever'.
            # We need to do this to keep track of the folder name
            # on the backend. The API abstracts this anyway.
            new_display_name = imap_folder_path(
                display_name, separator=crispin_client.folder_delimiter)
        else:
            new_display_name = display_name
        crispin_client.conn.rename_folder(old_name, new_display_name)

    if new_display_name != display_name:
        with session_scope(account_id) as db_session:
            category = db_session.query(Category).get(category_id)
            category.display_name = new_display_name