summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--service/pixelated/adapter/model/mail.py44
-rw-r--r--service/pixelated/adapter/services/draft_service.py7
-rw-r--r--service/pixelated/adapter/services/mail_service.py4
-rw-r--r--service/pixelated/adapter/services/mailboxes.py14
-rw-r--r--service/pixelated/application.py2
-rw-r--r--service/pixelated/config/app_factory.py4
-rw-r--r--service/pixelated/config/leap.py (renamed from service/pixelated/config/initialize_leap.py)1
-rw-r--r--service/pixelated/config/welcome_mail.py42
-rw-r--r--service/pixelated/maintenance.py2
-rw-r--r--service/pixelated/resources/mails_resource.py2
-rw-r--r--service/test/integration/test_contacts.py2
-rw-r--r--service/test/integration/test_tags.py2
-rw-r--r--service/test/integration/test_welcome_mail.py15
-rw-r--r--service/test/support/integration/app_test_client.py2
-rw-r--r--service/test/unit/adapter/test_draft_service.py2
-rw-r--r--service/test/unit/adapter/test_mail_service.py6
-rw-r--r--service/test/unit/adapter/test_mailboxes.py1
-rw-r--r--service/test/unit/config/test_welcome_mail.py32
18 files changed, 70 insertions, 114 deletions
diff --git a/service/pixelated/adapter/model/mail.py b/service/pixelated/adapter/model/mail.py
index 99ff0297..7c4223de 100644
--- a/service/pixelated/adapter/model/mail.py
+++ b/service/pixelated/adapter/model/mail.py
@@ -14,20 +14,21 @@
# 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
+import os
+import re
+import logging
+import dateutil.parser as dateparser
from uuid import uuid4
+from email import message_from_file
from email.mime.text import MIMEText
from email.header import decode_header
-
+from email.MIMEMultipart import MIMEMultipart
+from pycryptopp.hash import sha256
from leap.mail.imap.fields import fields
import leap.mail.walk as walk
-import dateutil.parser as dateparser
from pixelated.adapter.model.status import Status
-import pixelated.support.date
-from email.MIMEMultipart import MIMEMultipart
-from pycryptopp.hash import sha256
-import re
+from pixelated.support import date
from pixelated.support.functional import compact
-import logging
logger = logging.getLogger(__name__)
@@ -207,7 +208,7 @@ class InputMail(Mail):
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()
+ input_mail.headers['Date'] = date.iso_now()
# XXX this is overriding the property in PixelatedMail
input_mail.body = mail_dict.get('body', '')
@@ -218,6 +219,20 @@ class InputMail(Mail):
input_mail._status = set(mail_dict.get('status', []))
return input_mail
+ @staticmethod
+ def from_python_mail(mail):
+ input_mail = InputMail()
+ input_mail.headers = {key.capitalize(): value for key, value in mail.items()}
+ input_mail.headers['Date'] = date.iso_now()
+ input_mail.headers['Subject'] = mail['Subject']
+ input_mail.headers['To'] = InputMail.FROM_EMAIL_ADDRESS
+ input_mail._mime = MIMEMultipart()
+ for payload in mail.get_payload():
+ input_mail._mime.attach(payload)
+ if payload.get_content_type() == 'text/plain':
+ input_mail.body = payload.as_string()
+ return input_mail
+
class PixelatedMail(Mail):
@@ -305,7 +320,7 @@ class PixelatedMail(Mail):
try:
_headers['Date'] = self._get_date()
except Exception:
- _headers['Date'] = pixelated.support.date.iso_now()
+ _headers['Date'] = date.iso_now()
if self.parts and len(self.parts['alternatives']) > 1:
_headers['content_type'] = 'multipart/alternative; boundary="%s"' % self.boundary
@@ -341,10 +356,10 @@ class PixelatedMail(Mail):
else:
# we can't get a date for this mail, so lets just use now
logger.warning('Encountered a mail with missing date and received header fields. ID %s' % self.fdoc.content.get('uid', None))
- date = pixelated.support.date.iso_now()
+ date = date.iso_now()
return dateparser.parse(date).isoformat()
except (ValueError, TypeError):
- date = pixelated.support.date.iso_now()
+ date = date.iso_now()
return dateparser.parse(date).isoformat()
@property
@@ -487,3 +502,10 @@ class PixelatedMail(Mail):
dict_mail['replying']['all']['to-field'] = recipients
dict_mail['replying']['all']['cc-field'] = ccs
return dict_mail
+
+
+def welcome_mail():
+ current_path = os.path.dirname(os.path.abspath(__file__))
+ with open(os.path.join(current_path, '..', '..', 'assets', 'welcome.mail')) as mail_template_file:
+ mail_template = message_from_file(mail_template_file)
+ return InputMail.from_python_mail(mail_template)
diff --git a/service/pixelated/adapter/services/draft_service.py b/service/pixelated/adapter/services/draft_service.py
index df295eec..c8df0a05 100644
--- a/service/pixelated/adapter/services/draft_service.py
+++ b/service/pixelated/adapter/services/draft_service.py
@@ -22,13 +22,10 @@ class DraftService(object):
self._mailboxes = mailboxes
def create_draft(self, input_mail):
- pixelated_mail = self._drafts().add(input_mail)
+ pixelated_mail = self._mailboxes.drafts.add(input_mail)
return pixelated_mail
def update_draft(self, ident, input_mail):
pixelated_mail = self.create_draft(input_mail)
- self._drafts().remove(ident)
+ self._mailboxes.drafts.remove(ident)
return pixelated_mail
-
- def _drafts(self):
- return self._mailboxes.drafts()
diff --git a/service/pixelated/adapter/services/mail_service.py b/service/pixelated/adapter/services/mail_service.py
index e392bc44..4e6b6aa8 100644
--- a/service/pixelated/adapter/services/mail_service.py
+++ b/service/pixelated/adapter/services/mail_service.py
@@ -76,8 +76,8 @@ class MailService(object):
def move_to_sent(self, last_draft_ident, mail):
if last_draft_ident:
- self.mailboxes.drafts().remove(last_draft_ident)
- return self.mailboxes.sent().add(mail)
+ self.mailboxes.drafts.remove(last_draft_ident)
+ return self.mailboxes.sent.add(mail)
def mark_as_read(self, mail_id):
mail = self.mail(mail_id)
diff --git a/service/pixelated/adapter/services/mailboxes.py b/service/pixelated/adapter/services/mailboxes.py
index e9fe6ce5..c2b61ca8 100644
--- a/service/pixelated/adapter/services/mailboxes.py
+++ b/service/pixelated/adapter/services/mailboxes.py
@@ -15,6 +15,7 @@
# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
from pixelated.adapter.services.mailbox import Mailbox
from pixelated.adapter.listeners.mailbox_indexer_listener import MailboxIndexerListener
+from pixelated.adapter.model.mail import welcome_mail
class Mailboxes(object):
@@ -33,15 +34,19 @@ class Mailboxes(object):
MailboxIndexerListener.listen(self.account, mailbox_name, self.querier)
return Mailbox.create(mailbox_name, self.querier, self.search_engine)
+ @property
def inbox(self):
return self._create_or_get('INBOX')
+ @property
def drafts(self):
return self._create_or_get('DRAFTS')
+ @property
def trash(self):
return self._create_or_get('TRASH')
+ @property
def sent(self):
return self._create_or_get('SENT')
@@ -49,10 +54,10 @@ class Mailboxes(object):
return [self._create_or_get(leap_mailbox_name) for leap_mailbox_name in self.account.mailboxes]
def move_to_trash(self, mail_id):
- return self._move_to(mail_id, self.trash())
+ return self._move_to(mail_id, self.trash)
def move_to_inbox(self, mail_id):
- return self._move_to(mail_id, self.inbox())
+ return self._move_to(mail_id, self.inbox)
def _move_to(self, mail_id, mailbox):
mail = self.querier.mail(mail_id)
@@ -62,3 +67,8 @@ class Mailboxes(object):
def mail(self, mail_id):
return self.querier.mail(mail_id)
+
+ def add_welcome_mail_for_fresh_user(self):
+ if self.inbox.fresh:
+ mail = welcome_mail()
+ self.inbox.add(mail)
diff --git a/service/pixelated/application.py b/service/pixelated/application.py
index b63e10fb..55946a5e 100644
--- a/service/pixelated/application.py
+++ b/service/pixelated/application.py
@@ -26,7 +26,7 @@ from OpenSSL import crypto
from pixelated.config import arguments
from pixelated.resources import loading_page
-from pixelated.config.initialize_leap import initialize_leap
+from pixelated.config.leap import initialize_leap
from pixelated.config import logger, app_factory
diff --git a/service/pixelated/config/app_factory.py b/service/pixelated/config/app_factory.py
index 477317e1..717c2bb9 100644
--- a/service/pixelated/config/app_factory.py
+++ b/service/pixelated/config/app_factory.py
@@ -23,7 +23,6 @@ from pixelated.adapter.soledad.soledad_querier import SoledadQuerier
from pixelated.adapter.search import SearchEngine
from pixelated.adapter.services.draft_service import DraftService
from pixelated.adapter.listeners.mailbox_indexer_listener import MailboxIndexerListener
-from .welcome_mail import check_welcome_mail
def init_app(leap_home, leap_session):
@@ -37,7 +36,8 @@ def init_app(leap_home, leap_session):
lambda: leap_session.smtp.ensure_running())
pixelated_mailboxes = Mailboxes(leap_session.account, soledad_querier, search_engine)
- check_welcome_mail(pixelated_mailboxes.inbox())
+
+ pixelated_mailboxes.add_welcome_mail_for_fresh_user()
draft_service = DraftService(pixelated_mailboxes)
mail_service = MailService(pixelated_mailboxes, pixelated_mail_sender, soledad_querier, search_engine)
diff --git a/service/pixelated/config/initialize_leap.py b/service/pixelated/config/leap.py
index 1f8c5655..0ff6ea18 100644
--- a/service/pixelated/config/initialize_leap.py
+++ b/service/pixelated/config/leap.py
@@ -1,3 +1,4 @@
+from __future__ import absolute_import
from pixelated.config import credentials
from leap.common.events import server as events_server
import pixelated.bitmask_libraries.certs as certs
diff --git a/service/pixelated/config/welcome_mail.py b/service/pixelated/config/welcome_mail.py
deleted file mode 100644
index 5dcbf9bc..00000000
--- a/service/pixelated/config/welcome_mail.py
+++ /dev/null
@@ -1,42 +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 PARTICULAR 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 os
-from pixelated.adapter.model.mail import InputMail
-from pixelated.support.date import iso_now
-from email import message_from_file
-from email.MIMEMultipart import MIMEMultipart
-
-
-def check_welcome_mail(mailbox):
- if mailbox.fresh:
- welcome_mail = build_welcome_mail()
- mailbox.add(welcome_mail)
-
-
-def build_welcome_mail():
- current_path = os.path.dirname(os.path.abspath(__file__))
- with open(os.path.join(current_path, '..', 'assets', 'welcome.mail')) as mail_template_file:
- mail_template = message_from_file(mail_template_file)
- welcome_mail = InputMail()
- welcome_mail.headers['To'] = InputMail.FROM_EMAIL_ADDRESS
- welcome_mail.headers['Subject'] = mail_template['Subject']
- welcome_mail.headers['Date'] = iso_now()
- welcome_mail._mime = MIMEMultipart()
- for payload in mail_template.get_payload():
- welcome_mail._mime.attach(payload)
- if payload.get_content_type() == 'text/plain':
- welcome_mail.body = payload.as_string()
- return welcome_mail
diff --git a/service/pixelated/maintenance.py b/service/pixelated/maintenance.py
index 756ad435..97504103 100644
--- a/service/pixelated/maintenance.py
+++ b/service/pixelated/maintenance.py
@@ -17,7 +17,7 @@
from mailbox import Maildir
from twisted.internet import reactor, defer
from twisted.internet.threads import deferToThread
-from pixelated.config.initialize_leap import initialize_leap
+from pixelated.config.leap import initialize_leap
from pixelated.config import logger, arguments
from leap.mail.imap.fields import WithMsgFields
diff --git a/service/pixelated/resources/mails_resource.py b/service/pixelated/resources/mails_resource.py
index 3822abd3..c4b578ba 100644
--- a/service/pixelated/resources/mails_resource.py
+++ b/service/pixelated/resources/mails_resource.py
@@ -73,7 +73,7 @@ class MailsResource(Resource):
def on_error(event):
delivery_error_mail = InputMail.delivery_error_template(delivery_address=event.content)
- self._mail_service.mailboxes.inbox().add(delivery_error_mail)
+ self._mail_service.mailboxes.inbox.add(delivery_error_mail)
register(signal=proto.SMTP_SEND_MESSAGE_ERROR, callback=on_error)
diff --git a/service/test/integration/test_contacts.py b/service/test/integration/test_contacts.py
index f9cde9e5..1d82b0d7 100644
--- a/service/test/integration/test_contacts.py
+++ b/service/test/integration/test_contacts.py
@@ -87,7 +87,7 @@ class ContactsTest(SoledadTestBase):
self.add_mail_to_inbox(to_be_bounced)
bounced_mail_template = MailBuilder().build_input_mail()
- bounced_mail = self.mailboxes.inbox().add(bounced_mail_template)
+ bounced_mail = self.mailboxes.inbox.add(bounced_mail_template)
bounced_mail.hdoc.content = self._bounced_mail_hdoc_content()
bounced_mail.save()
self.search_engine.index_mail(bounced_mail)
diff --git a/service/test/integration/test_tags.py b/service/test/integration/test_tags.py
index 976b6d96..168e035f 100644
--- a/service/test/integration/test_tags.py
+++ b/service/test/integration/test_tags.py
@@ -86,5 +86,5 @@ class TagsTest(SoledadTestBase):
response = self.post_tags(mail.ident, self._tags_json([tag.name.upper()]))
self.assertEquals("None of the following words can be used as tags: %s" % tag.name, response)
- mail = self.mailboxes.inbox().mail(mail.ident)
+ mail = self.mailboxes.inbox.mail(mail.ident)
self.assertNotIn('drafts', mail.tags)
diff --git a/service/test/integration/test_welcome_mail.py b/service/test/integration/test_welcome_mail.py
index ed37f50e..a5ca555a 100644
--- a/service/test/integration/test_welcome_mail.py
+++ b/service/test/integration/test_welcome_mail.py
@@ -15,21 +15,20 @@
# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
from test.support.integration import SoledadTestBase
-from pixelated.config.welcome_mail import check_welcome_mail
class TestWelcomeMail(SoledadTestBase):
- def test_that_a_fresh_INBOX_will_receive_a_welcome_mail_only_once(self):
- inbox = self.mailboxes.inbox()
- check_welcome_mail(inbox) # adds a mail
- check_welcome_mail(inbox) # should not repeat
-
+ def test_welcome_mail_is_added_only_once(self):
+ self.mailboxes.add_welcome_mail_for_fresh_user()
+ self.mailboxes.add_welcome_mail_for_fresh_user()
inbox_mails = self.get_mails_by_tag('inbox')
self.assertEquals(1, len(inbox_mails))
+ def test_empty_mailbox_doesnt_mean_fresh_mailbox(self):
+ self.mailboxes.add_welcome_mail_for_fresh_user()
+ inbox_mails = self.get_mails_by_tag('inbox')
self.delete_mail(inbox_mails[0].ident)
- check_welcome_mail(inbox) # it is empty, but not fresh anymore
-
+ self.mailboxes.add_welcome_mail_for_fresh_user()
inbox_mails = self.get_mails_by_tag('inbox')
self.assertEquals(0, len(inbox_mails))
diff --git a/service/test/support/integration/app_test_client.py b/service/test/support/integration/app_test_client.py
index 25171353..a1b0c3b8 100644
--- a/service/test/support/integration/app_test_client.py
+++ b/service/test/support/integration/app_test_client.py
@@ -125,7 +125,7 @@ class AppTestClient(object):
self.soledad_querier.soledad.create_doc(_dict)
def add_mail_to_inbox(self, input_mail):
- mail = self.mailboxes.inbox().add(input_mail)
+ mail = self.mailboxes.inbox.add(input_mail)
if input_mail.tags:
mail.update_tags(input_mail.tags)
self.search_engine.index_mail(mail)
diff --git a/service/test/unit/adapter/test_draft_service.py b/service/test/unit/adapter/test_draft_service.py
index 0dd6cd2a..79eca5f6 100644
--- a/service/test/unit/adapter/test_draft_service.py
+++ b/service/test/unit/adapter/test_draft_service.py
@@ -12,7 +12,7 @@ class DraftServiceTest(unittest.TestCase):
self.mailboxes = mock()
self.drafts_mailbox = mock()
self.draft_service = DraftService(self.mailboxes)
- when(self.mailboxes).drafts().thenReturn(self.drafts_mailbox)
+ self.mailboxes.drafts = self.drafts_mailbox
def test_add_draft(self):
mail = InputMail()
diff --git a/service/test/unit/adapter/test_mail_service.py b/service/test/unit/adapter/test_mail_service.py
index f4b89f57..f5e29b0c 100644
--- a/service/test/unit/adapter/test_mail_service.py
+++ b/service/test/unit/adapter/test_mail_service.py
@@ -27,9 +27,9 @@ class TestMailService(unittest.TestCase):
self.drafts = mock()
self.querier = mock()
self.mailboxes = mock()
- self.mailboxes.drafts = lambda: self.drafts
- self.mailboxes.trash = lambda: mock()
- self.mailboxes.sent = lambda: mock()
+ self.mailboxes.drafts = self.drafts
+ self.mailboxes.trash = mock()
+ self.mailboxes.sent = mock()
self.mail_sender = mock()
self.search_engine = mock()
diff --git a/service/test/unit/adapter/test_mailboxes.py b/service/test/unit/adapter/test_mailboxes.py
index 5b4548eb..6ff3849b 100644
--- a/service/test/unit/adapter/test_mailboxes.py
+++ b/service/test/unit/adapter/test_mailboxes.py
@@ -23,6 +23,7 @@ from mock import MagicMock
class PixelatedMailboxesTest(unittest.TestCase):
+
def setUp(self):
self.querier = mock()
self.search_engine = mock()
diff --git a/service/test/unit/config/test_welcome_mail.py b/service/test/unit/config/test_welcome_mail.py
deleted file mode 100644
index 3971c73f..00000000
--- a/service/test/unit/config/test_welcome_mail.py
+++ /dev/null
@@ -1,32 +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 PARTICULAR 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 unittest
-
-from pixelated.config.welcome_mail import build_welcome_mail
-from pixelated.adapter.model.mail import InputMail
-
-
-class WelcomeMailTest(unittest.TestCase):
-
- def test_build_plain_welcome_mail(self):
- user_address = InputMail.FROM_EMAIL_ADDRESS = 'welcomed@user'
- mail = build_welcome_mail()
- self.assertEquals(user_address, mail.to)
- self.assertEquals('Welcome to Pixelated Mail', mail.headers['Subject'])
- self.assertIn('How to use it', mail.body)
- self.assertIn('text/plain', mail._mime.as_string())
- self.assertIn('text/html', mail._mime.as_string())
- self.assertTrue(mail.headers['Date'])