summaryrefslogtreecommitdiff
path: root/backends/sqlcipher.py
diff options
context:
space:
mode:
Diffstat (limited to 'backends/sqlcipher.py')
-rw-r--r--backends/sqlcipher.py163
1 files changed, 0 insertions, 163 deletions
diff --git a/backends/sqlcipher.py b/backends/sqlcipher.py
deleted file mode 100644
index 5d2569bf..00000000
--- a/backends/sqlcipher.py
+++ /dev/null
@@ -1,163 +0,0 @@
-"""A U1DB backend that uses SQLCipher as its persistence layer."""
-
-import os
-from pysqlcipher import dbapi2
-import time
-
-from leap import util
-from u1db.backends import sqlite_backend
-util.logger.debug(
- "Monkey-patching u1db.backends.sqlite_backend with pysqlcipher.dbapi2..."
-)
-sqlite_backend.dbapi2 = dbapi2
-
-from u1db import (
- errors,
-)
-
-from leap.soledad.backends.leap_backend import LeapDocument
-
-
-def open(path, password, create=True, document_factory=None, soledad=None):
- """Open a database at the given location.
-
- Will raise u1db.errors.DatabaseDoesNotExist if create=False and the
- database does not already exist.
-
- :param path: The filesystem path for the database to open.
- :param create: True/False, should the database be created if it doesn't
- already exist?
- :param document_factory: A function that will be called with the same
- parameters as Document.__init__.
- :return: An instance of Database.
- """
- return SQLCipherDatabase.open_database(
- path, password, create=create, document_factory=document_factory,
- soledad=soledad)
-
-
-class DatabaseIsNotEncrypted(Exception):
- """
- Exception raised when trying to open non-encrypted databases.
- """
- pass
-
-
-class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase):
- """A U1DB implementation that uses SQLCipher as its persistence layer."""
-
- _index_storage_value = 'expand referenced encrypted'
-
- @classmethod
- def set_pragma_key(cls, db_handle, key):
- db_handle.cursor().execute("PRAGMA key = '%s'" % key)
-
- def __init__(self, sqlite_file, password, document_factory=None,
- soledad=None):
- """Create a new sqlcipher file."""
- self._check_if_db_is_encrypted(sqlite_file)
- self._db_handle = dbapi2.connect(sqlite_file)
- SQLCipherDatabase.set_pragma_key(self._db_handle, password)
- self._real_replica_uid = None
- self._ensure_schema()
- self._soledad = soledad
-
- def factory(doc_id=None, rev=None, json='{}', has_conflicts=False,
- encrypted_json=None, syncable=True):
- return LeapDocument(doc_id=doc_id, rev=rev, json=json,
- has_conflicts=has_conflicts,
- encrypted_json=encrypted_json,
- syncable=syncable, soledad=self._soledad)
- self.set_document_factory(factory)
-
- def _check_if_db_is_encrypted(self, sqlite_file):
- if not os.path.exists(sqlite_file):
- return
- else:
- try:
- # try to open an encrypted database with the regular u1db
- # backend should raise a DatabaseError exception.
- sqlite_backend.SQLitePartialExpandDatabase(sqlite_file)
- raise DatabaseIsNotEncrypted()
- except dbapi2.DatabaseError:
- pass
-
- @classmethod
- def _open_database(cls, sqlite_file, password, document_factory=None,
- soledad=None):
- if not os.path.isfile(sqlite_file):
- raise errors.DatabaseDoesNotExist()
- tries = 2
- while True:
- # Note: There seems to be a bug in sqlite 3.5.9 (with python2.6)
- # where without re-opening the database on Windows, it
- # doesn't see the transaction that was just committed
- db_handle = dbapi2.connect(sqlite_file)
- SQLCipherDatabase.set_pragma_key(db_handle, password)
- c = db_handle.cursor()
- v, err = cls._which_index_storage(c)
- db_handle.close()
- if v is not None:
- break
- # possibly another process is initializing it, wait for it to be
- # done
- if tries == 0:
- raise err # go for the richest error?
- tries -= 1
- time.sleep(cls.WAIT_FOR_PARALLEL_INIT_HALF_INTERVAL)
- return SQLCipherDatabase._sqlite_registry[v](
- sqlite_file, password, document_factory=document_factory,
- soledad=soledad)
-
- @classmethod
- def open_database(cls, sqlite_file, password, create, backend_cls=None,
- document_factory=None, soledad=None):
- """Open U1DB database using SQLCipher as backend."""
- try:
- return cls._open_database(sqlite_file, password,
- document_factory=document_factory,
- soledad=soledad)
- except errors.DatabaseDoesNotExist:
- if not create:
- raise
- if backend_cls is None:
- # default is SQLCipherPartialExpandDatabase
- backend_cls = SQLCipherDatabase
- return backend_cls(sqlite_file, password,
- document_factory=document_factory,
- soledad=soledad)
-
- def sync(self, url, creds=None, autocreate=True):
- """
- Synchronize encrypted documents with remote replica exposed at url.
- """
- from u1db.sync import Synchronizer
- from leap.soledad.backends.leap_backend import LeapSyncTarget
- return Synchronizer(
- self,
- LeapSyncTarget(url,
- creds=creds,
- soledad=self._soledad)).sync(autocreate=autocreate)
-
- def _extra_schema_init(self, c):
- c.execute(
- 'ALTER TABLE document '
- 'ADD COLUMN syncable BOOL NOT NULL DEFAULT TRUE')
-
- def _put_and_update_indexes(self, old_doc, doc):
- super(SQLCipherDatabase, self)._put_and_update_indexes(old_doc, doc)
- c = self._db_handle.cursor()
- c.execute('UPDATE document SET syncable=? WHERE doc_id=?',
- (doc.syncable, doc.doc_id))
-
- def _get_doc(self, doc_id, check_for_conflicts=False):
- doc = super(SQLCipherDatabase, self)._get_doc(doc_id,
- check_for_conflicts)
- if doc:
- c = self._db_handle.cursor()
- c.execute('SELECT syncable FROM document WHERE doc_id=?',
- (doc.doc_id,))
- doc.syncable = bool(c.fetchone()[0])
- return doc
-
-sqlite_backend.SQLiteDatabase.register_implementation(SQLCipherDatabase)