summaryrefslogtreecommitdiff
path: root/src/leap/mx
diff options
context:
space:
mode:
Diffstat (limited to 'src/leap/mx')
-rw-r--r--src/leap/mx/mail_receiver.py13
-rw-r--r--src/leap/mx/soledadhelper.py89
-rw-r--r--src/leap/mx/tests/tester.py9
3 files changed, 106 insertions, 5 deletions
diff --git a/src/leap/mx/mail_receiver.py b/src/leap/mx/mail_receiver.py
index 276ae13..3b3997a 100644
--- a/src/leap/mx/mail_receiver.py
+++ b/src/leap/mx/mail_receiver.py
@@ -83,7 +83,7 @@ class MailReceiver(Service):
MAX_BOUNCE_DELTA = timedelta(days=5)
def __init__(self, users_cdb, directories, bounce_from,
- bounce_subject):
+ bounce_subject, incoming_api_helper=False):
"""
Constructor
@@ -107,6 +107,7 @@ class MailReceiver(Service):
self._bounce_subject = bounce_subject
self._bounce_timestamp = {}
self._processing_skipped = False
+ self._incoming_api = incoming_api_helper
def startService(self):
"""
@@ -237,8 +238,14 @@ class MailReceiver(Service):
"I know: %r | %r" % (uuid, doc))
raise Exception("No uuid or doc")
- log.msg("Exporting message for %s" % (uuid,))
- yield self._users_cdb.put_doc(uuid, doc)
+ if self._incoming_api:
+ log.msg("Exporting message for %s over Incoming API" % (uuid,))
+ # TODO: Stop using ServerDocument when old code gets deprecated
+ content = doc.content[ENC_JSON_KEY]
+ yield self._incoming_api.put_doc(uuid, doc.doc_id, content)
+ else:
+ log.msg("Exporting message for %s directly into CouchDB" % (uuid,))
+ yield self._users_cdb.put_doc(uuid, doc)
log.msg("Done exporting")
def _remove(self, filepath):
diff --git a/src/leap/mx/soledadhelper.py b/src/leap/mx/soledadhelper.py
new file mode 100644
index 0000000..4b2a099
--- /dev/null
+++ b/src/leap/mx/soledadhelper.py
@@ -0,0 +1,89 @@
+# -*- encoding: utf-8 -*-
+# soledadhelper.py
+# Copyright (C) 2017 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/>.
+
+
+"""
+Classes for working with Soledad Incoming API.
+See: http://soledad.readthedocs.io/en/latest/incoming_box.html
+"""
+
+
+import base64
+import treq
+from six import raise_from
+from io import BytesIO
+from twisted.internet import defer
+
+
+class UnavailableIncomingAPIException(Exception):
+ pass
+
+
+class SoledadIncomingAPI:
+ """
+ Delivers messages using Soledad Incoming API.
+ """
+
+ def __init__(self, host, port, token):
+ """
+ Creates a SoledadIncomingAPI helper to deliver messages into user's
+ database.
+
+ :param host: A hostname string for the Soledad incoming service host.
+ This will usually be localhost, unless served over stunnel.
+ :type host: str
+ :param port: The port of the Soledad incoming service host.
+ :type port: int
+ :param token: Incoming service authentication token as configured in
+ Soledad.
+ :type token: str
+ """
+ self._incoming_url = "http://%s:%s/incoming/" % (host, port)
+ b64_token = base64.b64encode(token)
+ self._auth_header = {'Authorization': ['Token %s' % b64_token]}
+
+ @defer.inlineCallbacks
+ def put_doc(self, uuid, doc_id, content):
+ """
+ Make a PUT request to Soledad's incoming API, delivering a message into
+ user's database.
+
+ :param uuid: The uuid of a user
+ :type uuid: str
+ :param content: Message content.
+ :type content: str
+
+ :return: A deferred which fires after the HTTP request is complete, or
+ which fails with the correspondent exception if there was any
+ error.
+ """
+ url = self._incoming_url + "user-%s/%s" % (uuid, doc_id)
+ try:
+ response = yield treq.put(
+ url,
+ BytesIO(str(content)),
+ headers=self._auth_header,
+ persistent=False)
+ except Exception as original_exception:
+ error_message = "Server unreacheable or unknown error: %s"
+ error_message %= (original_exception.message)
+ our_exception = UnavailableIncomingAPIException(error_message)
+ raise_from(our_exception, original_exception)
+ if not response.code == 200:
+ error_message = '%s returned status %s instead of 200'
+ error_message %= (url, response.code)
+ raise UnavailableIncomingAPIException(error_message)
diff --git a/src/leap/mx/tests/tester.py b/src/leap/mx/tests/tester.py
index 05d2d05..8ca0987 100644
--- a/src/leap/mx/tests/tester.py
+++ b/src/leap/mx/tests/tester.py
@@ -2,10 +2,11 @@ import ConfigParser
import sys
import os
-from twisted.internet import reactor, defer
+from twisted.internet import reactor
from twisted.python import filepath, log
from leap.mx import couchdbhelper
+from leap.mx import soledadhelper
from leap.mx.mail_receiver import MailReceiver
if __name__ == "__main__":
@@ -30,6 +31,10 @@ if __name__ == "__main__":
dbName="identities",
username=user,
password=password)
+ incoming_api = False
+ if config.has_section("incoming api"):
+ args = (config.get(option) for option in ["host", "port", "token"])
+ incoming_api = soledadhelper.SoledadIncomingAPI(*args)
# Mail receiver
mail_couch_url_prefix = "http://%s:%s@%s:%s" % (user,
@@ -37,7 +42,7 @@ if __name__ == "__main__":
server,
port)
- mr = MailReceiver(mail_couch_url_prefix, cdb, [])
+ mr = MailReceiver(mail_couch_url_prefix, cdb, [], incoming_api)
fpath = filepath.FilePath(fullpath)
d = mr._process_incoming_email(None, fpath, 0)