diff options
Diffstat (limited to 'common')
16 files changed, 62 insertions, 834 deletions
diff --git a/common/src/leap/soledad/common/l2db/__init__.py b/common/src/leap/soledad/common/l2db/__init__.py index cc121d06..c0bd15fe 100644 --- a/common/src/leap/soledad/common/l2db/__init__.py +++ b/common/src/leap/soledad/common/l2db/__init__.py @@ -464,8 +464,8 @@ class DocumentBase(object): """ # Since this is just for testing, we don't worry about comparing # against things that aren't a Document. - return ((self.doc_id, self.rev, self.get_json()) - < (other.doc_id, other.rev, other.get_json())) + return ((self.doc_id, self.rev, self.get_json()) < + (other.doc_id, other.rev, other.get_json())) def get_json(self): """Get the json serialization of this document.""" diff --git a/common/src/leap/soledad/common/l2db/backends/sqlite_backend.py b/common/src/leap/soledad/common/l2db/backends/sqlite_backend.py index 309000ee..ba273039 100644 --- a/common/src/leap/soledad/common/l2db/backends/sqlite_backend.py +++ b/common/src/leap/soledad/common/l2db/backends/sqlite_backend.py @@ -156,7 +156,7 @@ class SQLiteDatabase(CommonBackend): def _initialize(self, c): """Create the schema in the database.""" - #read the script with sql commands + # read the script with sql commands # TODO: Change how we set up the dependency. Most likely use something # like lp:dirspec to grab the file from a common resource # directory. Doesn't specifically need to be handled until we get @@ -172,7 +172,7 @@ class SQLiteDatabase(CommonBackend): if not line: continue c.execute(line) - #add extra fields + # add extra fields self._extra_schema_init(c) # A unique identifier should be set for this replica. Implementations # don't have to strictly use uuid here, but we do want the uid to be @@ -509,7 +509,8 @@ class SQLiteDatabase(CommonBackend): def _put_doc_if_newer(self, doc, save_conflict, replica_uid=None, replica_gen=None, replica_trans_id=None): with self._db_handle: - return super(SQLiteDatabase, self)._put_doc_if_newer(doc, + return super(SQLiteDatabase, self)._put_doc_if_newer( + doc, save_conflict=save_conflict, replica_uid=replica_uid, replica_gen=replica_gen, replica_trans_id=replica_trans_id) @@ -620,14 +621,14 @@ class SQLiteDatabase(CommonBackend): novalue_where = ["d.doc_id = d%d.doc_id" " AND d%d.field_name = ?" % (i, i) for i in range(len(definition))] - wildcard_where = [novalue_where[i] - + (" AND d%d.value NOT NULL" % (i,)) + wildcard_where = [novalue_where[i] + + (" AND d%d.value NOT NULL" % (i,)) for i in range(len(definition))] - exact_where = [novalue_where[i] - + (" AND d%d.value = ?" % (i,)) + exact_where = [novalue_where[i] + + (" AND d%d.value = ?" % (i,)) for i in range(len(definition))] - like_where = [novalue_where[i] - + (" AND d%d.value GLOB ?" % (i,)) + like_where = [novalue_where[i] + + (" AND d%d.value GLOB ?" % (i,)) for i in range(len(definition))] is_wildcard = False # Merge the lists together, so that: @@ -672,7 +673,8 @@ class SQLiteDatabase(CommonBackend): try: c.execute(statement, tuple(args)) except dbapi2.OperationalError, e: - raise dbapi2.OperationalError(str(e) + + raise dbapi2.OperationalError( + str(e) + '\nstatement: %s\nargs: %s\n' % (statement, args)) res = c.fetchall() results = [] @@ -771,7 +773,8 @@ class SQLiteDatabase(CommonBackend): try: c.execute(statement, tuple(args)) except dbapi2.OperationalError, e: - raise dbapi2.OperationalError(str(e) + + raise dbapi2.OperationalError( + str(e) + '\nstatement: %s\nargs: %s\n' % (statement, args)) res = c.fetchall() results = [] @@ -800,7 +803,8 @@ class SQLiteDatabase(CommonBackend): try: c.execute(statement, tuple(definition)) except dbapi2.OperationalError, e: - raise dbapi2.OperationalError(str(e) + + raise dbapi2.OperationalError( + str(e) + '\nstatement: %s\nargs: %s\n' % (statement, tuple(definition))) return c.fetchall() diff --git a/common/src/leap/soledad/common/l2db/commandline/__init__.py b/common/src/leap/soledad/common/l2db/commandline/__init__.py deleted file mode 100644 index 3f32e381..00000000 --- a/common/src/leap/soledad/common/l2db/commandline/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2011 Canonical Ltd. -# -# This file is part of u1db. -# -# u1db is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation. -# -# u1db 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 Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with u1db. If not, see <http://www.gnu.org/licenses/>. diff --git a/common/src/leap/soledad/common/l2db/commandline/client.py b/common/src/leap/soledad/common/l2db/commandline/client.py deleted file mode 100644 index 15bf8561..00000000 --- a/common/src/leap/soledad/common/l2db/commandline/client.py +++ /dev/null @@ -1,497 +0,0 @@ -# Copyright 2011 Canonical Ltd. -# -# This file is part of u1db. -# -# u1db is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation. -# -# u1db 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 Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with u1db. If not, see <http://www.gnu.org/licenses/>. - -"""Commandline bindings for the u1db-client program.""" - -import argparse -import os -try: - import simplejson as json -except ImportError: - import json # noqa -import sys - -from u1db import ( - Document, - open as u1db_open, - sync, - errors, - ) -from u1db.commandline import command -from u1db.remote import ( - http_database, - http_target, - ) - - -client_commands = command.CommandGroup() - - -def set_oauth_credentials(client): - keys = os.environ.get('OAUTH_CREDENTIALS', None) - if keys is not None: - consumer_key, consumer_secret, \ - token_key, token_secret = keys.split(":") - client.set_oauth_credentials(consumer_key, consumer_secret, - token_key, token_secret) - - -class OneDbCmd(command.Command): - """Base class for commands operating on one local or remote database.""" - - def _open(self, database, create): - if database.startswith(('http://', 'https://')): - db = http_database.HTTPDatabase(database) - set_oauth_credentials(db) - db.open(create) - return db - else: - return u1db_open(database, create) - - -class CmdCreate(OneDbCmd): - """Create a new document from scratch""" - - name = 'create' - - @classmethod - def _populate_subparser(cls, parser): - parser.add_argument('database', - help='The local or remote database to update', - metavar='database-path-or-url') - parser.add_argument('infile', nargs='?', default=None, - help='The file to read content from.') - parser.add_argument('--id', dest='doc_id', default=None, - help='Set the document identifier') - - def run(self, database, infile, doc_id): - if infile is None: - infile = self.stdin - db = self._open(database, create=False) - doc = db.create_doc_from_json(infile.read(), doc_id=doc_id) - self.stderr.write('id: %s\nrev: %s\n' % (doc.doc_id, doc.rev)) - -client_commands.register(CmdCreate) - - -class CmdDelete(OneDbCmd): - """Delete a document from the database""" - - name = 'delete' - - @classmethod - def _populate_subparser(cls, parser): - parser.add_argument('database', - help='The local or remote database to update', - metavar='database-path-or-url') - parser.add_argument('doc_id', help='The document id to retrieve') - parser.add_argument('doc_rev', - help='The revision of the document (which is being superseded.)') - - def run(self, database, doc_id, doc_rev): - db = self._open(database, create=False) - doc = Document(doc_id, doc_rev, None) - db.delete_doc(doc) - self.stderr.write('rev: %s\n' % (doc.rev,)) - -client_commands.register(CmdDelete) - - -class CmdGet(OneDbCmd): - """Extract a document from the database""" - - name = 'get' - - @classmethod - def _populate_subparser(cls, parser): - parser.add_argument('database', - help='The local or remote database to query', - metavar='database-path-or-url') - parser.add_argument('doc_id', help='The document id to retrieve.') - parser.add_argument('outfile', nargs='?', default=None, - help='The file to write the document to', - type=argparse.FileType('wb')) - - def run(self, database, doc_id, outfile): - if outfile is None: - outfile = self.stdout - try: - db = self._open(database, create=False) - except errors.DatabaseDoesNotExist: - self.stderr.write("Database does not exist.\n") - return 1 - doc = db.get_doc(doc_id) - if doc is None: - self.stderr.write('Document not found (id: %s)\n' % (doc_id,)) - return 1 # failed - if doc.is_tombstone(): - outfile.write('[document deleted]\n') - else: - outfile.write(doc.get_json() + '\n') - self.stderr.write('rev: %s\n' % (doc.rev,)) - if doc.has_conflicts: - self.stderr.write("Document has conflicts.\n") - -client_commands.register(CmdGet) - - -class CmdGetDocConflicts(OneDbCmd): - """Get the conflicts from a document""" - - name = 'get-doc-conflicts' - - @classmethod - def _populate_subparser(cls, parser): - parser.add_argument('database', - help='The local database to query', - metavar='database-path') - parser.add_argument('doc_id', help='The document id to retrieve.') - - def run(self, database, doc_id): - try: - db = self._open(database, False) - except errors.DatabaseDoesNotExist: - self.stderr.write("Database does not exist.\n") - return 1 - conflicts = db.get_doc_conflicts(doc_id) - if not conflicts: - if db.get_doc(doc_id) is None: - self.stderr.write("Document does not exist.\n") - return 1 - self.stdout.write("[") - for i, doc in enumerate(conflicts): - if i: - self.stdout.write(",") - self.stdout.write( - json.dumps(dict(rev=doc.rev, content=doc.content), indent=4)) - self.stdout.write("]\n") - -client_commands.register(CmdGetDocConflicts) - - -class CmdInitDB(OneDbCmd): - """Create a new database""" - - name = 'init-db' - - @classmethod - def _populate_subparser(cls, parser): - parser.add_argument('database', - help='The local or remote database to create', - metavar='database-path-or-url') - parser.add_argument('--replica-uid', default=None, - help='The unique identifier for this database (not for remote)') - - def run(self, database, replica_uid): - db = self._open(database, create=True) - if replica_uid is not None: - db._set_replica_uid(replica_uid) - -client_commands.register(CmdInitDB) - - -class CmdPut(OneDbCmd): - """Add a document to the database""" - - name = 'put' - - @classmethod - def _populate_subparser(cls, parser): - parser.add_argument('database', - help='The local or remote database to update', - metavar='database-path-or-url'), - parser.add_argument('doc_id', help='The document id to retrieve') - parser.add_argument('doc_rev', - help='The revision of the document (which is being superseded.)') - parser.add_argument('infile', nargs='?', default=None, - help='The filename of the document that will be used for content', - type=argparse.FileType('rb')) - - def run(self, database, doc_id, doc_rev, infile): - if infile is None: - infile = self.stdin - try: - db = self._open(database, create=False) - doc = Document(doc_id, doc_rev, infile.read()) - doc_rev = db.put_doc(doc) - self.stderr.write('rev: %s\n' % (doc_rev,)) - except errors.DatabaseDoesNotExist: - self.stderr.write("Database does not exist.\n") - except errors.RevisionConflict: - if db.get_doc(doc_id) is None: - self.stderr.write("Document does not exist.\n") - else: - self.stderr.write("Given revision is not current.\n") - except errors.ConflictedDoc: - self.stderr.write( - "Document has conflicts.\n" - "Inspect with get-doc-conflicts, then resolve.\n") - else: - return - return 1 - -client_commands.register(CmdPut) - - -class CmdResolve(OneDbCmd): - """Resolve a conflicted document""" - - name = 'resolve-doc' - - @classmethod - def _populate_subparser(cls, parser): - parser.add_argument('database', - help='The local or remote database to update', - metavar='database-path-or-url'), - parser.add_argument('doc_id', help='The conflicted document id') - parser.add_argument('doc_revs', metavar="doc-rev", nargs="+", - help='The revisions that the new content supersedes') - parser.add_argument('--infile', nargs='?', default=None, - help='The filename of the document that will be used for content', - type=argparse.FileType('rb')) - - def run(self, database, doc_id, doc_revs, infile): - if infile is None: - infile = self.stdin - try: - db = self._open(database, create=False) - except errors.DatabaseDoesNotExist: - self.stderr.write("Database does not exist.\n") - return 1 - doc = db.get_doc(doc_id) - if doc is None: - self.stderr.write("Document does not exist.\n") - return 1 - doc.set_json(infile.read()) - db.resolve_doc(doc, doc_revs) - self.stderr.write("rev: %s\n" % db.get_doc(doc_id).rev) - if doc.has_conflicts: - self.stderr.write("Document still has conflicts.\n") - -client_commands.register(CmdResolve) - - -class CmdSync(command.Command): - """Synchronize two databases""" - - name = 'sync' - - @classmethod - def _populate_subparser(cls, parser): - parser.add_argument('source', help='database to sync from') - parser.add_argument('target', help='database to sync to') - - def _open_target(self, target): - if target.startswith(('http://', 'https://')): - st = http_target.HTTPSyncTarget.connect(target) - set_oauth_credentials(st) - else: - db = u1db_open(target, create=True) - st = db.get_sync_target() - return st - - def run(self, source, target): - """Start a Sync request.""" - source_db = u1db_open(source, create=False) - st = self._open_target(target) - syncer = sync.Synchronizer(source_db, st) - syncer.sync() - source_db.close() - -client_commands.register(CmdSync) - - -class CmdCreateIndex(OneDbCmd): - """Create an index""" - - name = "create-index" - - @classmethod - def _populate_subparser(cls, parser): - parser.add_argument('database', help='The local database to update', - metavar='database-path') - parser.add_argument('index', help='the name of the index') - parser.add_argument('expression', help='an index expression', - nargs='+') - - def run(self, database, index, expression): - try: - db = self._open(database, create=False) - db.create_index(index, *expression) - except errors.DatabaseDoesNotExist: - self.stderr.write("Database does not exist.\n") - return 1 - except errors.IndexNameTakenError: - self.stderr.write("There is already a different index named %r.\n" - % (index,)) - return 1 - except errors.IndexDefinitionParseError: - self.stderr.write("Bad index expression.\n") - return 1 - -client_commands.register(CmdCreateIndex) - - -class CmdListIndexes(OneDbCmd): - """List existing indexes""" - - name = "list-indexes" - - @classmethod - def _populate_subparser(cls, parser): - parser.add_argument('database', help='The local database to query', - metavar='database-path') - - def run(self, database): - try: - db = self._open(database, create=False) - except errors.DatabaseDoesNotExist: - self.stderr.write("Database does not exist.\n") - return 1 - for (index, expression) in db.list_indexes(): - self.stdout.write("%s: %s\n" % (index, ", ".join(expression))) - -client_commands.register(CmdListIndexes) - - -class CmdDeleteIndex(OneDbCmd): - """Delete an index""" - - name = "delete-index" - - @classmethod - def _populate_subparser(cls, parser): - parser.add_argument('database', help='The local database to update', - metavar='database-path') - parser.add_argument('index', help='the name of the index') - - def run(self, database, index): - try: - db = self._open(database, create=False) - except errors.DatabaseDoesNotExist: - self.stderr.write("Database does not exist.\n") - return 1 - db.delete_index(index) - -client_commands.register(CmdDeleteIndex) - - -class CmdGetIndexKeys(OneDbCmd): - """Get the index's keys""" - - name = "get-index-keys" - - @classmethod - def _populate_subparser(cls, parser): - parser.add_argument('database', help='The local database to query', - metavar='database-path') - parser.add_argument('index', help='the name of the index') - - def run(self, database, index): - try: - db = self._open(database, create=False) - for key in db.get_index_keys(index): - self.stdout.write("%s\n" % (", ".join( - [i.encode('utf-8') for i in key],))) - except errors.DatabaseDoesNotExist: - self.stderr.write("Database does not exist.\n") - except errors.IndexDoesNotExist: - self.stderr.write("Index does not exist.\n") - else: - return - return 1 - -client_commands.register(CmdGetIndexKeys) - - -class CmdGetFromIndex(OneDbCmd): - """Find documents by searching an index""" - - name = "get-from-index" - argv = None - - @classmethod - def _populate_subparser(cls, parser): - parser.add_argument('database', help='The local database to query', - metavar='database-path') - parser.add_argument('index', help='the name of the index') - parser.add_argument('values', metavar="value", - help='the value to look up (one per index column)', - nargs="+") - - def run(self, database, index, values): - try: - db = self._open(database, create=False) - docs = db.get_from_index(index, *values) - except errors.DatabaseDoesNotExist: - self.stderr.write("Database does not exist.\n") - except errors.IndexDoesNotExist: - self.stderr.write("Index does not exist.\n") - except errors.InvalidValueForIndex: - index_def = db._get_index_definition(index) - len_diff = len(index_def) - len(values) - if len_diff == 0: - # can't happen (HAH) - raise - argv = self.argv if self.argv is not None else sys.argv - self.stderr.write( - "Invalid query: " - "index %r requires %d query expression%s%s.\n" - "For example, the following would be valid:\n" - " %s %s %r %r %s\n" - % (index, - len(index_def), - "s" if len(index_def) > 1 else "", - ", not %d" % len(values) if len(values) else "", - argv[0], argv[1], database, index, - " ".join(map(repr, - values[:len(index_def)] - + ["*" for i in range(len_diff)])), - )) - except errors.InvalidGlobbing: - argv = self.argv if self.argv is not None else sys.argv - fixed = [] - for (i, v) in enumerate(values): - fixed.append(v) - if v.endswith('*'): - break - # values has at least one element, so i is defined - fixed.extend('*' * (len(values) - i - 1)) - self.stderr.write( - "Invalid query: a star can only be followed by stars.\n" - "For example, the following would be valid:\n" - " %s %s %r %r %s\n" - % (argv[0], argv[1], database, index, - " ".join(map(repr, fixed)))) - - else: - self.stdout.write("[") - for i, doc in enumerate(docs): - if i: - self.stdout.write(",") - self.stdout.write( - json.dumps( - dict(id=doc.doc_id, rev=doc.rev, content=doc.content), - indent=4)) - self.stdout.write("]\n") - return - return 1 - -client_commands.register(CmdGetFromIndex) - - -def main(args): - return client_commands.run_argv(args, sys.stdin, sys.stdout, sys.stderr) diff --git a/common/src/leap/soledad/common/l2db/commandline/command.py b/common/src/leap/soledad/common/l2db/commandline/command.py deleted file mode 100644 index eace0560..00000000 --- a/common/src/leap/soledad/common/l2db/commandline/command.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright 2011 Canonical Ltd. -# -# This file is part of u1db. -# -# u1db is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation. -# -# u1db 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 Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with u1db. If not, see <http://www.gnu.org/licenses/>. - -"""Command infrastructure for u1db""" - -import argparse -import inspect - - -class CommandGroup(object): - """A collection of commands.""" - - def __init__(self, description=None): - self.commands = {} - self.description = description - - def register(self, cmd): - """Register a new command to be incorporated with this group.""" - self.commands[cmd.name] = cmd - - def make_argparser(self): - """Create an argparse.ArgumentParser""" - parser = argparse.ArgumentParser(description=self.description) - subs = parser.add_subparsers(title='commands') - for name, cmd in sorted(self.commands.iteritems()): - sub = subs.add_parser(name, help=cmd.__doc__) - sub.set_defaults(subcommand=cmd) - cmd._populate_subparser(sub) - return parser - - def run_argv(self, argv, stdin, stdout, stderr): - """Run a command, from a sys.argv[1:] style input.""" - parser = self.make_argparser() - args = parser.parse_args(argv) - cmd = args.subcommand(stdin, stdout, stderr) - params, _, _, _ = inspect.getargspec(cmd.run) - vals = [] - for param in params[1:]: - vals.append(getattr(args, param)) - return cmd.run(*vals) - - -class Command(object): - """Definition of a Command that can be run. - - :cvar name: The name of the command, so that you can run - 'u1db-client <name>'. - """ - - name = None - - def __init__(self, stdin, stdout, stderr): - self.stdin = stdin - self.stdout = stdout - self.stderr = stderr - - @classmethod - def _populate_subparser(cls, parser): - """Child classes should override this to provide their arguments.""" - raise NotImplementedError(cls._populate_subparser) - - def run(self, *args): - """This is where the magic happens. - - Subclasses should implement this, requesting their specific arguments. - """ - raise NotImplementedError(self.run) diff --git a/common/src/leap/soledad/common/l2db/commandline/serve.py b/common/src/leap/soledad/common/l2db/commandline/serve.py deleted file mode 100644 index 5e10f9cb..00000000 --- a/common/src/leap/soledad/common/l2db/commandline/serve.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright 2011 Canonical Ltd. -# -# This file is part of u1db. -# -# u1db is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation. -# -# u1db 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 Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with u1db. If not, see <http://www.gnu.org/licenses/>. - -"""Build server for u1db-serve.""" -import os - -from paste import httpserver - -from u1db.remote import ( - http_app, - server_state, - cors_middleware - ) - - -class DbListingServerState(server_state.ServerState): - """ServerState capable of listing dbs.""" - - def global_info(self): - """Return list of dbs.""" - dbs = [] - for fname in os.listdir(self._workingdir): - p = os.path.join(self._workingdir, fname) - if os.path.isfile(p) and os.access(p, os.R_OK|os.W_OK): - try: - with open(p, 'rb') as f: - header = f.read(16) - if header == "SQLite format 3\000": - dbs.append(fname) - except IOError: - pass - return {"databases": dict.fromkeys(dbs), "db_count": len(dbs)} - - -def make_server(host, port, working_dir, accept_cors_connections=None): - """Make a server on host and port exposing dbs living in working_dir.""" - state = DbListingServerState() - state.set_workingdir(working_dir) - application = http_app.HTTPApp(state) - if accept_cors_connections: - application = cors_middleware.CORSMiddleware(application, - accept_cors_connections) - server = httpserver.WSGIServer(application, (host, port), - httpserver.WSGIHandler) - return server diff --git a/common/src/leap/soledad/common/l2db/errors.py b/common/src/leap/soledad/common/l2db/errors.py index e5ee8f45..b502fc2d 100644 --- a/common/src/leap/soledad/common/l2db/errors.py +++ b/common/src/leap/soledad/common/l2db/errors.py @@ -185,8 +185,7 @@ class UnknownAuthMethod(U1DBError): # mapping wire (transimission) descriptions/tags for errors to the exceptions wire_description_to_exc = dict( (x.wire_description, x) for x in globals().values() - if getattr(x, 'wire_description', None) not in (None, "error") -) + if getattr(x, 'wire_description', None) not in (None, "error")) wire_description_to_exc["error"] = U1DBError diff --git a/common/src/leap/soledad/common/l2db/query_parser.py b/common/src/leap/soledad/common/l2db/query_parser.py index 7f07b554..dd35b12a 100644 --- a/common/src/leap/soledad/common/l2db/query_parser.py +++ b/common/src/leap/soledad/common/l2db/query_parser.py @@ -358,8 +358,8 @@ class Parser(object): @classmethod def register_transormation(cls, transform): assert transform.name not in cls._transformations, ( - "Transform %s already registered for %s" - % (transform.name, cls._transformations[transform.name])) + "Transform %s already registered for %s" + % (transform.name, cls._transformations[transform.name])) cls._transformations[transform.name] = transform diff --git a/common/src/leap/soledad/common/l2db/remote/cors_middleware.py b/common/src/leap/soledad/common/l2db/remote/cors_middleware.py deleted file mode 100644 index 8041b968..00000000 --- a/common/src/leap/soledad/common/l2db/remote/cors_middleware.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright 2012 Canonical Ltd. -# -# This file is part of u1db. -# -# u1db is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation. -# -# u1db 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 Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with u1db. If not, see <http://www.gnu.org/licenses/>. -"""U1DB Cross-Origin Resource Sharing WSGI middleware.""" - - -class CORSMiddleware(object): - """U1DB Cross-Origin Resource Sharing WSGI middleware.""" - - def __init__(self, app, accept_cors_connections): - self.origins = ' '.join(accept_cors_connections) - self.app = app - - def _cors_headers(self): - return [('access-control-allow-origin', self.origins), - ('access-control-allow-headers', - 'authorization, content-type, x-requested-with'), - ('access-control-allow-methods', - 'GET, POST, PUT, DELETE, OPTIONS')] - - def __call__(self, environ, start_response): - def wrap_start_response(status, headers, exc_info=None): - headers += self._cors_headers() - return start_response(status, headers, exc_info) - - if environ['REQUEST_METHOD'].lower() == 'options': - wrap_start_response("200 OK", [('content-type', 'text/plain')]) - return [''] - - return self.app(environ, wrap_start_response) diff --git a/common/src/leap/soledad/common/l2db/remote/http_app.py b/common/src/leap/soledad/common/l2db/remote/http_app.py index 3b65f5f7..65277bd1 100644 --- a/common/src/leap/soledad/common/l2db/remote/http_app.py +++ b/common/src/leap/soledad/common/l2db/remote/http_app.py @@ -18,7 +18,6 @@ """ HTTP Application exposing U1DB. """ - # TODO -- deprecate, use twisted/txaio. import functools @@ -340,18 +339,18 @@ class DocResource(object): headers={ 'x-u1db-rev': '', 'x-u1db-has-conflicts': 'false' - }) + }) return headers = { 'x-u1db-rev': doc.rev, 'x-u1db-has-conflicts': json.dumps(doc.has_conflicts) - } + } if doc.is_tombstone(): self.responder.send_response_json( - http_errors.wire_description_to_status[ - errors.DOCUMENT_DELETED], - error=errors.DOCUMENT_DELETED, - headers=headers) + http_errors.wire_description_to_status[ + errors.DOCUMENT_DELETED], + error=errors.DOCUMENT_DELETED, + headers=headers) else: self.responder.send_response_content( doc.get_json(), headers=headers) @@ -431,7 +430,7 @@ class SyncResource(object): self.responder.start_response(200) self.responder.start_stream(), header = {"new_generation": new_gen, - "new_transaction_id": self.sync_exch.new_trans_id} + "new_transaction_id": self.sync_exch.new_trans_id} if self.replica_uid is not None: header['replica_uid'] = self.replica_uid self.responder.stream_entry(header) @@ -462,10 +461,11 @@ class HTTPResponder(object): return self._started = True status_text = httplib.responses[status] - self._write = self._start_response('%d %s' % (status, status_text), - [('content-type', self.content_type), - ('cache-control', 'no-cache')] + - headers.items()) + self._write = self._start_response( + '%d %s' % (status, status_text), + [('content-type', self.content_type), + ('cache-control', 'no-cache')] + + headers.items()) # xxx version in headers if obj_dic is not None: self._no_initial_obj = False diff --git a/common/src/leap/soledad/common/l2db/remote/http_database.py b/common/src/leap/soledad/common/l2db/remote/http_database.py index d8dcfd55..b2b48dee 100644 --- a/common/src/leap/soledad/common/l2db/remote/http_database.py +++ b/common/src/leap/soledad/common/l2db/remote/http_database.py @@ -92,9 +92,9 @@ class HTTPDatabase(http_client.HTTPClientBase, Database): return None except errors.HTTPError, e: if (e.status == DOCUMENT_DELETED_STATUS and - 'x-u1db-rev' in e.headers): - res = None - headers = e.headers + 'x-u1db-rev' in e.headers): + res = None + headers = e.headers else: raise doc_rev = headers['x-u1db-rev'] diff --git a/common/src/leap/soledad/common/l2db/remote/http_target.py b/common/src/leap/soledad/common/l2db/remote/http_target.py index 598170e4..7e7f366f 100644 --- a/common/src/leap/soledad/common/l2db/remote/http_target.py +++ b/common/src/leap/soledad/common/l2db/remote/http_target.py @@ -47,7 +47,7 @@ class HTTPSyncTarget(http_client.HTTPClientBase, SyncTarget): if self._trace_hook: # for tests self._trace_hook('record_sync_info') self._request_json('PUT', ['sync-from', source_replica_uid], {}, - {'generation': source_replica_generation, + {'generation': source_replica_generation, 'transaction_id': source_transaction_id}) def _parse_sync_stream(self, data, return_doc_cb, ensure_callback=None): diff --git a/common/src/leap/soledad/common/l2db/remote/oauth_middleware.py b/common/src/leap/soledad/common/l2db/remote/oauth_middleware.py deleted file mode 100644 index 5772580a..00000000 --- a/common/src/leap/soledad/common/l2db/remote/oauth_middleware.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright 2012 Canonical Ltd. -# -# This file is part of u1db. -# -# u1db is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation. -# -# u1db 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 Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with u1db. If not, see <http://www.gnu.org/licenses/>. -"""U1DB OAuth authorisation WSGI middleware.""" -import httplib -from oauth import oauth -try: - import simplejson as json -except ImportError: - import json # noqa -from urllib import quote -from wsgiref.util import shift_path_info - - -sign_meth_HMAC_SHA1 = oauth.OAuthSignatureMethod_HMAC_SHA1() -sign_meth_PLAINTEXT = oauth.OAuthSignatureMethod_PLAINTEXT() - - -class OAuthMiddleware(object): - """U1DB OAuth Authorisation WSGI middleware.""" - - # max seconds the request timestamp is allowed to be shifted - # from arrival time - timestamp_threshold = 300 - - def __init__(self, app, base_url, prefix='/~/'): - self.app = app - self.base_url = base_url - self.prefix = prefix - - def get_oauth_data_store(self): - """Provide a oauth.OAuthDataStore.""" - raise NotImplementedError(self.get_oauth_data_store) - - def _error(self, start_response, status, description, message=None): - start_response("%d %s" % (status, httplib.responses[status]), - [('content-type', 'application/json')]) - err = {"error": description} - if message: - err['message'] = message - return [json.dumps(err)] - - def __call__(self, environ, start_response): - if self.prefix and not environ['PATH_INFO'].startswith(self.prefix): - return self._error(start_response, 400, "bad request") - headers = {} - if 'HTTP_AUTHORIZATION' in environ: - headers['Authorization'] = environ['HTTP_AUTHORIZATION'] - oauth_req = oauth.OAuthRequest.from_request( - http_method=environ['REQUEST_METHOD'], - http_url=self.base_url + environ['PATH_INFO'], - headers=headers, - query_string=environ['QUERY_STRING'] - ) - if oauth_req is None: - return self._error(start_response, 401, "unauthorized", - "Missing OAuth.") - try: - self.verify(environ, oauth_req) - except oauth.OAuthError, e: - return self._error(start_response, 401, "unauthorized", - e.message) - shift_path_info(environ) - return self.app(environ, start_response) - - def verify(self, environ, oauth_req): - """Verify OAuth request, put user_id in the environ.""" - oauth_server = oauth.OAuthServer(self.get_oauth_data_store()) - oauth_server.timestamp_threshold = self.timestamp_threshold - oauth_server.add_signature_method(sign_meth_HMAC_SHA1) - oauth_server.add_signature_method(sign_meth_PLAINTEXT) - consumer, token, parameters = oauth_server.verify_request(oauth_req) - # filter out oauth bits - environ['QUERY_STRING'] = '&'.join("%s=%s" % (quote(k, safe=''), - quote(v, safe='')) - for k, v in parameters.iteritems()) - return consumer, token diff --git a/common/src/leap/soledad/common/l2db/remote/server_state.py b/common/src/leap/soledad/common/l2db/remote/server_state.py index 6c1104c6..f131e09e 100644 --- a/common/src/leap/soledad/common/l2db/remote/server_state.py +++ b/common/src/leap/soledad/common/l2db/remote/server_state.py @@ -18,6 +18,7 @@ import os import errno + class ServerState(object): """Passed to a Request when it is instantiated. diff --git a/common/src/leap/soledad/common/l2db/remote/ssl_match_hostname.py b/common/src/leap/soledad/common/l2db/remote/ssl_match_hostname.py index fbabc177..ce82f1b2 100644 --- a/common/src/leap/soledad/common/l2db/remote/ssl_match_hostname.py +++ b/common/src/leap/soledad/common/l2db/remote/ssl_match_hostname.py @@ -52,13 +52,14 @@ def match_hostname(cert, hostname): return dnsnames.append(value) if len(dnsnames) > 1: - raise CertificateError("hostname %r " - "doesn't match either of %s" + raise CertificateError( + "hostname %r doesn't match either of %s" % (hostname, ', '.join(map(repr, dnsnames)))) elif len(dnsnames) == 1: - raise CertificateError("hostname %r " - "doesn't match %r" + raise CertificateError( + "hostname %r doesn't match %r" % (hostname, dnsnames[0])) else: - raise CertificateError("no appropriate commonName or " + raise CertificateError( + "no appropriate commonName or " "subjectAltName fields were found") diff --git a/common/src/leap/soledad/common/l2db/sync.py b/common/src/leap/soledad/common/l2db/sync.py index 26e67140..c612629f 100644 --- a/common/src/leap/soledad/common/l2db/sync.py +++ b/common/src/leap/soledad/common/l2db/sync.py @@ -53,7 +53,8 @@ class Synchronizer(object): """ # Increases self.num_inserted depending whether the document # was effectively inserted. - state, _ = self.source._put_doc_if_newer(doc, save_conflict=True, + state, _ = self.source._put_doc_if_newer( + doc, save_conflict=True, replica_uid=self.target_replica_uid, replica_gen=replica_gen, replica_trans_id=trans_id) if state == 'inserted': @@ -85,10 +86,10 @@ class Synchronizer(object): new generation. """ cur_gen, trans_id = self.source._get_generation_info() - if (cur_gen == start_generation + self.num_inserted - and self.num_inserted > 0): - self.sync_target.record_sync_info( - self.source._replica_uid, cur_gen, trans_id) + last_gen = start_generation + self.num_inserted + if (cur_gen == last_gen and self.num_inserted > 0): + self.sync_target.record_sync_info( + self.source._replica_uid, cur_gen, trans_id) def sync(self, callback=None, autocreate=False): """Synchronize documents between source and target.""" @@ -124,15 +125,17 @@ class Synchronizer(object): if self.target_replica_uid is None: target_last_known_gen, target_last_known_trans_id = 0, '' else: - target_last_known_gen, target_last_known_trans_id = \ - self.source._get_replica_gen_and_trans_id(self.target_replica_uid) + target_last_known_gen, target_last_known_trans_id = ( + self.source._get_replica_gen_and_trans_id( # nopep8 + self.target_replica_uid)) if not changes and target_last_known_gen == target_gen: if target_trans_id != target_last_known_trans_id: raise errors.InvalidTransactionId return my_gen changed_doc_ids = [doc_id for doc_id, _, _ in changes] # prepare to send all the changed docs - docs_to_send = self.source.get_docs(changed_doc_ids, + docs_to_send = self.source.get_docs( + changed_doc_ids, check_for_conflicts=False, include_deleted=True) # TODO: there must be a way to not iterate twice docs_by_generation = zip( @@ -172,7 +175,7 @@ class SyncExchange(object): self._db._last_exchange_log = { 'receive': {'docs': self._incoming_trace}, 'return': None - } + } def _set_trace_hook(self, cb): self._trace_hook = cb @@ -198,7 +201,8 @@ class SyncExchange(object): :param source_gen: The source generation of doc. :return: None """ - state, at_gen = self._db._put_doc_if_newer(doc, save_conflict=False, + state, at_gen = self._db._put_doc_if_newer( + doc, save_conflict=False, replica_uid=self.source_replica_uid, replica_gen=source_gen, replica_trans_id=trans_id) if state == 'inserted': @@ -217,7 +221,7 @@ class SyncExchange(object): self._db._last_exchange_log['receive'].update({ 'source_uid': self.source_replica_uid, 'source_gen': source_gen - }) + }) def find_changes_to_return(self): """Find changes to return. @@ -232,7 +236,7 @@ class SyncExchange(object): """ self._db._last_exchange_log['receive'].update({ # for tests 'last_known_gen': self.source_last_known_generation - }) + }) self._trace('before whats_changed') gen, trans_id, changes = self._db.whats_changed( self.source_last_known_generation) @@ -242,9 +246,9 @@ class SyncExchange(object): seen_ids = self.seen_ids # changed docs that weren't superseded by or converged with self.changes_to_return = [ - (doc_id, gen, trans_id) for (doc_id, gen, trans_id) in changes + (doc_id, gen, trans_id) for (doc_id, gen, trans_id) in changes if # there was a subsequent update - if doc_id not in seen_ids or seen_ids.get(doc_id) < gen] + doc_id not in seen_ids or seen_ids.get(doc_id) < gen] return self.new_gen def return_docs(self, return_doc_cb): |