diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/leap/mx/mail_receiver.py | 13 | ||||
-rw-r--r-- | src/leap/mx/soledadhelper.py | 89 | ||||
-rw-r--r-- | src/leap/mx/tests/tester.py | 9 |
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) |