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
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)
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()
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)
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)
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))
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
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)
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)
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)
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)
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
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
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
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)
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)
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)
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)
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)
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)
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
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()
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)
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'])
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])
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)
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)
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)
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)
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)
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')
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)
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()
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)
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)
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)
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
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()
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)
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
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)
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
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