summaryrefslogtreecommitdiff
path: root/service/pixelated/adapter/mail.py
diff options
context:
space:
mode:
authorDuda Dornelles <ddornell@thoughtworks.com>2014-12-22 09:35:01 -0200
committerDuda Dornelles <ddornell@thoughtworks.com>2014-12-22 09:38:08 -0200
commit66839f191708a0725c8d9841d5266ad13af8ee81 (patch)
tree337fb19e99625cc3243f998a2351cea123c037a2 /service/pixelated/adapter/mail.py
parenta0cbaf84fd86fc91cba892a7a19b0ade7c017a3c (diff)
refactoring package structure
Diffstat (limited to 'service/pixelated/adapter/mail.py')
-rw-r--r--service/pixelated/adapter/mail.py361
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