From a3af87094198c6d70787d0126f8a2de0fcf0e071 Mon Sep 17 00:00:00 2001 From: Bruno Wagner Date: Wed, 15 Oct 2014 16:08:47 +0200 Subject: Changed pixelated mail file to mail, because it has more than pixelated mail --- service/pixelated/adapter/mail.py | 274 +++++++++++++++++++++ service/pixelated/adapter/pixelated_mail.py | 274 --------------------- service/pixelated/adapter/soledad_querier.py | 2 +- service/pixelated/controllers/mails_controller.py | 2 +- service/pixelated/user_agent.py | 2 +- service/test/adapter/draft_service_test.py | 2 +- service/test/support/integration_helper.py | 2 +- service/test/support/test_helper.py | 2 +- .../unit/adapter/pixelated_mail_sender_test.py | 2 +- service/test/unit/adapter/pixelated_mail_test.py | 2 +- .../test/unit/adapter/pixelated_mailbox_test.py | 2 +- 11 files changed, 283 insertions(+), 283 deletions(-) create mode 100644 service/pixelated/adapter/mail.py delete mode 100644 service/pixelated/adapter/pixelated_mail.py (limited to 'service') diff --git a/service/pixelated/adapter/mail.py b/service/pixelated/adapter/mail.py new file mode 100644 index 00000000..cd345f13 --- /dev/null +++ b/service/pixelated/adapter/mail.py @@ -0,0 +1,274 @@ +# +# Copyright (c) 2014 ThoughtWorks, Inc. +# +# Pixelated is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pixelated is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PCULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with Pixelated. If not, see . +import json + +from leap.mail.imap.fields import fields +import leap.mail.walk as walk +import dateutil.parser as dateparser +from pixelated.adapter.status import Status +import pixelated.support.date +from email.MIMEMultipart import MIMEMultipart +from email.MIMEText import MIMEText +from pycryptopp.hash import sha256 + + +class Mail: + + @property + def to(self): + return self.headers['To'] + + @property + def cc(self): + return self.headers['Cc'] + + @property + def bcc(self): + return self.headers['Bcc'] + + @property + def date(self): + return self.headers['Date'] + + def as_dict(self): + return { + 'header': {k.lower(): v for k, v in self.headers.items()}, + 'ident': self.ident, + 'tags': list(self.tags), + 'status': list(self.status), + 'security_casing': {}, + 'body': self.body + } + + +class InputMail(Mail): + FROM_EMAIL_ADDRESS = None + + def __init__(self): + self._raw_message = None + self._fd = None + self._hd = None + self._bd = None + self._mime = None + self._chash = None + + @property + def _mime_multipart(self): + if self._mime: + return self._mime + mime = MIMEMultipart() + for key, value in self.headers.items(): + mime[str(key)] = str(value) + mime.attach(MIMEText(self.body, 'plain')) + self._mime = mime + return mime + + @property + def ident(self): + return self._get_chash() + + def _raw(self): + if not self._raw_message: + self._raw_message = self._mime_multipart.as_string() + return self._raw_message + + def _get_chash(self): + if not self._chash: + self._chash = sha256.SHA256(self._raw()).hexdigest() + return self._chash + + def _get_for_save(self, next_uid, mailbox): + docs = [self._fdoc(next_uid, mailbox), self._hdoc()] + docs.extend([m for m in self._cdocs()]) + return docs + + def _fdoc(self, next_uid, mailbox): + if self._fd: + return self._fd + + fd = {} + fd[fields.MBOX_KEY] = mailbox + fd[fields.UID_KEY] = next_uid + fd[fields.CONTENT_HASH_KEY] = self._get_chash() + fd[fields.SIZE_KEY] = len(self._raw()) + fd[fields.MULTIPART_KEY] = True + fd[fields.RECENT_KEY] = True + fd[fields.TYPE_KEY] = fields.TYPE_FLAGS_VAL + fd[fields.FLAGS_KEY] = Status.to_flags(self.status) + self._fd = fd + return fd + + def _get_body_phash(self): + return walk.get_body_phash_multi(walk.get_payloads(self._mime_multipart)) + + def _hdoc(self): + if self._hd: + return self._hd + + hd = {} + hd[fields.HEADERS_KEY] = self.headers + hd[fields.DATE_KEY] = self.headers['Date'] + hd[fields.CONTENT_HASH_KEY] = self._get_chash() + hd[fields.MSGID_KEY] = '' + hd[fields.MULTIPART_KEY] = True + hd[fields.SUBJECT_KEY] = self.headers.get('Subject') + hd[fields.TYPE_KEY] = fields.TYPE_HEADERS_VAL + hd[fields.BODY_KEY] = self._get_body_phash() + hd[fields.PARTS_MAP_KEY] = walk.walk_msg_tree(walk.get_parts(self._mime_multipart), body_phash=self._get_body_phash()) + + self._hd = hd + return hd + + def _cdocs(self): + return walk.get_raw_docs(self._mime_multipart, self._mime_multipart.walk()) + + def to_mime_multipart(self): + mime_multipart = MIMEMultipart() + + for header in ['To', 'Cc', 'Bcc']: + if self.headers[header]: + mime_multipart[header] = ", ".join(self.headers[header]) + + if self.headers['Subject']: + mime_multipart['Subject'] = self.headers['Subject'] + + mime_multipart['Date'] = self.headers['Date'] + mime_multipart.attach(MIMEText(self.body, 'plain')) + return mime_multipart + + def to_smtp_format(self): + mime_multipart = self.to_mime_multipart() + mime_multipart['From'] = InputMail.FROM_EMAIL_ADDRESS + return mime_multipart.as_string() + + @staticmethod + def from_dict(mail_dict): + input_mail = InputMail() + input_mail.headers = {key.capitalize(): value for key, value in mail_dict.get('header', {}).items()} + input_mail.headers['Date'] = pixelated.support.date.iso_now() + input_mail.body = mail_dict.get('body', '') + input_mail.tags = set(mail_dict.get('tags', [])) + input_mail.status = set(mail_dict.get('status', [])) + return input_mail + + +class PixelatedMail(Mail): + + @staticmethod + def from_soledad(fdoc, hdoc, bdoc, soledad_querier=None): + mail = PixelatedMail() + mail.bdoc = bdoc + mail.fdoc = fdoc + mail.hdoc = hdoc + mail.querier = soledad_querier + return mail + + @property + def body(self): + return self.bdoc.content['raw'] + + @property + def headers(self): + _headers = {} + + for header in ['To', 'Cc', 'Bcc']: + header_value = self.hdoc.content['headers'].get(header) + if not header_value: + continue + _headers[header] = header_value if type(header_value) is list else header_value.split(', ') + + for header in ['From', 'Subject']: + _headers[header] = self.hdoc.content['headers'].get(header) + + _headers['Date'] = self._get_date() + + return _headers + + def _get_date(self): + date = self.hdoc.content.get('date', None) + if not date: + date = self.hdoc.content['received'].split(";")[-1].strip() + return dateparser.parse(date).isoformat() + + @property + def status(self): + return Status.from_flags(self.flags) + + @property + def flags(self): + return self.fdoc.content.get('flags') + + @property + def security_casing(self): + return {} + + @property + def tags(self): + _tags = self.hdoc.content['headers'].get('X-Tags', '[]') + return set(_tags) if type(_tags) is list or type(_tags) is set else set(json.loads(_tags)) + + @property + def ident(self): + return self.fdoc.content.get('chash') + + @property + def mailbox_name(self): + return self.fdoc.content.get('mbox') + + @property + def is_recent(self): + return Status('recent') in self.status + + @property + def uid(self): + return self.fdoc.content['uid'] + + def save(self): + return self.querier.save_mail(self) + + def set_mailbox(self, mailbox_name): + self.fdoc.content['mbox'] = mailbox_name + + def remove_all_tags(self): + self.update_tags(set([])) + + def update_tags(self, tags): + self._persist_mail_tags(tags) + return self.tags + + def mark_as_read(self): + self.fdoc.content['flags'].append(Status.SEEN) + self.save() + return self + + def mark_as_unread(self): + if Status.SEEN in self.fdoc.content['flags']: + self.fdoc.content['flags'].remove(Status.SEEN) + self.save() + return self + + def mark_as_not_recent(self): + if Status.RECENT in self.fdoc.content['flags']: + self.fdoc.content['flags'].remove(Status.RECENT) + self.save() + return self + + def _persist_mail_tags(self, current_tags): + self.hdoc.content['headers']['X-Tags'] = json.dumps(list(current_tags)) + self.save() + + def has_tag(self, tag): + return tag in self.tags diff --git a/service/pixelated/adapter/pixelated_mail.py b/service/pixelated/adapter/pixelated_mail.py deleted file mode 100644 index cd345f13..00000000 --- a/service/pixelated/adapter/pixelated_mail.py +++ /dev/null @@ -1,274 +0,0 @@ -# -# Copyright (c) 2014 ThoughtWorks, Inc. -# -# Pixelated is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Pixelated is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PCULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with Pixelated. If not, see . -import json - -from leap.mail.imap.fields import fields -import leap.mail.walk as walk -import dateutil.parser as dateparser -from pixelated.adapter.status import Status -import pixelated.support.date -from email.MIMEMultipart import MIMEMultipart -from email.MIMEText import MIMEText -from pycryptopp.hash import sha256 - - -class Mail: - - @property - def to(self): - return self.headers['To'] - - @property - def cc(self): - return self.headers['Cc'] - - @property - def bcc(self): - return self.headers['Bcc'] - - @property - def date(self): - return self.headers['Date'] - - def as_dict(self): - return { - 'header': {k.lower(): v for k, v in self.headers.items()}, - 'ident': self.ident, - 'tags': list(self.tags), - 'status': list(self.status), - 'security_casing': {}, - 'body': self.body - } - - -class InputMail(Mail): - FROM_EMAIL_ADDRESS = None - - def __init__(self): - self._raw_message = None - self._fd = None - self._hd = None - self._bd = None - self._mime = None - self._chash = None - - @property - def _mime_multipart(self): - if self._mime: - return self._mime - mime = MIMEMultipart() - for key, value in self.headers.items(): - mime[str(key)] = str(value) - mime.attach(MIMEText(self.body, 'plain')) - self._mime = mime - return mime - - @property - def ident(self): - return self._get_chash() - - def _raw(self): - if not self._raw_message: - self._raw_message = self._mime_multipart.as_string() - return self._raw_message - - def _get_chash(self): - if not self._chash: - self._chash = sha256.SHA256(self._raw()).hexdigest() - return self._chash - - def _get_for_save(self, next_uid, mailbox): - docs = [self._fdoc(next_uid, mailbox), self._hdoc()] - docs.extend([m for m in self._cdocs()]) - return docs - - def _fdoc(self, next_uid, mailbox): - if self._fd: - return self._fd - - fd = {} - fd[fields.MBOX_KEY] = mailbox - fd[fields.UID_KEY] = next_uid - fd[fields.CONTENT_HASH_KEY] = self._get_chash() - fd[fields.SIZE_KEY] = len(self._raw()) - fd[fields.MULTIPART_KEY] = True - fd[fields.RECENT_KEY] = True - fd[fields.TYPE_KEY] = fields.TYPE_FLAGS_VAL - fd[fields.FLAGS_KEY] = Status.to_flags(self.status) - self._fd = fd - return fd - - def _get_body_phash(self): - return walk.get_body_phash_multi(walk.get_payloads(self._mime_multipart)) - - def _hdoc(self): - if self._hd: - return self._hd - - hd = {} - hd[fields.HEADERS_KEY] = self.headers - hd[fields.DATE_KEY] = self.headers['Date'] - hd[fields.CONTENT_HASH_KEY] = self._get_chash() - hd[fields.MSGID_KEY] = '' - hd[fields.MULTIPART_KEY] = True - hd[fields.SUBJECT_KEY] = self.headers.get('Subject') - hd[fields.TYPE_KEY] = fields.TYPE_HEADERS_VAL - hd[fields.BODY_KEY] = self._get_body_phash() - hd[fields.PARTS_MAP_KEY] = walk.walk_msg_tree(walk.get_parts(self._mime_multipart), body_phash=self._get_body_phash()) - - self._hd = hd - return hd - - def _cdocs(self): - return walk.get_raw_docs(self._mime_multipart, self._mime_multipart.walk()) - - def to_mime_multipart(self): - mime_multipart = MIMEMultipart() - - for header in ['To', 'Cc', 'Bcc']: - if self.headers[header]: - mime_multipart[header] = ", ".join(self.headers[header]) - - if self.headers['Subject']: - mime_multipart['Subject'] = self.headers['Subject'] - - mime_multipart['Date'] = self.headers['Date'] - mime_multipart.attach(MIMEText(self.body, 'plain')) - return mime_multipart - - def to_smtp_format(self): - mime_multipart = self.to_mime_multipart() - mime_multipart['From'] = InputMail.FROM_EMAIL_ADDRESS - return mime_multipart.as_string() - - @staticmethod - def from_dict(mail_dict): - input_mail = InputMail() - input_mail.headers = {key.capitalize(): value for key, value in mail_dict.get('header', {}).items()} - input_mail.headers['Date'] = pixelated.support.date.iso_now() - input_mail.body = mail_dict.get('body', '') - input_mail.tags = set(mail_dict.get('tags', [])) - input_mail.status = set(mail_dict.get('status', [])) - return input_mail - - -class PixelatedMail(Mail): - - @staticmethod - def from_soledad(fdoc, hdoc, bdoc, soledad_querier=None): - mail = PixelatedMail() - mail.bdoc = bdoc - mail.fdoc = fdoc - mail.hdoc = hdoc - mail.querier = soledad_querier - return mail - - @property - def body(self): - return self.bdoc.content['raw'] - - @property - def headers(self): - _headers = {} - - for header in ['To', 'Cc', 'Bcc']: - header_value = self.hdoc.content['headers'].get(header) - if not header_value: - continue - _headers[header] = header_value if type(header_value) is list else header_value.split(', ') - - for header in ['From', 'Subject']: - _headers[header] = self.hdoc.content['headers'].get(header) - - _headers['Date'] = self._get_date() - - return _headers - - def _get_date(self): - date = self.hdoc.content.get('date', None) - if not date: - date = self.hdoc.content['received'].split(";")[-1].strip() - return dateparser.parse(date).isoformat() - - @property - def status(self): - return Status.from_flags(self.flags) - - @property - def flags(self): - return self.fdoc.content.get('flags') - - @property - def security_casing(self): - return {} - - @property - def tags(self): - _tags = self.hdoc.content['headers'].get('X-Tags', '[]') - return set(_tags) if type(_tags) is list or type(_tags) is set else set(json.loads(_tags)) - - @property - def ident(self): - return self.fdoc.content.get('chash') - - @property - def mailbox_name(self): - return self.fdoc.content.get('mbox') - - @property - def is_recent(self): - return Status('recent') in self.status - - @property - def uid(self): - return self.fdoc.content['uid'] - - def save(self): - return self.querier.save_mail(self) - - def set_mailbox(self, mailbox_name): - self.fdoc.content['mbox'] = mailbox_name - - def remove_all_tags(self): - self.update_tags(set([])) - - def update_tags(self, tags): - self._persist_mail_tags(tags) - return self.tags - - def mark_as_read(self): - self.fdoc.content['flags'].append(Status.SEEN) - self.save() - return self - - def mark_as_unread(self): - if Status.SEEN in self.fdoc.content['flags']: - self.fdoc.content['flags'].remove(Status.SEEN) - self.save() - return self - - def mark_as_not_recent(self): - if Status.RECENT in self.fdoc.content['flags']: - self.fdoc.content['flags'].remove(Status.RECENT) - self.save() - return self - - def _persist_mail_tags(self, current_tags): - self.hdoc.content['headers']['X-Tags'] = json.dumps(list(current_tags)) - self.save() - - def has_tag(self, tag): - return tag in self.tags diff --git a/service/pixelated/adapter/soledad_querier.py b/service/pixelated/adapter/soledad_querier.py index f121a463..318e1539 100644 --- a/service/pixelated/adapter/soledad_querier.py +++ b/service/pixelated/adapter/soledad_querier.py @@ -13,7 +13,7 @@ # # You should have received a copy of the GNU Affero General Public License # along with Pixelated. If not, see . -from pixelated.adapter.pixelated_mail import PixelatedMail +from pixelated.adapter.mail import PixelatedMail class SoledadQuerier: diff --git a/service/pixelated/controllers/mails_controller.py b/service/pixelated/controllers/mails_controller.py index f54fa959..6765fc78 100644 --- a/service/pixelated/controllers/mails_controller.py +++ b/service/pixelated/controllers/mails_controller.py @@ -15,7 +15,7 @@ # along with Pixelated. If not, see . import json -from pixelated.adapter.pixelated_mail import InputMail +from pixelated.adapter.mail import InputMail from pixelated.controllers import respond_json from flask import request import dateutil.parser as dateparser diff --git a/service/pixelated/user_agent.py b/service/pixelated/user_agent.py index 687433d7..d8c3d4a6 100644 --- a/service/pixelated/user_agent.py +++ b/service/pixelated/user_agent.py @@ -28,7 +28,7 @@ from pixelated.bitmask_libraries.config import LeapConfig from pixelated.bitmask_libraries.provider import LeapProvider from pixelated.bitmask_libraries.auth import LeapAuthenticator, LeapCredentials from pixelated.adapter.mail_service import MailService -from pixelated.adapter.pixelated_mail import InputMail +from pixelated.adapter.mail import InputMail from pixelated.adapter.soledad_querier import SoledadQuerier from pixelated.adapter.search import SearchEngine from pixelated.adapter.draft_service import DraftService diff --git a/service/test/adapter/draft_service_test.py b/service/test/adapter/draft_service_test.py index 99b1e3b8..cdba2acc 100644 --- a/service/test/adapter/draft_service_test.py +++ b/service/test/adapter/draft_service_test.py @@ -1,6 +1,6 @@ import unittest -from pixelated.adapter.pixelated_mail import InputMail +from pixelated.adapter.mail import InputMail from pixelated.adapter.draft_service import DraftService import test_helper from mockito import * diff --git a/service/test/support/integration_helper.py b/service/test/support/integration_helper.py index 54252f26..0d5d1125 100644 --- a/service/test/support/integration_helper.py +++ b/service/test/support/integration_helper.py @@ -24,7 +24,7 @@ from pixelated.adapter.search import SearchEngine from pixelated.adapter.tag_service import TagService from pixelated.adapter.draft_service import DraftService import pixelated.user_agent -from pixelated.adapter.pixelated_mail import PixelatedMail, InputMail +from pixelated.adapter.mail import PixelatedMail, InputMail from pixelated.adapter.pixelated_mailboxes import PixelatedMailBoxes from pixelated.adapter.soledad_querier import SoledadQuerier from pixelated.controllers import * diff --git a/service/test/support/test_helper.py b/service/test/support/test_helper.py index 4ea4afb5..881e1e5c 100644 --- a/service/test/support/test_helper.py +++ b/service/test/support/test_helper.py @@ -15,7 +15,7 @@ # along with Pixelated. If not, see . from datetime import datetime -from pixelated.adapter.pixelated_mail import InputMail +from pixelated.adapter.mail import InputMail LEAP_FLAGS = ['\\Seen', diff --git a/service/test/unit/adapter/pixelated_mail_sender_test.py b/service/test/unit/adapter/pixelated_mail_sender_test.py index 207baadb..01de1cb0 100644 --- a/service/test/unit/adapter/pixelated_mail_sender_test.py +++ b/service/test/unit/adapter/pixelated_mail_sender_test.py @@ -15,7 +15,7 @@ # along with Pixelated. If not, see . import unittest -from pixelated.adapter.pixelated_mail import PixelatedMail +from pixelated.adapter.mail import PixelatedMail from pixelated.adapter.pixelated_mail_sender import PixelatedMailSender from mockito import * from test.support import test_helper diff --git a/service/test/unit/adapter/pixelated_mail_test.py b/service/test/unit/adapter/pixelated_mail_test.py index 63995e0e..20038e96 100644 --- a/service/test/unit/adapter/pixelated_mail_test.py +++ b/service/test/unit/adapter/pixelated_mail_test.py @@ -16,7 +16,7 @@ import unittest import pixelated.support.date -from pixelated.adapter.pixelated_mail import PixelatedMail, InputMail +from pixelated.adapter.mail import PixelatedMail, InputMail from mockito import * from test.support import test_helper diff --git a/service/test/unit/adapter/pixelated_mailbox_test.py b/service/test/unit/adapter/pixelated_mailbox_test.py index 060bbc43..0a007ba6 100644 --- a/service/test/unit/adapter/pixelated_mailbox_test.py +++ b/service/test/unit/adapter/pixelated_mailbox_test.py @@ -15,7 +15,7 @@ # along with Pixelated. If not, see . import unittest -from pixelated.adapter.pixelated_mail import PixelatedMail +from pixelated.adapter.mail import PixelatedMail from pixelated.adapter.pixelated_mailbox import PixelatedMailbox from mockito import * from test.support import test_helper -- cgit v1.2.3