summaryrefslogtreecommitdiff
path: root/service
diff options
context:
space:
mode:
Diffstat (limited to 'service')
-rw-r--r--service/pixelated/adapter/model/mail.py31
-rw-r--r--service/pixelated/adapter/soledad/soledad_facade_mixin.py6
-rw-r--r--service/pixelated/assets/welcome.mail74
-rw-r--r--service/pixelated/config/app_factory.py4
-rw-r--r--service/pixelated/config/welcome_mail.py48
-rw-r--r--service/test/integration/test_welcome_mail.py35
-rw-r--r--service/test/unit/adapter/test_mail.py19
-rw-r--r--service/test/unit/config/test_welcome_mail.py32
8 files changed, 237 insertions, 12 deletions
diff --git a/service/pixelated/adapter/model/mail.py b/service/pixelated/adapter/model/mail.py
index 618b980a..8e7b745c 100644
--- a/service/pixelated/adapter/model/mail.py
+++ b/service/pixelated/adapter/model/mail.py
@@ -239,22 +239,33 @@ class PixelatedMail(Mail):
content_type = self._parse_charset_header(part['headers'].get('Content-Type'))
try:
- decoding_map = {
- 'quoted-printable': lambda content, content_type: unicode(content.decode('quopri'), content_type),
- 'base64': lambda content, content_type: content.decode('base64').decode('utf-8'),
- '7bit': lambda content, content_type: content.encode(content_type),
- '8bit': lambda content, content_type: content.encode(content_type)
- }
- if encoding:
- return decoding_map[encoding](part['content'], content_type)
- else:
- return part['content']
+ decoding_func = self._decoding_function_for_encoding(encoding)
+ return self._decode_content_with_fallback(part['content'], decoding_func, content_type)
except Exception:
logger.error('Failed to decode mail part with:')
logger.error('Content-Transfer-Encoding: %s' % encoding)
logger.error('Content-Type: %s' % part['headers'].get('Content-Type'))
raise
+ def _decoding_function_for_encoding(self, encoding):
+ decoding_map = {
+ 'quoted-printable': lambda content, content_type: content.decode('quopri').decode(content_type),
+ 'base64': lambda content, content_type: content.decode('base64').decode('utf-8'),
+ '7bit': lambda content, content_type: content.encode(content_type),
+ '8bit': lambda content, content_type: content.encode(content_type)
+ }
+ if encoding in decoding_map:
+ return decoding_map[encoding]
+ else:
+ return decoding_map['8bit']
+
+ def _decode_content_with_fallback(self, content, decode_func, content_type):
+ try:
+ return decode_func(content, content_type)
+ # return content.encode(content_type)
+ except ValueError:
+ return content.encode('ascii', 'ignore')
+
@property
def alternatives(self):
return self.parts.get('alternatives')
diff --git a/service/pixelated/adapter/soledad/soledad_facade_mixin.py b/service/pixelated/adapter/soledad/soledad_facade_mixin.py
index 280fc81e..761ef1e2 100644
--- a/service/pixelated/adapter/soledad/soledad_facade_mixin.py
+++ b/service/pixelated/adapter/soledad/soledad_facade_mixin.py
@@ -59,8 +59,10 @@ class SoledadDbFacadeMixin(object):
def get_mbox(self, mbox):
return self.soledad.get_from_index('by-type-and-mbox', 'mbox', mbox) if mbox else []
- def get_lastuid(self, mbox_doc):
- return mbox_doc.content['lastuid']
+ def get_lastuid(self, mbox):
+ if isinstance(mbox, str):
+ mbox = self.get_mbox(mbox)[0]
+ return mbox.content['lastuid']
def get_search_index_masterkey(self):
return self.soledad.get_from_index('by-type', 'index_key')
diff --git a/service/pixelated/assets/welcome.mail b/service/pixelated/assets/welcome.mail
new file mode 100644
index 00000000..e85694f1
--- /dev/null
+++ b/service/pixelated/assets/welcome.mail
@@ -0,0 +1,74 @@
+From: Pixelated Team <pixelated-team@pixelated-project.org>
+Date: Sat, 21 Mar 2015 19:30:09 -0300
+Subject: Welcome to Pixelated Mail
+To: Replace <will.be@the.user>
+Content-Type: multipart/alternative; boundary=000boundary000
+
+--000boundary000
+Content-Type: text/plain; charset=UTF-8
+
+Welcome to Pixelated Mail, a modern email with encryption.
+
+Pixelated Mail is an open source project that aims to provide secure email on the browser with all the functionality we've come to expect of a modern email client.
+
+How to use it
+Pixelated Mail should provide functionality that is similar to what you've come to expect of your email software. To the left, you will find a navigation bar that provides access to all your mailboxes and tags. Clicking on them will load the corresponding messages on the middle pane - the mail list. Clicking on a message will load it on this pane, but you know it already!
+
+To compose a message look for the big blue button on the top left. You can add tags to received messages by clicking on the "+" sign under the message subject. You can also find the encryption status of messages just above the sender/recipient information.
+
+A bit more about Pixelated
+Pixelated is an open source project licensed under AGPL 3.0. It is composed of 3 main parts, the User Agent (what you are using right now), the Dispatcher (what allows you to log in with different accounts to the same instance) and the Platform (which provides the email service you will use to send and receive messages - the server behind the @ sign on your new mail address). You can learn more by visiting https://pixelated-project.org/.
+
+About this message and encryption status
+This message was not encrypted, in other words, it could have been read by others at some point during transmission, like any other email client.
+To send encrypted messages you have to have the public keys of the recipients, in Pixelated you have 2 options:
+send an email to another Pixelated account: public keys are exchanged by default.
+send an email to another email provider: you should had exchanged public keys with the recipients previously.
+
+
+Enjoy your secure messaging!
+
+--000boundary000
+Content-Type: text/html; charset=UTF-8
+Content-Transfer-Encoding: quoted-printable
+<p>
+Welcome to Pixelated Mail, a modern email with encryption.
+</p>
+<p>
+Pixelated Mail is an open source project that aims to provide secure email on the browser with all the functionality we've come to expect of a modern email client.
+</p>
+<p>
+<b>How to use it</b><br>
+Pixelated Mail should provide functionality that is similar to what you've =
+come to expect of your email software. To the left, you will find a navigat=
+ion bar that provides access to all your mailboxes and tags. Clicking on th=
+em will load the corresponding messages on the middle pane - the mail list.=
+ Clicking on a message will load it on this pane, but you know it already!
+</p>
+<p>
+To compose a message look for the big blue button on the top left. You can =
+add tags to received messages by clicking on the "+" sign under the message=
+ subject. You can also find the encryption status of messages just above th=
+e sender/recipient information.
+</p>
+<p>
+<b>A bit more about Pixelated</b><br>
+Pixelated is an open source project licensed under AGPL 3.0. It is composed=
+ of 3 main parts, the User Agent (what you are using right now), the Dispat=
+cher (what allows you to log in with different accounts to the same instanc=
+e) and the Platform (which provides the email service you will use to send =
+and receive messages - the server behind the @ sign on your new mail addres=
+s). You can learn more by visiting <a src=3D"https://pixelated-project.org/=
+">https://pixelated-project.org/</a>.
+</p>
+<p>
+<b>About this message and encryption status</b><br>
+This message was not encrypted, in other words, it could have been read by others at some point during transmission like any other email client.
+To send encrypted messages you have to have the public Keys of the recipients, in Pixelated you have 2 options:
+<p>- sending an email to another Pixelated account: public keys are exchanged by default.
+<br>- sending an email to another email provider: you should had exchanged public keys with the recipients previously.
+</p>
+<p>
+Enjoy your secure messaging!
+</p>
+--000boundary000--
diff --git a/service/pixelated/config/app_factory.py b/service/pixelated/config/app_factory.py
index f20b1229..5dcf60cb 100644
--- a/service/pixelated/config/app_factory.py
+++ b/service/pixelated/config/app_factory.py
@@ -39,6 +39,7 @@ from leap.common.events import (
events_pb2 as proto
)
from twisted.web.server import Site
+from .welcome_mail import check_welcome_mail_wrapper
CREATE_KEYS_IF_KEYS_DONT_EXISTS_CALLBACK = 12345
@@ -117,6 +118,9 @@ def init_app(app, leap_home, leap_session):
search_engine=search_engine,
mail_service=mail_service))
+ register(signal=proto.SOLEDAD_DONE_DATA_SYNC,
+ callback=check_welcome_mail_wrapper(pixelated_mailboxes.inbox()))
+
register(signal=proto.SOLEDAD_DONE_DATA_SYNC, uid=CREATE_KEYS_IF_KEYS_DONT_EXISTS_CALLBACK,
callback=look_for_user_key_and_create_if_cant_find(leap_session))
diff --git a/service/pixelated/config/welcome_mail.py b/service/pixelated/config/welcome_mail.py
new file mode 100644
index 00000000..236c4331
--- /dev/null
+++ b/service/pixelated/config/welcome_mail.py
@@ -0,0 +1,48 @@
+#
+# 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
+
+
+def check_welcome_mail_wrapper(mailbox):
+ def wrapper(*args, **kwargs):
+ check_welcome_mail(mailbox)
+ return wrapper
diff --git a/service/test/integration/test_welcome_mail.py b/service/test/integration/test_welcome_mail.py
new file mode 100644
index 00000000..ed37f50e
--- /dev/null
+++ b/service/test/integration/test_welcome_mail.py
@@ -0,0 +1,35 @@
+#
+# 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/>.
+
+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
+
+ inbox_mails = self.get_mails_by_tag('inbox')
+ self.assertEquals(1, len(inbox_mails))
+
+ self.delete_mail(inbox_mails[0].ident)
+ check_welcome_mail(inbox) # it is empty, but not fresh anymore
+
+ inbox_mails = self.get_mails_by_tag('inbox')
+ self.assertEquals(0, len(inbox_mails))
diff --git a/service/test/unit/adapter/test_mail.py b/service/test/unit/adapter/test_mail.py
index d77816cd..1a9280ff 100644
--- a/service/test/unit/adapter/test_mail.py
+++ b/service/test/unit/adapter/test_mail.py
@@ -232,6 +232,25 @@ class TestPixelatedMail(unittest.TestCase):
self.assertEquals(u'H\xe4llo', mail.text_plain_body)
self.assertEquals(u'<p>H\xe4llo</p>', mail.html_body)
+ def test_broken_content_type_defaults_to_usascii(self):
+ plain_headers = {'Content-Type': 'I lie to you', 'Content-Transfer-Encoding': 'quoted-printable'}
+ html_headers = {'Content-Type': 'text/html;\ncharset=utf-8', 'Content-Transfer-Encoding': 'quoted-printable'}
+ parts = {'alternatives': [{'content': 'H=E4llo', 'headers': plain_headers}, {'content': '<p>H=C3=A4llo</p>', 'headers': html_headers}]}
+
+ mail = PixelatedMail.from_soledad(None, None, self._create_bdoc(raw='some raw body'), parts=parts, soledad_querier=None)
+
+ self.assertEquals(u'H=E4llo', mail.text_plain_body)
+
+ def test_broken_encoding_defaults_to_8bit(self):
+ plain_headers = {'Content-Type': 'text/plain;\ncharset=iso-8859-1', 'Content-Transfer-Encoding': 'I lie to you!'}
+ html_headers = {'Content-Type': 'text/html;\ncharset=utf-8', 'Content-Transfer-Encoding': 'quoted-printable'}
+ parts = {'alternatives': [{'content': 'H=E4llo', 'headers': plain_headers}, {'content': '<p>H=C3=A4llo</p>', 'headers': html_headers}]}
+
+ mail = PixelatedMail.from_soledad(None, None, self._create_bdoc(raw='some raw body'), parts=parts, soledad_querier=None)
+
+ self.assertEquals(u'H=E4llo', mail.text_plain_body)
+ self.assertEquals(u'<p>H\xe4llo</p>', mail.html_body)
+
def test_clean_line_breaks_on_address_headers(self):
many_recipients = 'One <one@mail.com>,\nTwo <two@mail.com>, Normal <normal@mail.com>,\nalone@mail.com'
headers = {'Cc': many_recipients,
diff --git a/service/test/unit/config/test_welcome_mail.py b/service/test/unit/config/test_welcome_mail.py
new file mode 100644
index 00000000..3971c73f
--- /dev/null
+++ b/service/test/unit/config/test_welcome_mail.py
@@ -0,0 +1,32 @@
+#
+# 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'])