diff options
author | Duda Dornelles <ddornell@thoughtworks.com> | 2014-12-22 09:35:01 -0200 |
---|---|---|
committer | Duda Dornelles <ddornell@thoughtworks.com> | 2014-12-22 09:38:08 -0200 |
commit | 66839f191708a0725c8d9841d5266ad13af8ee81 (patch) | |
tree | 337fb19e99625cc3243f998a2351cea123c037a2 /service/pixelated/adapter/mail.py | |
parent | a0cbaf84fd86fc91cba892a7a19b0ade7c017a3c (diff) |
refactoring package structure
Diffstat (limited to 'service/pixelated/adapter/mail.py')
-rw-r--r-- | service/pixelated/adapter/mail.py | 361 |
1 files changed, 0 insertions, 361 deletions
diff --git a/service/pixelated/adapter/mail.py b/service/pixelated/adapter/mail.py deleted file mode 100644 index b6b566bb..00000000 --- a/service/pixelated/adapter/mail.py +++ /dev/null @@ -1,361 +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 <http://www.gnu.org/licenses/>. -import json -from uuid import uuid4 -from email.mime.text import MIMEText - -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 pycryptopp.hash import sha256 -import re -import base64 - - -class Mail(object): - @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'] - - @property - def status(self): - return Status.from_flags(self.flags) - - @property - def flags(self): - return self.fdoc.content.get('flags') - - @property - def mailbox_name(self): - return self.fdoc.content.get('mbox') - - @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._charset())) - self._mime = mime - return mime - - def _charset(self): - if 'content_type' in self.headers and 'charset' in self.headers['content_type']: - return re.compile('.*charset=(.*)').match(self.headers['content_type']).group(1) - else: - return 'utf-8' - - @property - def raw(self): - return self._mime_multipart.as_string() - - def _get_chash(self): - return sha256.SHA256(self.raw).hexdigest() - - -class InputMail(Mail): - FROM_EMAIL_ADDRESS = None - - def __init__(self): - self._raw_message = None - self._fd = None - self._hd = None - self._bd = None - self._chash = None - self._mime = None - - @property - def ident(self): - return self._get_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())['part_map'] - - 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'] - if type(self.body) is list: - for part in self.body: - mime_multipart.attach(MIMEText(part['raw'], part['content-type'])) - else: - mime_multipart.attach(MIMEText(self.body, 'plain', 'utf-8')) - 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()} - - # XXX this is overriding the property in PixelatedMail - input_mail.headers['Date'] = pixelated.support.date.iso_now() - - # XXX this is overriding the property in PixelatedMail - input_mail.body = mail_dict.get('body', '') - - # XXX this is overriding the property in the PixelatedMail - 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, parts=None, soledad_querier=None): - mail = PixelatedMail() - mail.parts = parts - mail.boundary = str(uuid4()).replace('-', '') - mail.bdoc = bdoc - mail.fdoc = fdoc - mail.hdoc = hdoc - mail.querier = soledad_querier - mail._mime = None - return mail - - @property - def body(self): - if self.parts and len(self.parts['alternatives']) > 1: - body = '' - for alternative in self.parts['alternatives']: - body += '--' + self.boundary + '\n' - for header, value in alternative['headers'].items(): - body += '%s: %s\n' % (header, value) - body += '\n' - body += alternative['content'] - body += '\n' - body += '--' + self.boundary + '--' - return body - else: - if self.parts and self.parts['alternatives'][0]['headers'].get('Content-Transfer-Encoding', '') == 'base64': - return unicode(base64.b64decode(self.parts['alternatives'][0]['content']), 'utf-8') - else: - return self.bdoc.content['raw'] - - @property - def headers(self): - _headers = { - 'To': [], - 'Cc': [], - 'Bcc': [] - } - hdoc_headers = self.hdoc.content['headers'] - - for header in ['To', 'Cc', 'Bcc']: - header_value = hdoc_headers.get(header) - if not header_value: - continue - _headers[header] = header_value if type(header_value) is list else header_value.split(',') - _headers[header] = map(lambda x: x.strip(), _headers[header]) - - for header in ['From', 'Subject']: - _headers[header] = hdoc_headers.get(header) - - _headers['Date'] = self._get_date() - - if self.parts and len(self.parts['alternatives']) > 1: - _headers['content_type'] = 'multipart/alternative; boundary="%s"' % self.boundary - elif self.hdoc.content['headers'].get('Content-Type'): - _headers['content_type'] = hdoc_headers.get('Content-Type') - - if hdoc_headers.get('Reply-To'): - _headers['Reply-To'] = hdoc_headers.get('Reply-To') - - 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 security_casing(self): - casing = {"imprints": [], "locks": []} - if self.signed: - casing["imprints"].append({"state": "valid", "seal": {"validity": "valid"}}) - elif self.signed is None: - casing["imprints"].append({"state": "no_signature_information"}) - - if self.encrypted: - casing["locks"].append({"state": "valid"}) - - return casing - - @property - def tags(self): - _tags = self.fdoc.content.get('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): - if Status.SEEN in self.fdoc.content['flags']: - return 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.fdoc.content['tags'] = json.dumps(list(current_tags)) - self.save() - - def has_tag(self, tag): - return tag in self.tags - - @property - def signed(self): - signature = self.hdoc.content["headers"].get("X-Leap-Signature", None) - if signature is None: - return None - else: - return signature.startswith("valid") - - @property - def encrypted(self): - return self.hdoc.content["headers"].get("OpenPGP", None) is not None - - def as_dict(self): - dict_mail = {'header': {k.lower(): v for k, v in self.headers.items()}, - 'ident': self.ident, - 'tags': list(self.tags), - 'status': list(self.status), - 'security_casing': self.security_casing, - 'body': self.body, - 'mailbox': self.mailbox_name.lower(), - 'attachments': self.parts['attachments'] if self.parts else []} - dict_mail['replying'] = {'single': None, 'all': {'to-field': [], 'cc-field': []}} - - sender_mail = self.headers.get('Reply-To', self.headers['From']) - - recipients = [recipient for recipient in self.headers['To'] if recipient != InputMail.FROM_EMAIL_ADDRESS] - recipients.append(sender_mail) - ccs = [cc for cc in self.headers['Cc'] if cc != InputMail.FROM_EMAIL_ADDRESS] - - dict_mail['replying']['single'] = sender_mail - dict_mail['replying']['all']['to-field'] = recipients - dict_mail['replying']['all']['cc-field'] = ccs - return dict_mail |