diff --git a/client/src/leap/soledad/client/_blob.py b/client/src/leap/soledad/client/_blob.py index 2910fe75c993877b8153171c68a3199298dc6b71..4e3fb83bd7db5835a86474de805e1dc7e138b821 100644 --- a/client/src/leap/soledad/client/_blob.py +++ b/client/src/leap/soledad/client/_blob.py @@ -23,18 +23,6 @@ from _crypto import docinfo, BlobEncryptor, BlobDecryptor logger = Logger() -class BlobDoc(object): - - def __init__(self, doc_id, rev, content, blob_id=None): - - self.doc_id = doc_id - self.rev = rev - self.is_blob = True - self.blob_fd = content - if blob_id is None: - self.blob_id = uuid4().get_hex() - - class BlobManager(object): def __init__(self, local_path, remote, key, secret): @@ -51,9 +39,10 @@ class BlobManager(object): doc_info = docinfo(doc.doc_id, doc.rev) # TODO ------------------------------------------ - # this is wrong, is doing 2 stages. - # We should connect the pipeline: the uploader can be passed directly - # the crypto producer. + # this is wrong, is doing 2 stages. Cutting corners! + # We should connect the pipeline, use Tubes: + # the crypto producer can be passed to + # the uploader and react as data is written. # ------------------------------------------------ yield self._encrypt(doc_info, fd, up) yield self._upload(doc.blob_id, up) @@ -85,6 +74,8 @@ class BlobManager(object): @defer.inlineCallbacks def _encrypt(self, doc_info, payload, result): + # TODO WE SHOULD SKIP THE BASE64 STEP!!!! + # this is going to be uploaded in binary mode crypter = BlobEncryptor(doc_info, payload, result=result, secret=self.secret) yield crypter.encrypt() @@ -110,6 +101,22 @@ class BlobManager(object): defer.returnValue(blob) +class BlobDoc(object): + + # TODO probably not needed, but convenient for testing for now. + + def __init__(self, doc_id, rev, content, blob_id=None): + + self.doc_id = doc_id + self.rev = rev + self.is_blob = True + self.blob_fd = content + if blob_id is None: + blob_id = uuid4().get_hex() + self.blob_id = blob_id + + + class SQLiteBlobBackend(object): diff --git a/client/src/leap/soledad/client/_crypto.py b/client/src/leap/soledad/client/_crypto.py index bac71e9e692eaf04f8639f1ddd30b1b4b0c1c732..58a020b18aeb157a0c4fecf7c6372ccc13d0aa52 100644 --- a/client/src/leap/soledad/client/_crypto.py +++ b/client/src/leap/soledad/client/_crypto.py @@ -198,6 +198,8 @@ class BlobEncryptor(object): encrypted = self._aes_fd.getvalue() hmac = self._hmac.result.getvalue() + # TODO we should have a flag to control this ENCODING + # Since for real blobs we're not going to need it. self.result.write( base64.urlsafe_b64encode(preamble + encrypted + hmac)) self._preamble.close() @@ -230,6 +232,8 @@ class BlobDecryptor(object): self.result = result def decrypt(self): + # TODO we should have a flag to control this DECODING + # Since for real blobs we're not going to need it. try: data = base64.urlsafe_b64decode(self.ciphertext.getvalue()) except (TypeError, binascii.Error): diff --git a/client/src/leap/soledad/client/api.py b/client/src/leap/soledad/client/api.py index 52701945329e361465ab103a11372314776fe890..eb0fc20cd3700f067733cf24bb12fda8ec91710b 100644 --- a/client/src/leap/soledad/client/api.py +++ b/client/src/leap/soledad/client/api.py @@ -35,10 +35,11 @@ import ssl import uuid import urlparse +from collections import defaultdict from itertools import chain +from io import BytesIO from StringIO import StringIO -from collections import defaultdict from twisted.internet.defer import DeferredLock, returnValue, inlineCallbacks from zope.interface import implements @@ -61,6 +62,8 @@ from leap.soledad.client.secrets import SoledadSecrets from leap.soledad.client.shared_db import SoledadSharedDatabase from leap.soledad.client._crypto import SoledadCrypto +from _blob import BlobDoc, BlobManager + logger = logging.getLogger(name=__name__) @@ -130,7 +133,7 @@ class Soledad(object): def __init__(self, uuid, passphrase, secrets_path, local_db_path, server_url, cert_file, shared_db=None, - auth_token=None, syncable=True): + auth_token=None, syncable=True, blobs=False): """ Initialize configuration, cryptographic keys and dbs. @@ -222,10 +225,39 @@ class Soledad(object): self._dbpool.close() raise +# TODO ---------------------------------------------------------- +# HARD-CODED SHIT AHEAD XD +# |\___/| +# (,\ /,)\ +# / / \ +# (@_^_@)/ \ +# W//W_/ \ +# (//) | \ +# (/ /) _|_ / ) \ +# (// /) '/,_ _ _/ (~^-. +# (( // )) ,-{ _ `. +# (( /// )) '/\ / | +# (( ///)) `. { } +# ((/ )) .----~-.\ \-' +# ///.----..> \ +# ///-._ _ _ _} + + if blobs: + BLOB_SERVICE = 'http://localhost:8880/' + self.blobs = BlobManager( + '/tmp', BLOB_SERVICE, + key='blob_sikret_key', + secret=self._secrets.remote_storage_secret) + else: + self.blobs = False + +# --------------------------------------------------------------- + # # initialization/destruction methods # + def _init_config_with_defaults(self): """ Initialize configuration using default values for missing params. @@ -452,7 +484,31 @@ class Soledad(object): # create_doc (and probably to put_doc too). There are cases (mail # payloads for example) in which we already have the encoding in the # headers, so we don't need to guess it. - d = self._defer("create_doc", content, doc_id=doc_id) + + # TODO ------------------------------------------------------------ + # crazy shit ahead. Should check the type better. + + if self.blobs and isinstance(content, BytesIO): + blob_id = uuid.uuid4().get_hex() + _content = {'is_blob': True, 'blob_id': blob_id} + else: + _content = content + + def blobbify(doc): + # I need the doc_id of the newly created doc, + # and its revision, because we're gonna stream the doc to the + # server backend. + + print "[blobbify] CREATING BLOB DOC", doc.doc_id, content + blob_doc = BlobDoc(doc.doc_id, doc.rev, content, blob_id) + d = self.blobs.put(blob_doc) + d.addCallback(lambda _: doc) + return d + # ----------------------------------------------------------------- + + d = self._defer("create_doc", _content, doc_id=doc_id) + if self.blobs: + d.addCallback(blobbify) return d def create_doc_from_json(self, json, doc_id=None):