def test_remote_save_draft(db, config, message): """ Tests the save_draft function, which saves the draft to the remote. """ from inbox.actions.backends.gmail import remote_save_draft from inbox.sendmail.base import _parse_recipients from inbox.sendmail.message import create_email, Recipients from inbox.models import Account account = db.session.query(Account).get(ACCOUNT_ID) to, subject, body = message to_addr = _parse_recipients(to) recipients = Recipients(to_addr, [], []) email = create_email(account.sender_name, account.email_address, None, recipients, subject, body, None) date = datetime.utcnow() remote_save_draft(account, account.drafts_folder.name, email.to_string(), db.session, date) with crispin_client(account.id, account.provider) as c: criteria = ['NOT DELETED', 'SUBJECT "{0}"'.format(subject)] c.conn.select_folder(account.drafts_folder.name, readonly=False) draft_uids = c.conn.search(criteria) assert draft_uids, 'Message missing from Drafts folder' flags = c.conn.get_flags(draft_uids) for uid in draft_uids: f = flags.get(uid) assert f and '\\Draft' in f, "Message missing '\\Draft' flag" c.conn.delete_messages(draft_uids) c.conn.expunge()
def send(self, draft): """ Turn a draft object into a MIME message and send it. Parameters ---------- draft : models.message.Message object the draft message to send. """ blocks = [p.block for p in draft.attachments] attachments = generate_attachments(blocks) # Note that we intentionally don't set the Bcc header in the message we # construct. msg = create_email(sender_name=self.sender_name, sender_email=self.email_address, inbox_uid=draft.inbox_uid, to_addr=draft.to_addr, cc_addr=draft.cc_addr, bcc_addr=None, subject=draft.subject, html=draft.sanitized_body, in_reply_to=draft.in_reply_to, references=draft.references, attachments=attachments) recipient_emails = [ email for name, email in itertools.chain( draft.to_addr, draft.cc_addr, draft.bcc_addr) ] return self._send(recipient_emails, msg)
def save_draft(account_id, message_id, db_session): """ Sync a new/updated draft back to the remote backend. """ account = db_session.query(Account).get(account_id) message = db_session.query(Message).get(message_id) if message is None: log.info('tried to save nonexistent message as draft', message_id=message_id, account_id=account_id) return if not message.is_draft: log.warning('tried to save non-draft message as draft', message_id=message_id, account_id=account_id) return recipients = Recipients(message.to_addr, message.cc_addr, message.bcc_addr) blocks = [p.block for p in message.attachments] attachments = generate_attachments(blocks) mimemsg = create_email(account.name, account.email_address, message.inbox_uid, recipients, message.subject, message.sanitized_body, attachments) if account.drafts_folder is None: # account has no detected drafts folder - create one. drafts_folder = Folder.find_or_create(db_session, account, 'Drafts', 'drafts') account.drafts_folder = drafts_folder remote_save_draft = module_registry[account.provider].remote_save_draft remote_save_draft(account, account.drafts_folder.name, mimemsg.to_string(), message.created_at)
def create_gmail_reply(sender_name, sender_email, in_reply_to, references, inbox_uid, recipients, subject, body, attachments=None): """ Create a Gmail email reply. """ mimemsg = create_email(sender_name, sender_email, inbox_uid, recipients, subject, body, attachments) # Add general reply headers: if in_reply_to: mimemsg.headers['In-Reply-To'] = in_reply_to if references: mimemsg.headers['References'] = references # Set the 'Subject' header of the reply, required for Gmail. # Gmail requires the same subject as the original (adding Re:/Fwd: is fine # though) to group messages in the same conversation, # See: https://support.google.com/mail/answer/5900?hl=en mimemsg.headers['Subject'] = REPLYSTR + subject return smtp_attrs(mimemsg)
def save_draft(account_id, message_id, db_session): """ Sync a new/updated draft back to the remote backend. """ account = db_session.query(Account).get(account_id) message = db_session.query(Message).get(message_id) if message is None: log.info('tried to save nonexistent message as draft', message_id=message_id, account_id=account_id) return if not message.is_draft: log.warning('tried to save non-draft message as draft', message_id=message_id, account_id=account_id) return recipients = Recipients(message.to_addr, message.cc_addr, message.bcc_addr) blocks = [p.block for p in message.attachments] attachments = generate_attachments(blocks) mimemsg = create_email(account.sender_name, account.email_address, message.inbox_uid, recipients, message.subject, message.sanitized_body, attachments) if account.drafts_folder is None: # account has no detected drafts folder - create one. drafts_folder = Folder.find_or_create(db_session, account, 'Drafts', 'drafts') account.drafts_folder = drafts_folder remote_save_draft = module_registry[account.provider].remote_save_draft remote_save_draft(account, account.drafts_folder.name, mimemsg.to_string(), message.created_at)
def send(self, draft): """ Turn a draft object into a MIME message and send it. Parameters ---------- draft : models.message.Message object the draft message to send. """ blocks = [p.block for p in draft.attachments] attachments = generate_attachments(blocks) # Note that we intentionally don't set the Bcc header in the message we # construct. msg = create_email(sender_name=self.sender_name, sender_email=self.email_address, inbox_uid=draft.inbox_uid, to_addr=draft.to_addr, cc_addr=draft.cc_addr, bcc_addr=None, subject=draft.subject, html=draft.sanitized_body, in_reply_to=draft.in_reply_to, references=draft.references, attachments=attachments) recipient_emails = [email for name, email in itertools.chain( draft.to_addr, draft.cc_addr, draft.bcc_addr)] return self._send(recipient_emails, msg)
def create_gmail_email(sender_name, sender_email, inbox_uid, recipients, subject, body, attachments=None): """ Create a Gmail email. """ mimemsg = create_email(sender_name, sender_email, inbox_uid, recipients, subject, body, attachments) return smtp_attrs(mimemsg)
def send(self, draft): """ Turn a draft object into a MIME message and send it. Parameters ---------- draft : models.message.Message object the draft message to send. """ blocks = [p.block for p in draft.attachments] attachments = generate_attachments(draft, blocks) # @emfree - 3/19/2015 # # Note that we intentionally don't set the Bcc header in the message we # construct, because this would would result in a MIME message being # generated with a Bcc header which all recipients can see. # # Arguably we should send each Bcc'ed recipient a MIME message that has # a Bcc: <only them> header. This is what the Gmail web UI appears to # do. However, this would need to be carefully implemented and tested. # The current approach was chosen for its comparative simplicity. I'm # pretty sure that other clients do it this way as well. It is the # first of the three implementations described here: # http://tools.ietf.org/html/rfc2822#section-3.6.3 # # Note that we ensure in our SMTP code BCCed recipients still actually # get the message. # from_addr is only ever a list with one element from_addr = draft.from_addr[0] msg = create_email( from_name=from_addr[0], from_email=from_addr[1], reply_to=draft.reply_to, nylas_uid=draft.nylas_uid, to_addr=draft.to_addr, cc_addr=draft.cc_addr, bcc_addr=None, subject=draft.subject, html=draft.body, in_reply_to=draft.in_reply_to, references=draft.references, attachments=attachments, ) recipient_emails = [ email for name, email in itertools.chain( draft.to_addr, draft.cc_addr, draft.bcc_addr ) ] self._send(recipient_emails, msg) # Sent to all successfully self.log.info("Sending successful", draft_id=draft.id)
def _create_email(account, message, generate_new_uid=False): recipients = Recipients(message.to_addr, message.cc_addr, message.bcc_addr) blocks = [p.block for p in message.attachments] attachments = generate_attachments(blocks) inbox_uid = message.inbox_uid if generate_new_uid: inbox_uid = None return create_email(account.name, account.email_address, inbox_uid, recipients, message.subject, message.sanitized_body, attachments)
def send(self, draft): """ Turn a draft object into a MIME message and send it. Parameters ---------- draft : models.message.Message object the draft message to send. """ blocks = [p.block for p in draft.attachments] attachments = generate_attachments(blocks) # @emfree - 3/19/2015 # # Note that we intentionally don't set the Bcc header in the message we # construct, because this would would result in a MIME message being # generated with a Bcc header which all recipients can see. # # Arguably we should send each Bcc'ed recipient a MIME message that has # a Bcc: <only them> header. This is what the Gmail web UI appears to # do. However, this would need to be carefully implemented and tested. # The current approach was chosen for its comparative simplicity. I'm # pretty sure that other clients do it this way as well. It is the # first of the three implementations described here: # http://tools.ietf.org/html/rfc2822#section-3.6.3 # # Note that we ensure in our SMTP code BCCed recipients still actually # get the message. # from_addr is only ever a list with one element from_addr = draft.from_addr[0] msg = create_email(from_name=from_addr[0], from_email=from_addr[1], reply_to=draft.reply_to, nylas_uid=draft.nylas_uid, to_addr=draft.to_addr, cc_addr=draft.cc_addr, bcc_addr=None, subject=draft.subject, html=draft.body, in_reply_to=draft.in_reply_to, references=draft.references, attachments=attachments) recipient_emails = [email for name, email in itertools.chain( draft.to_addr, draft.cc_addr, draft.bcc_addr)] self._send(recipient_emails, msg) # Sent to all successfully self.log.info('Sending successful', sender=from_addr[1], recipients=recipient_emails)
def save_draft(account_id, message_id, db_session): """ Sync a new/updated draft back to the remote backend. """ account = db_session.query(Account).get(account_id) message = db_session.query(Message).get(message_id) assert message.is_draft recipients = Recipients(message.to_addr, message.cc_addr, message.bcc_addr) attachments = generate_attachments(message.attachments) mimemsg = create_email(account.sender_name, account.email_address, message.inbox_uid, recipients, message.subject, message.sanitized_body, attachments) remote_save_draft = module_registry[account.provider].remote_save_draft remote_save_draft(account, account.drafts_folder.name, mimemsg.to_string(), message.created_at)
def _create_email(account, message): blocks = [p.block for p in message.attachments] attachments = generate_attachments(blocks) msg = create_email(sender_name=account.name, sender_email=account.email_address, inbox_uid=message.inbox_uid, to_addr=message.to_addr, cc_addr=message.cc_addr, bcc_addr=message.bcc_addr, subject=message.subject, html=message.sanitized_body, in_reply_to=message.in_reply_to, references=message.references, attachments=attachments) return msg
def test_remote_delete_draft(db, config, message): """ Tests the delete_draft function, which deletes the draft from the remote. """ from inbox.actions.backends.gmail import remote_delete_draft, remote_save_draft from inbox.models import Account from inbox.sendmail.base import _parse_recipients from inbox.sendmail.message import Recipients, create_email account = db.session.query(Account).get(ACCOUNT_ID) to, subject, body = message to_addr = _parse_recipients(to) recipients = Recipients(to_addr, [], []) email = create_email( account.sender_name, account.email_address, None, recipients, subject, body, None, ) date = datetime.utcnow() # Save on remote remote_save_draft(account, account.drafts_folder.name, email.to_string(), db.session, date) inbox_uid = email.headers["X-INBOX-ID"] with crispin_client(account.id, account.provider) as c: criteria = [ "DRAFT", "NOT DELETED", "HEADER X-INBOX-ID {0}".format(inbox_uid) ] c.conn.select_folder(account.drafts_folder.name, readonly=False) uids = c.conn.search(criteria) assert uids, "Message missing from Drafts folder" # Delete on remote remote_delete_draft(account, account.drafts_folder.name, inbox_uid, db.session) c.conn.select_folder(account.drafts_folder.name, readonly=False) uids = c.conn.search(criteria) assert not uids, "Message still in Drafts folder"
def create_gmail_reply(sender_name, sender_email, in_reply_to, references, inbox_uid, recipients, subject, body, attachments=None): """ Create a Gmail email reply. """ mimemsg = create_email(sender_name, sender_email, inbox_uid, recipients, subject, body, attachments) # Add general reply headers: mimemsg.headers['In-Reply-To'] = in_reply_to mimemsg.headers['References'] = references # Set the 'Subject' header of the reply, required for Gmail. # Gmail requires the same subject as the original (adding Re:/Fwd: is fine # though) to group messages in the same conversation, # See: https://support.google.com/mail/answer/5900?hl=en mimemsg.headers['Subject'] = REPLYSTR + subject return smtp_attrs(mimemsg)
def _create_email(account, message): blocks = [p.block for p in message.attachments] attachments = generate_attachments(blocks) from_name, from_email = message.from_addr[0] msg = create_email(from_name=from_name, from_email=from_email, reply_to=message.reply_to, inbox_uid=message.inbox_uid, to_addr=message.to_addr, cc_addr=message.cc_addr, bcc_addr=message.bcc_addr, subject=message.subject, html=message.body, in_reply_to=message.in_reply_to, references=message.references, attachments=attachments) return msg
def test_remote_delete_draft(db, config, message): """ Tests the delete_draft function, which deletes the draft from the remote. """ from inbox.actions.backends.gmail import (remote_save_draft, remote_delete_draft) from inbox.sendmail.base import _parse_recipients from inbox.sendmail.message import create_email, Recipients from inbox.models import Account account = db.session.query(Account).get(ACCOUNT_ID) to, subject, body = message to_addr = _parse_recipients(to) recipients = Recipients(to_addr, [], []) email = create_email(account.sender_name, account.email_address, None, recipients, subject, body, None) date = datetime.utcnow() # Save on remote remote_save_draft(account, account.drafts_folder.name, email.to_string(), db.session, date) inbox_uid = email.headers['X-INBOX-ID'] with crispin_client(account.id, account.provider) as c: criteria = ['DRAFT', 'NOT DELETED', 'HEADER X-INBOX-ID {0}'.format(inbox_uid)] c.conn.select_folder(account.drafts_folder.name, readonly=False) uids = c.conn.search(criteria) assert uids, 'Message missing from Drafts folder' # Delete on remote remote_delete_draft(account, account.drafts_folder.name, inbox_uid, db.session) c.conn.select_folder(account.drafts_folder.name, readonly=False) uids = c.conn.search(criteria) assert not uids, 'Message still in Drafts folder'
def send_custom(self, draft, body, recipients): """ Turn a draft object into a MIME message, replacing the body with the provided body, and send it only to the provided recipients. Parameters ---------- draft: models.message.Message object the draft message to send. body: string message body to send in place of the existing body attribute in the draft. recipient_emails: email addresses to send copies of this message to. """ blocks = [p.block for p in draft.attachments] attachments = generate_attachments(blocks) from_addr = draft.from_addr[0] msg = create_email(from_name=from_addr[0], from_email=from_addr[1], reply_to=draft.reply_to, inbox_uid=draft.inbox_uid, to_addr=draft.to_addr, cc_addr=draft.cc_addr, bcc_addr=None, subject=draft.subject, html=body, in_reply_to=draft.in_reply_to, references=draft.references, attachments=attachments) recipient_emails = [email for name, email in recipients] self._send(recipient_emails, msg) # Sent successfully self.log.info('Sending successful', sender=from_addr[1], recipients=recipient_emails)
def save_draft(account_id, message_id, db_session): """ Sync a new/updated draft back to the remote backend. """ account = db_session.query(Account).get(account_id) message = db_session.query(Message).get(message_id) assert message.is_draft recipients = Recipients(message.to_addr, message.cc_addr, message.bcc_addr) attachments = generate_attachments(message.attachments) mimemsg = create_email(account.sender_name, account.email_address, message.inbox_uid, recipients, message.subject, message.sanitized_body, attachments) remote_save_draft = module_registry[account.provider].remote_save_draft remote_save_draft(account, account.drafts_folder.name, mimemsg.to_string(), message.created_at) # If this draft is created by an update_draft() call, # delete the one it supercedes on the remote. # Needed because our update_draft() creates a new draft # message but the user expects to see the one # updated draft only. if message.parent_draft: return delete_draft(account_id, message.parent_draft.id, db_session)
def send_custom(self, draft, body, recipients): """ Turn a draft object into a MIME message, replacing the body with the provided body, and send it only to the provided recipients. Parameters ---------- draft: models.message.Message object the draft message to send. body: string message body to send in place of the existing body attribute in the draft. recipient_emails: email addresses to send copies of this message to. """ blocks = [p.block for p in draft.attachments] attachments = generate_attachments(blocks) from_addr = draft.from_addr[0] msg = create_email(from_name=from_addr[0], from_email=from_addr[1], reply_to=draft.reply_to, nylas_uid=draft.nylas_uid, to_addr=draft.to_addr, cc_addr=draft.cc_addr, bcc_addr=None, subject=draft.subject, html=body, in_reply_to=draft.in_reply_to, references=draft.references, attachments=attachments) recipient_emails = [email for name, email in recipients] self._send(recipient_emails, msg) # Sent successfully self.log.info('Sending successful', sender=from_addr[1], recipients=recipient_emails)
def save_draft(account_id, message_id): """ Sync a new/updated draft back to the remote backend. """ with session_scope() as db_session: account = db_session.query(Account).get(account_id) message = db_session.query(SpoolMessage).get(message_id) recipients = Recipients(message.to_addr, message.cc_addr, message.bcc_addr) attachments = generate_attachments(message.attachments) mimemsg = create_email(account.sender_name, account.email_address, message.inbox_uid, recipients, message.subject, message.sanitized_body, attachments) remote_save_draft = module_registry[account.provider].remote_save_draft remote_save_draft(account, account.drafts_folder.name, mimemsg.to_string(), message.created_date) # If this draft is created by an update_draft() call, # delete the one it supercedes on the remote. # Needed because our update_draft() creates a new draft # message but the user expects to see the one # updated draft only. if message.parent_draft: return delete_draft(account_id, message.parent_draft.id)