summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
l---------src/leap/bitmask/mail/adaptors/tests/rfc822.message1
-rw-r--r--src/leap/bitmask/mail/adaptors/tests/test_models.py106
-rw-r--r--src/leap/bitmask/mail/adaptors/tests/test_soledad_adaptor.py529
-rw-r--r--src/leap/bitmask/mail/imap/tests/.gitignore1
-rwxr-xr-xsrc/leap/bitmask/mail/imap/tests/getmail344
-rwxr-xr-xsrc/leap/bitmask/mail/imap/tests/imapclient.py207
-rwxr-xr-xsrc/leap/bitmask/mail/imap/tests/regressions_mime_struct461
l---------src/leap/bitmask/mail/imap/tests/rfc822.message1
l---------src/leap/bitmask/mail/imap/tests/rfc822.multi-minimal.message1
l---------src/leap/bitmask/mail/imap/tests/rfc822.multi-nested.message1
l---------src/leap/bitmask/mail/imap/tests/rfc822.multi-signed.message1
l---------src/leap/bitmask/mail/imap/tests/rfc822.multi.message1
l---------src/leap/bitmask/mail/imap/tests/rfc822.plain.message1
-rwxr-xr-xsrc/leap/bitmask/mail/imap/tests/stress_tests_imap.zsh178
-rw-r--r--src/leap/bitmask/mail/imap/tests/test_imap.py1060
-rw-r--r--src/leap/bitmask/mail/imap/tests/walktree.py127
-rw-r--r--src/leap/bitmask/mail/incoming/tests/rfc822.multi-encrypt-signed.message61
-rw-r--r--src/leap/bitmask/mail/incoming/tests/test_incoming_mail.py391
-rw-r--r--src/leap/bitmask/mail/outgoing/tests/test_outgoing.py263
-rw-r--r--src/leap/bitmask/mail/smtp/tests/185CA770.key79
-rw-r--r--src/leap/bitmask/mail/smtp/tests/185CA770.pub52
-rw-r--r--src/leap/bitmask/mail/smtp/tests/cert/server.crt29
-rw-r--r--src/leap/bitmask/mail/smtp/tests/cert/server.key51
-rw-r--r--src/leap/bitmask/mail/smtp/tests/mail.txt10
-rw-r--r--src/leap/bitmask/mail/smtp/tests/test_gateway.py181
-rw-r--r--src/leap/bitmask/mail/tests/rfc822.bounce.message152
-rw-r--r--src/leap/bitmask/mail/tests/rfc822.message86
-rw-r--r--src/leap/bitmask/mail/tests/rfc822.multi-minimal.message16
-rw-r--r--src/leap/bitmask/mail/tests/rfc822.multi-nested.message619
-rw-r--r--src/leap/bitmask/mail/tests/rfc822.multi-signed.message238
-rw-r--r--src/leap/bitmask/mail/tests/rfc822.multi.message96
-rw-r--r--src/leap/bitmask/mail/tests/rfc822.plain.message66
-rw-r--r--src/leap/bitmask/mail/tests/test_mail.py399
-rw-r--r--src/leap/bitmask/mail/tests/test_mailbox_indexer.py250
-rw-r--r--src/leap/bitmask/mail/tests/test_walk.py81
35 files changed, 0 insertions, 6140 deletions
diff --git a/src/leap/bitmask/mail/adaptors/tests/rfc822.message b/src/leap/bitmask/mail/adaptors/tests/rfc822.message
deleted file mode 120000
index b19cc28..0000000
--- a/src/leap/bitmask/mail/adaptors/tests/rfc822.message
+++ /dev/null
@@ -1 +0,0 @@
-../../tests/rfc822.message \ No newline at end of file
diff --git a/src/leap/bitmask/mail/adaptors/tests/test_models.py b/src/leap/bitmask/mail/adaptors/tests/test_models.py
deleted file mode 100644
index b82cfad..0000000
--- a/src/leap/bitmask/mail/adaptors/tests/test_models.py
+++ /dev/null
@@ -1,106 +0,0 @@
-# -*- coding: utf-8 -*-
-# test_models.py
-# Copyright (C) 2014 LEAP
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-"""
-Tests for the leap.mail.adaptors.models module.
-"""
-from twisted.trial import unittest
-
-from leap.mail.adaptors import models
-
-
-class SerializableModelsTestCase(unittest.TestCase):
-
- def test_good_serialized_model(self):
-
- class M(models.SerializableModel):
- foo = 42
- bar = 33
- baaz_ = None
- _nope = 0
- __nope = 0
-
- def not_today(self):
- pass
-
- class IgnoreMe(object):
- pass
-
- def killmeplease(x):
- return x
-
- serialized = M.serialize()
- expected = {'foo': 42, 'bar': 33, 'baaz': None}
- self.assertEqual(serialized, expected)
-
-
-class DocumentWrapperTestCase(unittest.TestCase):
-
- def test_wrapper_defaults(self):
-
- class Wrapper(models.DocumentWrapper):
- class model(models.SerializableModel):
- foo = 42
- bar = 11
-
- wrapper = Wrapper()
- wrapper._ignored = True
- serialized = wrapper.serialize()
- expected = {'foo': 42, 'bar': 11}
- self.assertEqual(serialized, expected)
-
- def test_initialized_wrapper(self):
-
- class Wrapper(models.DocumentWrapper):
- class model(models.SerializableModel):
- foo = 42
- bar_ = 11
-
- wrapper = Wrapper(foo=0, bar=-1)
- serialized = wrapper.serialize()
- expected = {'foo': 0, 'bar': -1}
- self.assertEqual(serialized, expected)
-
- wrapper.foo = 23
- serialized = wrapper.serialize()
- expected = {'foo': 23, 'bar': -1}
- self.assertEqual(serialized, expected)
-
- wrapper = Wrapper(foo=0)
- serialized = wrapper.serialize()
- expected = {'foo': 0, 'bar': 11}
- self.assertEqual(serialized, expected)
-
- def test_invalid_initialized_wrapper(self):
-
- class Wrapper(models.DocumentWrapper):
- class model(models.SerializableModel):
- foo = 42
-
- def getwrapper():
- return Wrapper(bar=1)
- self.assertRaises(RuntimeError, getwrapper)
-
- def test_no_model_wrapper(self):
-
- class Wrapper(models.DocumentWrapper):
- pass
-
- def getwrapper():
- w = Wrapper()
- w.foo = None
-
- self.assertRaises(RuntimeError, getwrapper)
diff --git a/src/leap/bitmask/mail/adaptors/tests/test_soledad_adaptor.py b/src/leap/bitmask/mail/adaptors/tests/test_soledad_adaptor.py
deleted file mode 100644
index 73eaf16..0000000
--- a/src/leap/bitmask/mail/adaptors/tests/test_soledad_adaptor.py
+++ /dev/null
@@ -1,529 +0,0 @@
-# -*- coding: utf-8 -*-
-# test_soledad_adaptor.py
-# Copyright (C) 2014 LEAP
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-"""
-Tests for the Soledad Adaptor module - leap.mail.adaptors.soledad
-"""
-import os
-from functools import partial
-
-from twisted.internet import defer
-
-from leap.mail.adaptors import models
-from leap.mail.adaptors.soledad import SoledadDocumentWrapper
-from leap.mail.adaptors.soledad import SoledadIndexMixin
-from leap.mail.adaptors.soledad import SoledadMailAdaptor
-from leap.mail.testing.common import SoledadTestMixin
-
-from email.MIMEMultipart import MIMEMultipart
-from email.mime.text import MIMEText
-
-# DEBUG
-# import logging
-# logging.basicConfig(level=logging.DEBUG)
-
-
-class CounterWrapper(SoledadDocumentWrapper):
- class model(models.SerializableModel):
- counter = 0
- flag = None
-
-
-class CharacterWrapper(SoledadDocumentWrapper):
- class model(models.SerializableModel):
- name = ""
- age = 20
-
-
-class ActorWrapper(SoledadDocumentWrapper):
- class model(models.SerializableModel):
- type_ = "actor"
- name = None
-
- class __meta__(object):
- index = "name"
- list_index = ("by-type", "type_")
-
-
-class TestAdaptor(SoledadIndexMixin):
- indexes = {'by-name': ['name'],
- 'by-type-and-name': ['type', 'name'],
- 'by-type': ['type']}
-
-
-class SoledadDocWrapperTestCase(SoledadTestMixin):
- """
- Tests for the SoledadDocumentWrapper.
- """
- def assert_num_docs(self, num, docs):
- self.assertEqual(len(docs[1]), num)
-
- def test_create_single(self):
-
- store = self._soledad
- wrapper = CounterWrapper()
-
- def assert_one_doc(docs):
- self.assertEqual(docs[0], 1)
-
- d = wrapper.create(store)
- d.addCallback(lambda _: store.get_all_docs())
- d.addCallback(assert_one_doc)
- return d
-
- def test_create_many(self):
-
- store = self._soledad
- w1 = CounterWrapper()
- w2 = CounterWrapper(counter=1)
- w3 = CounterWrapper(counter=2)
- w4 = CounterWrapper(counter=3)
- w5 = CounterWrapper(counter=4)
-
- d1 = [w1.create(store),
- w2.create(store),
- w3.create(store),
- w4.create(store),
- w5.create(store)]
-
- d = defer.gatherResults(d1)
- d.addCallback(lambda _: store.get_all_docs())
- d.addCallback(partial(self.assert_num_docs, 5))
- return d
-
- def test_multiple_updates(self):
-
- store = self._soledad
- wrapper = CounterWrapper(counter=1)
- MAX = 100
-
- def assert_doc_id(doc):
- self.assertTrue(wrapper._doc_id is not None)
- return doc
-
- def assert_counter_initial_ok(doc):
- self.assertEqual(wrapper.counter, 1)
-
- def increment_counter(ignored):
- d1 = []
-
- def record_revision(revision):
- rev = int(revision.split(':')[1])
- self.results.append(rev)
-
- for i in list(range(MAX)):
- wrapper.counter += 1
- wrapper.flag = i % 2 == 0
- d = wrapper.update(store)
- d.addCallback(record_revision)
- d1.append(d)
-
- return defer.gatherResults(d1)
-
- def assert_counter_final_ok(doc):
- self.assertEqual(doc.content['counter'], MAX + 1)
- self.assertEqual(doc.content['flag'], False)
-
- def assert_results_ordered_list(ignored):
- self.assertEqual(self.results, sorted(range(2, MAX + 2)))
-
- d = wrapper.create(store)
- d.addCallback(assert_doc_id)
- d.addCallback(assert_counter_initial_ok)
- d.addCallback(increment_counter)
- d.addCallback(lambda _: store.get_doc(wrapper._doc_id))
- d.addCallback(assert_counter_final_ok)
- d.addCallback(assert_results_ordered_list)
- return d
-
- def test_delete(self):
- adaptor = TestAdaptor()
- store = self._soledad
-
- wrapper_list = []
-
- def get_or_create_bob(ignored):
- def add_to_list(wrapper):
- wrapper_list.append(wrapper)
- return wrapper
- wrapper = CharacterWrapper.get_or_create(
- store, 'by-name', 'bob')
- wrapper.addCallback(add_to_list)
- return wrapper
-
- def delete_bob(ignored):
- wrapper = wrapper_list[0]
- return wrapper.delete(store)
-
- d = adaptor.initialize_store(store)
- d.addCallback(lambda _: store.get_all_docs())
- d.addCallback(partial(self.assert_num_docs, 0))
-
- # this should create bob document
- d.addCallback(get_or_create_bob)
- d.addCallback(lambda _: store.get_all_docs())
- d.addCallback(partial(self.assert_num_docs, 1))
-
- d.addCallback(delete_bob)
- d.addCallback(lambda _: store.get_all_docs())
- d.addCallback(partial(self.assert_num_docs, 0))
- return d
-
- def test_get_or_create(self):
- adaptor = TestAdaptor()
- store = self._soledad
-
- def get_or_create_bob(ignored):
- wrapper = CharacterWrapper.get_or_create(
- store, 'by-name', 'bob')
- return wrapper
-
- d = adaptor.initialize_store(store)
- d.addCallback(lambda _: store.get_all_docs())
- d.addCallback(partial(self.assert_num_docs, 0))
-
- # this should create bob document
- d.addCallback(get_or_create_bob)
- d.addCallback(lambda _: store.get_all_docs())
- d.addCallback(partial(self.assert_num_docs, 1))
-
- # this should get us bob document
- d.addCallback(get_or_create_bob)
- d.addCallback(lambda _: store.get_all_docs())
- d.addCallback(partial(self.assert_num_docs, 1))
- return d
-
- def test_get_or_create_multi_index(self):
- adaptor = TestAdaptor()
- store = self._soledad
-
- def get_or_create_actor_harry(ignored):
- wrapper = ActorWrapper.get_or_create(
- store, 'by-type-and-name', 'harrison')
- return wrapper
-
- def create_director_harry(ignored):
- wrapper = ActorWrapper(name="harrison", type="director")
- return wrapper.create(store)
-
- d = adaptor.initialize_store(store)
- d.addCallback(lambda _: store.get_all_docs())
- d.addCallback(partial(self.assert_num_docs, 0))
-
- # this should create harrison document
- d.addCallback(get_or_create_actor_harry)
- d.addCallback(lambda _: store.get_all_docs())
- d.addCallback(partial(self.assert_num_docs, 1))
-
- # this should get us harrison document
- d.addCallback(get_or_create_actor_harry)
- d.addCallback(lambda _: store.get_all_docs())
- d.addCallback(partial(self.assert_num_docs, 1))
-
- # create director harry, should create new doc
- d.addCallback(create_director_harry)
- d.addCallback(lambda _: store.get_all_docs())
- d.addCallback(partial(self.assert_num_docs, 2))
-
- # this should get us harrison document, still 2 docs
- d.addCallback(get_or_create_actor_harry)
- d.addCallback(lambda _: store.get_all_docs())
- d.addCallback(partial(self.assert_num_docs, 2))
- return d
-
- def test_get_all(self):
- adaptor = TestAdaptor()
- store = self._soledad
- actor_names = ["harry", "carrie", "mark", "david"]
-
- def create_some_actors(ignored):
- deferreds = []
- for name in actor_names:
- dw = ActorWrapper.get_or_create(
- store, 'by-type-and-name', name)
- deferreds.append(dw)
- return defer.gatherResults(deferreds)
-
- d = adaptor.initialize_store(store)
- d.addCallback(lambda _: store.get_all_docs())
- d.addCallback(partial(self.assert_num_docs, 0))
-
- d.addCallback(create_some_actors)
-
- d.addCallback(lambda _: store.get_all_docs())
- d.addCallback(partial(self.assert_num_docs, 4))
-
- def assert_actor_list_is_expected(res):
- got = set([actor.name for actor in res])
- expected = set(actor_names)
- self.assertEqual(got, expected)
-
- d.addCallback(lambda _: ActorWrapper.get_all(store))
- d.addCallback(assert_actor_list_is_expected)
- return d
-
-HERE = os.path.split(os.path.abspath(__file__))[0]
-
-
-class MessageClass(object):
- def __init__(self, wrapper, uid):
- self.wrapper = wrapper
- self.uid = uid
-
- def get_wrapper(self):
- return self.wrapper
-
-
-class SoledadMailAdaptorTestCase(SoledadTestMixin):
- """
- Tests for the SoledadMailAdaptor.
- """
-
- def get_adaptor(self):
- adaptor = SoledadMailAdaptor()
- adaptor.store = self._soledad
- return adaptor
-
- def assert_num_docs(self, num, docs):
- self.assertEqual(len(docs[1]), num)
-
- def test_mail_adaptor_init(self):
- adaptor = self.get_adaptor()
- self.assertTrue(isinstance(adaptor.indexes, dict))
- self.assertTrue(len(adaptor.indexes) != 0)
-
- # Messages
-
- def test_get_msg_from_string(self):
- adaptor = self.get_adaptor()
-
- with open(os.path.join(HERE, "rfc822.message")) as f:
- raw = f.read()
-
- msg = adaptor.get_msg_from_string(MessageClass, raw)
-
- chash = ("D27B2771C0DCCDCB468EE65A4540438"
- "09DBD11588E87E951545BE0CBC321C308")
- phash = ("64934534C1C80E0D4FA04BE1CCBA104"
- "F07BCA5F469C86E2C0ABE1D41310B7299")
- subject = ("[Twisted-commits] rebuild now works on "
- "python versions from 2.2.0 and up.")
- self.assertTrue(msg.wrapper.fdoc is not None)
- self.assertTrue(msg.wrapper.hdoc is not None)
- self.assertTrue(msg.wrapper.cdocs is not None)
- self.assertEquals(len(msg.wrapper.cdocs), 1)
- self.assertEquals(msg.wrapper.fdoc.chash, chash)
- self.assertEquals(msg.wrapper.fdoc.size, 3837)
- self.assertEquals(msg.wrapper.hdoc.chash, chash)
- self.assertEqual(dict(msg.wrapper.hdoc.headers)['Subject'],
- subject)
- self.assertEqual(msg.wrapper.hdoc.subject, subject)
- self.assertEqual(msg.wrapper.cdocs[1].phash, phash)
-
- def test_get_msg_from_string_multipart(self):
- msg = MIMEMultipart()
- msg['Subject'] = 'Test multipart mail'
- msg.attach(MIMEText(u'a utf8 message', _charset='utf-8'))
- adaptor = self.get_adaptor()
-
- msg = adaptor.get_msg_from_string(MessageClass, msg.as_string())
-
- self.assertEqual(
- 'base64', msg.wrapper.cdocs[1].content_transfer_encoding)
- self.assertEqual(
- 'text/plain', msg.wrapper.cdocs[1].content_type)
- self.assertEqual(
- 'YSB1dGY4IG1lc3NhZ2U=\n', msg.wrapper.cdocs[1].raw)
-
- def test_get_msg_from_docs(self):
- adaptor = self.get_adaptor()
- mdoc = dict(
- fdoc="F-Foobox-deadbeef",
- hdoc="H-deadbeef",
- cdocs=["C-deadabad"])
- fdoc = dict(
- mbox_uuid="Foobox",
- flags=('\Seen', '\Nice'),
- tags=('Personal', 'TODO'),
- seen=False, deleted=False,
- recent=False, multi=False)
- hdoc = dict(
- chash="deadbeef",
- subject="Test Msg")
- cdocs = {
- 1: dict(
- raw='This is a test message')}
-
- msg = adaptor.get_msg_from_docs(
- MessageClass, mdoc, fdoc, hdoc, cdocs=cdocs)
- self.assertEqual(msg.wrapper.fdoc.flags,
- ('\Seen', '\Nice'))
- self.assertEqual(msg.wrapper.fdoc.tags,
- ('Personal', 'TODO'))
- self.assertEqual(msg.wrapper.fdoc.mbox_uuid, "Foobox")
- self.assertEqual(msg.wrapper.hdoc.multi, False)
- self.assertEqual(msg.wrapper.hdoc.subject,
- "Test Msg")
- self.assertEqual(msg.wrapper.cdocs[1].raw,
- "This is a test message")
-
- def test_get_msg_from_metamsg_doc_id(self):
- # TODO complete-me!
- pass
-
- test_get_msg_from_metamsg_doc_id.skip = "Not yet implemented"
-
- def test_create_msg(self):
- adaptor = self.get_adaptor()
-
- with open(os.path.join(HERE, "rfc822.message")) as f:
- raw = f.read()
- msg = adaptor.get_msg_from_string(MessageClass, raw)
-
- def check_create_result(created):
- # that's one mdoc, one hdoc, one fdoc, one cdoc
- self.assertEqual(len(created), 4)
- for doc in created:
- self.assertTrue(
- doc.__class__.__name__,
- "SoledadDocument")
-
- d = adaptor.create_msg(adaptor.store, msg)
- d.addCallback(check_create_result)
- return d
-
- def test_update_msg(self):
- adaptor = self.get_adaptor()
- with open(os.path.join(HERE, "rfc822.message")) as f:
- raw = f.read()
-
- def assert_msg_has_doc_id(ignored, msg):
- wrapper = msg.get_wrapper()
- self.assertTrue(wrapper.fdoc.doc_id is not None)
-
- def assert_msg_has_no_flags(ignored, msg):
- wrapper = msg.get_wrapper()
- self.assertEqual(wrapper.fdoc.flags, [])
-
- def update_msg_flags(ignored, msg):
- wrapper = msg.get_wrapper()
- wrapper.fdoc.flags = ["This", "That"]
- return wrapper.update(adaptor.store)
-
- def assert_msg_has_flags(ignored, msg):
- wrapper = msg.get_wrapper()
- self.assertEqual(wrapper.fdoc.flags, ["This", "That"])
-
- def get_fdoc_and_check_flags(ignored):
- def assert_doc_has_flags(doc):
- self.assertEqual(doc.content['flags'],
- ['This', 'That'])
- wrapper = msg.get_wrapper()
- d = adaptor.store.get_doc(wrapper.fdoc.doc_id)
- d.addCallback(assert_doc_has_flags)
- return d
-
- msg = adaptor.get_msg_from_string(MessageClass, raw)
- d = adaptor.create_msg(adaptor.store, msg)
- d.addCallback(lambda _: adaptor.store.get_all_docs())
- d.addCallback(partial(self.assert_num_docs, 4))
- d.addCallback(assert_msg_has_doc_id, msg)
- d.addCallback(assert_msg_has_no_flags, msg)
-
- # update it!
- d.addCallback(update_msg_flags, msg)
- d.addCallback(assert_msg_has_flags, msg)
- d.addCallback(get_fdoc_and_check_flags)
- return d
-
- # Mailboxes
-
- def test_get_or_create_mbox(self):
- adaptor = self.get_adaptor()
-
- def get_or_create_mbox(ignored):
- d = adaptor.get_or_create_mbox(adaptor.store, "Trash")
- return d
-
- def assert_good_doc(mbox_wrapper):
- self.assertTrue(mbox_wrapper.doc_id is not None)
- self.assertEqual(mbox_wrapper.mbox, "Trash")
- self.assertEqual(mbox_wrapper.type, "mbox")
- self.assertEqual(mbox_wrapper.closed, False)
- self.assertEqual(mbox_wrapper.subscribed, False)
-
- d = adaptor.initialize_store(adaptor.store)
- d.addCallback(get_or_create_mbox)
- d.addCallback(assert_good_doc)
- d.addCallback(lambda _: adaptor.store.get_all_docs())
- d.addCallback(partial(self.assert_num_docs, 1))
- return d
-
- def test_update_mbox(self):
- adaptor = self.get_adaptor()
-
- wrapper_ref = []
-
- def get_or_create_mbox(ignored):
- d = adaptor.get_or_create_mbox(adaptor.store, "Trash")
- return d
-
- def update_wrapper(wrapper, wrapper_ref):
- wrapper_ref.append(wrapper)
- wrapper.subscribed = True
- wrapper.closed = True
- d = adaptor.update_mbox(adaptor.store, wrapper)
- return d
-
- def get_mbox_doc_and_check_flags(res, wrapper_ref):
- wrapper = wrapper_ref[0]
-
- def assert_doc_has_flags(doc):
- self.assertEqual(doc.content['subscribed'], True)
- self.assertEqual(doc.content['closed'], True)
- d = adaptor.store.get_doc(wrapper.doc_id)
- d.addCallback(assert_doc_has_flags)
- return d
-
- d = adaptor.initialize_store(adaptor.store)
- d.addCallback(get_or_create_mbox)
- d.addCallback(update_wrapper, wrapper_ref)
- d.addCallback(get_mbox_doc_and_check_flags, wrapper_ref)
- return d
-
- def test_get_all_mboxes(self):
- adaptor = self.get_adaptor()
- mboxes = ("Sent", "Trash", "Personal", "ListFoo")
-
- def get_or_create_mboxes(ignored):
- d = []
- for mbox in mboxes:
- d.append(adaptor.get_or_create_mbox(
- adaptor.store, mbox))
- return defer.gatherResults(d)
-
- def get_all_mboxes(ignored):
- return adaptor.get_all_mboxes(adaptor.store)
-
- def assert_mboxes_match_expected(wrappers):
- names = [m.mbox for m in wrappers]
- self.assertEqual(set(names), set(mboxes))
-
- d = adaptor.initialize_store(adaptor.store)
- d.addCallback(get_or_create_mboxes)
- d.addCallback(get_all_mboxes)
- d.addCallback(assert_mboxes_match_expected)
- return d
diff --git a/src/leap/bitmask/mail/imap/tests/.gitignore b/src/leap/bitmask/mail/imap/tests/.gitignore
deleted file mode 100644
index 60baa9c..0000000
--- a/src/leap/bitmask/mail/imap/tests/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-data/*
diff --git a/src/leap/bitmask/mail/imap/tests/getmail b/src/leap/bitmask/mail/imap/tests/getmail
deleted file mode 100755
index dd3fa0b..0000000
--- a/src/leap/bitmask/mail/imap/tests/getmail
+++ /dev/null
@@ -1,344 +0,0 @@
-#!/usr/bin/env python
-
-# Copyright (c) Twisted Matrix Laboratories.
-# See LICENSE in twisted for details.
-
-# Modifications by LEAP Developers 2014 to fit
-# Bitmask configuration settings.
-"""
-Simple IMAP4 client which displays the subjects of all messages in a
-particular mailbox.
-"""
-
-import os
-import sys
-
-from twisted.internet import protocol
-from twisted.internet import ssl
-from twisted.internet import defer
-from twisted.internet import stdio
-from twisted.mail import imap4
-from twisted.protocols import basic
-from twisted.python import log
-
-# Global options stored here from main
-_opts = {}
-
-
-class TrivialPrompter(basic.LineReceiver):
- from os import linesep as delimiter
-
- promptDeferred = None
-
- def prompt(self, msg):
- assert self.promptDeferred is None
- self.display(msg)
- self.promptDeferred = defer.Deferred()
- return self.promptDeferred
-
- def display(self, msg):
- self.transport.write(msg)
-
- def lineReceived(self, line):
- if self.promptDeferred is None:
- return
- d, self.promptDeferred = self.promptDeferred, None
- d.callback(line)
-
-
-class SimpleIMAP4Client(imap4.IMAP4Client):
- """
- A client with callbacks for greeting messages from an IMAP server.
- """
- greetDeferred = None
-
- def serverGreeting(self, caps):
- self.serverCapabilities = caps
- if self.greetDeferred is not None:
- d, self.greetDeferred = self.greetDeferred, None
- d.callback(self)
-
-
-class SimpleIMAP4ClientFactory(protocol.ClientFactory):
- usedUp = False
-
- protocol = SimpleIMAP4Client
-
- def __init__(self, username, onConn):
- self.ctx = ssl.ClientContextFactory()
-
- self.username = username
- self.onConn = onConn
-
- def buildProtocol(self, addr):
- """
- Initiate the protocol instance. Since we are building a simple IMAP
- client, we don't bother checking what capabilities the server has. We
- just add all the authenticators twisted.mail has.
- """
- assert not self.usedUp
- self.usedUp = True
-
- p = self.protocol(self.ctx)
- p.factory = self
- p.greetDeferred = self.onConn
-
- p.registerAuthenticator(imap4.PLAINAuthenticator(self.username))
- p.registerAuthenticator(imap4.LOGINAuthenticator(self.username))
- p.registerAuthenticator(
- imap4.CramMD5ClientAuthenticator(self.username))
-
- return p
-
- def clientConnectionFailed(self, connector, reason):
- d, self.onConn = self.onConn, None
- d.errback(reason)
-
-
-def cbServerGreeting(proto, username, password):
- """
- Initial callback - invoked after the server sends us its greet message.
- """
- # Hook up stdio
- tp = TrivialPrompter()
- stdio.StandardIO(tp)
-
- # And make it easily accessible
- proto.prompt = tp.prompt
- proto.display = tp.display
-
- # Try to authenticate securely
- return proto.authenticate(
- password).addCallback(
- cbAuthentication,
- proto).addErrback(
- ebAuthentication, proto, username, password
- )
-
-
-def ebConnection(reason):
- """
- Fallback error-handler. If anything goes wrong, log it and quit.
- """
- log.startLogging(sys.stdout)
- log.err(reason)
- return reason
-
-
-def cbAuthentication(result, proto):
- """
- Callback after authentication has succeeded.
-
- Lists a bunch of mailboxes.
- """
- return proto.list("", "*"
- ).addCallback(cbMailboxList, proto
- )
-
-
-def ebAuthentication(failure, proto, username, password):
- """
- Errback invoked when authentication fails.
-
- If it failed because no SASL mechanisms match, offer the user the choice
- of logging in insecurely.
-
- If you are trying to connect to your Gmail account, you will be here!
- """
- failure.trap(imap4.NoSupportedAuthentication)
- return InsecureLogin(proto, username, password)
-
-
-def InsecureLogin(proto, username, password):
- """
- insecure-login.
- """
- return proto.login(username, password
- ).addCallback(cbAuthentication, proto
- )
-
-
-def cbMailboxList(result, proto):
- """
- Callback invoked when a list of mailboxes has been retrieved.
- If we have a selected mailbox in the global options, we directly pick it.
- Otherwise, we offer a prompt to let user choose one.
- """
- all_mbox_list = [e[2] for e in result]
- s = '\n'.join(['%d. %s' % (n + 1, m) for (n, m) in zip(range(len(all_mbox_list)), all_mbox_list)])
- if not s:
- return defer.fail(Exception("No mailboxes exist on server!"))
-
- selected_mailbox = _opts.get('mailbox')
-
- if not selected_mailbox:
- return proto.prompt(s + "\nWhich mailbox? [1] "
- ).addCallback(cbPickMailbox, proto, all_mbox_list
- )
- else:
- mboxes_lower = map(lambda s: s.lower(), all_mbox_list)
- index = mboxes_lower.index(selected_mailbox.lower()) + 1
- return cbPickMailbox(index, proto, all_mbox_list)
-
-
-def cbPickMailbox(result, proto, mboxes):
- """
- When the user selects a mailbox, "examine" it.
- """
- mbox = mboxes[int(result or '1') - 1]
- return proto.examine(mbox
- ).addCallback(cbExamineMbox, proto
- )
-
-
-def cbExamineMbox(result, proto):
- """
- Callback invoked when examine command completes.
-
- Retrieve the subject header of every message in the mailbox.
- """
- return proto.fetchSpecific('1:*',
- headerType='HEADER.FIELDS',
- headerArgs=['SUBJECT'],
- ).addCallback(cbFetch, proto,
- )
-
-
-def cbFetch(result, proto):
- """
- Display a listing of the messages in the mailbox, based on the collected
- headers.
- """
- selected_subject = _opts.get('subject', None)
- index = None
-
- if result:
- keys = result.keys()
- keys.sort()
-
- if selected_subject:
- for k in keys:
- # remove 'Subject: ' preffix plus eol
- subject = result[k][0][2][9:].rstrip('\r\n')
- if subject.lower() == selected_subject.lower():
- index = k
- break
- else:
- for k in keys:
- proto.display('%s %s' % (k, result[k][0][2]))
- else:
- print "Hey, an empty mailbox!"
-
- if not index:
- return proto.prompt("\nWhich message? [1] (Q quits) "
- ).addCallback(cbPickMessage, proto)
- else:
- return cbPickMessage(index, proto)
-
-
-def cbPickMessage(result, proto):
- """
- Pick a message.
- """
- if result == "Q":
- print "Bye!"
- return proto.logout()
-
- return proto.fetchSpecific(
- '%s' % result,
- headerType='',
- headerArgs=['BODY.PEEK[]'],
- ).addCallback(cbShowmessage, proto)
-
-
-def cbShowmessage(result, proto):
- """
- Display message.
- """
- if result:
- keys = result.keys()
- keys.sort()
- for k in keys:
- proto.display('%s %s' % (k, result[k][0][2]))
- else:
- print "Hey, an empty message!"
-
- return proto.logout()
-
-
-def cbClose(result):
- """
- Close the connection when we finish everything.
- """
- from twisted.internet import reactor
- reactor.stop()
-
-
-def main():
- import argparse
- import ConfigParser
- import sys
- from twisted.internet import reactor
-
- description = (
- 'Get messages from a LEAP IMAP Proxy.\nThis is a '
- 'debugging tool, do not use this to retrieve any sensitive '
- 'information, or we will send ninjas to your house!')
- epilog = (
- 'In case you want to automate the usage of this utility '
- 'you can place your credentials in a file pointed by '
- 'BITMASK_CREDENTIALS. You need to have a [Credentials] '
- 'section, with username=<user@provider> and password fields')
-
- parser = argparse.ArgumentParser(description=description, epilog=epilog)
- credentials = os.environ.get('BITMASK_CREDENTIALS')
-
- if credentials:
- try:
- config = ConfigParser.ConfigParser()
- config.read(credentials)
- username = config.get('Credentials', 'username')
- password = config.get('Credentials', 'password')
- except Exception, e:
- print "Error reading credentials file: {0}".format(e)
- sys.exit()
- else:
- parser.add_argument('username', type=str)
- parser.add_argument('password', type=str)
-
- parser.add_argument('--mailbox', dest='mailbox', default=None,
- help='Which mailbox to retrieve. Empty for interactive prompt.')
- parser.add_argument('--subject', dest='subject', default=None,
- help='A subject for retrieve a mail that matches. Empty for interactive prompt.')
-
- ns = parser.parse_args()
-
- if not credentials:
- username = ns.username
- password = ns.password
-
- _opts['mailbox'] = ns.mailbox
- _opts['subject'] = ns.subject
-
- hostname = "localhost"
- port = "1984"
-
- onConn = defer.Deferred(
- ).addCallback(cbServerGreeting, username, password
- ).addErrback(ebConnection
- ).addBoth(cbClose)
-
- factory = SimpleIMAP4ClientFactory(username, onConn)
-
- if port == '993':
- reactor.connectSSL(
- hostname, int(port), factory, ssl.ClientContextFactory())
- else:
- if not port:
- port = 143
- reactor.connectTCP(hostname, int(port), factory)
- reactor.run()
-
-
-if __name__ == '__main__':
- main()
diff --git a/src/leap/bitmask/mail/imap/tests/imapclient.py b/src/leap/bitmask/mail/imap/tests/imapclient.py
deleted file mode 100755
index c353cee..0000000
--- a/src/leap/bitmask/mail/imap/tests/imapclient.py
+++ /dev/null
@@ -1,207 +0,0 @@
-#!/usr/bin/env python
-
-# Copyright (c) Twisted Matrix Laboratories.
-# See LICENSE for details.
-
-
-"""
-Simple IMAP4 client which connects to our custome
-IMAP4 server: imapserver.py.
-"""
-
-import sys
-
-from twisted.internet import protocol
-from twisted.internet import defer
-from twisted.internet import stdio
-from twisted.mail import imap4
-from twisted.protocols import basic
-from twisted.python import util
-from twisted.python import log
-
-
-class TrivialPrompter(basic.LineReceiver):
- # from os import linesep as delimiter
-
- promptDeferred = None
-
- def prompt(self, msg):
- assert self.promptDeferred is None
- self.display(msg)
- self.promptDeferred = defer.Deferred()
- return self.promptDeferred
-
- def display(self, msg):
- self.transport.write(msg)
-
- def lineReceived(self, line):
- if self.promptDeferred is None:
- return
- d, self.promptDeferred = self.promptDeferred, None
- d.callback(line)
-
-
-class SimpleIMAP4Client(imap4.IMAP4Client):
-
- """
- Add callbacks when the client receives greeting messages from
- an IMAP server.
- """
- greetDeferred = None
-
- def serverGreeting(self, caps):
- self.serverCapabilities = caps
- if self.greetDeferred is not None:
- d, self.greetDeferred = self.greetDeferred, None
- d.callback(self)
-
-
-class SimpleIMAP4ClientFactory(protocol.ClientFactory):
- usedUp = False
- protocol = SimpleIMAP4Client
-
- def __init__(self, username, onConn):
- self.username = username
- self.onConn = onConn
-
- def buildProtocol(self, addr):
- assert not self.usedUp
- self.usedUp = True
-
- p = self.protocol()
- p.factory = self
- p.greetDeferred = self.onConn
-
- p.registerAuthenticator(imap4.PLAINAuthenticator(self.username))
- p.registerAuthenticator(imap4.LOGINAuthenticator(self.username))
- p.registerAuthenticator(
- imap4.CramMD5ClientAuthenticator(self.username))
-
- return p
-
- def clientConnectionFailed(self, connector, reason):
- d, self.onConn = self.onConn, None
- d.errback(reason)
-
-
-def cbServerGreeting(proto, username, password):
- """
- Initial callback - invoked after the server sends us its greet message.
- """
- # Hook up stdio
- tp = TrivialPrompter()
- stdio.StandardIO(tp)
-
- # And make it easily accessible
- proto.prompt = tp.prompt
- proto.display = tp.display
-
- # Try to authenticate securely
- return proto.authenticate(
- password).addCallback(
- cbAuthentication, proto).addErrback(
- ebAuthentication, proto, username, password)
-
-
-def ebConnection(reason):
- """
- Fallback error-handler. If anything goes wrong, log it and quit.
- """
- log.startLogging(sys.stdout)
- log.err(reason)
- return reason
-
-
-def cbAuthentication(result, proto):
- """
- Callback after authentication has succeeded.
- List a bunch of mailboxes.
- """
- return proto.list("", "*"
- ).addCallback(cbMailboxList, proto
- )
-
-
-def ebAuthentication(failure, proto, username, password):
- """
- Errback invoked when authentication fails.
- If it failed because no SASL mechanisms match, offer the user the choice
- of logging in insecurely.
- If you are trying to connect to your Gmail account, you will be here!
- """
- failure.trap(imap4.NoSupportedAuthentication)
- return proto.prompt(
- "No secure authentication available. Login insecurely? (y/N) "
- ).addCallback(cbInsecureLogin, proto, username, password
- )
-
-
-def cbInsecureLogin(result, proto, username, password):
- """
- Callback for "insecure-login" prompt.
- """
- if result.lower() == "y":
- # If they said yes, do it.
- return proto.login(username, password
- ).addCallback(cbAuthentication, proto
- )
- return defer.fail(Exception("Login failed for security reasons."))
-
-
-def cbMailboxList(result, proto):
- """
- Callback invoked when a list of mailboxes has been retrieved.
- """
- result = [e[2] for e in result]
- s = '\n'.join(
- ['%d. %s' % (n + 1, m) for (n, m) in zip(range(len(result)), result)])
- if not s:
- return defer.fail(Exception("No mailboxes exist on server!"))
- return proto.prompt(s + "\nWhich mailbox? [1] "
- ).addCallback(cbPickMailbox, proto, result
- )
-
-
-def cbPickMailbox(result, proto, mboxes):
- """
- When the user selects a mailbox, "examine" it.
- """
- mbox = mboxes[int(result or '1') - 1]
- return proto.status(mbox, 'MESSAGES', 'UNSEEN'
- ).addCallback(cbMboxStatus, proto)
-
-
-def cbMboxStatus(result, proto):
- print "You have %s messages (%s unseen)!" % (
- result['MESSAGES'], result['UNSEEN'])
- return proto.logout()
-
-
-def cbClose(result):
- """
- Close the connection when we finish everything.
- """
- from twisted.internet import reactor
- reactor.stop()
-
-
-def main():
- hostname = raw_input('IMAP4 Server Hostname: ')
- port = raw_input('IMAP4 Server Port (the default is 143): ')
- username = raw_input('IMAP4 Username: ')
- password = util.getPassword('IMAP4 Password: ')
-
- onConn = defer.Deferred(
- ).addCallback(cbServerGreeting, username, password
- ).addErrback(ebConnection
- ).addBoth(cbClose)
-
- factory = SimpleIMAP4ClientFactory(username, onConn)
-
- from twisted.internet import reactor
- conn = reactor.connectTCP(hostname, int(port), factory)
- reactor.run()
-
-
-if __name__ == '__main__':
- main()
diff --git a/src/leap/bitmask/mail/imap/tests/regressions_mime_struct b/src/leap/bitmask/mail/imap/tests/regressions_mime_struct
deleted file mode 100755
index 0332664..0000000
--- a/src/leap/bitmask/mail/imap/tests/regressions_mime_struct
+++ /dev/null
@@ -1,461 +0,0 @@
-#!/usr/bin/env python
-
-# -*- coding: utf-8 -*-
-# regression_mime_struct
-# Copyright (C) 2014 LEAP
-# Copyright (c) Twisted Matrix Laboratories.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-"""
-Simple Regression Tests for checking MIME struct handling using IMAP4 client.
-
-Iterates trough all mails under a given folder and tries to APPEND them to
-the server being tested. After FETCHING the pushed message, it compares
-the received version with the one that was saved, and exits with an error
-code if they do not match.
-"""
-import os
-import StringIO
-import sys
-
-from email.parser import Parser
-
-from twisted.internet import protocol
-from twisted.internet import ssl
-from twisted.internet import defer
-from twisted.internet import stdio
-from twisted.mail import imap4
-from twisted.protocols import basic
-from twisted.python import log
-
-
-REGRESSIONS_FOLDER = os.environ.get(
- "REGRESSIONS_FOLDER", "regressions_test")
-print "[+] Using regressions folder:", REGRESSIONS_FOLDER
-
-parser = Parser()
-
-
-def get_msg_parts(raw):
- """
- Return a representation of the parts of a message suitable for
- comparison.
-
- :param raw: string for the message
- :type raw: str
- """
- m = parser.parsestr(raw)
- return [dict(part.items())
- if part.is_multipart()
- else part.get_payload()
- for part in m.walk()]
-
-
-def compare_msg_parts(a, b):
- """
- Compare two sequences of parts of messages.
-
- :param a: part sequence for message a
- :param b: part sequence for message b
-
- :return: True if both message sequences are equivalent.
- :rtype: bool
- """
- # XXX This could be smarter and show the differences in the
- # different parts when/where they differ.
- #import pprint; pprint.pprint(a[0])
- #import pprint; pprint.pprint(b[0])
-
- def lowerkey(d):
- return dict((k.lower(), v.replace('\r', ''))
- for k, v in d.iteritems())
-
- def eq(x, y):
- # For dicts, we compare a variation with their keys
- # in lowercase, and \r removed from their values
- if all(map(lambda i: isinstance(i, dict), (x, y))):
- x, y = map(lowerkey, (x, y))
- return x == y
-
- compare_vector = map(lambda tup: eq(tup[0], tup[1]), zip(a, b))
- all_match = all(compare_vector)
-
- if not all_match:
- print "PARTS MISMATCH!"
- print "vector: ", compare_vector
- index = compare_vector.index(False)
- from pprint import pprint
- print "Expected:"
- pprint(a[index])
- print ("***")
- print "Found:"
- pprint(b[index])
- print
-
- return all_match
-
-
-def get_fd(string):
- """
- Return a file descriptor with the passed string
- as content.
- """
- fd = StringIO.StringIO()
- fd.write(string)
- fd.seek(0)
- return fd
-
-
-class TrivialPrompter(basic.LineReceiver):
- promptDeferred = None
-
- def prompt(self, msg):
- assert self.promptDeferred is None
- self.display(msg)
- self.promptDeferred = defer.Deferred()
- return self.promptDeferred
-
- def display(self, msg):
- self.transport.write(msg)
-
- def lineReceived(self, line):
- if self.promptDeferred is None:
- return
- d, self.promptDeferred = self.promptDeferred, None
- d.callback(line)
-
-
-class SimpleIMAP4Client(imap4.IMAP4Client):
- """
- A client with callbacks for greeting messages from an IMAP server.
- """
- greetDeferred = None
-
- def serverGreeting(self, caps):
- self.serverCapabilities = caps
- if self.greetDeferred is not None:
- d, self.greetDeferred = self.greetDeferred, None
- d.callback(self)
-
-
-class SimpleIMAP4ClientFactory(protocol.ClientFactory):
- usedUp = False
- protocol = SimpleIMAP4Client
-
- def __init__(self, username, onConn):
- self.ctx = ssl.ClientContextFactory()
-
- self.username = username
- self.onConn = onConn
-
- def buildProtocol(self, addr):
- """
- Initiate the protocol instance. Since we are building a simple IMAP
- client, we don't bother checking what capabilities the server has. We
- just add all the authenticators twisted.mail has. Note: Gmail no
- longer uses any of the methods below, it's been using XOAUTH since
- 2010.
- """
- assert not self.usedUp
- self.usedUp = True
-
- p = self.protocol(self.ctx)
- p.factory = self
- p.greetDeferred = self.onConn
-
- p.registerAuthenticator(imap4.PLAINAuthenticator(self.username))
- p.registerAuthenticator(imap4.LOGINAuthenticator(self.username))
- p.registerAuthenticator(
- imap4.CramMD5ClientAuthenticator(self.username))
-
- return p
-
- def clientConnectionFailed(self, connector, reason):
- d, self.onConn = self.onConn, None
- d.errback(reason)
-
-
-def cbServerGreeting(proto, username, password):
- """
- Initial callback - invoked after the server sends us its greet message.
- """
- # Hook up stdio
- tp = TrivialPrompter()
- stdio.StandardIO(tp)
-
- # And make it easily accessible
- proto.prompt = tp.prompt
- proto.display = tp.display
-
- # Try to authenticate securely
- return proto.authenticate(
- password).addCallback(
- cbAuthentication,
- proto).addErrback(
- ebAuthentication, proto, username, password
- )
-
-
-def ebConnection(reason):
- """
- Fallback error-handler. If anything goes wrong, log it and quit.
- """
- log.startLogging(sys.stdout)
- log.err(reason)
- return reason
-
-
-def cbAuthentication(result, proto):
- """
- Callback after authentication has succeeded.
-
- Lists a bunch of mailboxes.
- """
- return proto.select(
- REGRESSIONS_FOLDER
- ).addCallback(
- cbSelectMbox, proto
- ).addErrback(
- ebSelectMbox, proto, REGRESSIONS_FOLDER)
-
-
-def ebAuthentication(failure, proto, username, password):
- """
- Errback invoked when authentication fails.
-
- If it failed because no SASL mechanisms match, offer the user the choice
- of logging in insecurely.
-
- If you are trying to connect to your Gmail account, you will be here!
- """
- failure.trap(imap4.NoSupportedAuthentication)
- return InsecureLogin(proto, username, password)
-
-
-def InsecureLogin(proto, username, password):
- """
- Raise insecure-login error.
- """
- return proto.login(
- username, password
- ).addCallback(
- cbAuthentication, proto)
-
-
-def cbSelectMbox(result, proto):
- """
- Callback invoked when select command finishes successfully.
-
- If any message is in the test folder, it will flag them as deleted and
- expunge.
- If no messages found, it will start with the APPEND tests.
- """
- print "SELECT: %s EXISTS " % result.get("EXISTS", "??")
-
- if result["EXISTS"] != 0:
- # Flag as deleted, expunge, and do an examine again.
- print "There is mail here, will delete..."
- return cbDeleteAndExpungeTestFolder(proto)
-
- else:
- return cbAppendNextMessage(proto)
-
-
-def ebSelectMbox(failure, proto, folder):
- """
- Errback invoked when the examine command fails.
-
- Creates the folder.
- """
- log.err(failure)
- log.msg("Folder %r does not exist. Creating..." % (folder,))
- return proto.create(folder).addCallback(cbAuthentication, proto)
-
-
-def ebExpunge(failure):
- log.err(failure)
-
-
-def cbDeleteAndExpungeTestFolder(proto):
- """
- Callback invoked fom cbExamineMbox when the number of messages in the
- mailbox is not zero. It flags all messages as deleted and expunge the
- mailbox.
- """
- return proto.setFlags(
- "1:*", ("\\Deleted",)
- ).addCallback(
- lambda r: proto.expunge()
- ).addCallback(
- cbExpunge, proto
- ).addErrback(
- ebExpunge)
-
-
-def cbExpunge(result, proto):
- return proto.select(
- REGRESSIONS_FOLDER
- ).addCallback(
- cbSelectMbox, proto
- ).addErrback(ebSettingDeleted, proto)
-
-
-def ebSettingDeleted(failure, proto):
- """
- Report errors during deletion of messages in the mailbox.
- """
- print failure.getTraceback()
-
-
-def cbAppendNextMessage(proto):
- """
- Appends the next message in the global queue to the test folder.
- """
- # 1. Get the next test message from global tuple.
- try:
- next_sample = SAMPLES.pop()
- except IndexError:
- # we're done!
- return proto.logout()
-
- print "\nAPPEND %s" % (next_sample,)
- raw = open(next_sample).read()
- msg = get_fd(raw)
- return proto.append(
- REGRESSIONS_FOLDER, msg
- ).addCallback(
- lambda r: proto.select(REGRESSIONS_FOLDER)
- ).addCallback(
- cbAppend, proto, raw
- ).addErrback(
- ebAppend, proto, raw)
-
-
-def cbAppend(result, proto, orig_msg):
- """
- Fetches the message right after an append.
- """
- # XXX keep account of highest UID
- uid = "1:*"
-
- return proto.fetchSpecific(
- '%s' % uid,
- headerType='',
- headerArgs=['BODY.PEEK[]'],
- ).addCallback(
- cbCompareMessage, proto, orig_msg
- ).addErrback(ebAppend, proto, orig_msg)
-
-
-def ebAppend(failure, proto, raw):
- """
- Errorback for the append operation
- """
- print "ERROR WHILE APPENDING!"
- print failure.getTraceback()
-
-
-def cbPickMessage(result, proto):
- """
- Pick a message.
- """
- return proto.fetchSpecific(
- '%s' % result,
- headerType='',
- headerArgs=['BODY.PEEK[]'],
- ).addCallback(cbCompareMessage, proto)
-
-
-def cbCompareMessage(result, proto, raw):
- """
- Display message and compare it with the original one.
- """
- parts_orig = get_msg_parts(raw)
-
- if result:
- keys = result.keys()
- keys.sort()
- else:
- print "[-] GOT NO RESULT"
- return proto.logout()
-
- latest = max(keys)
-
- fetched_msg = result[latest][0][2]
- parts_fetched = get_msg_parts(fetched_msg)
-
- equal = compare_msg_parts(
- parts_orig,
- parts_fetched)
-
- if equal:
- print "[+] MESSAGES MATCH"
- return cbAppendNextMessage(proto)
- else:
- print "[-] ERROR: MESSAGES DO NOT MATCH !!!"
- print " ABORTING COMPARISON..."
- # FIXME logout and print the subject ...
- return proto.logout()
-
-
-def cbClose(result):
- """
- Close the connection when we finish everything.
- """
- from twisted.internet import reactor
- reactor.stop()
-
-
-def main():
- import glob
- import sys
-
- if len(sys.argv) != 4:
- print "Usage: regressions <user> <pass> <samples-folder>"
- sys.exit()
-
- hostname = "localhost"
- port = "1984"
- username = sys.argv[1]
- password = sys.argv[2]
-
- samplesdir = sys.argv[3]
-
- if not os.path.isdir(samplesdir):
- print ("Could not find samples folder! "
- "Make sure of copying mail_breaker contents there.")
- sys.exit()
-
- samples = glob.glob(samplesdir + '/*')
-
- global SAMPLES
- SAMPLES = []
- SAMPLES += samples
-
- onConn = defer.Deferred(
- ).addCallback(
- cbServerGreeting, username, password
- ).addErrback(
- ebConnection
- ).addBoth(cbClose)
-
- factory = SimpleIMAP4ClientFactory(username, onConn)
-
- from twisted.internet import reactor
- reactor.connectTCP(hostname, int(port), factory)
- reactor.run()
-
-
-if __name__ == '__main__':
- main()
diff --git a/src/leap/bitmask/mail/imap/tests/rfc822.message b/src/leap/bitmask/mail/imap/tests/rfc822.message
deleted file mode 120000
index b19cc28..0000000
--- a/src/leap/bitmask/mail/imap/tests/rfc822.message
+++ /dev/null
@@ -1 +0,0 @@
-../../tests/rfc822.message \ No newline at end of file
diff --git a/src/leap/bitmask/mail/imap/tests/rfc822.multi-minimal.message b/src/leap/bitmask/mail/imap/tests/rfc822.multi-minimal.message
deleted file mode 120000
index e0aa678..0000000
--- a/src/leap/bitmask/mail/imap/tests/rfc822.multi-minimal.message
+++ /dev/null
@@ -1 +0,0 @@
-../../tests/rfc822.multi-minimal.message \ No newline at end of file
diff --git a/src/leap/bitmask/mail/imap/tests/rfc822.multi-nested.message b/src/leap/bitmask/mail/imap/tests/rfc822.multi-nested.message
deleted file mode 120000
index 306d0de..0000000
--- a/src/leap/bitmask/mail/imap/tests/rfc822.multi-nested.message
+++ /dev/null
@@ -1 +0,0 @@
-../../tests/rfc822.multi-nested.message \ No newline at end of file
diff --git a/src/leap/bitmask/mail/imap/tests/rfc822.multi-signed.message b/src/leap/bitmask/mail/imap/tests/rfc822.multi-signed.message
deleted file mode 120000
index 4172244..0000000
--- a/src/leap/bitmask/mail/imap/tests/rfc822.multi-signed.message
+++ /dev/null
@@ -1 +0,0 @@
-../../tests/rfc822.multi-signed.message \ No newline at end of file
diff --git a/src/leap/bitmask/mail/imap/tests/rfc822.multi.message b/src/leap/bitmask/mail/imap/tests/rfc822.multi.message
deleted file mode 120000
index 62057d2..0000000
--- a/src/leap/bitmask/mail/imap/tests/rfc822.multi.message
+++ /dev/null
@@ -1 +0,0 @@
-../../tests/rfc822.multi.message \ No newline at end of file
diff --git a/src/leap/bitmask/mail/imap/tests/rfc822.plain.message b/src/leap/bitmask/mail/imap/tests/rfc822.plain.message
deleted file mode 120000
index 5bab0e8..0000000
--- a/src/leap/bitmask/mail/imap/tests/rfc822.plain.message
+++ /dev/null
@@ -1 +0,0 @@
-../../tests/rfc822.plain.message \ No newline at end of file
diff --git a/src/leap/bitmask/mail/imap/tests/stress_tests_imap.zsh b/src/leap/bitmask/mail/imap/tests/stress_tests_imap.zsh
deleted file mode 100755
index 544faca..0000000
--- a/src/leap/bitmask/mail/imap/tests/stress_tests_imap.zsh
+++ /dev/null
@@ -1,178 +0,0 @@
-#!/bin/zsh
-# BATCH STRESS TEST FOR IMAP ----------------------
-# http://imgs.xkcd.com/comics/science.jpg
-#
-# Run imaptest against a LEAP IMAP server
-# for a fixed period of time, and collect output.
-#
-# Author: Kali Kaneko
-# Date: 2014 01 26
-#
-# To run, you need to have `imaptest` in your path.
-# See:
-# http://www.imapwiki.org/ImapTest/Installation
-#
-# For the tests, I'm using a 10MB file sample that
-# can be downloaded from:
-# http://www.dovecot.org/tmp/dovecot-crlf
-#
-# Want to contribute to benchmarking?
-#
-# 1. Create a pristine account in a bitmask provider.
-#
-# 2. Launch your bitmask client, with different flags
-# if you desire.
-#
-# For example to try the nosync flag in sqlite:
-#
-# LEAP_SQLITE_NOSYNC=1 bitmask --debug -N --offline -l /tmp/leap.log
-#
-# 3. Run at several points in time (ie: just after
-# launching the bitmask client. one minute after,
-# ten minutes after)
-#
-# mkdir data
-# cd data
-# ../leap_tests_imap.zsh | tee sqlite_nosync_run2.log
-#
-# 4. Submit your results to: kali at leap dot se
-# together with the logs of the bitmask run.
-#
-# Please provide also details about your system, and
-# the type of hard disk setup you are running against.
-#
-
-# ------------------------------------------------
-# Edit these variables if you are too lazy to pass
-# the user and mbox as parameters. Like me.
-
-USER="test_f14@dev.bitmask.net"
-MBOX="~/leap/imaptest/data/dovecot-crlf"
-
-HOST="localhost"
-PORT="1984"
-
-# in case you have it aliased
-GREP="/bin/grep"
-IMAPTEST="imaptest"
-
-# -----------------------------------------------
-#
-# These should be kept constant across benchmarking
-# runs across different machines, for comparability.
-
-DURATION=200
-NUM_MSG=200
-
-
-# TODO add another function, and a cli flag, to be able
-# to take several aggretates spaced in time, along a period
-# of several minutes.
-
-imaptest_cmd() {
- stdbuf -o0 ${IMAPTEST} user=${USER} pass=1234 host=${HOST} \
- port=${PORT} mbox=${MBOX} clients=1 msgs=${NUM_MSG} \
- no_pipelining 2>/dev/null
-}
-
-stress_imap() {
- mkfifo imap_pipe
- cat imap_pipe | tee output &
- imaptest_cmd >> imap_pipe
-}
-
-wait_and_kill() {
- while :
- do
- sleep $DURATION
- pkill -2 imaptest
- rm imap_pipe
- break
- done
-}
-
-print_results() {
- sleep 1
- echo
- echo
- echo "AGGREGATED RESULTS"
- echo "----------------------"
- echo "\tavg\tstdev"
- $GREP "avg" ./output | sed -e 's/^ *//g' -e 's/ *$//g' | \
- gawk '
-function avg(data, count) {
- sum=0;
- for( x=0; x <= count-1; x++) {
- sum += data[x];
- }
- return sum/count;
-}
-function std_dev(data, count) {
- sum=0;
- for( x=0; x <= count-1; x++) {
- sum += data[x];
- }
- average = sum/count;
-
- sumsq=0;
- for( x=0; x <= count-1; x++) {
- sumsq += (data[x] - average)^2;
- }
- return sqrt(sumsq/count);
-}
-BEGIN {
- cnt = 0
-} END {
-
-printf("LOGI:\t%04.2lf\t%04.2f\n", avg(array[1], NR), std_dev(array[1], NR));
-printf("LIST:\t%04.2lf\t%04.2f\n", avg(array[2], NR), std_dev(array[2], NR));
-printf("STAT:\t%04.2lf\t%04.2f\n", avg(array[3], NR), std_dev(array[3], NR));
-printf("SELE:\t%04.2lf\t%04.2f\n", avg(array[4], NR), std_dev(array[4], NR));
-printf("FETC:\t%04.2lf\t%04.2f\n", avg(array[5], NR), std_dev(array[5], NR));
-printf("FET2:\t%04.2lf\t%04.2f\n", avg(array[6], NR), std_dev(array[6], NR));
-printf("STOR:\t%04.2lf\t%04.2f\n", avg(array[7], NR), std_dev(array[7], NR));
-printf("DELE:\t%04.2lf\t%04.2f\n", avg(array[8], NR), std_dev(array[8], NR));
-printf("EXPU:\t%04.2lf\t%04.2f\n", avg(array[9], NR), std_dev(array[9], NR));
-printf("APPE:\t%04.2lf\t%04.2f\n", avg(array[10], NR), std_dev(array[10], NR));
-printf("LOGO:\t%04.2lf\t%04.2f\n", avg(array[11], NR), std_dev(array[11], NR));
-
-print ""
-print "TOT samples", NR;
-}
-{
- it = cnt++;
- array[1][it] = $1;
- array[2][it] = $2;
- array[3][it] = $3;
- array[4][it] = $4;
- array[5][it] = $5;
- array[6][it] = $6;
- array[7][it] = $7;
- array[8][it] = $8;
- array[9][it] = $9;
- array[10][it] = $10;
- array[11][it] = $11;
-}'
-}
-
-
-{ test $1 = "--help" } && {
- echo "Usage: $0 [user@provider] [/path/to/sample.mbox]"
- exit 0
-}
-
-# If the first parameter is passed, take it as the user
-{ test $1 } && {
- USER=$1
-}
-
-# If the second parameter is passed, take it as the mbox
-{ test $2 } && {
- MBOX=$2
-}
-
-echo "[+] LEAP IMAP TESTS"
-echo "[+] Running imaptest for $DURATION seconds with $NUM_MSG messages"
-wait_and_kill &
-stress_imap
-print_results
diff --git a/src/leap/bitmask/mail/imap/tests/test_imap.py b/src/leap/bitmask/mail/imap/tests/test_imap.py
deleted file mode 100644
index 9cca17f..0000000
--- a/src/leap/bitmask/mail/imap/tests/test_imap.py
+++ /dev/null
@@ -1,1060 +0,0 @@
-# -*- coding: utf-8 -*-
-# test_imap.py
-# Copyright (C) 2013 LEAP
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-"""
-Test case for leap.email.imap.server
-TestCases taken from twisted tests and modified to make them work
-against our implementation of the IMAPAccount.
-
-@authors: Kali Kaneko, <kali@leap.se>
-XXX add authors from the original twisted tests.
-
-@license: GPLv3, see included LICENSE file
-"""
-# XXX review license of the original tests!!!
-import os
-import string
-import types
-
-
-from twisted.mail import imap4
-from twisted.internet import defer
-from twisted.python import util
-from twisted.python import failure
-
-from twisted import cred
-
-from leap.mail.imap.mailbox import IMAPMailbox
-from leap.mail.imap.messages import CaseInsensitiveDict
-from leap.mail.testing.imap import IMAP4HelperMixin
-
-
-TEST_USER = "testuser@leap.se"
-TEST_PASSWD = "1234"
-
-
-def strip(f):
- return lambda result, f=f: f()
-
-
-def sortNest(l):
- l = l[:]
- l.sort()
- for i in range(len(l)):
- if isinstance(l[i], types.ListType):
- l[i] = sortNest(l[i])
- elif isinstance(l[i], types.TupleType):
- l[i] = tuple(sortNest(list(l[i])))
- return l
-
-
-class TestRealm:
- """
- A minimal auth realm for testing purposes only
- """
- theAccount = None
-
- def requestAvatar(self, avatarId, mind, *interfaces):
- return imap4.IAccount, self.theAccount, lambda: None
-
-#
-# TestCases
-#
-
-# DEBUG ---
-# from twisted.internet.base import DelayedCall
-# DelayedCall.debug = True
-
-
-class LEAPIMAP4ServerTestCase(IMAP4HelperMixin):
-
- """
- Tests for the generic behavior of the LEAPIMAP4Server
- which, right now, it's just implemented in this test file as
- LEAPIMAPServer. We will move the implementation, together with
- authentication bits, to leap.mail.imap.server so it can be instantiated
- from the tac file.
-
- Right now this TestCase tries to mimmick as close as possible the
- organization from the twisted.mail.imap tests so we can achieve
- a complete implementation. The order in which they appear reflect
- the intended order of implementation.
- """
-
- #
- # mailboxes operations
- #
-
- def testCreate(self):
- """
- Test whether we can create mailboxes
- """
- succeed = ('testbox', 'test/box', 'test/', 'test/box/box', 'foobox')
- fail = ('testbox', 'test/box')
- acc = self.server.theAccount
-
- def cb():
- self.result.append(1)
-
- def eb(failure):
- self.result.append(0)
-
- def login():
- return self.client.login(TEST_USER, TEST_PASSWD)
-
- def create():
- create_deferreds = []
- for name in succeed + fail:
- d = self.client.create(name)
- d.addCallback(strip(cb)).addErrback(eb)
- create_deferreds.append(d)
- dd = defer.gatherResults(create_deferreds)
- dd.addCallbacks(self._cbStopClient, self._ebGeneral)
- return dd
-
- self.result = []
- d1 = self.connected.addCallback(strip(login))
- d1.addCallback(strip(create))
- d2 = self.loopback()
- d = defer.gatherResults([d1, d2], consumeErrors=True)
- d.addCallback(lambda _: acc.account.list_all_mailbox_names())
- return d.addCallback(self._cbTestCreate, succeed, fail)
-
- def _cbTestCreate(self, mailboxes, succeed, fail):
- self.assertEqual(self.result, [1] * len(succeed) + [0] * len(fail))
-
- answers = ([u'INBOX', u'testbox', u'test/box', u'test',
- u'test/box/box', 'foobox'])
- self.assertEqual(sorted(mailboxes), sorted([a for a in answers]))
-
- def testDelete(self):
- """
- Test whether we can delete mailboxes
- """
- def add_mailbox():
- return self.server.theAccount.addMailbox('test-delete/me')
-
- def login():
- return self.client.login(TEST_USER, TEST_PASSWD)
-
- def delete():
- return self.client.delete('test-delete/me')
-
- acc = self.server.theAccount.account
-
- d1 = self.connected.addCallback(add_mailbox)
- d1.addCallback(strip(login))
- d1.addCallbacks(strip(delete), self._ebGeneral)
- d1.addCallbacks(self._cbStopClient, self._ebGeneral)
- d2 = self.loopback()
- d = defer.gatherResults([d1, d2])
- d.addCallback(lambda _: acc.list_all_mailbox_names())
- d.addCallback(lambda mboxes: self.assertEqual(
- mboxes, ['INBOX']))
- return d
-
- def testIllegalInboxDelete(self):
- """
- Test what happens if we try to delete the user Inbox.
- We expect that operation to fail.
- """
- self.stashed = None
-
- def login():
- return self.client.login(TEST_USER, TEST_PASSWD)
-
- def delete():
- return self.client.delete('inbox')
-
- def stash(result):
- self.stashed = result
-
- d1 = self.connected.addCallback(strip(login))
- d1.addCallbacks(strip(delete), self._ebGeneral)
- d1.addBoth(stash)
- d1.addCallbacks(self._cbStopClient, self._ebGeneral)
- d2 = self.loopback()
- d = defer.gatherResults([d1, d2])
- d.addCallback(lambda _: self.failUnless(isinstance(self.stashed,
- failure.Failure)))
- return d
-
- def testNonExistentDelete(self):
- """
- Test what happens if we try to delete a non-existent mailbox.
- We expect an error raised stating 'No such mailbox'
- """
- def login():
- return self.client.login(TEST_USER, TEST_PASSWD)
-
- def delete():
- return self.client.delete('delete/me')
- self.failure = failure
-
- def deleteFailed(failure):
- self.failure = failure
-
- self.failure = None
- d1 = self.connected.addCallback(strip(login))
- d1.addCallback(strip(delete)).addErrback(deleteFailed)
- d1.addCallbacks(self._cbStopClient, self._ebGeneral)
- d2 = self.loopback()
- d = defer.gatherResults([d1, d2])
- d.addCallback(lambda _: self.assertTrue(
- str(self.failure.value).startswith('No such mailbox')))
- return d
-
- def testIllegalDelete(self):
- """
- Try deleting a mailbox with sub-folders, and \NoSelect flag set.
- An exception is expected.
- """
- acc = self.server.theAccount
-
- def login():
- return self.client.login(TEST_USER, TEST_PASSWD)
-
- def create_mailboxes():
- d1 = acc.addMailbox('delete')
- d2 = acc.addMailbox('delete/me')
- d = defer.gatherResults([d1, d2])
- return d
-
- def get_noselect_mailbox(mboxes):
- mbox = mboxes[0]
- return mbox.setFlags((r'\Noselect',))
-
- def delete_mbox(ignored):
- return self.client.delete('delete')
-
- def deleteFailed(failure):
- self.failure = failure
-
- self.failure = None
-
- d1 = self.connected.addCallback(strip(login))
- d1.addCallback(strip(create_mailboxes))
- d1.addCallback(get_noselect_mailbox)
-
- d1.addCallback(delete_mbox).addErrback(deleteFailed)
- d1.addCallbacks(self._cbStopClient, self._ebGeneral)
- d2 = self.loopback()
- d = defer.gatherResults([d1, d2])
- expected = ("Hierarchically inferior mailboxes exist "
- "and \\Noselect is set")
- d.addCallback(lambda _:
- self.assertTrue(self.failure is not None))
- d.addCallback(lambda _:
- self.assertEqual(str(self.failure.value), expected))
- return d
-
- # FIXME --- this test sometimes FAILS (timing issue).
- # Some of the deferreds used in the rename op is not waiting for the
- # operations properly
- def testRename(self):
- """
- Test whether we can rename a mailbox
- """
- def create_mbox():
- return self.server.theAccount.addMailbox('oldmbox')
-
- def login():
- return self.client.login(TEST_USER, TEST_PASSWD)
-
- def rename():
- return self.client.rename('oldmbox', 'newname')
-
- d1 = self.connected.addCallback(strip(create_mbox))
- d1.addCallback(strip(login))
- d1.addCallbacks(strip(rename), self._ebGeneral)
- d1.addCallbacks(self._cbStopClient, self._ebGeneral)
- d2 = self.loopback()
- d = defer.gatherResults([d1, d2])
- d.addCallback(lambda _:
- self.server.theAccount.account.list_all_mailbox_names())
- d.addCallback(lambda mboxes:
- self.assertItemsEqual(mboxes, ['INBOX', 'newname']))
- return d
-
- def testIllegalInboxRename(self):
- """
- Try to rename inbox. We expect it to fail. Then it would be not
- an inbox anymore, would it?
- """
- self.stashed = None
-
- def login():
- return self.client.login(TEST_USER, TEST_PASSWD)
-
- def rename():
- return self.client.rename('inbox', 'frotz')
-
- def stash(stuff):
- self.stashed = stuff
-
- d1 = self.connected.addCallback(strip(login))
- d1.addCallbacks(strip(rename), self._ebGeneral)
- d1.addBoth(stash)
- d1.addCallbacks(self._cbStopClient, self._ebGeneral)
- d2 = self.loopback()
- d = defer.gatherResults([d1, d2])
- d.addCallback(lambda _:
- self.failUnless(isinstance(
- self.stashed, failure.Failure)))
- return d
-
- def testHierarchicalRename(self):
- """
- Try to rename hierarchical mailboxes
- """
- acc = self.server.theAccount
-
- def add_mailboxes():
- return defer.gatherResults([
- acc.addMailbox('oldmbox/m1'),
- acc.addMailbox('oldmbox/m2')])
-
- def login():
- return self.client.login(TEST_USER, TEST_PASSWD)
-
- def rename():
- return self.client.rename('oldmbox', 'newname')
-
- d1 = self.connected.addCallback(strip(add_mailboxes))
- d1.addCallback(strip(login))
- d1.addCallbacks(strip(rename), self._ebGeneral)
- d1.addCallbacks(self._cbStopClient, self._ebGeneral)
- d2 = self.loopback()
- d = defer.gatherResults([d1, d2])
- d.addCallback(lambda _: acc.account.list_all_mailbox_names())
- return d.addCallback(self._cbTestHierarchicalRename)
-
- def _cbTestHierarchicalRename(self, mailboxes):
- expected = ['INBOX', 'newname/m1', 'newname/m2']
- self.assertEqual(sorted(mailboxes), sorted([s for s in expected]))
-
- def testSubscribe(self):
- """
- Test whether we can mark a mailbox as subscribed to
- """
- acc = self.server.theAccount
-
- def add_mailbox():
- return acc.addMailbox('this/mbox')
-
- def login():
- return self.client.login(TEST_USER, TEST_PASSWD)
-
- def subscribe():
- return self.client.subscribe('this/mbox')
-
- def get_subscriptions(ignored):
- return self.server.theAccount.getSubscriptions()
-
- d1 = self.connected.addCallback(strip(add_mailbox))
- d1.addCallback(strip(login))
- d1.addCallbacks(strip(subscribe), self._ebGeneral)
- d1.addCallbacks(self._cbStopClient, self._ebGeneral)
- d2 = self.loopback()
- d = defer.gatherResults([d1, d2])
- d.addCallback(get_subscriptions)
- d.addCallback(lambda subscriptions:
- self.assertEqual(subscriptions,
- ['this/mbox']))
- return d
-
- def testUnsubscribe(self):
- """
- Test whether we can unsubscribe from a set of mailboxes
- """
- acc = self.server.theAccount
-
- def add_mailboxes():
- return defer.gatherResults([
- acc.addMailbox('this/mbox'),
- acc.addMailbox('that/mbox')])
-
- def dc1():
- return acc.subscribe('this/mbox')
-
- def dc2():
- return acc.subscribe('that/mbox')
-
- def login():
- return self.client.login(TEST_USER, TEST_PASSWD)
-
- def unsubscribe():
- return self.client.unsubscribe('this/mbox')
-
- def get_subscriptions(ignored):
- return acc.getSubscriptions()
-
- d1 = self.connected.addCallback(strip(add_mailboxes))
- d1.addCallback(strip(login))
- d1.addCallback(strip(dc1))
- d1.addCallback(strip(dc2))
- d1.addCallbacks(strip(unsubscribe), self._ebGeneral)
- d1.addCallbacks(self._cbStopClient, self._ebGeneral)
- d2 = self.loopback()
- d = defer.gatherResults([d1, d2])
- d.addCallback(get_subscriptions)
- d.addCallback(lambda subscriptions:
- self.assertEqual(subscriptions,
- ['that/mbox']))
- return d
-
- def testSelect(self):
- """
- Try to select a mailbox
- """
- mbox_name = "TESTMAILBOXSELECT"
- self.selectedArgs = None
-
- acc = self.server.theAccount
-
- def add_mailbox():
- return acc.addMailbox(mbox_name, creation_ts=42)
-
- def login():
- return self.client.login(TEST_USER, TEST_PASSWD)
-
- def select():
- def selected(args):
- self.selectedArgs = args
- self._cbStopClient(None)
- d = self.client.select(mbox_name)
- d.addCallback(selected)
- return d
-
- d1 = self.connected.addCallback(strip(add_mailbox))
- d1.addCallback(strip(login))
- d1.addCallback(strip(select))
- # d1.addErrback(self._ebGeneral)
-
- d2 = self.loopback()
-
- d = defer.gatherResults([d1, d2])
- d.addCallback(self._cbTestSelect)
- return d
-
- def _cbTestSelect(self, ignored):
- self.assertTrue(self.selectedArgs is not None)
-
- self.assertEqual(self.selectedArgs, {
- 'EXISTS': 0, 'RECENT': 0, 'UIDVALIDITY': 42,
- 'FLAGS': ('\\Seen', '\\Answered', '\\Flagged',
- '\\Deleted', '\\Draft', '\\Recent', 'List'),
- 'READ-WRITE': True
- })
-
- #
- # capabilities
- #
-
- def testCapability(self):
- caps = {}
-
- def getCaps():
- def gotCaps(c):
- caps.update(c)
- self.server.transport.loseConnection()
- return self.client.getCapabilities().addCallback(gotCaps)
-
- d1 = self.connected
- d1.addCallback(
- strip(getCaps)).addErrback(self._ebGeneral)
-
- d = defer.gatherResults([self.loopback(), d1])
- expected = {'IMAP4rev1': None, 'NAMESPACE': None, 'LITERAL+': None,
- 'IDLE': None}
- d.addCallback(lambda _: self.assertEqual(expected, caps))
- return d
-
- def testCapabilityWithAuth(self):
- caps = {}
- self.server.challengers[
- 'CRAM-MD5'] = cred.credentials.CramMD5Credentials
-
- def getCaps():
- def gotCaps(c):
- caps.update(c)
- self.server.transport.loseConnection()
- return self.client.getCapabilities().addCallback(gotCaps)
- d1 = self.connected.addCallback(
- strip(getCaps)).addErrback(self._ebGeneral)
-
- d = defer.gatherResults([self.loopback(), d1])
-
- expCap = {'IMAP4rev1': None, 'NAMESPACE': None,
- 'IDLE': None, 'LITERAL+': None,
- 'AUTH': ['CRAM-MD5']}
-
- d.addCallback(lambda _: self.assertEqual(expCap, caps))
- return d
-
- #
- # authentication
- #
-
- def testLogout(self):
- """
- Test log out
- """
- self.loggedOut = 0
-
- def logout():
- def setLoggedOut():
- self.loggedOut = 1
- self.client.logout().addCallback(strip(setLoggedOut))
- self.connected.addCallback(strip(logout)).addErrback(self._ebGeneral)
- d = self.loopback()
- return d.addCallback(lambda _: self.assertEqual(self.loggedOut, 1))
-
- def testNoop(self):
- """
- Test noop command
- """
- self.responses = None
-
- def noop():
- def setResponses(responses):
- self.responses = responses
- self.server.transport.loseConnection()
- self.client.noop().addCallback(setResponses)
- self.connected.addCallback(strip(noop)).addErrback(self._ebGeneral)
- d = self.loopback()
- return d.addCallback(lambda _: self.assertEqual(self.responses, []))
-
- def testLogin(self):
- """
- Test login
- """
- def login():
- d = self.client.login(TEST_USER, TEST_PASSWD)
- d.addCallback(self._cbStopClient)
- d1 = self.connected.addCallback(
- strip(login)).addErrback(self._ebGeneral)
- d = defer.gatherResults([d1, self.loopback()])
- return d.addCallback(self._cbTestLogin)
-
- def _cbTestLogin(self, ignored):
- self.assertEqual(self.server.state, 'auth')
-
- def testFailedLogin(self):
- """
- Test bad login
- """
- def login():
- d = self.client.login("bad_user@leap.se", TEST_PASSWD)
- d.addBoth(self._cbStopClient)
-
- d1 = self.connected.addCallback(
- strip(login)).addErrback(self._ebGeneral)
- d2 = self.loopback()
- d = defer.gatherResults([d1, d2])
- return d.addCallback(self._cbTestFailedLogin)
-
- def _cbTestFailedLogin(self, ignored):
- self.assertEqual(self.server.state, 'unauth')
- self.assertEqual(self.server.account, None)
-
- def testLoginRequiringQuoting(self):
- """
- Test login requiring quoting
- """
- self.server.checker.userid = '{test}user@leap.se'
- self.server.checker.password = '{test}password'
-
- def login():
- d = self.client.login('{test}user@leap.se', '{test}password')
- d.addBoth(self._cbStopClient)
-
- d1 = self.connected.addCallback(
- strip(login)).addErrback(self._ebGeneral)
- d = defer.gatherResults([self.loopback(), d1])
- return d.addCallback(self._cbTestLoginRequiringQuoting)
-
- def _cbTestLoginRequiringQuoting(self, ignored):
- self.assertEqual(self.server.state, 'auth')
-
- #
- # Inspection
- #
-
- def testNamespace(self):
- """
- Test retrieving namespace
- """
- self.namespaceArgs = None
-
- def login():
- return self.client.login(TEST_USER, TEST_PASSWD)
-
- def namespace():
- def gotNamespace(args):
- self.namespaceArgs = args
- self._cbStopClient(None)
- return self.client.namespace().addCallback(gotNamespace)
-
- d1 = self.connected.addCallback(strip(login))
- d1.addCallback(strip(namespace))
- d1.addErrback(self._ebGeneral)
- d2 = self.loopback()
- d = defer.gatherResults([d1, d2])
- d.addCallback(lambda _: self.assertEqual(self.namespaceArgs,
- [[['', '/']], [], []]))
- return d
-
- def testExamine(self):
- """
- L{IMAP4Client.examine} issues an I{EXAMINE} command to the server and
- returns a L{Deferred} which fires with a C{dict} with as many of the
- following keys as the server includes in its response: C{'FLAGS'},
- C{'EXISTS'}, C{'RECENT'}, C{'UNSEEN'}, C{'READ-WRITE'}, C{'READ-ONLY'},
- C{'UIDVALIDITY'}, and C{'PERMANENTFLAGS'}.
-
- Unfortunately the server doesn't generate all of these so it's hard to
- test the client's handling of them here. See
- L{IMAP4ClientExamineTests} below.
-
- See U{RFC 3501<http://www.faqs.org/rfcs/rfc3501.html>}, section 6.3.2,
- for details.
- """
- # TODO implement the IMAP4ClientExamineTests testcase.
- mbox_name = "test_mailbox_e"
- acc = self.server.theAccount
- self.examinedArgs = None
-
- def add_mailbox():
- return acc.addMailbox(mbox_name, creation_ts=42)
-
- def login():
- return self.client.login(TEST_USER, TEST_PASSWD)
-
- def examine():
- def examined(args):
- self.examinedArgs = args
- self._cbStopClient(None)
- d = self.client.examine(mbox_name)
- d.addCallback(examined)
- return d
-
- d1 = self.connected.addCallback(strip(add_mailbox))
- d1.addCallback(strip(login))
- d1.addCallback(strip(examine))
- d1.addErrback(self._ebGeneral)
- d2 = self.loopback()
- d = defer.gatherResults([d1, d2])
- return d.addCallback(self._cbTestExamine)
-
- def _cbTestExamine(self, ignored):
- self.assertEqual(self.examinedArgs, {
- 'EXISTS': 0, 'RECENT': 0, 'UIDVALIDITY': 42,
- 'FLAGS': ('\\Seen', '\\Answered', '\\Flagged',
- '\\Deleted', '\\Draft', '\\Recent', 'List'),
- 'READ-WRITE': False})
-
- def _listSetup(self, f, f2=None):
-
- acc = self.server.theAccount
-
- def dc1():
- return acc.addMailbox('root_subthing', creation_ts=42)
-
- def dc2():
- return acc.addMailbox('root_another_thing', creation_ts=42)
-
- def dc3():
- return acc.addMailbox('non_root_subthing', creation_ts=42)
-
- def login():
- return self.client.login(TEST_USER, TEST_PASSWD)
-
- def listed(answers):
- self.listed = answers
-
- self.listed = None
- d1 = self.connected.addCallback(strip(login))
- d1.addCallback(strip(dc1))
- d1.addCallback(strip(dc2))
- d1.addCallback(strip(dc3))
-
- if f2 is not None:
- d1.addCallback(f2)
-
- d1.addCallbacks(strip(f), self._ebGeneral)
- d1.addCallbacks(listed, self._ebGeneral)
- d1.addCallbacks(self._cbStopClient, self._ebGeneral)
- d2 = self.loopback()
- return defer.gatherResults([d1, d2]).addCallback(lambda _: self.listed)
-
- def testList(self):
- """
- Test List command
- """
- def list():
- return self.client.list('root', '%')
-
- d = self._listSetup(list)
- d.addCallback(lambda listed: self.assertEqual(
- sortNest(listed),
- sortNest([
- (IMAPMailbox.init_flags, "/", "root_subthing"),
- (IMAPMailbox.init_flags, "/", "root_another_thing")
- ])
- ))
- return d
-
- def testLSub(self):
- """
- Test LSub command
- """
- acc = self.server.theAccount
-
- def subs_mailbox():
- # why not client.subscribe instead?
- return acc.subscribe('root_subthing')
-
- def lsub():
- return self.client.lsub('root', '%')
-
- d = self._listSetup(lsub, strip(subs_mailbox))
- d.addCallback(self.assertEqual,
- [(IMAPMailbox.init_flags, "/", "root_subthing")])
- return d
-
- def testStatus(self):
- """
- Test Status command
- """
- acc = self.server.theAccount
-
- def add_mailbox():
- return acc.addMailbox('root_subthings')
-
- # XXX FIXME ---- should populate this a little bit,
- # with unseen etc...
-
- def login():
- return self.client.login(TEST_USER, TEST_PASSWD)
-
- def status():
- return self.client.status(
- 'root_subthings', 'MESSAGES', 'UIDNEXT', 'UNSEEN')
-
- def statused(result):
- self.statused = result
-
- self.statused = None
-
- d1 = self.connected.addCallback(strip(add_mailbox))
- d1.addCallback(strip(login))
- d1.addCallbacks(strip(status), self._ebGeneral)
- d1.addCallbacks(statused, self._ebGeneral)
- d1.addCallbacks(self._cbStopClient, self._ebGeneral)
- d2 = self.loopback()
- d = defer.gatherResults([d1, d2])
- d.addCallback(lambda _: self.assertEqual(
- self.statused,
- {'MESSAGES': 0, 'UIDNEXT': '1', 'UNSEEN': 0}
- ))
- return d
-
- def testFailedStatus(self):
- """
- Test failed status command with a non-existent mailbox
- """
- def login():
- return self.client.login(TEST_USER, TEST_PASSWD)
-
- def status():
- return self.client.status(
- 'root/nonexistent', 'MESSAGES', 'UIDNEXT', 'UNSEEN')
-
- def statused(result):
- self.statused = result
-
- def failed(failure):
- self.failure = failure
-
- self.statused = self.failure = None
- d1 = self.connected.addCallback(strip(login))
- d1.addCallbacks(strip(status), self._ebGeneral)
- d1.addCallbacks(statused, failed)
- d1.addCallbacks(self._cbStopClient, self._ebGeneral)
- d2 = self.loopback()
- return defer.gatherResults([d1, d2]).addCallback(
- self._cbTestFailedStatus)
-
- def _cbTestFailedStatus(self, ignored):
- self.assertEqual(
- self.statused, None
- )
- self.assertEqual(
- self.failure.value.args,
- ('Could not open mailbox',)
- )
-
- #
- # messages
- #
-
- def testFullAppend(self):
- """
- Test appending a full message to the mailbox
- """
- infile = util.sibpath(__file__, 'rfc822.message')
- message = open(infile)
- acc = self.server.theAccount
- mailbox_name = "appendmbox/subthing"
-
- def add_mailbox():
- return acc.addMailbox(mailbox_name)
-
- def login():
- return self.client.login(TEST_USER, TEST_PASSWD)
-
- def append():
- return self.client.append(
- mailbox_name, message,
- ('\\SEEN', '\\DELETED'),
- 'Tue, 17 Jun 2003 11:22:16 -0600 (MDT)',
- )
-
- d1 = self.connected.addCallback(strip(add_mailbox))
- d1.addCallback(strip(login))
- d1.addCallbacks(strip(append), self._ebGeneral)
- d1.addCallbacks(self._cbStopClient, self._ebGeneral)
- d2 = self.loopback()
- d = defer.gatherResults([d1, d2])
-
- d.addCallback(lambda _: acc.getMailbox(mailbox_name))
- d.addCallback(lambda mb: mb.fetch(imap4.MessageSet(start=1), True))
- return d.addCallback(self._cbTestFullAppend, infile)
-
- def _cbTestFullAppend(self, fetched, infile):
- fetched = list(fetched)
- self.assertTrue(len(fetched) == 1)
- self.assertTrue(len(fetched[0]) == 2)
- uid, msg = fetched[0]
- parsed = self.parser.parse(open(infile))
- expected_body = parsed.get_payload()
- expected_headers = CaseInsensitiveDict(parsed.items())
-
- def assert_flags(flags):
- self.assertEqual(
- set(('\\SEEN', '\\DELETED')),
- set(flags))
-
- def assert_date(date):
- self.assertEqual(
- 'Tue, 17 Jun 2003 11:22:16 -0600 (MDT)',
- date)
-
- def assert_body(body):
- gotbody = body.read()
- self.assertEqual(expected_body, gotbody)
-
- def assert_headers(headers):
- self.assertItemsEqual(map(string.lower, expected_headers), headers)
-
- d = defer.maybeDeferred(msg.getFlags)
- d.addCallback(assert_flags)
-
- d.addCallback(lambda _: defer.maybeDeferred(msg.getInternalDate))
- d.addCallback(assert_date)
-
- d.addCallback(
- lambda _: defer.maybeDeferred(
- msg.getBodyFile, self._soledad))
- d.addCallback(assert_body)
-
- d.addCallback(lambda _: defer.maybeDeferred(msg.getHeaders, True))
- d.addCallback(assert_headers)
-
- return d
-
- def testPartialAppend(self):
- """
- Test partially appending a message to the mailbox
- """
- # TODO this test sometimes will fail because of the notify_just_mdoc
- infile = util.sibpath(__file__, 'rfc822.message')
-
- acc = self.server.theAccount
-
- def add_mailbox():
- return acc.addMailbox('PARTIAL/SUBTHING')
-
- def login():
- return self.client.login(TEST_USER, TEST_PASSWD)
-
- def append():
- message = file(infile)
- return self.client.sendCommand(
- imap4.Command(
- 'APPEND',
- 'PARTIAL/SUBTHING (\\SEEN) "Right now" '
- '{%d}' % os.path.getsize(infile),
- (), self.client._IMAP4Client__cbContinueAppend, message
- )
- )
- d1 = self.connected.addCallback(strip(add_mailbox))
- d1.addCallback(strip(login))
- d1.addCallbacks(strip(append), self._ebGeneral)
- d1.addCallbacks(self._cbStopClient, self._ebGeneral)
- d2 = self.loopback()
- d = defer.gatherResults([d1, d2])
-
- d.addCallback(lambda _: acc.getMailbox("PARTIAL/SUBTHING"))
- d.addCallback(lambda mb: mb.fetch(imap4.MessageSet(start=1), True))
- return d.addCallback(
- self._cbTestPartialAppend, infile)
-
- def _cbTestPartialAppend(self, fetched, infile):
- fetched = list(fetched)
- self.assertTrue(len(fetched) == 1)
- self.assertTrue(len(fetched[0]) == 2)
- uid, msg = fetched[0]
- parsed = self.parser.parse(open(infile))
- expected_body = parsed.get_payload()
-
- def assert_flags(flags):
- self.assertEqual(
- set((['\\SEEN'])), set(flags))
-
- def assert_body(body):
- gotbody = body.read()
- self.assertEqual(expected_body, gotbody)
-
- d = defer.maybeDeferred(msg.getFlags)
- d.addCallback(assert_flags)
-
- d.addCallback(lambda _: defer.maybeDeferred(msg.getBodyFile))
- d.addCallback(assert_body)
- return d
-
- def testCheck(self):
- """
- Test check command
- """
- def add_mailbox():
- return self.server.theAccount.addMailbox('root/subthing')
-
- def login():
- return self.client.login(TEST_USER, TEST_PASSWD)
-
- def select():
- return self.client.select('root/subthing')
-
- def check():
- return self.client.check()
-
- d = self.connected.addCallbacks(
- strip(add_mailbox), self._ebGeneral)
- d.addCallbacks(lambda _: login(), self._ebGeneral)
- d.addCallbacks(strip(select), self._ebGeneral)
- d.addCallbacks(strip(check), self._ebGeneral)
- d.addCallbacks(self._cbStopClient, self._ebGeneral)
- d2 = self.loopback()
- return defer.gatherResults([d, d2])
-
- # Okay, that was much fun indeed
-
- def testExpunge(self):
- """
- Test expunge command
- """
- acc = self.server.theAccount
- mailbox_name = 'mailboxexpunge'
-
- def add_mailbox():
- return acc.addMailbox(mailbox_name)
-
- def login():
- return self.client.login(TEST_USER, TEST_PASSWD)
-
- def select():
- return self.client.select(mailbox_name)
-
- def save_mailbox(mailbox):
- self.mailbox = mailbox
-
- def get_mailbox():
- d = acc.getMailbox(mailbox_name)
- d.addCallback(save_mailbox)
- return d
-
- def add_messages():
- d = self.mailbox.addMessage(
- 'test 1', flags=('\\Deleted', 'AnotherFlag'),
- notify_just_mdoc=False)
- d.addCallback(lambda _: self.mailbox.addMessage(
- 'test 2', flags=('AnotherFlag',),
- notify_just_mdoc=False))
- d.addCallback(lambda _: self.mailbox.addMessage(
- 'test 3', flags=('\\Deleted',),
- notify_just_mdoc=False))
- return d
-
- def expunge():
- return self.client.expunge()
-
- def expunged(results):
- self.failIf(self.server.mbox is None)
- self.results = results
-
- self.results = None
- d1 = self.connected.addCallback(strip(add_mailbox))
- d1.addCallback(strip(login))
- d1.addCallback(strip(get_mailbox))
- d1.addCallbacks(strip(add_messages), self._ebGeneral)
- d1.addCallbacks(strip(select), self._ebGeneral)
- d1.addCallbacks(strip(expunge), self._ebGeneral)
- d1.addCallbacks(expunged, self._ebGeneral)
- d1.addCallbacks(self._cbStopClient, self._ebGeneral)
- d2 = self.loopback()
- d = defer.gatherResults([d1, d2])
- d.addCallback(lambda _: self.mailbox.getMessageCount())
- return d.addCallback(self._cbTestExpunge)
-
- def _cbTestExpunge(self, count):
- # we only left 1 mssage with no deleted flag
- self.assertEqual(count, 1)
- # the uids of the deleted messages
- self.assertItemsEqual(self.results, [1, 3])
-
-
-class AccountTestCase(IMAP4HelperMixin):
- """
- Test the Account.
- """
- def _create_empty_mailbox(self):
- return self.server.theAccount.addMailbox('')
-
- def _create_one_mailbox(self):
- return self.server.theAccount.addMailbox('one')
-
- def test_illegalMailboxCreate(self):
- self.assertRaises(AssertionError, self._create_empty_mailbox)
-
-
-class IMAP4ServerSearchTestCase(IMAP4HelperMixin):
- """
- Tests for the behavior of the search_* functions in L{imap5.IMAP4Server}.
- """
- # XXX coming soon to your screens!
- pass
diff --git a/src/leap/bitmask/mail/imap/tests/walktree.py b/src/leap/bitmask/mail/imap/tests/walktree.py
deleted file mode 100644
index f259a55..0000000
--- a/src/leap/bitmask/mail/imap/tests/walktree.py
+++ /dev/null
@@ -1,127 +0,0 @@
-# -*- coding: utf-8 -*-
-# walktree.py
-# Copyright (C) 2013 LEAP
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-"""
-Tests for the walktree module.
-"""
-import os
-import sys
-import pprint
-from email import parser
-
-from leap.mail import walk as W
-
-DEBUG = os.environ.get("BITMASK_MAIL_DEBUG")
-
-
-p = parser.Parser()
-
-# TODO pass an argument of the type of message
-
-##################################################
-# Input from hell
-
-if len(sys.argv) > 1:
- FILENAME = sys.argv[1]
-else:
- FILENAME = "rfc822.multi-signed.message"
-
-"""
-FILENAME = "rfc822.plain.message"
-FILENAME = "rfc822.multi-minimal.message"
-"""
-
-msg = p.parse(open(FILENAME))
-DO_CHECK = False
-#################################################
-
-parts = W.get_parts(msg)
-
-if DEBUG:
- def trim(item):
- item = item[:10]
- [trim(part["phash"]) for part in parts if part.get('phash', None)]
-
-raw_docs = list(W.get_raw_docs(msg, parts))
-
-body_phash_fun = [W.get_body_phash_simple,
- W.get_body_phash_multi][int(msg.is_multipart())]
-body_phash = body_phash_fun(W.get_payloads(msg))
-parts_map = W.walk_msg_tree(parts, body_phash=body_phash)
-
-
-# TODO add missing headers!
-expected = {
- 'body': '1ddfa80485',
- 'multi': True,
- 'part_map': {
- 1: {
- 'headers': {'Content-Disposition': 'inline',
- 'Content-Type': 'multipart/mixed; '
- 'boundary="z0eOaCaDLjvTGF2l"'},
- 'multi': True,
- 'part_map': {1: {'ctype': 'text/plain',
- 'headers': [
- ('Content-Type',
- 'text/plain; charset=utf-8'),
- ('Content-Disposition',
- 'inline'),
- ('Content-Transfer-Encoding',
- 'quoted-printable')],
- 'multi': False,
- 'parts': 1,
- 'phash': '1ddfa80485',
- 'size': 206},
- 2: {'ctype': 'text/plain',
- 'headers': [('Content-Type',
- 'text/plain; charset=us-ascii'),
- ('Content-Disposition',
- 'attachment; '
- 'filename="attach.txt"')],
- 'multi': False,
- 'parts': 1,
- 'phash': '7a94e4d769',
- 'size': 133},
- 3: {'ctype': 'application/octet-stream',
- 'headers': [('Content-Type',
- 'application/octet-stream'),
- ('Content-Disposition',
- 'attachment; filename="hack.ico"'),
- ('Content-Transfer-Encoding',
- 'base64')],
- 'multi': False,
- 'parts': 1,
- 'phash': 'c42cccebbd',
- 'size': 12736}}},
- 2: {'ctype': 'application/pgp-signature',
- 'headers': [('Content-Type', 'application/pgp-signature')],
- 'multi': False,
- 'parts': 1,
- 'phash': '8f49fbf749',
- 'size': 877}}}
-
-if DEBUG and DO_CHECK:
- # TODO turn this into a proper unittest
- assert(parts_map == expected)
- print "Structure: OK"
-
-
-print
-print "RAW DOCS"
-pprint.pprint(raw_docs)
-print
-print "PARTS MAP"
-pprint.pprint(parts_map)
diff --git a/src/leap/bitmask/mail/incoming/tests/rfc822.multi-encrypt-signed.message b/src/leap/bitmask/mail/incoming/tests/rfc822.multi-encrypt-signed.message
deleted file mode 100644
index 98304f2..0000000
--- a/src/leap/bitmask/mail/incoming/tests/rfc822.multi-encrypt-signed.message
+++ /dev/null
@@ -1,61 +0,0 @@
-Content-Type: multipart/encrypted;
- boundary="Apple-Mail=_C01A1464-6C43-43BF-8F62-157335B7E25B";
- protocol="application/pgp-encrypted";
-Subject: Enc signed
-Mime-Version: 1.0 (Mac OS X Mail 9.3 \(3124\))
-From: Leap Test Key <leap@leap.se>
-Date: Tue, 24 May 2016 11:47:24 -0300
-Content-Description: OpenPGP encrypted message
-To: leap@leap.se
-
-This is an OpenPGP/MIME encrypted message (RFC 2440 and 3156)
---Apple-Mail=_C01A1464-6C43-43BF-8F62-157335B7E25B
-Content-Type: application/pgp-encrypted
-Content-Description: PGP/MIME Versions Identification
-
---Apple-Mail=_C01A1464-6C43-43BF-8F62-157335B7E25B
-Content-Disposition: inline;
- filename=encrypted.asc
-Content-Type: application/octet-stream;
- name=encrypted.asc
-Content-Description: OpenPGP encrypted message
-
------BEGIN PGP MESSAGE-----
-Version: GnuPG v2
-
-hQIMAyj9aG/xtZOwAQ/9Gft0KmOpgzL6z4wmVlLm2aeAvHolXmxWb7N/ByL/dZ4n
-YZd/GPRj42X3BwUrDEL5aO3Mcp+rqq8ACh9hsZXiau0Q9cs1K7Gr55Y06qLrIjom
-2fLqwLFBxCL2sAX1dvClgStyfsRFk9Y/+5tX+IjWaD8dAoRdxCO8IbUDuYGnaKld
-bB9h0NMfKVddCAvuQvX1Zc1Nx0Yb3Hd+ocDD7i9BVgX1BBiGu4/ElS3d32TAVCFs
-Na3tjitWB2G472CYu1O6exY7h1F5V4FHfXH6iMRJSYnvV2Jr+oPZENzNdEEA5H/H
-fUbpWrpKzPafjho9S5rJBBM/tqtmBQFBIdgFVcBVb+bXO6DJ8SMTLiiGcVUvvm1b
-9N2VQIhsxtZ8DpcHHSqFVgT2Gt4UkSrEleSoReg36TzS1s8Uw0oU068PwTe3K0Gx
-2pLMdT9NA6X/t7movpXP6tih1l6P5z62dxFl6W12J9OcegISCt0Q7gex1gk/a8zM
-rzBJC3mVxRiFlvHPBgD6oUKarnTJPQx5f5dFXg8DXBWR1Eh/aFjPQIzhZBYpmOi8
-HqgjcAA+WhMQ7v5c0enJoJJS+8Xfai/MK2vTUGsfAT6HqHLw1HSIn6XQGEf4sQ/U
-NfLeFHHbe9rTk8QhyjrSl2vvek2H4EBQVLF08/FUrAfPELUttOFtysQfC3+M0+PS
-6QGyeIlUjKpBJG7HBd4ibuKMQ5vnA+ACsg/TySYeCO6P85xsN+Lmqlr8cAICn/hR
-ezFSzlibaIelRgfDEDJdjVyCsa7qBMjhRCvGYBdkyTzIRq53qwD9pkhrQ6nwWQrv
-bBzyLrl+NVR8CTEOwbeFLI6qf68kblojk3lwo3Qi3psmeMJdiaV9uevsHrgmEFTH
-lZ3rFECPWzmrkMSfVjWu5d8jJqMcqa4lnGzFQKaB76I8BzGhCWrnuvHPB9c9SVhI
-AnAwNw3gY5xgsbXMxZhnPgYeBSViPkQkgRCWl8Jz41eiAJ3Gtj8QSSFWGHpX+MgP
-ohBaPHz6Fnkhz7Lok97e2AcuRZrDVKV6i28r8mizI3B2Mah6ZV0Yuv0EYNtzBv/v
-yV3nu4DWuOOU0301CXBayxJGX0h07z1Ycv7jWD6LNiBXa1vahtbU4WSYNkF0OJaz
-nf8O3CZy5twMq5kQYoPacdNNLregAmWquvE1nxqWbtHFMjtXitP7czxzUTU/DE+C
-jr+irDoYEregEKg9xov91UCRPZgxL+TML71+tSYOMO3JG6lbGw77PQ8s2So7xore
-8+FeDFPaaJqh6uhF5LETRSx8x/haZiXLd+WtO7wF8S3+Vz7AJIFIe8MUadZrYwnH
-wfMAktQKbep3iHCeZ5jHYA461AOhnCca2y+GoyHZUDDFwS1pC1RN4lMkafSE1AgH
-cmEcjLYsw1gqT0+DfqrvjbXmMjGgkgnkMybJH7df5TKu36Q0Nqvcbc2XLFkalr5V
-Vk0SScqKYnKL+cJjabqA8rKkeAh22E2FBCpKPqxSS3te2bRb3XBX26bP0LshkJuy
-GPu6LKvwmUn0obPKCnLJvb9ImIGZToXu6Fb/Cd2c3DG1IK5PptQz4f7ZRW98huPO
-2w59Bswwt5q4lQqsMEzVRnIDH45MmnhEUeS4NaxqLTO7eJpMpb4VxT2u/Ac3XWKp
-o2RE6CbqTyJ+n8tY9OwBRMKzdVd9RFAMqMHTzWTAuU4BgW2vT2sHYZdAsX8sktBr
-5mo9P3MqvgdPNpg8+AOB03JlIv0dzrAFWCZxxLLGIIIz0eXsjghHzQ9QjGfr0xFH
-Z79AKDjsoRisWyWCnadS2oM9fdAg4T/h1STnfxc44o7N1+ym7u58ODICFi+Kg8IR
-JBHIp3CK02JLTLd/WFhUVyWgc6l8gn+oBK+r7Dw+FTWhqX2/ZHCO8qKK1ZK3NIMn
-MBcSVvHSnTPtppb+oND5nk38xazVVHnwxNHaIh7g3NxDB4hl5rBhrWsgTNuqDDRU
-w7ufvMYr1AOV+8e92cHCEKPM19nFKEgaBFECEptEObesGI3QZPAESlojzQ3cDeBa
-=tEyc
------END PGP MESSAGE-----
-
---Apple-Mail=_C01A1464-6C43-43BF-8F62-157335B7E25B-- \ No newline at end of file
diff --git a/src/leap/bitmask/mail/incoming/tests/test_incoming_mail.py b/src/leap/bitmask/mail/incoming/tests/test_incoming_mail.py
deleted file mode 100644
index 29422ec..0000000
--- a/src/leap/bitmask/mail/incoming/tests/test_incoming_mail.py
+++ /dev/null
@@ -1,391 +0,0 @@
-# -*- coding: utf-8 -*-
-# test_incoming_mail.py
-# Copyright (C) 2015 LEAP
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-"""
-Test case for leap.mail.incoming.service
-
-@authors: Ruben Pollan, <meskio@sindominio.net>
-
-@license: GPLv3, see included LICENSE file
-"""
-
-import json
-import os
-import tempfile
-import uuid
-
-from email.mime.application import MIMEApplication
-from email.mime.multipart import MIMEMultipart
-from email.parser import Parser
-from mock import Mock
-
-from twisted.internet import defer
-from twisted.python import log
-
-from leap.keymanager.errors import KeyAddressMismatch
-from leap.mail.adaptors import soledad_indexes as fields
-from leap.mail.adaptors.soledad import cleanup_deferred_locks
-from leap.mail.adaptors.soledad import SoledadMailAdaptor
-from leap.mail.mail import MessageCollection
-from leap.mail.mailbox_indexer import MailboxIndexer
-
-from leap.mail.incoming.service import IncomingMail
-from leap.mail.rfc3156 import MultipartEncrypted, PGPEncrypted
-from leap.mail.testing import KeyManagerWithSoledadTestCase
-from leap.mail.testing import ADDRESS, ADDRESS_2
-from leap.soledad.common.document import SoledadDocument
-from leap.soledad.common.crypto import (
- EncryptionSchemes,
- ENC_JSON_KEY,
- ENC_SCHEME_KEY,
-)
-
-HERE = os.path.split(os.path.abspath(__file__))[0]
-
-# TODO: add some tests for encrypted, unencrypted, signed and unsgined messages
-
-
-class IncomingMailTestCase(KeyManagerWithSoledadTestCase):
- """
- Tests for the incoming mail parser
- """
- NICKSERVER = "http://domain"
- BODY = """
-Governments of the Industrial World, you weary giants of flesh and steel, I
-come from Cyberspace, the new home of Mind. On behalf of the future, I ask
-you of the past to leave us alone. You are not welcome among us. You have
-no sovereignty where we gather.
- """
- EMAIL = """from: Test from SomeDomain <%(from)s>
-to: %(to)s
-subject: independence of cyberspace
-
-%(body)s
- """ % {
- "from": ADDRESS_2,
- "to": ADDRESS,
- "body": BODY
- }
-
- def setUp(self):
- cleanup_deferred_locks()
- try:
- del self._soledad
- del self.km
- except AttributeError:
- pass
-
- # pytest handles correctly the setupEnv for the class,
- # but trial ignores it.
- if not getattr(self, 'tempdir', None):
- self.tempdir = tempfile.mkdtemp()
-
- def getCollection(_):
- adaptor = SoledadMailAdaptor()
- store = self._soledad
- adaptor.store = store
- mbox_indexer = MailboxIndexer(store)
- mbox_name = "INBOX"
- mbox_uuid = str(uuid.uuid4())
-
- def get_collection_from_mbox_wrapper(wrapper):
- wrapper.uuid = mbox_uuid
- return MessageCollection(
- adaptor, store,
- mbox_indexer=mbox_indexer, mbox_wrapper=wrapper)
-
- d = adaptor.initialize_store(store)
- d.addCallback(lambda _: mbox_indexer.create_table(mbox_uuid))
- d.addCallback(
- lambda _: adaptor.get_or_create_mbox(store, mbox_name))
- d.addCallback(get_collection_from_mbox_wrapper)
- return d
-
- def setUpFetcher(inbox_collection):
- self.fetcher = IncomingMail(
- self.km,
- self._soledad,
- inbox_collection,
- ADDRESS)
-
- # The messages don't exist on soledad will fail on deletion
- self.fetcher._delete_incoming_message = Mock(
- return_value=defer.succeed(None))
-
- d = KeyManagerWithSoledadTestCase.setUp(self)
- d.addCallback(getCollection)
- d.addCallback(setUpFetcher)
- d.addErrback(log.err)
- return d
-
- def tearDown(self):
- d = KeyManagerWithSoledadTestCase.tearDown(self)
- return d
-
- def testExtractOpenPGPHeader(self):
- """
- Test the OpenPGP header key extraction
- """
- KEYURL = "https://leap.se/key.txt"
- OpenPGP = "id=12345678; url=\"%s\"; preference=signencrypt" % (KEYURL,)
-
- message = Parser().parsestr(self.EMAIL)
- message.add_header("OpenPGP", OpenPGP)
- self.fetcher._keymanager.fetch_key = Mock(
- return_value=defer.succeed(None))
-
- def fetch_key_called(ret):
- self.fetcher._keymanager.fetch_key.assert_called_once_with(
- ADDRESS_2, KEYURL)
-
- d = self._create_incoming_email(message.as_string())
- d.addCallback(
- lambda email:
- self._mock_soledad_get_from_index(fields.JUST_MAIL_IDX, [email]))
- d.addCallback(lambda _: self.fetcher.fetch())
- d.addCallback(fetch_key_called)
- return d
-
- def testExtractOpenPGPHeaderInvalidUrl(self):
- """
- Test the OpenPGP header key extraction
- """
- KEYURL = "https://someotherdomain.com/key.txt"
- OpenPGP = "id=12345678; url=\"%s\"; preference=signencrypt" % (KEYURL,)
-
- message = Parser().parsestr(self.EMAIL)
- message.add_header("OpenPGP", OpenPGP)
- self.fetcher._keymanager.fetch_key = Mock()
-
- def fetch_key_called(ret):
- self.assertFalse(self.fetcher._keymanager.fetch_key.called)
-
- d = self._create_incoming_email(message.as_string())
- d.addCallback(
- lambda email:
- self._mock_soledad_get_from_index(fields.JUST_MAIL_IDX, [email]))
- d.addCallback(lambda _: self.fetcher.fetch())
- d.addCallback(fetch_key_called)
- return d
-
- def testExtractAttachedKey(self):
- KEY = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n..."
-
- message = MIMEMultipart()
- message.add_header("from", ADDRESS_2)
- key = MIMEApplication("", "pgp-keys")
- key.set_payload(KEY)
- message.attach(key)
- self.fetcher._keymanager.put_raw_key = Mock(
- return_value=defer.succeed(None))
-
- def put_raw_key_called(_):
- self.fetcher._keymanager.put_raw_key.assert_called_once_with(
- KEY, address=ADDRESS_2)
-
- d = self._do_fetch(message.as_string())
- d.addCallback(put_raw_key_called)
- return d
-
- def testExtractInvalidAttachedKey(self):
- KEY = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n..."
-
- message = MIMEMultipart()
- message.add_header("from", ADDRESS_2)
- key = MIMEApplication("", "pgp-keys")
- key.set_payload(KEY)
- message.attach(key)
- self.fetcher._keymanager.put_raw_key = Mock(
- return_value=defer.fail(KeyAddressMismatch()))
-
- def put_raw_key_called(_):
- self.fetcher._keymanager.put_raw_key.assert_called_once_with(
- KEY, address=ADDRESS_2)
-
- d = self._do_fetch(message.as_string())
- d.addCallback(put_raw_key_called)
- d.addErrback(log.err)
- return d
-
- def testExtractAttachedKeyAndNotOpenPGPHeader(self):
- KEY = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n..."
- KEYURL = "https://leap.se/key.txt"
- OpenPGP = "id=12345678; url=\"%s\"; preference=signencrypt" % (KEYURL,)
-
- message = MIMEMultipart()
- message.add_header("from", ADDRESS_2)
- message.add_header("OpenPGP", OpenPGP)
- key = MIMEApplication("", "pgp-keys")
- key.set_payload(KEY)
- message.attach(key)
-
- self.fetcher._keymanager.put_raw_key = Mock(
- return_value=defer.succeed(None))
- self.fetcher._keymanager.fetch_key = Mock()
-
- def put_raw_key_called(_):
- self.fetcher._keymanager.put_raw_key.assert_called_once_with(
- KEY, address=ADDRESS_2)
- self.assertFalse(self.fetcher._keymanager.fetch_key.called)
-
- d = self._do_fetch(message.as_string())
- d.addCallback(put_raw_key_called)
- return d
-
- def testExtractOpenPGPHeaderIfInvalidAttachedKey(self):
- KEY = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n..."
- KEYURL = "https://leap.se/key.txt"
- OpenPGP = "id=12345678; url=\"%s\"; preference=signencrypt" % (KEYURL,)
-
- message = MIMEMultipart()
- message.add_header("from", ADDRESS_2)
- message.add_header("OpenPGP", OpenPGP)
- key = MIMEApplication("", "pgp-keys")
- key.set_payload(KEY)
- message.attach(key)
-
- self.fetcher._keymanager.put_raw_key = Mock(
- return_value=defer.fail(KeyAddressMismatch()))
- self.fetcher._keymanager.fetch_key = Mock()
-
- def put_raw_key_called(_):
- self.fetcher._keymanager.put_raw_key.assert_called_once_with(
- KEY, address=ADDRESS_2)
- self.fetcher._keymanager.fetch_key.assert_called_once_with(
- ADDRESS_2, KEYURL)
-
- d = self._do_fetch(message.as_string())
- d.addCallback(put_raw_key_called)
- return d
-
- def testAddDecryptedHeader(self):
- class DummyMsg():
-
- def __init__(self):
- self.headers = {}
-
- def add_header(self, k, v):
- self.headers[k] = v
-
- msg = DummyMsg()
- self.fetcher._add_decrypted_header(msg)
-
- self.assertEquals(msg.headers['X-Leap-Encryption'], 'decrypted')
-
- def testDecryptEmail(self):
-
- self.fetcher._decryption_error = Mock()
- self.fetcher._add_decrypted_header = Mock()
-
- def create_encrypted_message(encstr):
- message = Parser().parsestr(self.EMAIL)
- newmsg = MultipartEncrypted('application/pgp-encrypted')
- for hkey, hval in message.items():
- newmsg.add_header(hkey, hval)
-
- encmsg = MIMEApplication(
- encstr, _subtype='octet-stream', _encoder=lambda x: x)
- encmsg.add_header('content-disposition', 'attachment',
- filename='msg.asc')
- # create meta message
- metamsg = PGPEncrypted()
- metamsg.add_header('Content-Disposition', 'attachment')
- # attach pgp message parts to new message
- newmsg.attach(metamsg)
- newmsg.attach(encmsg)
- return newmsg
-
- def decryption_error_not_called(_):
- self.assertFalse(self.fetcher._decryption_error.called,
- "There was some errors with decryption")
-
- def add_decrypted_header_called(_):
- self.assertTrue(self.fetcher._add_decrypted_header.called,
- "There was some errors with decryption")
-
- d = self.km.encrypt(self.EMAIL, ADDRESS, sign=ADDRESS_2)
- d.addCallback(create_encrypted_message)
- d.addCallback(
- lambda message:
- self._do_fetch(message.as_string()))
- d.addCallback(decryption_error_not_called)
- d.addCallback(add_decrypted_header_called)
- return d
-
- def testValidateSignatureFromEncryptedEmailFromAppleMail(self):
- enc_signed_file = os.path.join(
- HERE, 'rfc822.multi-encrypt-signed.message')
- self.fetcher._add_verified_signature_header = Mock()
-
- def add_verified_signature_header_called(_):
- self.assertTrue(self.fetcher._add_verified_signature_header.called,
- "There was some errors verifying signature")
-
- with open(enc_signed_file) as f:
- enc_signed_raw = f.read()
-
- d = self._do_fetch(enc_signed_raw)
- d.addCallback(add_verified_signature_header_called)
- return d
-
- def testListener(self):
- self.called = False
-
- def listener(uid):
- self.called = True
-
- def listener_called(_):
- self.assertTrue(self.called)
-
- self.fetcher.add_listener(listener)
- d = self._do_fetch(self.EMAIL)
- d.addCallback(listener_called)
- return d
-
- def _do_fetch(self, message):
- d = self._create_incoming_email(message)
- d.addCallback(
- lambda email:
- self._mock_soledad_get_from_index(fields.JUST_MAIL_IDX, [email]))
- d.addCallback(lambda _: self.fetcher.fetch())
- return d
-
- def _create_incoming_email(self, email_str):
- email = SoledadDocument()
- data = json.dumps(
- {"incoming": True, "content": email_str},
- ensure_ascii=False)
-
- def set_email_content(encr_data):
- email.content = {
- fields.INCOMING_KEY: True,
- fields.ERROR_DECRYPTING_KEY: False,
- ENC_SCHEME_KEY: EncryptionSchemes.PUBKEY,
- ENC_JSON_KEY: encr_data
- }
- return email
- d = self.km.encrypt(data, ADDRESS, fetch_remote=False)
- d.addCallback(set_email_content)
- return d
-
- def _mock_soledad_get_from_index(self, index_name, value):
- get_from_index = self._soledad.get_from_index
-
- def soledad_mock(idx_name, *key_values):
- if index_name == idx_name:
- return defer.succeed(value)
- return get_from_index(idx_name, *key_values)
- self.fetcher._soledad.get_from_index = Mock(side_effect=soledad_mock)
diff --git a/src/leap/bitmask/mail/outgoing/tests/test_outgoing.py b/src/leap/bitmask/mail/outgoing/tests/test_outgoing.py
deleted file mode 100644
index dd053c1..0000000
--- a/src/leap/bitmask/mail/outgoing/tests/test_outgoing.py
+++ /dev/null
@@ -1,263 +0,0 @@
-# -*- coding: utf-8 -*-
-# test_gateway.py
-# Copyright (C) 2013 LEAP
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-
-"""
-SMTP gateway tests.
-"""
-import re
-from copy import deepcopy
-from StringIO import StringIO
-from email.parser import Parser
-from datetime import datetime
-from twisted.internet.defer import fail
-from twisted.mail.smtp import User
-from twisted.python import log
-
-from mock import Mock
-
-from leap.mail.rfc3156 import RFC3156CompliantGenerator
-from leap.mail.outgoing.service import OutgoingMail
-from leap.mail.testing import ADDRESS, ADDRESS_2, PUBLIC_KEY_2
-from leap.mail.testing import KeyManagerWithSoledadTestCase
-from leap.mail.testing.smtp import getSMTPFactory
-from leap.keymanager import errors
-
-
-BEGIN_PUBLIC_KEY = "-----BEGIN PGP PUBLIC KEY BLOCK-----"
-
-TEST_USER = u'anotheruser@leap.se'
-
-
-class TestOutgoingMail(KeyManagerWithSoledadTestCase):
- EMAIL_DATA = ['HELO gateway.leap.se',
- 'MAIL FROM: <%s>' % ADDRESS_2,
- 'RCPT TO: <%s>' % ADDRESS,
- 'DATA',
- 'From: User <%s>' % ADDRESS_2,
- 'To: Leap <%s>' % ADDRESS,
- 'Date: ' + datetime.now().strftime('%c'),
- 'Subject: test message',
- '',
- 'This is a secret message.',
- 'Yours,',
- 'A.',
- '',
- '.',
- 'QUIT']
-
- def setUp(self):
- self.lines = [line for line in self.EMAIL_DATA[4:12]]
- self.lines.append('') # add a trailing newline
- self.raw = '\r\n'.join(self.lines)
- self.expected_body = '\r\n'.join(self.EMAIL_DATA[9:12]) + "\r\n"
- self.fromAddr = ADDRESS_2
-
- class opts:
- cert = u'/tmp/cert'
- key = u'/tmp/cert'
- hostname = 'remote'
- port = 666
- self.opts = opts
-
- def init_outgoing_and_proto(_):
- self.outgoing_mail = OutgoingMail(
- self.fromAddr, self.km, opts.cert,
- opts.key, opts.hostname, opts.port)
-
- user = TEST_USER
-
- # TODO -- this shouldn't need SMTP to be tested!? or does it?
- self.proto = getSMTPFactory(
- {user: None}, {user: self.km}, {user: None})
- self.dest = User(ADDRESS, 'gateway.leap.se', self.proto, ADDRESS_2)
-
- d = KeyManagerWithSoledadTestCase.setUp(self)
- d.addCallback(init_outgoing_and_proto)
- return d
-
- def test_message_encrypt(self):
- """
- Test if message gets encrypted to destination email.
- """
- def check_decryption(res):
- decrypted, _ = res
- self.assertEqual(
- '\n' + self.expected_body,
- decrypted,
- 'Decrypted text differs from plaintext.')
-
- d = self._set_sign_used(ADDRESS)
- d.addCallback(
- lambda _:
- self.outgoing_mail._maybe_encrypt_and_sign(self.raw, self.dest))
- d.addCallback(self._assert_encrypted)
- d.addCallback(lambda message: self.km.decrypt(
- message.get_payload(1).get_payload(), ADDRESS))
- d.addCallback(check_decryption)
- return d
-
- def test_message_encrypt_sign(self):
- """
- Test if message gets encrypted to destination email and signed with
- sender key.
- '"""
- def check_decryption_and_verify(res):
- decrypted, signkey = res
- self.assertEqual(
- '\n' + self.expected_body,
- decrypted,
- 'Decrypted text differs from plaintext.')
- self.assertTrue(ADDRESS_2 in signkey.address,
- "Verification failed")
-
- d = self._set_sign_used(ADDRESS)
- d.addCallback(
- lambda _:
- self.outgoing_mail._maybe_encrypt_and_sign(self.raw, self.dest))
- d.addCallback(self._assert_encrypted)
- d.addCallback(lambda message: self.km.decrypt(
- message.get_payload(1).get_payload(), ADDRESS, verify=ADDRESS_2))
- d.addCallback(check_decryption_and_verify)
- return d
-
- def test_message_sign(self):
- """
- Test if message is signed with sender key.
- """
- # mock the key fetching
- self.km._fetch_keys_from_server = Mock(
- return_value=fail(errors.KeyNotFound()))
- recipient = User('ihavenopubkey@nonleap.se',
- 'gateway.leap.se', self.proto, ADDRESS)
- self.outgoing_mail = OutgoingMail(
- self.fromAddr, self.km, self.opts.cert, self.opts.key,
- self.opts.hostname, self.opts.port)
-
- def check_signed(res):
- message, _ = res
- self.assertTrue('Content-Type' in message)
- self.assertEqual('multipart/signed', message.get_content_type())
- self.assertEqual('application/pgp-signature',
- message.get_param('protocol'))
- self.assertEqual('pgp-sha512', message.get_param('micalg'))
- # assert content of message
- body = (message.get_payload(0)
- .get_payload(0)
- .get_payload(decode=True))
- self.assertEqual(self.expected_body,
- body)
- # assert content of signature
- self.assertTrue(
- message.get_payload(1).get_payload().startswith(
- '-----BEGIN PGP SIGNATURE-----\n'),
- 'Message does not start with signature header.')
- self.assertTrue(
- message.get_payload(1).get_payload().endswith(
- '-----END PGP SIGNATURE-----\n'),
- 'Message does not end with signature footer.')
- return message
-
- def verify(message):
- # replace EOL before verifying (according to rfc3156)
- fp = StringIO()
- g = RFC3156CompliantGenerator(
- fp, mangle_from_=False, maxheaderlen=76)
- g.flatten(message.get_payload(0))
- signed_text = re.sub('\r?\n', '\r\n',
- fp.getvalue())
-
- def assert_verify(key):
- self.assertTrue(ADDRESS_2 in key.address,
- 'Signature could not be verified.')
-
- d = self.km.verify(
- signed_text, ADDRESS_2,
- detached_sig=message.get_payload(1).get_payload())
- d.addCallback(assert_verify)
- return d
-
- d = self.outgoing_mail._maybe_encrypt_and_sign(self.raw, recipient)
- d.addCallback(check_signed)
- d.addCallback(verify)
- return d
-
- def test_attach_key(self):
- d = self.outgoing_mail._maybe_encrypt_and_sign(self.raw, self.dest)
- d.addCallback(self._assert_encrypted)
- d.addCallback(self._check_headers, self.lines[:4])
- d.addCallback(lambda message: self.km.decrypt(
- message.get_payload(1).get_payload(), ADDRESS))
- d.addCallback(lambda (decrypted, _):
- self._check_key_attachment(Parser().parsestr(decrypted)))
- return d
-
- def test_attach_key_not_known(self):
- unknown_address = "someunknownaddress@somewhere.com"
- lines = deepcopy(self.lines)
- lines[1] = "To: <%s>" % (unknown_address,)
- raw = '\r\n'.join(lines)
- dest = User(unknown_address, 'gateway.leap.se', self.proto, ADDRESS_2)
-
- d = self.outgoing_mail._maybe_encrypt_and_sign(
- raw, dest, fetch_remote=False)
- d.addCallback(lambda (message, _):
- self._check_headers(message, lines[:4]))
- d.addCallback(self._check_key_attachment)
- d.addErrback(log.err)
- return d
-
- def _check_headers(self, message, headers):
- msgstr = message.as_string(unixfrom=False)
- for header in headers:
- self.assertTrue(header in msgstr,
- "Missing header: %s" % (header,))
- return message
-
- def _check_key_attachment(self, message):
- for payload in message.get_payload():
- if payload.is_multipart():
- return self._check_key_attachment(payload)
- if 'application/pgp-keys' == payload.get_content_type():
- keylines = PUBLIC_KEY_2.split('\n')
- key = BEGIN_PUBLIC_KEY + '\n\n' + '\n'.join(keylines[4:-1])
- self.assertTrue(key in payload.get_payload(decode=True),
- "Key attachment don't match")
- return
- self.fail("No public key attachment found")
-
- def _set_sign_used(self, address):
- def set_sign(key):
- key.sign_used = True
- return self.km.put_key(key)
-
- d = self.km.get_key(address, fetch_remote=False)
- d.addCallback(set_sign)
- return d
-
- def _assert_encrypted(self, res):
- message, _ = res
- self.assertTrue('Content-Type' in message)
- self.assertEqual('multipart/encrypted', message.get_content_type())
- self.assertEqual('application/pgp-encrypted',
- message.get_param('protocol'))
- self.assertEqual(2, len(message.get_payload()))
- self.assertEqual('application/pgp-encrypted',
- message.get_payload(0).get_content_type())
- self.assertEqual('application/octet-stream',
- message.get_payload(1).get_content_type())
- return message
diff --git a/src/leap/bitmask/mail/smtp/tests/185CA770.key b/src/leap/bitmask/mail/smtp/tests/185CA770.key
deleted file mode 100644
index 587b416..0000000
--- a/src/leap/bitmask/mail/smtp/tests/185CA770.key
+++ /dev/null
@@ -1,79 +0,0 @@
------BEGIN PGP PRIVATE KEY BLOCK-----
-Version: GnuPG v1.4.10 (GNU/Linux)
-
-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
-=6HcJ
------END PGP PRIVATE KEY BLOCK-----
diff --git a/src/leap/bitmask/mail/smtp/tests/185CA770.pub b/src/leap/bitmask/mail/smtp/tests/185CA770.pub
deleted file mode 100644
index 38af19f..0000000
--- a/src/leap/bitmask/mail/smtp/tests/185CA770.pub
+++ /dev/null
@@ -1,52 +0,0 @@
------BEGIN PGP PUBLIC KEY BLOCK-----
-Version: GnuPG v1.4.10 (GNU/Linux)
-
-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
-q3iu
-=RChS
------END PGP PUBLIC KEY BLOCK-----
diff --git a/src/leap/bitmask/mail/smtp/tests/cert/server.crt b/src/leap/bitmask/mail/smtp/tests/cert/server.crt
deleted file mode 100644
index a27391c..0000000
--- a/src/leap/bitmask/mail/smtp/tests/cert/server.crt
+++ /dev/null
@@ -1,29 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIFBjCCAu4CCQCWn3oMoQrDJTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJV
-UzETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
-cyBQdHkgTHRkMB4XDTEzMTAyMzE0NDUwNFoXDTE2MDcxOTE0NDUwNFowRTELMAkG
-A1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0
-IFdpZGdpdHMgUHR5IEx0ZDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
-APexTvEvG7cSmZdAERHt9TB11cSor54Y/F7NmYMdSOJNi4Y0kwkSslpdfipi+mt/
-NFg/uGKi1mcgvuXdVbVPZ9rCgVpIzMncO8RAP7a5+I2zKUzqMCCbLH16sYpo/rDk
-VQ5V15TwLsTzOFGG8Cgp68TR8zHuZ4Edf2zMGC1IaiJ6W38LTnJgsowYOCFDAF3z
-L36kxMO5gNGEUYV6tjltx+rAcXka3po+xiAgvW6q65UUgDHcIdEGG2dc9bkxxPl7
-RkprF2RwwADNzYS7Tn+Hpmjy06pfYZHNME+Iw515bCRF3GQFUU4BpGnY7EO+h4P9
-Kb1h948gUT9/oswXG+q2Kwk8AoggMJkUOWDFiCa5UjW1GBoxxb7VtZ+QTJXxlFWc
-M2VzT7M/HX+P4b05vY4MXJjxPAFKrAGS7J8DKW8WJNUnXa9XSDBHg5qijDzZ/zGm
-HTdG6iADnJLmOHBQgFQ12a/n9mYV2GPVC6FlgDzG9f0/SUPBUCafyWYz1LwKY4VM
-2NLx/iwYMQsNIMSZQfNmufNDBr70+BShe3ZpbmKB/J33d87AuJd2HjnsThTEAAr+
-6CejyYmwFutoDUCF8IaKGJEp7OGP2//ub4nt5WwW8DYLRi8EqtzEnxPo5ZiayHMY
-GHR1jpX1O5JVJFUE79bZCFFHKmtJc4kVZS4m4rTLsk83AgMBAAEwDQYJKoZIhvcN
-AQEFBQADggIBAEt4PIRqVuALQSdgZ+GiZYuvEVjxoDVtMSc/ym93Gi8R7DDivFH9
-4suQc5QUiuEF8lpEtkmh+PZ+oFdQkjhBH80h7p4BUSyBy5Yi6dy7ATTlBAqwzCYZ
-4wzHeJzu1SI6FinZLksoULbcw04n410aGHkLa6I9O3vCC4kXSnBlwU1sUsJphxM2
-3pkHBpvv79XYf5kFqZPzF16aO7rxFuVvqgXLyzwuyP9kH5zMA21Kioxs/pNyg1lm
-5h0VinpHLPse+4tYih1L1WLMpEZiSwZgFhoRtlcdIVXokZPaX4G2EkdrMmSQruWg
-Uz8Av6LEYHmRfbYwYM2kEX/+AF8thpTQDbvxjqYk5oyGX4wpKGpih1ac/jYu3O8B
-VLhbxZlBYcLxCqqNsGJrWaiHj2Jf4GhUB0O9hXfaZDMqEGXT9GzOz0yF6b3pDQVy
-H0lKIBb+kQzB/jhZKu4vrTAowXtt/av5d7D+rpAU1SxfUhBOPNSRoJUI5NSBbokp
-a7u4azdB2IQETX3d2rhDk09EbG1XmMi5Vg1oa8nxfMOWXZnDMusJoZClKjrthmwd
-rtR5et44XYhX6p217RBkYMDOVFT7aZpu4SaFeqZIuarVYodSmgXToOFXPsrLppRQ
-adOT0FpU64RPNrQz5NF1bSIjqrHSaRVacf8yr7qqxNnpMsrtkDJzsMBz
------END CERTIFICATE-----
diff --git a/src/leap/bitmask/mail/smtp/tests/cert/server.key b/src/leap/bitmask/mail/smtp/tests/cert/server.key
deleted file mode 100644
index 197a449..0000000
--- a/src/leap/bitmask/mail/smtp/tests/cert/server.key
+++ /dev/null
@@ -1,51 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-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
------END RSA PRIVATE KEY-----
diff --git a/src/leap/bitmask/mail/smtp/tests/mail.txt b/src/leap/bitmask/mail/smtp/tests/mail.txt
deleted file mode 100644
index 9542047..0000000
--- a/src/leap/bitmask/mail/smtp/tests/mail.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-HELO drebs@riseup.net
-MAIL FROM: drebs@riseup.net
-RCPT TO: drebs@riseup.net
-RCPT TO: drebs@leap.se
-DATA
-Subject: leap test
-
-Hello world!
-.
-QUIT
diff --git a/src/leap/bitmask/mail/smtp/tests/test_gateway.py b/src/leap/bitmask/mail/smtp/tests/test_gateway.py
deleted file mode 100644
index 9d88afb..0000000
--- a/src/leap/bitmask/mail/smtp/tests/test_gateway.py
+++ /dev/null
@@ -1,181 +0,0 @@
-# -*- coding: utf-8 -*-
-# test_gateway.py
-# Copyright (C) 2013 LEAP
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-"""
-SMTP gateway tests.
-"""
-import re
-import tempfile
-from datetime import datetime
-
-from twisted.internet import reactor
-from twisted.internet.defer import inlineCallbacks, fail, succeed, Deferred
-from twisted.test import proto_helpers
-
-from mock import Mock
-
-from leap.keymanager import openpgp, errors
-from leap.mail.testing import KeyManagerWithSoledadTestCase
-from leap.mail.testing import ADDRESS, ADDRESS_2
-from leap.mail.testing.smtp import getSMTPFactory, TEST_USER
-
-
-# some regexps
-IP_REGEX = "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}" + \
- "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])"
-HOSTNAME_REGEX = "(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*" + \
- "([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])"
-IP_OR_HOST_REGEX = '(' + IP_REGEX + '|' + HOSTNAME_REGEX + ')'
-
-
-class TestSmtpGateway(KeyManagerWithSoledadTestCase):
-
- EMAIL_DATA = ['HELO gateway.leap.se',
- 'MAIL FROM: <%s>' % ADDRESS_2,
- 'RCPT TO: <%s>' % ADDRESS,
- 'DATA',
- 'From: User <%s>' % ADDRESS_2,
- 'To: Leap <%s>' % ADDRESS,
- 'Date: ' + datetime.now().strftime('%c'),
- 'Subject: test message',
- '',
- 'This is a secret message.',
- 'Yours,',
- 'A.',
- '',
- '.',
- 'QUIT']
-
- def setUp(self):
- # pytest handles correctly the setupEnv for the class,
- # but trial ignores it.
- if not getattr(self, 'tempdir', None):
- self.tempdir = tempfile.mkdtemp()
- return KeyManagerWithSoledadTestCase.setUp(self)
-
- def tearDown(self):
- return KeyManagerWithSoledadTestCase.tearDown(self)
-
- def assertMatch(self, string, pattern, msg=None):
- if not re.match(pattern, string):
- msg = self._formatMessage(msg, '"%s" does not match pattern "%s".'
- % (string, pattern))
- raise self.failureException(msg)
-
- @inlineCallbacks
- def test_gateway_accepts_valid_email(self):
- """
- Test if SMTP server responds correctly for valid interaction.
- """
-
- SMTP_ANSWERS = ['220 ' + IP_OR_HOST_REGEX +
- ' NO UCE NO UBE NO RELAY PROBES',
- '250 ' + IP_OR_HOST_REGEX + ' Hello ' +
- IP_OR_HOST_REGEX + ', nice to meet you',
- '250 Sender address accepted',
- '250 Recipient address accepted',
- '354 Continue']
-
- user = TEST_USER
- proto = getSMTPFactory({user: None}, {user: self.km}, {user: None})
- transport = proto_helpers.StringTransport()
- proto.makeConnection(transport)
- reply = ""
- for i, line in enumerate(self.EMAIL_DATA):
- reply += yield self.getReply(line + '\r\n', proto, transport)
- self.assertMatch(reply, '\r\n'.join(SMTP_ANSWERS),
- 'Did not get expected answer from gateway.')
- proto.setTimeout(None)
-
- @inlineCallbacks
- def test_missing_key_rejects_address(self):
- """
- Test if server rejects to send unencrypted when 'encrypted_only' is
- True.
- """
- # remove key from key manager
- pubkey = yield self.km.get_key(ADDRESS)
- pgp = openpgp.OpenPGPScheme(
- self._soledad, gpgbinary=self.gpg_binary_path)
- yield pgp.delete_key(pubkey)
- # mock the key fetching
- self.km._fetch_keys_from_server = Mock(
- return_value=fail(errors.KeyNotFound()))
- user = TEST_USER
- proto = getSMTPFactory(
- {user: None}, {user: self.km}, {user: None},
- encrypted_only=True)
- transport = proto_helpers.StringTransport()
- proto.makeConnection(transport)
- yield self.getReply(self.EMAIL_DATA[0] + '\r\n', proto, transport)
- yield self.getReply(self.EMAIL_DATA[1] + '\r\n', proto, transport)
- reply = yield self.getReply(self.EMAIL_DATA[2] + '\r\n',
- proto, transport)
- # ensure the address was rejected
- self.assertEqual(
- '550 Cannot receive for specified address\r\n',
- reply,
- 'Address should have been rejected with appropriate message.')
- proto.setTimeout(None)
-
- @inlineCallbacks
- def test_missing_key_accepts_address(self):
- """
- Test if server accepts to send unencrypted when 'encrypted_only' is
- False.
- """
- # remove key from key manager
- pubkey = yield self.km.get_key(ADDRESS)
- pgp = openpgp.OpenPGPScheme(
- self._soledad, gpgbinary=self.gpg_binary_path)
- yield pgp.delete_key(pubkey)
- # mock the key fetching
- self.km._fetch_keys_from_server = Mock(
- return_value=fail(errors.KeyNotFound()))
- user = TEST_USER
- proto = getSMTPFactory({user: None}, {user: self.km}, {user: None})
- transport = proto_helpers.StringTransport()
- proto.makeConnection(transport)
- yield self.getReply(self.EMAIL_DATA[0] + '\r\n', proto, transport)
- yield self.getReply(self.EMAIL_DATA[1] + '\r\n', proto, transport)
- reply = yield self.getReply(self.EMAIL_DATA[2] + '\r\n',
- proto, transport)
- # ensure the address was accepted
- self.assertEqual(
- '250 Recipient address accepted\r\n',
- reply,
- 'Address should have been accepted with appropriate message.')
- proto.setTimeout(None)
-
- def getReply(self, line, proto, transport):
- proto.lineReceived(line)
-
- if line[:4] not in ['HELO', 'MAIL', 'RCPT', 'DATA']:
- return succeed("")
-
- def check_transport(_):
- reply = transport.value()
- if reply:
- transport.clear()
- return succeed(reply)
-
- d = Deferred()
- d.addCallback(check_transport)
- reactor.callLater(0, lambda: d.callback(None))
- return d
-
- return check_transport(None)
diff --git a/src/leap/bitmask/mail/tests/rfc822.bounce.message b/src/leap/bitmask/mail/tests/rfc822.bounce.message
deleted file mode 100644
index 7a51ac0..0000000
--- a/src/leap/bitmask/mail/tests/rfc822.bounce.message
+++ /dev/null
@@ -1,152 +0,0 @@
-Return-Path: <>
-X-Original-To: yoyo@dev.pixelated-project.org
-Delivered-To: a6973ec1af0a6d1e2a1e4db4ff85f6c2@deliver.local
-Received: by dev1.dev.pixelated-project.org (Postfix)
- id 92CEA83164; Thu, 16 Jun 2016 14:53:34 +0200 (CEST)
-Date: Thu, 16 Jun 2016 14:53:34 +0200 (CEST)
-From: MAILER-DAEMON@dev1.dev.pixelated-project.org (Mail Delivery System)
-Subject: Undelivered Mail Returned to Sender
-To: yoyo@dev.pixelated-project.org
-Auto-Submitted: auto-replied
-MIME-Version: 1.0
-Content-Type: multipart/report; report-type=delivery-status;
- boundary="8F60183010.1466081614/dev1.dev.pixelated-project.org"
-Message-Id: <20160616125334.92CEA83164@dev1.dev.pixelated-project.org>
-
-This is a MIME-encapsulated message.
-
---8F60183010.1466081614/dev1.dev.pixelated-project.org
-Content-Description: Notification
-Content-Type: text/plain; charset=us-ascii
-
-This is the mail system at host dev1.dev.pixelated-project.org.
-
-I'm sorry to have to inform you that your message could not
-be delivered to one or more recipients. It's attached below.
-
-For further assistance, please send mail to postmaster.
-
-If you do so, please include this problem report. You can
-delete your own text from the attached returned message.
-
- The mail system
-
-<nobody@leap.se>: host caribou.leap.se[176.53.69.122] said: 550 5.1.1
- <nobody@leap.se>: Recipient address rejected: User unknown in virtual alias
- table (in reply to RCPT TO command)
-
---8F60183010.1466081614/dev1.dev.pixelated-project.org
-Content-Description: Delivery report
-Content-Type: message/delivery-status
-
-Reporting-MTA: dns; dev1.dev.pixelated-project.org
-X-Postfix-Queue-ID: 8F60183010
-X-Postfix-Sender: rfc822; yoyo@dev.pixelated-project.org
-Arrival-Date: Thu, 16 Jun 2016 14:53:33 +0200 (CEST)
-
-Final-Recipient: rfc822; nobody@leap.se
-Original-Recipient: rfc822;nobody@leap.se
-Action: failed
-Status: 5.1.1
-Remote-MTA: dns; caribou.leap.se
-Diagnostic-Code: smtp; 550 5.1.1 <nobody@leap.se>: Recipient address rejected:
- User unknown in virtual alias table
-
---8F60183010.1466081614/dev1.dev.pixelated-project.org
-Content-Description: Undelivered Message
-Content-Type: message/rfc822
-
-Return-Path: <yoyo@dev.pixelated-project.org>
-Received: from leap.mail-0.4.0rc1+111.g736ea86 (localhost [127.0.0.1])
- (using TLSv1 with cipher ECDHE-RSA-AES128-SHA (128/128 bits))
- (Client CN "yoyo@dev.pixelated-project.org", Issuer "Pixelated Project Root CA (client certificates only!)" (verified OK))
- by dev1.dev.pixelated-project.org (Postfix) with ESMTPS id 8F60183010
- for <nobody@leap.se>; Thu, 16 Jun 2016 14:53:33 +0200 (CEST)
-MIME-Version: 1.0
-Content-Type: multipart/signed; protocol="application/pgp-signature";
- micalg="pgp-sha512"; boundary="===============7598747164910592838=="
-To: nobody@leap.se
-Subject: vrgg
-From: yoyo@dev.pixelated-project.org
-Date: Thu, 16 Jun 2016 13:53:32 -0000
-Message-Id: <20160616125332.16961.677041909.5@dev1.dev.pixelated-project.org>
-OpenPGP: id=CB546109E857BC34DFF2BCB3288870B39C400C24;
- url="https://dev.pixelated-project.org/key/yoyo"; preference="signencrypt"
-
---===============7598747164910592838==
-Content-Type: multipart/mixed; boundary="===============3737055506052708210=="
-MIME-Version: 1.0
-To: nobody@leap.se
-Subject: vrgg
-From: yoyo@dev.pixelated-project.org
-Date: Thu, 16 Jun 2016 13:53:32 -0000
-
---===============3737055506052708210==
-Content-Type: text/plain; charset="utf-8"
-MIME-Version: 1.0
-Content-Transfer-Encoding: base64
-
-
---===============3737055506052708210==
-Content-Type: application/pgp-keys
-MIME-Version: 1.0
-content-disposition: attachment; filename="yoyo@dev.pixelated-project.org-email-key.asc"
-Content-Transfer-Encoding: base64
-
-LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tCgptUUlOQkZkZ01BZ0JFQURIWWpU
-T20wcTdOT0lYVUpoTmlHVXg2S05OZ1M0Q0I2VlMvbGtab2UvYjZuRjdCSENmCkFnRVkxeFlxMkIv
-MzA3YzBtNTZWMEZvOWt2ZmZCUWhQckU5WG9rckI5blRlN1RsSDZUNTdiV09LSWMyMHhNSy8KSlVU
-djZ3UEpybjdLN0VyNEdxbzdrUmpWcFVBcWlBbGFxMkhVYllGd2NEMnBIb0VENmU2L01CZDBVUTFX
-b2s4QQpPNURDc2ZmeWhBZ0NFU1poK2w2VHlsVEJXYTJDTmJvUTl0SWtPZ0ZWTk9kTW9uWkxoTk1N
-Y0tIeU54dmF5bUdCCjhjQlRISVE2UWhGRThvR2JDRTdvczdZWWhyTmNmcUsyMzJJQllzTHNXN3Vk
-QmdwRTA0YkpwQWlvbW1zTHBCYmwKV0pCSjdqeEhwWmhJR3JGL1ltejNsSXpkbm9Mb3BSSWJyS0pC
-MmxaVDhIUHBlTVVJdVE2eHErd3RhQXFJVzlPTgo5U29uZWYyVU5BL3VseW1LeDRkOFhxbEwxY3hE
-aDFQU1E5YVlPcVg0RDlrMklmOXZmR2hET0xVMzR2Y2VFOC8vCnM1WGdTY2ZFbHg2SWlEVWZHdGx2
-aE5zQUM4TmhhUU1sOHJjUXVoRDA2RFdvSUowMVhkeFJVM2JSVVZkc0I1NWMKcXRWSHJMbVBVb256
-NU13MGFURzlTZzZudUlQcU1QOVNKRlBzbVpzR3ZYVnZWbCtSNzl1SFBlc25yWkoyTjZqOQpNaUth
-S045NFBhL1dJUnRoYWdzVnpHeHNtd2orTVZCRkZKRmh0TUtnNlFzYUsvbzRLNGJFR1ZLdWNXQk1i
-MnNxCldmd0o0SndTcHcrOHgyS3p6aXhWTllTZXhRdm9oMkc3RDRmRXdISDJzazNST3k3dTlldjhs
-bEVqUFFBUkFRQUIKdEQ5NWIzbHZRR1JsZGk1d2FYaGxiR0YwWldRdGNISnZhbVZqZEM1dmNtY2dQ
-SGx2ZVc5QVpHVjJMbkJwZUdWcwpZWFJsWkMxd2NtOXFaV04wTG05eVp6NkpBajRFRXdFQ0FDZ0ZB
-bGRnTUFnQ0d5OEZDUUhnTUZnR0N3a0lCd01DCkJoVUlBZ2tLQ3dRV0FnTUJBaDRCQWhlQUFBb0pF
-Q2lJY0xPY1FBd2s4djBQL2o2MmNyNjRUMlZPMVNKdHp1RlEKWjVpeVJsVFVHSGN2NW5hQjlUSDdI
-VVB3cTVwekZiTkg5SnhNRjVFRWtvZjdvV0hWeldWVTFBM1NDdzVNZ2FFbwppWTk5ZFBGNzdHazJ4
-ZEczNXZlWmIwWkg2WkVLdks1S042VXBucG5IeStxaVZVc1FLcE9DdUZKNkF0UlVEOTRJClJ2YnUv
-S1hsMHdORDlzVXFlYkJZN1BBSlRNY1RjLzVEdWpIT1Erd3VlSkFtaFZZbEozVnpZK1lBS2t5U05B
-QVoKZ3VVenNyUm5xQWU5SmU5TGgrcERpcVpHT2tEK1Z3b2kvRlVPQXJwbWFnNzZONTVjR3hiK2VG
-QUlzRHYrM1NNOQpjUDFyQkFON2lEaGgvdkdJeHgzMFlrYUlpMmpmcXg3VXUydnNwSXh6K0NsWWdi
-dm1wZm1CWmFqVzYzR0FsK3YvCngrby92eFZmVTMraTZ3alFjRS8vRTBTR2pvY3lQdUw0ZTZLNERy
-S3k2SHQycjBQckdHVFZ0dUZPaWU2dnVzbVcKL09sdVB1dGszU3o1S1BmRDFpRXBobmpPQ0pNRkZx
-Z2xRM1pPa3MweG00WGdwWW1ycnpQcXc1WWlzK1NEVjhobwp6anlrSzRWUlcrcC9IcUVzU29GQm5a
-MG5XSmg2Q1pZOExIeVNiMVJwaFlMRFpWd21JRXd1OW12Vm1ISVIyWUZVCllNZEx4UExiOFZNei9t
-QWpMb2Q0OGNSSzdSTzBSZ1RoMTUyK0VieXRGR3k5Y2tiS3VzRmJzVTFCQjN2MFJyUlUKenozTTcx
-T3hjcFhVQ0tpWlI0MEVYZnErSnVtZVFudm1wSWdZdUNaQkh5MzJwQUJuOHNDdUlrMStyQnp4bXdt
-bgp0WGh0K0RvNlExYXYyVjZYR00xV2xoKzEKPU8zaHEKLS0tLS1FTkQgUEdQIFBVQkxJQyBLRVkg
-QkxPQ0stLS0tLQo=
---===============3737055506052708210==--
-
---===============7598747164910592838==
-Content-Type: application/pgp-signature; name="signature.asc"
-MIME-Version: 1.0
-Content-Description: OpenPGP Digital Signature
-
------BEGIN PGP SIGNATURE-----
-
-iQIcBAABCgAGBQJXYqFNAAoJECiIcLOcQAwkDEIQAL67/XJXDv+lusoy18jr7Ony
-WQEP0pIRLp4GywGpH3dAITFAkuamO4VX3QEdVGjOHNoaT8VkSVWf9mnsYLl+Mh2v
-1OIwMv0u8WyVtrcxyXijIznnJv8X1RgyCzpUJcmOh04VZcDyxKbnFHWSDMfJ4Jtq
-qnXDONcfEeT8pwrGjP5qzTgcF/irG3w5svyQjEtj6kycddYtqUc9Hx3cMaRIzsHg
-kuUzznSzU/6P0Z345q/kXyYvU9rlcsP9vogrsqL2ueLwYSipxUJQUrRWG82FYoCo
-PAKNdGIt0xl2gEW+xWZkJqFarPiUFCx//+bVBelKrqj6rjwbj+E7mHJW318JYVHQ
-en3Smv7pEWlT4hZHXnoe8ng6TAvKzQjf7/bUxq2JpKSycp2hDO3Qz3Tv+kc+jC/r
-5UDWe/flR+syq8lAQTRSn6057g3BgDG2RtAwsjedg1aTFSrljSxbKlK4vsj5Muek
-Olq9+MUdMFSE3Jj/JC2COcS3rlt/Qt+JLDYXKahU3CodaSgF2dobikDe1bW0/QNS
-7O4Ng2PK0pA416RCFRUgPXerUnMGiWAiq7BoRHeym9y7fkHYhIYGpPVKXJ6t67y5
-JjvuzwfwG8SZTp4Wy2pg1Mr6znm6uVBxUDxTHyP3BjciI1zpEigOIg9UwJ9nCDxL
-uUGz4VqipNKbkpRkjLLW
-=3IaF
------END PGP SIGNATURE-----
-
---===============7598747164910592838==--
-
---8F60183010.1466081614/dev1.dev.pixelated-project.org--
diff --git a/src/leap/bitmask/mail/tests/rfc822.message b/src/leap/bitmask/mail/tests/rfc822.message
deleted file mode 100644
index ee97ab9..0000000
--- a/src/leap/bitmask/mail/tests/rfc822.message
+++ /dev/null
@@ -1,86 +0,0 @@
-Return-Path: <twisted-commits-admin@twistedmatrix.com>
-Delivered-To: exarkun@meson.dyndns.org
-Received: from localhost [127.0.0.1]
- by localhost with POP3 (fetchmail-6.2.1)
- for exarkun@localhost (single-drop); Thu, 20 Mar 2003 14:50:20 -0500 (EST)
-Received: from pyramid.twistedmatrix.com (adsl-64-123-27-105.dsl.austtx.swbell.net [64.123.27.105])
- by intarweb.us (Postfix) with ESMTP id 4A4A513EA4
- for <exarkun@meson.dyndns.org>; Thu, 20 Mar 2003 14:49:27 -0500 (EST)
-Received: from localhost ([127.0.0.1] helo=pyramid.twistedmatrix.com)
- by pyramid.twistedmatrix.com with esmtp (Exim 3.35 #1 (Debian))
- id 18w648-0007Vl-00; Thu, 20 Mar 2003 13:51:04 -0600
-Received: from acapnotic by pyramid.twistedmatrix.com with local (Exim 3.35 #1 (Debian))
- id 18w63j-0007VK-00
- for <twisted-commits@twistedmatrix.com>; Thu, 20 Mar 2003 13:50:39 -0600
-To: twisted-commits@twistedmatrix.com
-From: etrepum CVS <etrepum@twistedmatrix.com>
-Reply-To: twisted-python@twistedmatrix.com
-X-Mailer: CVSToys
-Message-Id: <E18w63j-0007VK-00@pyramid.twistedmatrix.com>
-Subject: [Twisted-commits] rebuild now works on python versions from 2.2.0 and up.
-Sender: twisted-commits-admin@twistedmatrix.com
-Errors-To: twisted-commits-admin@twistedmatrix.com
-X-BeenThere: twisted-commits@twistedmatrix.com
-X-Mailman-Version: 2.0.11
-Precedence: bulk
-List-Help: <mailto:twisted-commits-request@twistedmatrix.com?subject=help>
-List-Post: <mailto:twisted-commits@twistedmatrix.com>
-List-Subscribe: <http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-commits>,
- <mailto:twisted-commits-request@twistedmatrix.com?subject=subscribe>
-List-Id: <twisted-commits.twistedmatrix.com>
-List-Unsubscribe: <http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-commits>,
- <mailto:twisted-commits-request@twistedmatrix.com?subject=unsubscribe>
-List-Archive: <http://twistedmatrix.com/pipermail/twisted-commits/>
-Date: Thu, 20 Mar 2003 13:50:39 -0600
-
-Modified files:
-Twisted/twisted/python/rebuild.py 1.19 1.20
-
-Log message:
-rebuild now works on python versions from 2.2.0 and up.
-
-
-ViewCVS links:
-http://twistedmatrix.com/users/jh.twistd/viewcvs/cgi/viewcvs.cgi/twisted/python/rebuild.py.diff?r1=text&tr1=1.19&r2=text&tr2=1.20&cvsroot=Twisted
-
-Index: Twisted/twisted/python/rebuild.py
-diff -u Twisted/twisted/python/rebuild.py:1.19 Twisted/twisted/python/rebuild.py:1.20
---- Twisted/twisted/python/rebuild.py:1.19 Fri Jan 17 13:50:49 2003
-+++ Twisted/twisted/python/rebuild.py Thu Mar 20 11:50:08 2003
-@@ -206,15 +206,27 @@
- clazz.__dict__.clear()
- clazz.__getattr__ = __getattr__
- clazz.__module__ = module.__name__
-+ if newclasses:
-+ import gc
-+ if (2, 2, 0) <= sys.version_info[:3] < (2, 2, 2):
-+ hasBrokenRebuild = 1
-+ gc_objects = gc.get_objects()
-+ else:
-+ hasBrokenRebuild = 0
- for nclass in newclasses:
- ga = getattr(module, nclass.__name__)
- if ga is nclass:
- log.msg("WARNING: new-class %s not replaced by reload!" % reflect.qual(nclass))
- else:
-- import gc
-- for r in gc.get_referrers(nclass):
-- if isinstance(r, nclass):
-+ if hasBrokenRebuild:
-+ for r in gc_objects:
-+ if not getattr(r, '__class__', None) is nclass:
-+ continue
- r.__class__ = ga
-+ else:
-+ for r in gc.get_referrers(nclass):
-+ if getattr(r, '__class__', None) is nclass:
-+ r.__class__ = ga
- if doLog:
- log.msg('')
- log.msg(' (fixing %s): ' % str(module.__name__))
-
-
-_______________________________________________
-Twisted-commits mailing list
-Twisted-commits@twistedmatrix.com
-http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-commits
diff --git a/src/leap/bitmask/mail/tests/rfc822.multi-minimal.message b/src/leap/bitmask/mail/tests/rfc822.multi-minimal.message
deleted file mode 100644
index 582297c..0000000
--- a/src/leap/bitmask/mail/tests/rfc822.multi-minimal.message
+++ /dev/null
@@ -1,16 +0,0 @@
-Content-Type: multipart/mixed; boundary="===============6203542367371144092=="
-MIME-Version: 1.0
-Subject: [TEST] 010 - Inceptos cum lorem risus congue
-From: testmailbitmaskspam@gmail.com
-To: test_c5@dev.bitmask.net
-
---===============6203542367371144092==
-Content-Type: text/plain; charset="us-ascii"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 7bit
-
-Howdy from python!
-The subject: [TEST] 010 - Inceptos cum lorem risus congue
-Current date & time: Wed Jan 8 16:36:21 2014
-Trying to attach: []
---===============6203542367371144092==--
diff --git a/src/leap/bitmask/mail/tests/rfc822.multi-nested.message b/src/leap/bitmask/mail/tests/rfc822.multi-nested.message
deleted file mode 100644
index 694bef5..0000000
--- a/src/leap/bitmask/mail/tests/rfc822.multi-nested.message
+++ /dev/null
@@ -1,619 +0,0 @@
-From: TEST <test.bitmask@example.com>
-Content-Type: multipart/alternative;
- boundary="Apple-Mail=_F4EF9C8E-2E66-4FC6-8840-F435ADBED5C8"
-X-Smtp-Server: smtp.example.com:test.bitmask
-Subject: test simple attachment
-X-Universally-Unique-Identifier: 0ea1b4b2-cdb8-43c3-b54c-dc88a19c6e0a
-Date: Wed, 8 Jul 2015 04:25:56 +0900
-Message-Id: <47278179-628A-43F5-95C9-BC7E1753C521@example.com>
-To: test_alpha14_001@dev.bitmask.net
-Mime-Version: 1.0 (Apple Message framework v1251.1)
-
-
---Apple-Mail=_F4EF9C8E-2E66-4FC6-8840-F435ADBED5C8
-Content-Transfer-Encoding: 7bit
-Content-Type: text/plain;
- charset=us-ascii
-
-this is a simple attachment
---Apple-Mail=_F4EF9C8E-2E66-4FC6-8840-F435ADBED5C8
-Content-Type: multipart/related;
- type="text/html";
- boundary="Apple-Mail=_C7D5288F-B043-4A7F-AF3F-1EDF1A78438B"
-
-
---Apple-Mail=_C7D5288F-B043-4A7F-AF3F-1EDF1A78438B
-Content-Transfer-Encoding: 7bit
-Content-Type: text/html;
- charset=us-ascii
-
-<html><head></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; ">this is a simple attachment<img height="286" width="300" apple-width="yes" apple-height="yes" id="fd3d0c89-709d-419f-b293-a6827f75c8d4" src="cid:163B7957-4342-485F-8FD6-D46A4A53A2C1"></body></html>
---Apple-Mail=_C7D5288F-B043-4A7F-AF3F-1EDF1A78438B
-Content-Transfer-Encoding: base64
-Content-Disposition: inline;
- filename="saing_ergol.jpg"
-Content-Type: image/jpg;
- x-mac-hide-extension=yes;
- x-unix-mode=0600;
- name="saint_ergol.jpg"
-Content-Id: <163B7957-4342-485F-8FD6-D46A4A53A2C1>
-
-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
-64D/2Q==
-
---Apple-Mail=_C7D5288F-B043-4A7F-AF3F-1EDF1A78438B--
-
---Apple-Mail=_F4EF9C8E-2E66-4FC6-8840-F435ADBED5C8--
diff --git a/src/leap/bitmask/mail/tests/rfc822.multi-signed.message b/src/leap/bitmask/mail/tests/rfc822.multi-signed.message
deleted file mode 100644
index 9907c2d..0000000
--- a/src/leap/bitmask/mail/tests/rfc822.multi-signed.message
+++ /dev/null
@@ -1,238 +0,0 @@
-Date: Mon, 6 Jan 2014 04:40:47 -0400
-From: Kali Kaneko <kali@leap.se>
-To: penguin@example.com
-Subject: signed message
-Message-ID: <20140106084047.GA21317@samsara.lan>
-MIME-Version: 1.0
-Content-Type: multipart/signed; micalg=pgp-sha1;
- protocol="application/pgp-signature"; boundary="z9ECzHErBrwFF8sy"
-Content-Disposition: inline
-User-Agent: Mutt/1.5.21 (2012-12-30)
-
-
---z9ECzHErBrwFF8sy
-Content-Type: multipart/mixed; boundary="z0eOaCaDLjvTGF2l"
-Content-Disposition: inline
-
-
---z0eOaCaDLjvTGF2l
-Content-Type: text/plain; charset=utf-8
-Content-Disposition: inline
-Content-Transfer-Encoding: quoted-printable
-
-This is an example of a signed message,
-with attachments.
-
-
---=20
-Nihil sine chao! =E2=88=B4
-
---z0eOaCaDLjvTGF2l
-Content-Type: text/plain; charset=us-ascii
-Content-Disposition: attachment; filename="attach.txt"
-
-this is attachment in plain text.
-
---z0eOaCaDLjvTGF2l
-Content-Type: application/octet-stream
-Content-Disposition: attachment; filename="hack.ico"
-Content-Transfer-Encoding: base64
-
-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
-AAAAEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8Af39/AL+/vwA/Pz8A39/fAF9fXwCfn58A
-Hx8fAO/v7wAvLy8Ab29vAI+PjwAPDw8A0NDQALCwsABQUFAA9/f3ABcXFwDn5+cAJycnAMjI
-yABHR0cAqKioAGdnZwCXl5cAd3d3AIeHhwAHBwcA2NjYALi4uABXV1cANTU1ADo6OgD7+/sA
-CwsLAPPz8wATExMA6+vrABsbGwDj4+MAIyMjANTU1AArKysAzMzMAMTExABLS0sAtLS0AKys
-rABbW1sApKSkAGNjYwCbm5sAa2trAJOTkwBzc3MAi4uLAHt7ewCDg4MAAwMDANzc3AAyMjIA
-vLy8AFNTUwD9/f0ABQUFAPn5+QAJCQkADQ0NAPHx8QDt7e0AFRUVAOnp6QAZGRkA5eXlAB0d
-HQDh4eEAISEhACUlJQDa2toAKSkpANbW1gDS0tIAysrKADw8PADGxsYAwsLCAEVFRQBJSUkA
-urq6ALa2tgCysrIArq6uAFlZWQCqqqoAXV1dAKampgBlZWUAoqKiAJ2dnQBtbW0AmZmZAHFx
-cQCVlZUAeXl5AH19fQCJiYkAhYWFAAEBAQACAgIABAQEAP7+/gAGBgYA/Pz8AAgICAD6+voA
-CgoKAPj4+AAMDAwA9vb2APT09AASEhIA8vLyABQUFADu7u4AFhYWAOzs7AAYGBgA6urqAOjo
-6AAeHh4AICAgAOTk5AAiIiIA4uLiACQkJADg4OAAJiYmAN7e3gDd3d0AKCgoANvb2wAqKioA
-2dnZACwsLADX19cALi4uANXV1QAxMTEA09PTADMzMwDR0dEANDQ0AM3NzQA5OTkAy8vLADs7
-OwDJyckAPT09AMfHxwBAQEAAxcXFAMPDwwDBwcEAwMDAAL6+vgBKSkoAvb29ALu7uwC5ubkA
-UVFRALe3twBSUlIAtbW1AFRUVACzs7MAVlZWAFhYWABaWloAra2tAFxcXACrq6sAXl5eAKmp
-qQCnp6cAZGRkAKOjowChoaEAaGhoAKCgoACenp4AnJycAG5ubgCampoAcHBwAJiYmABycnIA
-lpaWAJSUlAB2dnYAkpKSAHh4eACQkJAAenp6AI6OjgB8fHwAjIyMAIiIiACCgoIAhISEAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAC1WlpaWlpaWlpaWlpaWlpaWlpaHjAHr6taWlpaWlpaWlpa
-WlpaWlpaq68HMB5aWlpap6KlWzBaA6KoWlpaWlq1WgEBAQEBAQEBAQEBAQEBAQEBAQGXNkUB
-AQEBAQEBAQEBAQEBAQEBAQFFNpcBAQEBASg4EI6HPa5lfgEBAQEBWloBAQEBAQEBAQEBAQEB
-AQEBAQEBlzZFAQEBAQEBAQEBAQEBAQEBAQEBRTaXAQEBETpEAAAAAAAAAH/FbwEBAVpaAQEB
-AQEBAQEBAQEBAQEBAQEBAZc2RQEBAQEBAQEBAQEBAQEBAQEBAUU2lwEBhFQAAAAAAAAAAAAA
-ALJCAQFaWgEBAQEBAQEBAQEBAQEBAQEBAQGXNkUBAQEBAQEBAQEBAQEBAQEBAQFFNpcBeJoA
-AAAAAAAAAAAAAAAAMQEBWloBAQEBAQEBAQEBAQEBAQEBAQEBlzZFAQEBAQEBAQEBAQEBAQEB
-AQEBRTZSATUAAAAAAAAAAAAAAAAAAABnAVpaAQEBAQEBAQEBAQEBAQEBAQEBAZc2RQEBAQEB
-AQEBAQEBAQEBAQEBAUU2Tx1wAAAAAAAAAAAAAAAAAAAAgkaoWgEBAQEBAQEBAQEBAQEBAQEB
-AQGXNkUBAQEBAQEBAQEBAQEBAQEBAQFFNgVrAAAAAAAAAAAAAAAAAAAAAABioloBAQEBAQEB
-AQEBAQEBAQEBAQEBlzZFAQEBAQEBAQEBAQEBAQEBAQEBRWcqngAAAAAAAAAAAAAAAAAAAAAA
-tANaAQEBAQEBAQEBAQEBAQEBAQEBAZc2RQEBAQEBAQEBAQEBAQEBAQEBAUXDpIcAAAAAAAAA
-AAAAAAAAAAAAAJRaWgEBAQEBAQEBAQEBAQEBAQEBAQGXNkUBAQEBAQEBAQEBAQEBAQEBAQFF
-wa9HAAAAAAAAAAAAAAAAAAAAAABOMFoBAQEBAQEBAQEBAQEBAQEBAQEBlzZFAQEBAQEBAQEB
-AQEBAQEBAQEBRWVZggAAAAAAAAAAAAAAAAAAAAAAjltaAQEBAQEBAQEBAQEBAQEBAQEBAZc2
-RQEBAQEBAQEBAQEBAQEBAQEBAUXFmZYAAAAAAAAAAAAAAAAAAAAAAKqlWgEBAQEBAQEBAQEB
-AQEBAQEBAQGXNkUBAQEBAQEBAQEBAQEBAQEBAQFFNorHAAAAAAAAAAAAAAAAAAAAAABloloB
-AQEBAQEBAQEBAQEBAQEBAQEBlzZFAQEBAQEBAQEBAQEBAQEBAQEBRTY8UwAAAAAAAAAAAAAA
-AAAAAAASEz5aAQEBAQEBAQEBAQEBAQEBAQEBAZc2RQEBAQEBAQEBAQEBAQEBAQEBAUU2lQFd
-AAAAAAAAAAAAAAAAAAAA0AFaWgEBAQEBAQEBAQEBAQEBAQEBAQGXNkUBAQEBAQEBAQEBAQEB
-AQEBAQFFNpcBhoUAAAAAAAAAAAAAAAAAVxEBWloBAQEBAQEBAQEBAQEBAQEBAQEBlzZFAQEB
-AQEBAQEBAQEBAQEBAQEBRTaXAQGXTQAAAAAAAAAAAAAAnCgBAVpaAQEBAQEBAQEBAQEBAQEB
-AQEBAZc2RQEBAQEBAQEBAQEBAQEBAQEBAUU2lwEBASiwAAAAAAAAAAAcwncBAQFaWgEBAQEB
-AQEBAQEBAQEBAQEBAQGXNkUBAQEBAQEBAQEBAQEBAQEBAQFFNpcBAQEBASy8khINgiFojQEB
-AQEBWjCVl5eXl5eXl5dSUpeXl5eXl5eTHsWdlZeXl5eXl5eXl5eXl5eXl5eVncUek5eXl1I8
-ipsvs6iVBU9Sl5eXlTAHNjY2NjY2Zb1ivbtiY2c2NjY2NsVlxjY2NjY2NjY2NjY2NjY2NjY2
-NsZlxTY2NjY2xr8yFxcXusHGNjY2NjYHW3hFRUURAY8HC7Jh0ahFb3pFRRGdxkp4RUVFRUVF
-RUVFRUVFRUVFRXhKxp0RRUVFIkKhDLkxwMiXInNFRUV4W1oBAQEBCcclAAAAAAAAnK0BAQEB
-lzZFAQEBAQEBAQEBAQEBAQEBAQEBRTaXAQEBAQ4ucAAAAAAAdAaNAQEBAVpaAQEBpYMAAAAA
-AAAAAAAAGHUBAZc2RQEBAQEBAQEBAQEBAQEBAQEBAUU2lwEBAWtwAAAAAAAAAAAADboBAQFa
-WgEBHnIAAAAAAAAAAAAAAACxcwGXNkUBAQEBAQEBAQEBAQEBAQEBAQFFNpcBAcQAAAAAAAAA
-AAAAAABtwQEBWloBiCcAAAAAAAAAAAAAAAAAAM0BUjZFAQEBAQEBAQEBAQEBAQEBAQEBRTaX
-AbsAAAAAAAAAAAAAAAAAAHCiAVpaAQYAAAAAAAAAAAAAAAAAAAAck082RQEBAQEBAQEBAQEB
-AQEBAQEBAUU2UUVLAAAAAAAAAAAAAAAAAAAAIQEePkoNAAAAAAAAAAAAAAAAAAAAAMCLxkUB
-AQEBAQEBAQEBAQEBAQEBAQFFNgViAAAAAAAAAAAAAAAAAAAAAACppKK9AAAAAAAAAAAAAAAA
-AAAAAACQnxlFAQEBAQEBAQEBAQEBAQEBAQEBRcZPrAAAAAAAAAAAAAAAAAAAAAAAZqOjCwAA
-AAAAAAAAAAAAAAAAAAAAQ7i/RQEBAQEBAQEBAQEBAQEBAQEBAUUZVSsAAAAAAAAAAAAAAAAA
-AAAAAFRZpT8AAAAAAAAAAAAAAAAAAAAAAADKvkUBAQEBAQEBAQEBAQEBAQEBAQFFZVpJAAAA
-AAAAAAAAAAAAAAAAAAAUXKU/AAAAAAAAAAAAAAAAAAAAAAAAyr5FAQEBAQEBAQEBAQEBAQEB
-AQEBRWVaSQAAAAAAAAAAAAAAAAAAAAAAFFyjCwAAAAAAAAAAAAAAAAAAAAAAdl40RQEBAQEB
-AQEBAQEBAQEBAQEBAUUZVSsAAAAAAAAAAAAAAAAAAAAAAKCoVrcAAAAAAAAAAAAAAAAAAAAA
-ACCZxUUBAQEBAQEBAQEBAQEBAQEBAQFFxo1fAAAAAAAAAAAAAAAAAAAAAABpVqh+fQAAAAAA
-AAAAAAAAAAAAAADRijZFAQEBAQEBAQEBAQEBAQEBAQEBRTaKXAAAAAAAAAAAAAAAAAAAAAA7
-LANaAWgAAAAAAAAAAAAAAAAAAABJSJE2RQEBAQEBAQEBAQEBAQEBAQEBAUU2KgEKAAAAAAAA
-AAAAAAAAAAAAHwGrWgF8kAAAAAAAAAAAAAAAAAAAZQGXNkUBAQEBAQEBAQEBAQEBAQEBAQFF
-NpcBHm0AAAAAAAAAAAAAAAAAEk8BWloBAZVLAAAAAAAAAAAAAAAANwEBlzZFAQEBAQEBAQEB
-AQEBAQEBAQEBRTaXAQHFAAAAAAAAAAAAAAAAQx4BAVpaAQEBj1QAAAAAAAAAAAByGQEBAZc2
-RQEBAQEBAQEBAQEBAQEBAQEBAUU2lwEBARcSAAAAAAAAAAAAjJkBAQFaWgEBAQFxuphuAAAA
-ABK8jwEBAQGXNkUBAQEBAQEBAQEBAQEBAQEBAQFFNpcBAQEBSMlLAAAAAG0rDEUBAQEBWlt4
-RUVFeAFFLWU6DC8FcXNFRUURncZKeEVFRUVFRUVFRUVFRUVFRUV4SsadEUVFRXUBhC8MOmWi
-JgF3RUVFeFsHNjY2NjY2Z7+9Yru+wzY2NjY2NsVlxjY2NjY2NsU0vr6/wzY2NjY2NsZlxTY2
-NjY2NmUytbO3Yhk2NjY2NjYHMJWXl5eXl5eXl5eXl5eXl5eXl5MexZ2Vl5eXHQWdXgwMYKKK
-T5eXl5WdxR6Tl5eXKgWVrWfOvquPipWXl5eVMFoBAQEBAQEBAQEBAQEBAQEBAQEBlzZFAQEB
-AYE5kHYAAEMpvJEBAQEBRTaXAQEBAXFiBEcAAG4Spi8BAQEBAVpaAQEBAQEBAQEBAQEBAQEB
-AQEBAZc2RQEBAcF7AAAAAAAAAABBaUIBAUU2lwEBAZsgAAAAAAAAAAAAFooBAQFaWgEBAQEB
-AQEBAQEBAQEBAQEBAQGXNkUBAQsAAAAAAAAAAAAAAACxcwFFNpcBAQ92AAAAAAAAAAAAAABN
-UQEBWloBAQEBAQEBAQEBAQEBAQEBAQEBlzZFAcwAAAAAAAAAAAAAAAAAABgBejaXAZd5AAAA
-AAAAAAAAAAAAAImAAVpaAQEBAQEBAQEBAQEBAQEBAQEBAZc2c1JDAAAAAAAAAAAAAAAAAAAA
-W3E2KgGeAAAAAAAAAAAAAAAAAAAAMwGrWgEBAQEBAQEBAQEBAQEBAQEBAQGXNm9kAAAAAAAA
-AAAAAAAAAAAAAAQJZ4ukAAAAAAAAAAAAAAAAAAAAAHKVpVoBAQEBAQEBAQEBAQEBAQEBAQEB
-l8OGKQAAAAAAAAAAAAAAAAAAAAAcor+LNQAAAAAAAAAAAAAAAAAAAAAAaqJaAQEBAQEBAQEB
-AQEBAQEBAQEBAZdjHmwAAAAAAAAAAAAAAAAAAAAAAM8ymT0AAAAAAAAAAAAAAAAAAAAAAFg+
-WgEBAQEBAQEBAQEBAQEBAQEBAQGXvWUAAAAAAAAAAAAAAAAAAAAAAABhuFmCAAAAAAAAAAAA
-AAAAAAAAAACOW1oBAQEBAQEBAQEBAQEBAQEBAQEBl7vOAAAAAAAAAAAAAAAAAAAAAAAAtGCv
-RwAAAAAAAAAAAAAAAAAAAAAATjBaAQEBAQEBAQEBAQEBAQEBAQEBAZcHYgAAAAAAAAAAAAAA
-AAAAAAAAAAu4pIcAAAAAAAAAAAAAAAAAAAAAAD1aWgEBAQEBAQEBAQEBAQEBAQEBAQGXNBUj
-AAAAAAAAAAAAAAAAAAAAAAAyvSpXAAAAAAAAAAAAAAAAAAAAAAAYpFoBAQEBAQEBAQEBAQEB
-AQEBAQEBl2ckVAAAAAAAAAAAAAAAAAAAAACDiMMFzAAAAAAAAAAAAAAAAAAAAAAAr6NaAQEB
-AQEBAQEBAQEBAQEBAQEBAZc2b7sAAAAAAAAAAAAAAAAAAAAAaW82HRMlAAAAAAAAAAAAAAAA
-AAAAlECpWgEBAQEBAQEBAQEBAQEBAQEBAQGXNngBBAAAAAAAAAAAAAAAAAAAKUZ3NpcBzwAA
-AAAAAAAAAAAAAAAAAA8BWloBAQEBAQEBAQEBAQEBAQEBAQEBlzZFAZGCAAAAAAAAAAAAAAAA
-dC0BRTaXAXGwAAAAAAAAAAAAAAAAAAIBAVpaAQEBAQEBAQEBAQEBAQEBAQEBAZc2RQEBlY4A
-AAAAAAAAAAAACD4BAUU2lwEBd7YAAAAAAAAAAAAAbmtvAQFaWgEBAQEBAQEBAQEBAQEBAQEB
-AQGXNkUBAQEJyw0AAAAAAAB0M0wBAQFFNpcBAQEBF1AAAAAAAAAAVD4BAQEBWloBAQEBAQEB
-AQEBAQEBAQEBAQEBlzZFAQEBAQETB7ymprxliwEBAQEBRTaXAQEBAQF1qxqsV7QbVXEBAQEB
-AVq1WlpaWlpaWlpaWlpaWlpaWlpaHjAHr6taWlpaPqKkPj6kLadaWlpaq68HMB5aWlpaqaNW
-pz4DLaQeWlpaWlq1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
-
---z0eOaCaDLjvTGF2l--
-
---z9ECzHErBrwFF8sy
-Content-Type: application/pgp-signature
-
------BEGIN PGP SIGNATURE-----
-Version: GnuPG v1.4.15 (GNU/Linux)
-
-iQIcBAEBAgAGBQJSymwPAAoJECNji/csWTvBhtcP/2AKF0uk6ljrfMWhNBSFwDqv
-kYng3slREnF/pxnIGOpR2GAxPBPjRipZOuUU8QL+pXBwk5kWzb9RYpr26xMYWRtl
-vXdVbob5NolNEYrqTkkQ1kejERQGFyescsUJDcEDXJl024czKWbxHTYYN4vlYJMK
-PZ5mPSdADFn970PnVXfNix3Rjvv7SFQGammDBGjQzyROkoiDKPZcomp6dzm6zEXC
-w8i42WfHU8GkyVVNvXZI52Xw3LUXiXsJ58B1V1O5U42facepG6S+S0DC/PWptqPw
-sAM9/YGkvBNWrsJA/BavXPRLE1gVpu+hZZEsOqRvs244k7JTrVo54xDbdeOT2nTr
-BDk4e88vmCVKGgE9MZjDbjgOHDZhmsxNQm4DBGRH2huF0noUc/8Sm4KhSO49S2mN
-QjIT5QrPerQNiP5QtShHZRJX7ElXYZWX1SG/c9jQjfd0W1XK/cGtwClICe+lpprt
-mLC2607yalbRhCxV9bQlVUnd2tY3NY4UgIKgCEiEwb1hf/k9jQDvpk16VuNWSZQJ
-jFeg9F2WdNjQMp79cyvnayyhjS9o/K2LbSIgJi7KdlQcVZ/2DQfbMjCwByR7P9g8
-gcAKh8V7E6IpAu1mnvs4FDagipppK6hOTRj2s/I3xZzneprSK1WaVro/8LAWZe9X
-sSdfcAhT7Tno7PB/Acoh
-=+okv
------END PGP SIGNATURE-----
-
---z9ECzHErBrwFF8sy--
diff --git a/src/leap/bitmask/mail/tests/rfc822.multi.message b/src/leap/bitmask/mail/tests/rfc822.multi.message
deleted file mode 100644
index 30f74e5..0000000
--- a/src/leap/bitmask/mail/tests/rfc822.multi.message
+++ /dev/null
@@ -1,96 +0,0 @@
-Date: Fri, 19 May 2000 09:55:48 -0400 (EDT)
-From: Doug Sauder <doug@penguin.example.com>
-To: Joe Blow <blow@example.com>
-Subject: Test message from PINE
-Message-ID: <Pine.LNX.4.21.0005190951410.8452-102000@penguin.example.com>
-MIME-Version: 1.0
-Content-Type: MULTIPART/MIXED; BOUNDARY="-1463757054-952513540-958744548=:8452"
-
- This message is in MIME format. The first part should be readable text,
- while the remaining parts are likely unreadable without MIME-aware tools.
- Send mail to mime@docserver.cac.washington.edu for more info.
-
----1463757054-952513540-958744548=:8452
-Content-Type: TEXT/PLAIN; charset=US-ASCII
-
-This is a test message from PINE MUA.
-
-
----1463757054-952513540-958744548=:8452
-Content-Type: APPLICATION/octet-stream; name="redball.png"
-Content-Transfer-Encoding: BASE64
-Content-ID: <Pine.LNX.4.21.0005190955480.8452@penguin.example.com>
-Content-Description: A PNG graphic file
-Content-Disposition: attachment; filename="redball.png"
-
-iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8A
-AAABAAALAAAVAAAaAAAXAAARAAAKAAADAAAcAAAyAABEAABNAABIAAA9AAAj
-AAAWAAAmAABhAAB7AACGAACHAAB9AAB0AABgAAA5AAAUAAAGAAAnAABLAABv
-AACQAAClAAC7AAC/AACrAAChAACMAABzAABbAAAuAAAIAABMAAB3AACZAAC0
-GRnKODjVPT3bKSndBQW4AACoAAB5AAAxAAAYAAAEAABFAACaAAC7JCTRYWHf
-hITmf3/mVlbqHx/SAAC5AACjAABdAABCAAAoAAAJAABnAAC6Dw/QVFTek5Pl
-rKzpmZntZWXvJSXXAADBAACxAACcAABtAABTAAA2AAAbAAAFAABKAACBAADL
-ICDdZ2fonJzrpqbtiorvUVHvFBTRAADDAAC2AAB4AABeAABAAAAiAABXAACS
-AADCAADaGxvoVVXseHjveHjvV1fvJibhAADOAAC3AACnAACVAABHAAArAAAP
-AACdAADFAADhBQXrKCjvPDzvNTXvGxvjAADQAADJAAC1AACXAACEAABsAABP
-AAASAAACAABiAADpAADvAgLnAADYAADLAAC6AACwAABwAAATAAAkAABYAADI
-AADTAADNAACzAACDAABuAAAeAAB+AADAAACkAACNAAB/AABpAABQAAAwAACR
-AACpAAC8AACqAACbAABlAABJAAAqAAAOAAA0AACsAACvAACtAACmAACJAAB6
-AABrAABaAAA+AAApAABqAACCAACfAACeAACWAACPAAB8AAAZAAAHAABVAACO
-AACKAAA4AAAQAAA/AAByAACAAABcAAA3AAAsAABmAABDAABWAAAgAAAzAAA8
-AAA6AAAfAAAMAAAdAAANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8
-LtlFAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu
-MT1evmgAAAIISURBVHicY2CAg/8QwIABmJhZWFnZ2Dk4MaU5uLh5eHn5+LkF
-BDlQJf8zC/EIi4iKiUtI8koJScsgyf5nlpWTV1BUUlZRVVPX4NFk1UJIyghp
-6+jq6RsYGhmbKJgK85mZW8Dk/rNaSlhZ29ja2Ts4Ojkr6Li4urFDNf53N/Ow
-8vTy9vH18w8IDAoWDQkNC4+ASP5ni4wKio6JjYtPSExKTnFWSE1LF4A69n9G
-ZlZ2Tm5efkFhUXFySWlZlEd5RSVY7j+TkGRVdU1tXX1DY1Ozcktpa1t7h2Yn
-OAj+d7l1tyo79vT29SdNSJ44SbFVdHIo9xSIHNPUaWqTpifNSJrZnK00S0U1
-a/acUG5piNz/uXLzVJ2qm6dXz584S2WB1cJFi5cshZr539xVftnyFKUVTi2T
-VjqvyhJLXb1m7TqoHPt6F/HW0g0bN63crGqVtWXrtu07BJihcsw71+zanRW8
-Z89eq337RQ/Ip60xO3gIElX/LbikDm8T36KwbNmRo7O3zpHkPSZwHBqL//8f
-lz1x2OOkyKJTi7aqbzutfUZI2gIuF8F2lr/D5dw2+fZdwpl8YVOlI+CJ4/9/
-joOyYed5QzMvhGqnm2V0WiClm///D0lfXHtJ6vLlK9w7rx7vQk5SQJbFtSms
-1y9evXid7QZacgOxmSxktNzdtSwwU+J/VICaCPFIYU3XAJhIOtjf5sfyAAAA
-JXRFWHRDb21tZW50AGNsaXAyZ2lmIHYuMC42IGJ5IFl2ZXMgUGlndWV0NnM7
-vAAAAABJRU5ErkJggg==
----1463757054-952513540-958744548=:8452
-Content-Type: APPLICATION/octet-stream; name="blueball.png"
-Content-Transfer-Encoding: BASE64
-Content-ID: <Pine.LNX.4.21.0005190955481.8452@penguin.example.com>
-Content-Description: A PNG graphic file
-Content-Disposition: attachment; filename="blueball.png"
-
-iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8A
-AAgAABAAABgAAAAACCkAEEIAEEoACDEAEFIIIXMIKXsIKYQIIWsAGFoACDkI
-IWMQOZwYQqUYQq0YQrUQOaUQMZQAGFIQMYwpUrU5Y8Y5Y84pWs4YSs4YQs4Y
-Qr1Ca8Z7nNacvd6Mtd5jlOcxa94hUt4YStYYQsYQMaUAACHO5+/n7++cxu9S
-hO8pWucQOa1Ke86tzt6lzu9ajO8QMZxahNat1ufO7++Mve9Ke+8YOaUYSsaM
-vee15++Uve8AAClajOdzpe9rnO8IKYwxY+8pWu8IIXsAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADB
-Mg1VAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu
-MT1evmgAAAGISURBVHicddJtV5swGAbgEk6AJhBSk4bMCUynBSLaqovbrG/b
-fPn/vyh70lbsscebL5xznTsh5BmNhgQoRChwo50EOIohUYLDj4zHhKYQkrEo
-Qdvock4ne0IKMVUpKZLQDeqSTIsv+18PyqqWUw2IBsRM7307PPp+fDJrWtnp
-LDJvewYxnewfnvanZ+fzpmwXijC8KbqEa3Fx2ff91Y95U9XCUpaDeQwiMpHX
-P/v+1++bWVPWQoGFawtjury9vru/f/C1Vi7ezT0WWpQHf/7+u/G71aLThK/M
-jRxmT6KdzZ9fGk9yatMsTgZLl3XVgFRAC6spj/13enssqJVtWVa3NdBSacL8
-+VZmYqKmdd1CSYoOiMOSGwtzlqqlFFIuOqv0a1ZEZrUkWICLLFW266y1KvWE
-1zV/iDAH1EopnVLCiygZCIomH3NCKX0lnI+B1iuuzCGTxwXjnDO4d7NpbX42
-YJJHkBwmAm2TxwAZg40J3+Xtbv1rgOAZwG0NxW62p+lT+Yi747sD/wEUVMzY
-mWkOvwAAACV0RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBieSBZdmVzIFBp
-Z3VldDZzO7wAAAAASUVORK5CYII=
----1463757054-952513540-958744548=:8452--
diff --git a/src/leap/bitmask/mail/tests/rfc822.plain.message b/src/leap/bitmask/mail/tests/rfc822.plain.message
deleted file mode 100644
index fc627c3..0000000
--- a/src/leap/bitmask/mail/tests/rfc822.plain.message
+++ /dev/null
@@ -1,66 +0,0 @@
-From pyar-bounces@python.org.ar Wed Jan 8 14:46:02 2014
-Return-Path: <pyar-bounces@python.org.ar>
-X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on spamd2.riseup.net
-X-Spam-Level: **
-X-Spam-Pyzor: Reported 0 times.
-X-Spam-Status: No, score=2.1 required=8.0 tests=AM_TRUNCATED,CK_419SIZE,
- CK_NAIVER_NO_DNS,CK_NAIVE_NO_DNS,ENV_FROM_DIFF0,HAS_REPLY_TO,LINK_NR_TOP,
- NO_REAL_NAME,RDNS_NONE,RISEUP_SPEAR_C shortcircuit=no autolearn=disabled
- version=3.3.2
-Delivered-To: kali@leap.se
-Received: from mx1.riseup.net (mx1-pn.riseup.net [10.0.1.33])
- (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))
- (Client CN "*.riseup.net", Issuer "Gandi Standard SSL CA" (not verified))
- by vireo.riseup.net (Postfix) with ESMTPS id 6C39A8F
- for <kali@leap.se>; Wed, 8 Jan 2014 18:46:02 +0000 (UTC)
-Received: from pyar.usla.org.ar (unknown [190.228.30.157])
- by mx1.riseup.net (Postfix) with ESMTP id F244C533F4
- for <kali@leap.se>; Wed, 8 Jan 2014 10:46:01 -0800 (PST)
-Received: from [127.0.0.1] (localhost [127.0.0.1])
- by pyar.usla.org.ar (Postfix) with ESMTP id CC51D26A4F
- for <kali@leap.se>; Wed, 8 Jan 2014 15:46:00 -0300 (ART)
-MIME-Version: 1.0
-Content-Type: text/plain; charset="iso-8859-1"
-Content-Transfer-Encoding: quoted-printable
-From: pyar-request@python.org.ar
-To: kali@leap.se
-Subject: confirm 0e47e4342e4d42508e8c283175b05b3377148ac2
-Reply-To: pyar-request@python.org.ar
-Auto-Submitted: auto-replied
-Message-ID: <mailman.245.1389206759.1579.pyar@python.org.ar>
-Date: Wed, 08 Jan 2014 15:45:59 -0300
-Precedence: bulk
-X-BeenThere: pyar@python.org.ar
-X-Mailman-Version: 2.1.15
-List-Id: Python Argentina <pyar.python.org.ar>
-X-List-Administrivia: yes
-Errors-To: pyar-bounces@python.org.ar
-Sender: "pyar" <pyar-bounces@python.org.ar>
-X-Virus-Scanned: clamav-milter 0.97.8 at mx1
-X-Virus-Status: Clean
-
-Mailing list subscription confirmation notice for mailing list pyar
-
-We have received a request de kaliyuga@riseup.net for subscription of
-your email address, "kaliyuga@riseup.net", to the pyar@python.org.ar
-mailing list. To confirm that you want to be added to this mailing
-list, simply reply to this message, keeping the Subject: header
-intact. Or visit this web page:
-
- http://listas.python.org.ar/confirm/pyar/0e47e4342e4d42508e8c283175b05b=
-3377148ac2
-
-
-Or include the following line -- and only the following line -- in a
-message to pyar-request@python.org.ar:
-
- confirm 0e47e4342e4d42508e8c283175b05b3377148ac2
-
-Note that simply sending a `reply' to this message should work from
-most mail readers, since that usually leaves the Subject: line in the
-right form (additional "Re:" text in the Subject: is okay).
-
-If you do not wish to be subscribed to this list, please simply
-disregard this message. If you think you are being maliciously
-subscribed to the list, or have any other questions, send them to
-pyar-owner@python.org.ar.
diff --git a/src/leap/bitmask/mail/tests/test_mail.py b/src/leap/bitmask/mail/tests/test_mail.py
deleted file mode 100644
index f9cded2..0000000
--- a/src/leap/bitmask/mail/tests/test_mail.py
+++ /dev/null
@@ -1,399 +0,0 @@
-# -*- coding: utf-8 -*-
-# test_mail.py
-# Copyright (C) 2014 LEAP
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-"""
-Tests for the mail module.
-"""
-import os
-import time
-import uuid
-
-from functools import partial
-from email.parser import Parser
-from email.Utils import formatdate
-
-from leap.mail.adaptors.soledad import SoledadMailAdaptor
-from leap.mail.mail import MessageCollection, Account, _unpack_headers
-from leap.mail.mailbox_indexer import MailboxIndexer
-from leap.mail.testing.common import SoledadTestMixin
-
-HERE = os.path.split(os.path.abspath(__file__))[0]
-
-
-def _get_raw_msg(multi=False):
- if multi:
- sample = "rfc822.multi.message"
- else:
- sample = "rfc822.message"
- with open(os.path.join(HERE, sample)) as f:
- raw = f.read()
- return raw
-
-
-def _get_parsed_msg(multi=False):
- mail_parser = Parser()
- raw = _get_raw_msg(multi=multi)
- return mail_parser.parsestr(raw)
-
-
-def _get_msg_time():
- timestamp = time.mktime((2010, 12, 12, 1, 1, 1, 1, 1, 1))
- return formatdate(timestamp)
-
-
-class CollectionMixin(object):
-
- def get_collection(self, mbox_collection=True, mbox_name=None,
- mbox_uuid=None):
- """
- Get a collection for tests.
- """
- adaptor = SoledadMailAdaptor()
- store = self._soledad
- adaptor.store = store
-
- if mbox_collection:
- mbox_indexer = MailboxIndexer(store)
- mbox_name = mbox_name or "TestMbox"
- mbox_uuid = mbox_uuid or str(uuid.uuid4())
- else:
- mbox_indexer = mbox_name = None
-
- def get_collection_from_mbox_wrapper(wrapper):
- wrapper.uuid = mbox_uuid
- return MessageCollection(
- adaptor, store,
- mbox_indexer=mbox_indexer, mbox_wrapper=wrapper)
-
- d = adaptor.initialize_store(store)
- if mbox_collection:
- d.addCallback(lambda _: mbox_indexer.create_table(mbox_uuid))
- d.addCallback(lambda _: adaptor.get_or_create_mbox(store, mbox_name))
- d.addCallback(get_collection_from_mbox_wrapper)
- return d
-
-
-# TODO profile add_msg. Why are these tests so SLOW??!
-class MessageTestCase(SoledadTestMixin, CollectionMixin):
- """
- Tests for the Message class.
- """
- msg_flags = ('\Recent', '\Unseen', '\TestFlag')
- msg_tags = ('important', 'todo', 'wonderful')
- internal_date = "19-Mar-2015 19:22:21 -0500"
-
- maxDiff = None
-
- def _do_insert_msg(self, multi=False):
- """
- Inserts and return a regular message, for tests.
- """
- def insert_message(collection):
- self._mbox_uuid = collection.mbox_uuid
- return collection.add_msg(
- raw, flags=self.msg_flags, tags=self.msg_tags,
- date=self.internal_date)
-
- raw = _get_raw_msg(multi=multi)
-
- d = self.get_collection()
- d.addCallback(insert_message)
- return d
-
- def get_inserted_msg(self, multi=False):
- d = self._do_insert_msg(multi=multi)
- d.addCallback(lambda _: self.get_collection(mbox_uuid=self._mbox_uuid))
- d.addCallback(lambda col: col.get_message_by_uid(1))
- return d
-
- def test_get_flags(self):
- d = self.get_inserted_msg()
- d.addCallback(self._test_get_flags_cb)
- return d
-
- def _test_get_flags_cb(self, msg):
- self.assertTrue(msg is not None)
- self.assertEquals(tuple(msg.get_flags()), self.msg_flags)
-
- def test_get_internal_date(self):
- d = self.get_inserted_msg()
- d.addCallback(self._test_get_internal_date_cb)
-
- def _test_get_internal_date_cb(self, msg):
- self.assertTrue(msg is not None)
- self.assertDictEqual(msg.get_internal_date(),
- self.internal_date)
-
- def test_get_headers(self):
- d = self.get_inserted_msg()
- d.addCallback(self._test_get_headers_cb)
- return d
-
- def _test_get_headers_cb(self, msg):
- self.assertTrue(msg is not None)
- expected = [
- (str(key.lower()), str(value))
- for (key, value) in _get_parsed_msg().items()]
- self.assertItemsEqual(_unpack_headers(msg.get_headers()), expected)
-
- def test_get_body_file(self):
- d = self.get_inserted_msg(multi=True)
- d.addCallback(self._test_get_body_file_cb)
- return d
-
- def _test_get_body_file_cb(self, msg):
- self.assertTrue(msg is not None)
- orig = _get_parsed_msg(multi=True)
- expected = orig.get_payload()[0].get_payload()
- d = msg.get_body_file(self._soledad)
-
- def assert_body(fd):
- self.assertTrue(fd is not None)
- self.assertEqual(fd.read(), expected)
- d.addCallback(assert_body)
- return d
-
- def test_get_size(self):
- d = self.get_inserted_msg()
- d.addCallback(self._test_get_size_cb)
- return d
-
- def _test_get_size_cb(self, msg):
- self.assertTrue(msg is not None)
- expected = len(_get_parsed_msg().as_string())
- self.assertEqual(msg.get_size(), expected)
-
- def test_is_multipart_no(self):
- d = self.get_inserted_msg()
- d.addCallback(self._test_is_multipart_no_cb)
- return d
-
- def _test_is_multipart_no_cb(self, msg):
- self.assertTrue(msg is not None)
- expected = _get_parsed_msg().is_multipart()
- self.assertEqual(msg.is_multipart(), expected)
-
- def test_is_multipart_yes(self):
- d = self.get_inserted_msg(multi=True)
- d.addCallback(self._test_is_multipart_yes_cb)
- return d
-
- def _test_is_multipart_yes_cb(self, msg):
- self.assertTrue(msg is not None)
- expected = _get_parsed_msg(multi=True).is_multipart()
- self.assertEqual(msg.is_multipart(), expected)
-
- def test_get_subpart(self):
- d = self.get_inserted_msg(multi=True)
- d.addCallback(self._test_get_subpart_cb)
- return d
-
- def _test_get_subpart_cb(self, msg):
- self.assertTrue(msg is not None)
-
- def test_get_tags(self):
- d = self.get_inserted_msg()
- d.addCallback(self._test_get_tags_cb)
- return d
-
- def _test_get_tags_cb(self, msg):
- self.assertTrue(msg is not None)
- self.assertEquals(msg.get_tags(), self.msg_tags)
-
-
-class MessageCollectionTestCase(SoledadTestMixin, CollectionMixin):
- """
- Tests for the MessageCollection class.
- """
- _mbox_uuid = None
-
- def assert_collection_count(self, _, expected):
- def _assert_count(count):
- self.assertEqual(count, expected)
-
- d = self.get_collection()
- d.addCallback(lambda col: col.count())
- d.addCallback(_assert_count)
- return d
-
- def add_msg_to_collection(self):
- raw = _get_raw_msg()
-
- def add_msg_to_collection(collection):
- # We keep the uuid in case we need to instantiate the same
- # collection afterwards.
- self._mbox_uuid = collection.mbox_uuid
- d = collection.add_msg(raw, date=_get_msg_time())
- return d
-
- d = self.get_collection()
- d.addCallback(add_msg_to_collection)
- return d
-
- def test_is_mailbox_collection(self):
- d = self.get_collection()
- d.addCallback(self._test_is_mailbox_collection_cb)
- return d
-
- def _test_is_mailbox_collection_cb(self, collection):
- self.assertTrue(collection.is_mailbox_collection())
-
- def test_get_uid_next(self):
- d = self.add_msg_to_collection()
- d.addCallback(lambda _: self.get_collection())
- d.addCallback(lambda col: col.get_uid_next())
- d.addCallback(self._test_get_uid_next_cb)
-
- def _test_get_uid_next_cb(self, next_uid):
- self.assertEqual(next_uid, 2)
-
- def test_add_and_count_msg(self):
- d = self.add_msg_to_collection()
- d.addCallback(self._test_add_and_count_msg_cb)
- return d
-
- def _test_add_and_count_msg_cb(self, _):
- return partial(self.assert_collection_count, expected=1)
-
- def test_copy_msg(self):
- # TODO ---- update when implementing messagecopier
- # interface
- pass
- test_copy_msg.skip = "Not yet implemented"
-
- def test_delete_msg(self):
- d = self.add_msg_to_collection()
-
- def del_msg(collection):
- def _delete_it(msg):
- self.assertTrue(msg is not None)
- return collection.delete_msg(msg)
-
- d = collection.get_message_by_uid(1)
- d.addCallback(_delete_it)
- return d
-
- # We need to instantiate an mbox collection with the same uuid that
- # the one in which we inserted the doc.
- d.addCallback(lambda _: self.get_collection(mbox_uuid=self._mbox_uuid))
- d.addCallback(del_msg)
- d.addCallback(self._test_delete_msg_cb)
- return d
-
- def _test_delete_msg_cb(self, _):
- return partial(self.assert_collection_count, expected=0)
-
- def test_update_flags(self):
- d = self.add_msg_to_collection()
- d.addCallback(self._test_update_flags_cb)
- return d
-
- def _test_update_flags_cb(self, msg):
- pass
-
- def test_update_tags(self):
- d = self.add_msg_to_collection()
- d.addCallback(self._test_update_tags_cb)
- return d
-
- def _test_update_tags_cb(self, msg):
- pass
-
-
-class AccountTestCase(SoledadTestMixin):
- """
- Tests for the Account class.
- """
- def get_account(self, user_id):
- store = self._soledad
- return Account(store, user_id)
-
- def test_add_mailbox(self):
- acc = self.get_account('some_user_id')
- d = acc.callWhenReady(lambda _: acc.add_mailbox("TestMailbox"))
- d.addCallback(lambda _: acc.list_all_mailbox_names())
- d.addCallback(self._test_add_mailbox_cb)
- return d
-
- def _test_add_mailbox_cb(self, mboxes):
- expected = ['INBOX', 'TestMailbox']
- self.assertItemsEqual(mboxes, expected)
-
- def test_delete_mailbox(self):
- acc = self.get_account('some_user_id')
- d = acc.callWhenReady(lambda _: acc.delete_mailbox("Inbox"))
- d.addCallback(lambda _: acc.list_all_mailbox_names())
- d.addCallback(self._test_delete_mailbox_cb)
- return d
-
- def _test_delete_mailbox_cb(self, mboxes):
- expected = []
- self.assertItemsEqual(mboxes, expected)
-
- def test_rename_mailbox(self):
- acc = self.get_account('some_user_id')
- d = acc.callWhenReady(lambda _: acc.add_mailbox("OriginalMailbox"))
- d.addCallback(lambda _: acc.rename_mailbox(
- "OriginalMailbox", "RenamedMailbox"))
- d.addCallback(lambda _: acc.list_all_mailbox_names())
- d.addCallback(self._test_rename_mailbox_cb)
- return d
-
- def _test_rename_mailbox_cb(self, mboxes):
- expected = ['INBOX', 'RenamedMailbox']
- self.assertItemsEqual(mboxes, expected)
-
- def test_get_all_mailboxes(self):
- acc = self.get_account('some_user_id')
- d = acc.callWhenReady(lambda _: acc.add_mailbox("OneMailbox"))
- d.addCallback(lambda _: acc.add_mailbox("TwoMailbox"))
- d.addCallback(lambda _: acc.add_mailbox("ThreeMailbox"))
- d.addCallback(lambda _: acc.add_mailbox("anotherthing"))
- d.addCallback(lambda _: acc.add_mailbox("anotherthing2"))
- d.addCallback(lambda _: acc.get_all_mailboxes())
- d.addCallback(self._test_get_all_mailboxes_cb)
- return d
-
- def _test_get_all_mailboxes_cb(self, mailboxes):
- expected = ["INBOX", "OneMailbox", "TwoMailbox", "ThreeMailbox",
- "anotherthing", "anotherthing2"]
- names = [m.mbox for m in mailboxes]
- self.assertItemsEqual(names, expected)
-
- def test_get_collection_by_mailbox(self):
- acc = self.get_account('some_user_id')
- d = acc.callWhenReady(lambda _: acc.get_collection_by_mailbox("INBOX"))
- d.addCallback(self._test_get_collection_by_mailbox_cb)
- return d
-
- def _test_get_collection_by_mailbox_cb(self, collection):
- self.assertTrue(collection.is_mailbox_collection())
-
- def assert_uid_next_empty_collection(uid):
- self.assertEqual(uid, 1)
- d = collection.get_uid_next()
- d.addCallback(assert_uid_next_empty_collection)
- return d
-
- def test_get_collection_by_docs(self):
- pass
-
- test_get_collection_by_docs.skip = "Not yet implemented"
-
- def test_get_collection_by_tag(self):
- pass
-
- test_get_collection_by_tag.skip = "Not yet implemented"
diff --git a/src/leap/bitmask/mail/tests/test_mailbox_indexer.py b/src/leap/bitmask/mail/tests/test_mailbox_indexer.py
deleted file mode 100644
index 5c1891d..0000000
--- a/src/leap/bitmask/mail/tests/test_mailbox_indexer.py
+++ /dev/null
@@ -1,250 +0,0 @@
-# -*- coding: utf-8 -*-
-# test_mailbox_indexer.py
-# Copyright (C) 2014 LEAP
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-"""
-Tests for the mailbox_indexer module.
-"""
-import uuid
-from functools import partial
-
-from leap.mail import mailbox_indexer as mi
-from leap.mail.testing.common import SoledadTestMixin
-
-hash_test0 = '590c9f8430c7435807df8ba9a476e3f1295d46ef210f6efae2043a4c085a569e'
-hash_test1 = '1b4f0e9851971998e732078544c96b36c3d01cedf7caa332359d6f1d83567014'
-hash_test2 = '60303ae22b998861bce3b28f33eec1be758a213c86c93c076dbe9f558c11c752'
-hash_test3 = 'fd61a03af4f77d870fc21e05e7e80678095c92d808cfb3b5c279ee04c74aca13'
-hash_test4 = 'a4e624d686e03ed2767c0abd85c14426b0b1157d2ce81d27bb4fe4f6f01d688a'
-
-
-def fmt_hash(mailbox_uuid, hash):
- return "M-" + mailbox_uuid.replace('-', '_') + "-" + hash
-
-mbox_id = str(uuid.uuid4())
-
-
-class MailboxIndexerTestCase(SoledadTestMixin):
- """
- Tests for the MailboxUID class.
- """
- def get_mbox_uid(self):
- m_uid = mi.MailboxIndexer(self._soledad)
- return m_uid
-
- def list_mail_tables_cb(self, ignored):
- def filter_mailuid_tables(tables):
- filtered = [
- table[0] for table in tables if
- table[0].startswith(mi.MailboxIndexer.table_preffix)]
- return filtered
-
- sql = "SELECT name FROM sqlite_master WHERE type='table';"
- d = self._soledad.raw_sqlcipher_query(sql)
- d.addCallback(filter_mailuid_tables)
- return d
-
- def select_uid_rows(self, mailbox):
- sql = "SELECT * FROM %s%s;" % (
- mi.MailboxIndexer.table_preffix, mailbox.replace('-', '_'))
- d = self._soledad.raw_sqlcipher_query(sql)
- return d
-
- def test_create_table(self):
- def assert_table_created(tables):
- self.assertEqual(
- tables, ["leapmail_uid_" + mbox_id.replace('-', '_')])
-
- m_uid = self.get_mbox_uid()
- d = m_uid.create_table(mbox_id)
- d.addCallback(self.list_mail_tables_cb)
- d.addCallback(assert_table_created)
- return d
-
- def test_create_and_delete_table(self):
- def assert_table_deleted(tables):
- self.assertEqual(tables, [])
-
- m_uid = self.get_mbox_uid()
- d = m_uid.create_table(mbox_id)
- d.addCallback(lambda _: m_uid.delete_table(mbox_id))
- d.addCallback(self.list_mail_tables_cb)
- d.addCallback(assert_table_deleted)
- return d
-
- def test_insert_doc(self):
- m_uid = self.get_mbox_uid()
-
- h1 = fmt_hash(mbox_id, hash_test0)
- h2 = fmt_hash(mbox_id, hash_test1)
- h3 = fmt_hash(mbox_id, hash_test2)
- h4 = fmt_hash(mbox_id, hash_test3)
- h5 = fmt_hash(mbox_id, hash_test4)
-
- def assert_uid_rows(rows):
- expected = [(1, h1), (2, h2), (3, h3), (4, h4), (5, h5)]
- self.assertEquals(rows, expected)
-
- d = m_uid.create_table(mbox_id)
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h1))
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h2))
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h3))
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h4))
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h5))
- d.addCallback(lambda _: self.select_uid_rows(mbox_id))
- d.addCallback(assert_uid_rows)
- return d
-
- def test_insert_doc_return(self):
- m_uid = self.get_mbox_uid()
-
- def assert_rowid(rowid, expected=None):
- self.assertEqual(rowid, expected)
-
- h1 = fmt_hash(mbox_id, hash_test0)
- h2 = fmt_hash(mbox_id, hash_test1)
- h3 = fmt_hash(mbox_id, hash_test2)
-
- d = m_uid.create_table(mbox_id)
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h1))
- d.addCallback(partial(assert_rowid, expected=1))
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h2))
- d.addCallback(partial(assert_rowid, expected=2))
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h3))
- d.addCallback(partial(assert_rowid, expected=3))
- return d
-
- def test_delete_doc(self):
- m_uid = self.get_mbox_uid()
-
- h1 = fmt_hash(mbox_id, hash_test0)
- h2 = fmt_hash(mbox_id, hash_test1)
- h3 = fmt_hash(mbox_id, hash_test2)
- h4 = fmt_hash(mbox_id, hash_test3)
- h5 = fmt_hash(mbox_id, hash_test4)
-
- def assert_uid_rows(rows):
- expected = [(4, h4), (5, h5)]
- self.assertEquals(rows, expected)
-
- d = m_uid.create_table(mbox_id)
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h1))
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h2))
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h3))
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h4))
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h5))
-
- d.addCallbacks(lambda _: m_uid.delete_doc_by_uid(mbox_id, 1))
- d.addCallbacks(lambda _: m_uid.delete_doc_by_uid(mbox_id, 2))
- d.addCallbacks(lambda _: m_uid.delete_doc_by_hash(mbox_id, h3))
-
- d.addCallback(lambda _: self.select_uid_rows(mbox_id))
- d.addCallback(assert_uid_rows)
- return d
-
- def test_get_doc_id_from_uid(self):
- m_uid = self.get_mbox_uid()
-
- h1 = fmt_hash(mbox_id, hash_test0)
-
- def assert_doc_hash(res):
- self.assertEqual(res, h1)
-
- d = m_uid.create_table(mbox_id)
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h1))
- d.addCallback(lambda _: m_uid.get_doc_id_from_uid(mbox_id, 1))
- d.addCallback(assert_doc_hash)
- return d
-
- def test_count(self):
- m_uid = self.get_mbox_uid()
-
- h1 = fmt_hash(mbox_id, hash_test0)
- h2 = fmt_hash(mbox_id, hash_test1)
- h3 = fmt_hash(mbox_id, hash_test2)
- h4 = fmt_hash(mbox_id, hash_test3)
- h5 = fmt_hash(mbox_id, hash_test4)
-
- d = m_uid.create_table(mbox_id)
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h1))
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h2))
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h3))
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h4))
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h5))
-
- def assert_count_after_inserts(count):
- self.assertEquals(count, 5)
-
- d.addCallback(lambda _: m_uid.count(mbox_id))
- d.addCallback(assert_count_after_inserts)
-
- d.addCallbacks(lambda _: m_uid.delete_doc_by_uid(mbox_id, 1))
- d.addCallbacks(lambda _: m_uid.delete_doc_by_uid(mbox_id, 2))
-
- def assert_count_after_deletions(count):
- self.assertEquals(count, 3)
-
- d.addCallback(lambda _: m_uid.count(mbox_id))
- d.addCallback(assert_count_after_deletions)
- return d
-
- def test_get_next_uid(self):
- m_uid = self.get_mbox_uid()
-
- h1 = fmt_hash(mbox_id, hash_test0)
- h2 = fmt_hash(mbox_id, hash_test1)
- h3 = fmt_hash(mbox_id, hash_test2)
- h4 = fmt_hash(mbox_id, hash_test3)
- h5 = fmt_hash(mbox_id, hash_test4)
-
- d = m_uid.create_table(mbox_id)
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h1))
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h2))
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h3))
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h4))
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h5))
-
- def assert_next_uid(result, expected=1):
- self.assertEquals(result, expected)
-
- d.addCallback(lambda _: m_uid.get_next_uid(mbox_id))
- d.addCallback(partial(assert_next_uid, expected=6))
- return d
-
- def test_all_uid_iter(self):
-
- m_uid = self.get_mbox_uid()
-
- h1 = fmt_hash(mbox_id, hash_test0)
- h2 = fmt_hash(mbox_id, hash_test1)
- h3 = fmt_hash(mbox_id, hash_test2)
- h4 = fmt_hash(mbox_id, hash_test3)
- h5 = fmt_hash(mbox_id, hash_test4)
-
- d = m_uid.create_table(mbox_id)
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h1))
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h2))
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h3))
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h4))
- d.addCallback(lambda _: m_uid.insert_doc(mbox_id, h5))
- d.addCallback(lambda _: m_uid.delete_doc_by_uid(mbox_id, 1))
- d.addCallback(lambda _: m_uid.delete_doc_by_uid(mbox_id, 4))
-
- def assert_all_uid(result, expected=[2, 3, 5]):
- self.assertEquals(result, expected)
-
- d.addCallback(lambda _: m_uid.all_uid_iter(mbox_id))
- d.addCallback(partial(assert_all_uid))
- return d
diff --git a/src/leap/bitmask/mail/tests/test_walk.py b/src/leap/bitmask/mail/tests/test_walk.py
deleted file mode 100644
index 826ec10..0000000
--- a/src/leap/bitmask/mail/tests/test_walk.py
+++ /dev/null
@@ -1,81 +0,0 @@
-"""
-Tests for leap.mail.walk module
-"""
-import os.path
-from email.parser import Parser
-
-from leap.mail import walk
-
-CORPUS = {
- 'simple': 'rfc822.message',
- 'multimin': 'rfc822.multi-minimal.message',
- 'multisigned': 'rfc822.multi-signed.message',
- 'bounced': 'rfc822.bounce.message',
-}
-
-_here = os.path.dirname(__file__)
-_parser = Parser()
-
-
-# tests
-
-
-def test_simple_mail():
- msg = _parse('simple')
- tree = walk.get_tree(msg)
- assert len(tree['part_map']) == 0
- assert tree['ctype'] == 'text/plain'
- assert tree['multi'] is False
-
-
-def test_multipart_minimal():
- msg = _parse('multimin')
- tree = walk.get_tree(msg)
-
- assert tree['multi'] is True
- assert len(tree['part_map']) == 1
- first = tree['part_map'][1]
- assert first['multi'] is False
- assert first['ctype'] == 'text/plain'
-
-
-def test_multi_signed():
- msg = _parse('multisigned')
- tree = walk.get_tree(msg)
- assert tree['multi'] is True
- assert len(tree['part_map']) == 2
-
- _first = tree['part_map'][1]
- _second = tree['part_map'][2]
- assert len(_first['part_map']) == 3
- assert(_second['multi'] is False)
-
-
-def test_bounce_mime():
- msg = _parse('bounced')
- tree = walk.get_tree(msg)
-
- ctypes = [tree['part_map'][index]['ctype']
- for index in sorted(tree['part_map'].keys())]
- third = tree['part_map'][3]
- three_one_ctype = third['part_map'][1]['ctype']
- assert three_one_ctype == 'multipart/signed'
-
- assert ctypes == [
- 'text/plain',
- 'message/delivery-status',
- 'message/rfc822']
-
-
-# utils
-
-def _parse(name):
- _str = _get_string_for_message(name)
- return _parser.parsestr(_str)
-
-
-def _get_string_for_message(name):
- filename = os.path.join(_here, CORPUS[name])
- with open(filename) as f:
- msgstr = f.read()
- return msgstr