[libgda] Initial SQLCipher provider
- From: Vivien Malerba <vivien src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgda] Initial SQLCipher provider
- Date: Mon, 13 Sep 2010 21:03:32 +0000 (UTC)
commit a7ae522d95afe600eb12cae17f35bb1a1e2dfbc2
Author: Vivien Malerba <malerba gnome-db org>
Date: Mon Sep 13 23:02:23 2010 +0200
Initial SQLCipher provider
Uses Zetetic's SQLCipher (http://www.zetetic.net/code/sqlcipher)
version 1.1.7 to create the "SQLCipher" database provider
with which one can access encrypted SQLite databases
configure.ac | 17 +
doc/C/prov-notes.xml | 51 +
libgda/sqlite/Makefile.am | 34 +-
libgda/sqlite/gda-sqlite-blob-op.c | 6 +-
libgda/sqlite/gda-sqlite-handler-bin.c | 6 +-
libgda/sqlite/gda-sqlite-handler-boolean.c | 6 +-
libgda/sqlite/gda-sqlite-provider.c | 29 +-
libgda/sqlite/gda-sqlite-pstmt.c | 6 +-
libgda/sqlite/gda-sqlite-recordset.c | 6 +-
libgda/sqlite/gda-sqlite.h | 21 +-
libgda/sqlite/gda-symbols-util.c | 4 +
libgda/sqlite/gda-symbols-util.h | 3 +
po/POTFILES.in | 13 +
po/POTFILES.skip | 1 +
providers/Makefile.am | 7 +-
providers/sqlcipher/.gitignore | 2 +
providers/sqlcipher/COPYING.sqlcipher | 28 +
providers/sqlcipher/Makefile.am | 57 +
providers/sqlcipher/NOTE_for_new_SQLCipher_version | 17 +
providers/sqlcipher/libgda-sqlcipher-4.0.pc.in | 9 +
providers/sqlcipher/libmain.c | 105 +
providers/sqlcipher/sqlcipher.patch | 2483 ++++++++++++++++++++
.../sqlcipher/sqlcipher_specs_add_column.xml.in | 1 +
providers/sqlcipher/sqlcipher_specs_auth.xml.in | 6 +
.../sqlcipher/sqlcipher_specs_create_db.xml.in | 1 +
.../sqlcipher/sqlcipher_specs_create_index.xml.in | 1 +
.../sqlcipher/sqlcipher_specs_create_table.xml.in | 1 +
.../sqlcipher/sqlcipher_specs_create_view.xml.in | 1 +
providers/sqlcipher/sqlcipher_specs_drop_db.xml.in | 1 +
.../sqlcipher/sqlcipher_specs_drop_index.xml.in | 1 +
.../sqlcipher/sqlcipher_specs_drop_table.xml.in | 1 +
.../sqlcipher/sqlcipher_specs_drop_view.xml.in | 1 +
providers/sqlcipher/sqlcipher_specs_dsn.xml.in | 1 +
.../sqlcipher/sqlcipher_specs_rename_table.xml.in | 1 +
34 files changed, 2886 insertions(+), 42 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 870b2e6..c0a3041 100644
--- a/configure.ac
+++ b/configure.ac
@@ -419,6 +419,20 @@ dnl *********************************
GOBJECT_INTROSPECTION_CHECK([0.6.5])
dnl ******************************
+dnl Checks for libcrypto
+dnl ******************************
+
+LIBCRYPTO_MODULES="libcrypto"
+PKG_CHECK_MODULES(LIBCRYPTO, $LIBCRYPTO_MODULES, have_crypto=yes, have_crypto=no)
+AM_CONDITIONAL(HAVE_LIBCRYPTO, test x"$have_crypto" = "xyes")
+if test x"$have_crypto" = "xyes"
+then
+ LIBCRYPTO_CFLAGS="$LIBCRYPTO_CFLAGS -DHAVE_LIBCRYPTO"
+fi
+AC_SUBST(LIBCRYPTO_CFLAGS)
+AC_SUBST(LIBCRYPTO_LIBS)
+
+dnl ******************************
dnl Checks for libgcrypt
dnl ******************************
LIBGCRYPT_CFLAGS=""
@@ -1810,6 +1824,8 @@ providers/skel-implementation/capi/Makefile
providers/skel-implementation/capi/libgda-capi-4.0.pc
providers/skel-implementation/models/Makefile
providers/skel-implementation/models/libgda-models-4.0.pc
+providers/sqlcipher/Makefile
+providers/sqlcipher/libgda-sqlcipher-4.0.pc
libgda-report/Makefile
libgda-report/engine/Makefile
libgda-report/DocBook/Makefile
@@ -1895,6 +1911,7 @@ dnl echo " ODBC = `if test x$odbcdir != x; then echo yes; else echo no; fi`
echo " Oracle = `if test x$oracledir != x; then echo yes; else echo no; fi`"
echo " PostgreSQL = `if test x$postgresdir != x; then echo yes; else echo no; fi`"
echo " SQLite = yes `if test x$have_sqlite = xyes; then echo '(from system installation)'; else echo '(embedded)'; fi`"
+echo " SQLCipher = `if test x$have_crypto != xyes; then echo no; else echo yes; fi`"
dnl echo " Sybase = `if test x$sybasedir != x; then echo yes; else echo no; fi`"
dnl echo " xBase (dBase, Clipper, FoxPro) = `if test x$xbasedir != x; then echo yes; else echo no; fi`"
dnl echo " LDAP = `if test x$ldapdir != x; then echo yes; else echo no; fi`"
diff --git a/doc/C/prov-notes.xml b/doc/C/prov-notes.xml
index 41be401..2d90032 100644
--- a/doc/C/prov-notes.xml
+++ b/doc/C/prov-notes.xml
@@ -40,5 +40,56 @@
Also refer to the <link linkend="limitations_oracle">Oracle's provider's limitations</link>.
</para>
</sect1>
+
+ <sect1 id="provider_notes_sqlcipher"><title>For SQLCipher</title>
+ <para>
+ The SQLCipher database provider allows one to connect to a database encrypted using the
+ <ulink url="http://www.zetetic.net/code/sqlcipher">SQLCipher</ulink> adaptations to the SQLite
+ database. This section deals about how to manage the passphrase associated with a database file, please
+ also consult the information <ulink url="http://www.zetetic.net/code/sqlcipher">provided by SQLCipher</ulink>
+ before attempting to use this database provider.
+ </para>
+ <para>
+ The first noticeable point is that any SQLite database file can be opened using the SQLCipher and
+ will remain useable with the "standard" SQLite provider as long as it's not explicitely encrypted using
+ a passphrase.
+ </para>
+ <sect2>
+ <title>How to create and encrypted database</title>
+ <para>
+ To create an encrypted database, you can use the <application>gda-sql</application> and when prompted
+ enter the requested passphrase, as:
+ <programlisting><![CDATA[prompt> gda-sql-4.0 "SQLCipher://DB_NAME=testcrypt"
+Welcome to the GDA SQL console, version 4.1.11
+
+Type: .copyright to show usage and distribution terms
+ .? for help with internal commands
+ .q (or CTRL-D) to quit
+ (the '.' can be replaced by a '\')
+ or any query terminated by a semicolon
+
+Opening connection 'c0' for: SQLCipher://DB_NAME=testcrypt
+ Password for 'c0':]]></programlisting>
+ </para>
+ </sect2>
+ <sect2>
+ <title>How to encrypt an existing database</title>
+ <para>
+ To encrypt an existing database, connect to the database using the SQLCipher provider and execute the
+ following SQL command (replace the passphrase with the requested passphrase):
+ <programlisting>PRAGMA key = 'passphrase';</programlisting>
+ setting the requested passphrase which will be later required to reopen the database. This step
+ prevents opening the database file by the "standard" SQLite provider.
+ </para>
+ </sect2>
+ <sect2>
+ <title>How to change the passphrase of an encrypted database</title>
+ <para>
+ To change an encrypted database's passphrase, open a connection to the database and enter the following
+ SQL command (replace the passphrase with the requested new passphrase):
+ <programlisting>PRAGMA rekey = 'passphrase';</programlisting>
+ </para>
+ </sect2>
+ </sect1>
</chapter>
diff --git a/libgda/sqlite/Makefile.am b/libgda/sqlite/Makefile.am
index 703ccea..0c0f4b6 100644
--- a/libgda/sqlite/Makefile.am
+++ b/libgda/sqlite/Makefile.am
@@ -10,7 +10,7 @@ sqliteinc= -I$(top_srcdir)/libgda/sqlite/sqlite-src \
endif
SUBDIRS+= virtual
-noinst_LTLIBRARIES = libgda-sqlite.la
+noinst_LTLIBRARIES = libgda-sqlite.la libgda-sqlcipher.la
if BDBSQL
noinst_LTLIBRARIES += libgda-bdbsqlite.la
endif
@@ -63,7 +63,7 @@ libgda_sqlite_la_SOURCES = \
gda-symbols-util.h \
gda-symbols-util.c
-libgda_sqlite_la_CFLAGS = -DSEARCH_LIB_PATH=\""$(SQLITE_PATH)"\"
+libgda_sqlite_la_CFLAGS = -DSEARCH_LIB_PATH=\""$(SQLITE_PATH)"\" -DCLASS_PREFIX=\""GdaSqlite"\"
libgda_sqlite_la_LDFLAGS = -export-dynamic -module -avoid-version $(NO_UNDEFINED)
libgda_sqlite_la_LIBADD = \
virtual/libgda-virtual-4.0.la \
@@ -94,10 +94,38 @@ libgda_bdbsqlite_la_SOURCES = \
gda-symbols-util.h \
gda-symbols-util.c
-libgda_bdbsqlite_la_CFLAGS = $(BDBSQL_CFLAGS) -DWITH_BDBSQLITE -DSEARCH_LIB_PATH=\""$(BDBSQL_PATH)"\"
+libgda_bdbsqlite_la_CFLAGS = $(BDBSQL_CFLAGS) -DWITH_BDBSQLITE -DCLASS_PREFIX=\""GdaDBDSql"\" -DSEARCH_LIB_PATH=\""$(BDBSQL_PATH)"\"
libgda_bdbsqlite_la_LDFLAGS = -export-dynamic -module -avoid-version $(NO_UNDEFINED)
libgda_bdbsqlite_la_LIBADD =
+libgda_sqlcipher_la_SOURCES = \
+ $(sqliteheaders) \
+ gda-sqlite-blob-op.c \
+ gda-sqlite-blob-op.h \
+ gda-sqlite-ddl.c \
+ gda-sqlite-ddl.h \
+ gda-sqlite-handler-bin.c \
+ gda-sqlite-handler-bin.h \
+ gda-sqlite-handler-boolean.c \
+ gda-sqlite-handler-boolean.h \
+ gda-sqlite-meta.c \
+ gda-sqlite-meta.h \
+ gda-sqlite-provider.c \
+ gda-sqlite-pstmt.h \
+ gda-sqlite-pstmt.c \
+ gda-sqlite-recordset.c \
+ gda-sqlite-recordset.h \
+ gda-sqlite-util.c \
+ gda-sqlite-util.h \
+ gda-sqlite.h \
+ keywords_hash.h \
+ gda-symbols-util.h \
+ gda-symbols-util.c
+
+libgda_sqlcipher_la_CFLAGS = -DSTATIC_SQLITE -DSQLITE_HAS_CODEC -DCLASS_PREFIX=\""GdaSQLCipher"\" -DSEARCH_LIB_PATH=\"""\"
+libgda_sqlcipher_la_LDFLAGS = -export-dynamic -module -avoid-version $(NO_UNDEFINED)
+libgda_sqlcipher_la_LIBADD =
+
gdaincludedir=$(includedir)/libgda-$(GDA_ABI_MAJOR_VERSION).$(GDA_ABI_MINOR_VERSION)/libgda/sqlite
gdainclude_HEADERS=$(sqliteheaders)
diff --git a/libgda/sqlite/gda-sqlite-blob-op.c b/libgda/sqlite/gda-sqlite-blob-op.c
index 751ceeb..24d7b3f 100644
--- a/libgda/sqlite/gda-sqlite-blob-op.c
+++ b/libgda/sqlite/gda-sqlite-blob-op.c
@@ -65,11 +65,7 @@ _gda_sqlite_blob_op_get_type (void)
};
g_static_mutex_lock (®istering);
if (type == 0)
-#ifdef WITH_BDBSQLITE
- type = g_type_register_static (GDA_TYPE_BLOB_OP, "GdaDBDSqlBlobOp", &info, 0);
-#else
- type = g_type_register_static (GDA_TYPE_BLOB_OP, "GdaSqliteBlobOp", &info, 0);
-#endif
+ type = g_type_register_static (GDA_TYPE_BLOB_OP, CLASS_PREFIX "BlobOp", &info, 0);
g_static_mutex_unlock (®istering);
}
return type;
diff --git a/libgda/sqlite/gda-sqlite-handler-bin.c b/libgda/sqlite/gda-sqlite-handler-bin.c
index de9eaa0..ff0437b 100644
--- a/libgda/sqlite/gda-sqlite-handler-bin.c
+++ b/libgda/sqlite/gda-sqlite-handler-bin.c
@@ -76,11 +76,7 @@ _gda_sqlite_handler_bin_get_type (void)
g_static_mutex_lock (®istering);
if (type == 0) {
-#ifdef WITH_BDBSQLITE
- type = g_type_register_static (G_TYPE_OBJECT, "GdaDBDSqlHandlerBin", &info, 0);
-#else
- type = g_type_register_static (G_TYPE_OBJECT, "GdaSqliteHandlerBin", &info, 0);
-#endif
+ type = g_type_register_static (G_TYPE_OBJECT, CLASS_PREFIX "HandlerBin", &info, 0);
g_type_add_interface_static (type, GDA_TYPE_DATA_HANDLER, &data_entry_info);
}
g_static_mutex_unlock (®istering);
diff --git a/libgda/sqlite/gda-sqlite-handler-boolean.c b/libgda/sqlite/gda-sqlite-handler-boolean.c
index ecc6141..a644e4c 100644
--- a/libgda/sqlite/gda-sqlite-handler-boolean.c
+++ b/libgda/sqlite/gda-sqlite-handler-boolean.c
@@ -78,11 +78,7 @@ _gda_sqlite_handler_boolean_get_type (void)
g_static_mutex_lock (®istering);
if (type == 0) {
-#ifdef WITH_BDBSQLITE
- type = g_type_register_static (G_TYPE_OBJECT, "GdaDBDSqlHandlerBoolean", &info, 0);
-#else
- type = g_type_register_static (G_TYPE_OBJECT, "GdaSqliteHandlerBoolean", &info, 0);
-#endif
+ type = g_type_register_static (G_TYPE_OBJECT, CLASS_PREFIX "HandlerBoolean", &info, 0);
g_type_add_interface_static (type, GDA_TYPE_DATA_HANDLER, &data_entry_info);
}
g_static_mutex_unlock (®istering);
diff --git a/libgda/sqlite/gda-sqlite-provider.c b/libgda/sqlite/gda-sqlite-provider.c
index 75df1ff..a65c3b7 100644
--- a/libgda/sqlite/gda-sqlite-provider.c
+++ b/libgda/sqlite/gda-sqlite-provider.c
@@ -496,20 +496,24 @@ gda_sqlite_provider_get_type (void)
g_static_mutex_lock (®istering);
if (type == 0) {
#ifdef WITH_BDBSQLITE
- type = g_type_register_static (GDA_TYPE_SERVER_PROVIDER, "GdaDBDSqlProvider", &info, 0);
+ type = g_type_register_static (GDA_TYPE_SERVER_PROVIDER, CLASS_PREFIX "Provider", &info, 0);
#else
- #ifdef HAVE_SQLITE
+ #ifdef STATIC_SQLITE
+ type = g_type_register_static (GDA_TYPE_SERVER_PROVIDER, CLASS_PREFIX "Provider", &info, 0);
+ #else
+ #ifdef HAVE_SQLITE
GModule *module2;
module2 = find_sqlite_library ("libsqlite3");
if (module2)
load_symbols (module2);
if (s3r)
- type = g_type_register_static (GDA_TYPE_SERVER_PROVIDER, "GdaSqliteProvider", &info, 0);
+ type = g_type_register_static (GDA_TYPE_SERVER_PROVIDER, CLASS_PREFIX "Provider", &info, 0);
else
g_warning (_("Can't find libsqlite3." G_MODULE_SUFFIX " file."));
- #else
- type = g_type_register_static (GDA_TYPE_SERVER_PROVIDER, "GdaSqliteProvider", &info, 0);
+ #else
+ type = g_type_register_static (GDA_TYPE_SERVER_PROVIDER, CLASS_PREFIX "Provider", &info, 0);
+ #endif
#endif
#endif
}
@@ -552,6 +556,7 @@ gda_sqlite_provider_open_connection (GdaServerProvider *provider, GdaConnection
gint errmsg;
SqliteConnectionData *cdata;
gchar *dup = NULL;
+ const gchar *passphrase = NULL;
g_return_val_if_fail (GDA_IS_SQLITE_PROVIDER (provider), FALSE);
g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
@@ -571,6 +576,8 @@ gda_sqlite_provider_open_connection (GdaServerProvider *provider, GdaConnection
is_virtual = gda_quark_list_find (params, "_IS_VIRTUAL");
with_fk = gda_quark_list_find (params, "FK");
use_extra_functions = gda_quark_list_find (params, "LOAD_GDA_FUNCTIONS");
+ if (auth)
+ passphrase = gda_quark_list_find (auth, "PASSWORD");
if (! is_virtual) {
if (!dbname) {
@@ -667,6 +674,18 @@ gda_sqlite_provider_open_connection (GdaServerProvider *provider, GdaConnection
return FALSE;
}
+#ifdef SQLITE_HAS_CODEC
+ if (passphrase && *passphrase && SQLITE3_CALL (sqlite3_key)) {
+ errmsg = SQLITE3_CALL (sqlite3_key) (cdata->connection, (void*) passphrase, strlen (passphrase));
+ if (errmsg != SQLITE_OK) {
+ gda_connection_add_event_string (cnc, _("Wrong encryption passphrase"));
+ gda_sqlite_free_cnc_data (cdata);
+ g_static_rec_mutex_unlock (&cnc_mutex);
+ return FALSE;
+ }
+ }
+#endif
+
gda_connection_internal_set_provider_data (cnc, cdata, (GDestroyNotify) gda_sqlite_free_cnc_data);
/* use extended result codes */
diff --git a/libgda/sqlite/gda-sqlite-pstmt.c b/libgda/sqlite/gda-sqlite-pstmt.c
index 2341fd5..20d0496 100644
--- a/libgda/sqlite/gda-sqlite-pstmt.c
+++ b/libgda/sqlite/gda-sqlite-pstmt.c
@@ -57,11 +57,7 @@ _gda_sqlite_pstmt_get_type (void)
g_static_mutex_lock (®istering);
if (type == 0)
-#ifdef WITH_BDBSQLITE
- type = g_type_register_static (GDA_TYPE_PSTMT, "GdaDBDSqlPStmt", &info, 0);
-#else
- type = g_type_register_static (GDA_TYPE_PSTMT, "GdaSqlitePStmt", &info, 0);
-#endif
+ type = g_type_register_static (GDA_TYPE_PSTMT, CLASS_PREFIX "PStmt", &info, 0);
g_static_mutex_unlock (®istering);
}
return type;
diff --git a/libgda/sqlite/gda-sqlite-recordset.c b/libgda/sqlite/gda-sqlite-recordset.c
index 6ae3fa6..8a19b13 100644
--- a/libgda/sqlite/gda-sqlite-recordset.c
+++ b/libgda/sqlite/gda-sqlite-recordset.c
@@ -132,11 +132,7 @@ _gda_sqlite_recordset_get_type (void)
};
g_static_mutex_lock (®istering);
if (type == 0)
-#ifdef WITH_BDBSQLITE
- type = g_type_register_static (GDA_TYPE_DATA_SELECT, "GdaDBDSqlRecordset", &info, 0);
-#else
- type = g_type_register_static (GDA_TYPE_DATA_SELECT, "GdaSqliteRecordset", &info, 0);
-#endif
+ type = g_type_register_static (GDA_TYPE_DATA_SELECT, CLASS_PREFIX "Recordset", &info, 0);
g_static_mutex_unlock (®istering);
}
diff --git a/libgda/sqlite/gda-sqlite.h b/libgda/sqlite/gda-sqlite.h
index 16b1a7a..3a7298d 100644
--- a/libgda/sqlite/gda-sqlite.h
+++ b/libgda/sqlite/gda-sqlite.h
@@ -33,16 +33,21 @@
#include "gda-symbols-util.h"
#define SQLITE3_CALL(x) (s3r->x)
#else
- #ifdef HAVE_SQLITE
- #include <sqlite3.h>
- #include "gda-symbols-util.h"
- #define SQLITE3_CALL(x) (s3r->x)
- #if (SQLITE_VERSION_NUMBER < 3005000)
- typedef sqlite_int64 sqlite3_int64;
- #endif
- #else
+ #ifdef STATIC_SQLITE
#include "sqlite-src/sqlite3.h"
#define SQLITE3_CALL(x) (x)
+ #else
+ #ifdef HAVE_SQLITE
+ #include <sqlite3.h>
+ #include "gda-symbols-util.h"
+ #define SQLITE3_CALL(x) (s3r->x)
+ #if (SQLITE_VERSION_NUMBER < 3005000)
+ typedef sqlite_int64 sqlite3_int64;
+ #endif
+ #else
+ #include "sqlite-src/sqlite3.h"
+ #define SQLITE3_CALL(x) (x)
+ #endif
#endif
#endif
diff --git a/libgda/sqlite/gda-symbols-util.c b/libgda/sqlite/gda-symbols-util.c
index 1da7dcc..a043934 100644
--- a/libgda/sqlite/gda-symbols-util.c
+++ b/libgda/sqlite/gda-symbols-util.c
@@ -271,6 +271,10 @@ load_symbols (GModule *module)
goto onerror;
if (! g_module_symbol (module, "sqlite3_value_type", (gpointer*) &(s3r->sqlite3_value_type)))
goto onerror;
+ if (! g_module_symbol (module, "sqlite3_key", (gpointer*) &(s3r->sqlite3_key)))
+ s3r->sqlite3_key = NULL;
+ if (! g_module_symbol (module, "sqlite3_rekey", (gpointer*) &(s3r->sqlite3_key)))
+ s3r->sqlite3_rekey = NULL;
return;
onerror:
diff --git a/libgda/sqlite/gda-symbols-util.h b/libgda/sqlite/gda-symbols-util.h
index 6f929e8..b543740 100644
--- a/libgda/sqlite/gda-symbols-util.h
+++ b/libgda/sqlite/gda-symbols-util.h
@@ -106,6 +106,9 @@ typedef struct {
int (*sqlite3_value_int)(sqlite3_value*);
const unsigned char * (*sqlite3_value_text)(sqlite3_value*);
int (*sqlite3_value_type)(sqlite3_value*);
+
+ int (*sqlite3_key)(sqlite3 *, const void *, int);
+ int (*sqlite3_rekey)(sqlite3 *, const void *, int);
} Sqlite3ApiRoutines;
extern Sqlite3ApiRoutines *s3r;
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 4826287..bcb9f58 100755
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -203,6 +203,19 @@ providers/reuseable/mysql/gda-mysql-meta.c
providers/reuseable/mysql/gda-mysql-reuseable.c
providers/reuseable/postgres/gda-postgres-meta.c
providers/reuseable/postgres/gda-postgres-reuseable.c
+providers/sqlcipher/libmain.c
+providers/sqlcipher/sqlcipher_specs_add_column.xml.in
+providers/sqlcipher/sqlcipher_specs_auth.xml.in
+providers/sqlcipher/sqlcipher_specs_create_db.xml.in
+providers/sqlcipher/sqlcipher_specs_create_index.xml.in
+providers/sqlcipher/sqlcipher_specs_create_table.xml.in
+providers/sqlcipher/sqlcipher_specs_create_view.xml.in
+providers/sqlcipher/sqlcipher_specs_drop_db.xml.in
+providers/sqlcipher/sqlcipher_specs_drop_index.xml.in
+providers/sqlcipher/sqlcipher_specs_drop_table.xml.in
+providers/sqlcipher/sqlcipher_specs_drop_view.xml.in
+providers/sqlcipher/sqlcipher_specs_dsn.xml.in
+providers/sqlcipher/sqlcipher_specs_rename_table.xml.in
providers/sqlite/libmain.c
providers/sqlite/sqlite_specs_add_column.xml.in
providers/sqlite/sqlite_specs_create_db.xml.in
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index 3d3762d..84bab04 100755
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -49,6 +49,7 @@ providers/skel-implementation/capi/libmain.c
providers/skel-implementation/models/gda-models-provider.c
providers/skel-implementation/models/libmain.c
providers/skel-implementation/models/models_specs_dsn.xml.in
+providers/sqlcipher/sqlite3.c
providers/sybase/gda-sybase-provider.c
providers/sybase/gda-sybase-recordset.c
providers/sybase/gda-sybase-types.c
diff --git a/providers/Makefile.am b/providers/Makefile.am
index b5fce61..c783825 100644
--- a/providers/Makefile.am
+++ b/providers/Makefile.am
@@ -62,6 +62,10 @@ if LIBSOUP
GDA_WEB_SERVER=web
endif
+if HAVE_LIBCRYPTO
+GDA_SQLCIPHER_SERVER=sqlcipher
+endif
+
SUBDIRS = \
reuseable \
sqlite \
@@ -73,7 +77,8 @@ SUBDIRS = \
$(GDA_MYSQL_SERVER) \
$(GDA_JAVA_SERVER) \
$(GDA_ORACLE_SERVER) \
- $(GDA_WEB_SERVER)
+ $(GDA_WEB_SERVER) \
+ $(GDA_SQLCIPHER_SERVER)
# $(GDA_FREETDS_SERVER) \
# $(GDA_IBMDB2_SERVER) \
# $(GDA_FIREBIRD_SERVER) \
diff --git a/providers/sqlcipher/.gitignore b/providers/sqlcipher/.gitignore
new file mode 100644
index 0000000..c989ca2
--- /dev/null
+++ b/providers/sqlcipher/.gitignore
@@ -0,0 +1,2 @@
+sqlite3.c
+sqlite3.h
\ No newline at end of file
diff --git a/providers/sqlcipher/COPYING.sqlcipher b/providers/sqlcipher/COPYING.sqlcipher
new file mode 100644
index 0000000..b6e96ae
--- /dev/null
+++ b/providers/sqlcipher/COPYING.sqlcipher
@@ -0,0 +1,28 @@
+Please find below the licence under which SQLCipher is distributed
+------------------------------------------------------------------
+
+
+Copyright (c) 2008, ZETETIC LLC
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the ZETETIC LLC nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/providers/sqlcipher/Makefile.am b/providers/sqlcipher/Makefile.am
new file mode 100644
index 0000000..c835e4c
--- /dev/null
+++ b/providers/sqlcipher/Makefile.am
@@ -0,0 +1,57 @@
+providerdir=$(libdir)/libgda-$(GDA_ABI_MAJOR_VERSION).$(GDA_ABI_MINOR_VERSION)/providers
+provider_LTLIBRARIES = libgda-sqlcipher.la
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/libgda \
+ -I$(top_srcdir)/libgda/sqlite \
+ -I$(top_builddir) \
+ $(LIBGDA_CFLAGS) \
+ $(LIBCRYPTO_CFLAGS) \
+ $(sqliteinc) \
+ -DSQLITE_HAS_CODEC -DSQLITE_API= -DSQLITE_PRIVATE= -DSQLITE_ENABLE_COLUMN_METADATA -DSQLITE_THREADSAFE=1 -DSQLITE_OMIT_LOAD_EXTENSION=1
+
+sqlite_sources = sqlite3.c sqlite3.h
+$(sqlite_sources): sqlcipher.patch $(top_srcdir)/libgda/sqlite/sqlite-src/sqlite3.c $(top_srcdir)/libgda/sqlite/sqlite-src/sqlite3.h
+ cp $(top_srcdir)/libgda/sqlite/sqlite-src/sqlite3.c sqlite3.c
+ cp $(top_srcdir)/libgda/sqlite/sqlite-src/sqlite3.h sqlite3.h
+ patch -p 0 < sqlcipher.patch
+
+libgda_sqlcipher_la_SOURCES = \
+ $(sqlite_sources) \
+ libmain.c
+
+libgda_sqlcipher_la_LDFLAGS = -export-dynamic -module -avoid-version $(LIBTOOL_PROV_EXPORT_OPTIONS)
+libgda_sqlcipher_la_LIBADD = \
+ $(top_builddir)/libgda/sqlite/libgda-sqlcipher.la \
+ $(LIBCRYPTO_LIBS)
+
+xmldir = $(datadir)/libgda-4.0
+xml_in_files = \
+ sqlcipher_specs_auth.xml.in \
+ sqlcipher_specs_dsn.xml.in \
+ sqlcipher_specs_create_db.xml.in \
+ sqlcipher_specs_drop_db.xml.in \
+ sqlcipher_specs_create_table.xml.in \
+ sqlcipher_specs_drop_table.xml.in \
+ sqlcipher_specs_create_index.xml.in \
+ sqlcipher_specs_drop_index.xml.in \
+ sqlcipher_specs_rename_table.xml.in \
+ sqlcipher_specs_add_column.xml.in \
+ sqlcipher_specs_create_view.xml.in \
+ sqlcipher_specs_drop_view.xml.in
+
+ INTLTOOL_XML_RULE@
+
+xml_DATA = $(xml_in_files:.xml.in=.xml)
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libgda-sqlcipher-4.0.pc
+
+EXTRA_DIST = $(xml_in_files) \
+ libgda-sqlcipher-4.0.pc.in \
+ sqlcipher.patch
+
+CLEANFILES = $(sqlite_sources)
+
+DISTCLEANFILES = $(xml_DATA)
diff --git a/providers/sqlcipher/NOTE_for_new_SQLCipher_version b/providers/sqlcipher/NOTE_for_new_SQLCipher_version
new file mode 100644
index 0000000..845bbb0
--- /dev/null
+++ b/providers/sqlcipher/NOTE_for_new_SQLCipher_version
@@ -0,0 +1,17 @@
+To upgrade SQLCipher version, all is needed is to create a patch against
+the sqlite3.c and sqlite3.h files in ${top_srcdir}/libgda/sqlite/sqlite-src
+and replace the sqlcipher.patch file:
+
+
+* define the LIBGDA_SOURCE_DIR to point to the top source directory where Libgda's files are
+* download the SQLCipher code source and extract (from http://www.zetetic.net/code/sqlcipher or
+ http://www.zetetic.net/code/sqlcipher)
+* into the extracted archive, run:
+ ./configure --enable-tempstore=yes CFLAGS="-DSQLITE_HAS_CODEC"
+ make sqlite3.c
+ cp $LIBGDA_SOURCE_DIR/libgda/sqlite/sqlite-src/sqlite3.c sqlite3.c.sqlite
+ cp $LIBGDA_SOURCE_DIR/libgda/sqlite/sqlite-src/sqlite3.h sqlite3.h.sqlite
+ diff -u sqlite3.h.sqlite sqlite3.h > sqlcipher.patch
+ diff -u sqlite3.c.sqlite sqlite3.c >> sqlcipher.patch
+ mv sqlcipher.patch $LIBGDA_SOURCE_DIR/providers/sqlcipher/
+* you can now delete all these temporary files
\ No newline at end of file
diff --git a/providers/sqlcipher/libgda-sqlcipher-4.0.pc.in b/providers/sqlcipher/libgda-sqlcipher-4.0.pc.in
new file mode 100644
index 0000000..72d2e7c
--- /dev/null
+++ b/providers/sqlcipher/libgda-sqlcipher-4.0.pc.in
@@ -0,0 +1,9 @@
+prefix= prefix@
+exec_prefix= exec_prefix@
+libdir= libdir@
+includedir= includedir@
+
+Name: libgda-bdbsql- GDA_ABI_MAJOR_VERSION@ GDA_ABI_MINOR_VERSION@
+Description: GDA (GNOME Data Access) BDBSql provider
+Requires: libgda- GDA_ABI_MAJOR_VERSION@ GDA_ABI_MINOR_VERSION@
+Version: @VERSION@
diff --git a/providers/sqlcipher/libmain.c b/providers/sqlcipher/libmain.c
new file mode 100644
index 0000000..3105c96
--- /dev/null
+++ b/providers/sqlcipher/libmain.c
@@ -0,0 +1,105 @@
+/* GDA
+ * Copyright (C) 2010 The GNOME Foundation
+ *
+ * AUTHORS:
+ * Vivien Malerba <malerba gnome-db org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <gmodule.h>
+#include <libgda/gda-config.h>
+#include "gda-sqlite-provider.h"
+#include <libgda/gda-server-provider-extra.h>
+#include <libgda/binreloc/gda-binreloc.h>
+#include <libgda/sqlite/gda-symbols-util.h>
+
+static gchar *module_path = NULL;
+const gchar *plugin_get_name (void);
+const gchar *plugin_get_description (void);
+gchar *plugin_get_dsn_spec (void);
+GdaServerProvider *plugin_create_provider (void);
+
+/*
+ * Functions executed when calling g_module_open() and g_module_close()
+ */
+const gchar *
+g_module_check_init (GModule *module)
+{
+ /*g_module_make_resident (module);*/
+ return NULL;
+}
+
+void
+g_module_unload (GModule *module)
+{
+ g_free (module_path);
+ module_path = NULL;
+}
+
+/*
+ * Normal plugin functions
+ */
+void
+plugin_init (const gchar *real_path)
+{
+ if (real_path)
+ module_path = g_strdup (real_path);
+}
+
+const gchar *
+plugin_get_name (void)
+{
+ return "SQLCipher";
+}
+
+const gchar *
+plugin_get_description (void)
+{
+ return _("Provider for SQLCipher");
+}
+
+gchar *
+plugin_get_dsn_spec (void)
+{
+ gchar *ret, *dir;
+
+ dir = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, NULL);
+ ret = gda_server_provider_load_file_contents (module_path, dir, "sqlcipher_specs_dsn.xml");
+ g_free (dir);
+ return ret;
+}
+
+gchar *
+plugin_get_auth_spec (void)
+{
+ gchar *ret, *dir;
+
+ dir = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, NULL);
+ ret = gda_server_provider_load_file_contents (module_path, dir, "sqlcipher_specs_auth.xml");
+ g_free (dir);
+ return ret;
+}
+
+GdaServerProvider *
+plugin_create_provider (void)
+{
+ GdaServerProvider *prov;
+
+ prov = (GdaServerProvider*) g_object_new (GDA_TYPE_SQLITE_PROVIDER, NULL);
+ g_object_set_data ((GObject *) prov, "GDA_PROVIDER_DIR", module_path);
+ return prov;
+}
diff --git a/providers/sqlcipher/sqlcipher.patch b/providers/sqlcipher/sqlcipher.patch
new file mode 100644
index 0000000..77d2090
--- /dev/null
+++ b/providers/sqlcipher/sqlcipher.patch
@@ -0,0 +1,2483 @@
+--- sqlite3.c.sqlite 2010-09-13 21:54:33.000000000 +0200
++++ sqlite3.c 2010-09-13 21:51:52.000000000 +0200
+@@ -10994,6 +10994,1320 @@
+ #endif /* _SQLITEINT_H_ */
+
+ /************** End of sqliteInt.h *******************************************/
++/************** Begin file crypto.c ******************************************/
++/*
++** SQLCipher
++** crypto.c developed by Stephen Lombardo (Zetetic LLC)
++** sjlombardo at zetetic dot net
++** http://zetetic.net
++**
++** Copyright (c) 2009, ZETETIC LLC
++** All rights reserved.
++**
++** Redistribution and use in source and binary forms, with or without
++** modification, are permitted provided that the following conditions are met:
++** * Redistributions of source code must retain the above copyright
++** notice, this list of conditions and the following disclaimer.
++** * Redistributions in binary form must reproduce the above copyright
++** notice, this list of conditions and the following disclaimer in the
++** documentation and/or other materials provided with the distribution.
++** * Neither the name of the ZETETIC LLC nor the
++** names of its contributors may be used to endorse or promote products
++** derived from this software without specific prior written permission.
++**
++** THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY
++** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
++** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
++** DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY
++** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
++** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
++** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
++** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++**
++*/
++/* BEGIN CRYPTO */
++#ifdef SQLITE_HAS_CODEC
++
++#include <openssl/evp.h>
++#include <openssl/rand.h>
++#include <openssl/hmac.h>
++/************** Include btreeInt.h in the middle of crypto.c *****************/
++/************** Begin file btreeInt.h ****************************************/
++/*
++** 2004 April 6
++**
++** The author disclaims copyright to this source code. In place of
++** a legal notice, here is a blessing:
++**
++** May you do good and not evil.
++** May you find forgiveness for yourself and forgive others.
++** May you share freely, never taking more than you give.
++**
++*************************************************************************
++** This file implements a external (disk-based) database using BTrees.
++** For a detailed discussion of BTrees, refer to
++**
++** Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
++** "Sorting And Searching", pages 473-480. Addison-Wesley
++** Publishing Company, Reading, Massachusetts.
++**
++** The basic idea is that each page of the file contains N database
++** entries and N+1 pointers to subpages.
++**
++** ----------------------------------------------------------------
++** | Ptr(0) | Key(0) | Ptr(1) | Key(1) | ... | Key(N-1) | Ptr(N) |
++** ----------------------------------------------------------------
++**
++** All of the keys on the page that Ptr(0) points to have values less
++** than Key(0). All of the keys on page Ptr(1) and its subpages have
++** values greater than Key(0) and less than Key(1). All of the keys
++** on Ptr(N) and its subpages have values greater than Key(N-1). And
++** so forth.
++**
++** Finding a particular key requires reading O(log(M)) pages from the
++** disk where M is the number of entries in the tree.
++**
++** In this implementation, a single file can hold one or more separate
++** BTrees. Each BTree is identified by the index of its root page. The
++** key and data for any entry are combined to form the "payload". A
++** fixed amount of payload can be carried directly on the database
++** page. If the payload is larger than the preset amount then surplus
++** bytes are stored on overflow pages. The payload for an entry
++** and the preceding pointer are combined to form a "Cell". Each
++** page has a small header which contains the Ptr(N) pointer and other
++** information such as the size of key and data.
++**
++** FORMAT DETAILS
++**
++** The file is divided into pages. The first page is called page 1,
++** the second is page 2, and so forth. A page number of zero indicates
++** "no such page". The page size can be any power of 2 between 512 and 65536.
++** Each page can be either a btree page, a freelist page, an overflow
++** page, or a pointer-map page.
++**
++** The first page is always a btree page. The first 100 bytes of the first
++** page contain a special header (the "file header") that describes the file.
++** The format of the file header is as follows:
++**
++** OFFSET SIZE DESCRIPTION
++** 0 16 Header string: "SQLite format 3\000"
++** 16 2 Page size in bytes.
++** 18 1 File format write version
++** 19 1 File format read version
++** 20 1 Bytes of unused space at the end of each page
++** 21 1 Max embedded payload fraction
++** 22 1 Min embedded payload fraction
++** 23 1 Min leaf payload fraction
++** 24 4 File change counter
++** 28 4 Reserved for future use
++** 32 4 First freelist page
++** 36 4 Number of freelist pages in the file
++** 40 60 15 4-byte meta values passed to higher layers
++**
++** 40 4 Schema cookie
++** 44 4 File format of schema layer
++** 48 4 Size of page cache
++** 52 4 Largest root-page (auto/incr_vacuum)
++** 56 4 1=UTF-8 2=UTF16le 3=UTF16be
++** 60 4 User version
++** 64 4 Incremental vacuum mode
++** 68 4 unused
++** 72 4 unused
++** 76 4 unused
++**
++** All of the integer values are big-endian (most significant byte first).
++**
++** The file change counter is incremented when the database is changed
++** This counter allows other processes to know when the file has changed
++** and thus when they need to flush their cache.
++**
++** The max embedded payload fraction is the amount of the total usable
++** space in a page that can be consumed by a single cell for standard
++** B-tree (non-LEAFDATA) tables. A value of 255 means 100%. The default
++** is to limit the maximum cell size so that at least 4 cells will fit
++** on one page. Thus the default max embedded payload fraction is 64.
++**
++** If the payload for a cell is larger than the max payload, then extra
++** payload is spilled to overflow pages. Once an overflow page is allocated,
++** as many bytes as possible are moved into the overflow pages without letting
++** the cell size drop below the min embedded payload fraction.
++**
++** The min leaf payload fraction is like the min embedded payload fraction
++** except that it applies to leaf nodes in a LEAFDATA tree. The maximum
++** payload fraction for a LEAFDATA tree is always 100% (or 255) and it
++** not specified in the header.
++**
++** Each btree pages is divided into three sections: The header, the
++** cell pointer array, and the cell content area. Page 1 also has a 100-byte
++** file header that occurs before the page header.
++**
++** |----------------|
++** | file header | 100 bytes. Page 1 only.
++** |----------------|
++** | page header | 8 bytes for leaves. 12 bytes for interior nodes
++** |----------------|
++** | cell pointer | | 2 bytes per cell. Sorted order.
++** | array | | Grows downward
++** | | v
++** |----------------|
++** | unallocated |
++** | space |
++** |----------------| ^ Grows upwards
++** | cell content | | Arbitrary order interspersed with freeblocks.
++** | area | | and free space fragments.
++** |----------------|
++**
++** The page headers looks like this:
++**
++** OFFSET SIZE DESCRIPTION
++** 0 1 Flags. 1: intkey, 2: zerodata, 4: leafdata, 8: leaf
++** 1 2 byte offset to the first freeblock
++** 3 2 number of cells on this page
++** 5 2 first byte of the cell content area
++** 7 1 number of fragmented free bytes
++** 8 4 Right child (the Ptr(N) value). Omitted on leaves.
++**
++** The flags define the format of this btree page. The leaf flag means that
++** this page has no children. The zerodata flag means that this page carries
++** only keys and no data. The intkey flag means that the key is a integer
++** which is stored in the key size entry of the cell header rather than in
++** the payload area.
++**
++** The cell pointer array begins on the first byte after the page header.
++** The cell pointer array contains zero or more 2-byte numbers which are
++** offsets from the beginning of the page to the cell content in the cell
++** content area. The cell pointers occur in sorted order. The system strives
++** to keep free space after the last cell pointer so that new cells can
++** be easily added without having to defragment the page.
++**
++** Cell content is stored at the very end of the page and grows toward the
++** beginning of the page.
++**
++** Unused space within the cell content area is collected into a linked list of
++** freeblocks. Each freeblock is at least 4 bytes in size. The byte offset
++** to the first freeblock is given in the header. Freeblocks occur in
++** increasing order. Because a freeblock must be at least 4 bytes in size,
++** any group of 3 or fewer unused bytes in the cell content area cannot
++** exist on the freeblock chain. A group of 3 or fewer free bytes is called
++** a fragment. The total number of bytes in all fragments is recorded.
++** in the page header at offset 7.
++**
++** SIZE DESCRIPTION
++** 2 Byte offset of the next freeblock
++** 2 Bytes in this freeblock
++**
++** Cells are of variable length. Cells are stored in the cell content area at
++** the end of the page. Pointers to the cells are in the cell pointer array
++** that immediately follows the page header. Cells is not necessarily
++** contiguous or in order, but cell pointers are contiguous and in order.
++**
++** Cell content makes use of variable length integers. A variable
++** length integer is 1 to 9 bytes where the lower 7 bits of each
++** byte are used. The integer consists of all bytes that have bit 8 set and
++** the first byte with bit 8 clear. The most significant byte of the integer
++** appears first. A variable-length integer may not be more than 9 bytes long.
++** As a special case, all 8 bytes of the 9th byte are used as data. This
++** allows a 64-bit integer to be encoded in 9 bytes.
++**
++** 0x00 becomes 0x00000000
++** 0x7f becomes 0x0000007f
++** 0x81 0x00 becomes 0x00000080
++** 0x82 0x00 becomes 0x00000100
++** 0x80 0x7f becomes 0x0000007f
++** 0x8a 0x91 0xd1 0xac 0x78 becomes 0x12345678
++** 0x81 0x81 0x81 0x81 0x01 becomes 0x10204081
++**
++** Variable length integers are used for rowids and to hold the number of
++** bytes of key and data in a btree cell.
++**
++** The content of a cell looks like this:
++**
++** SIZE DESCRIPTION
++** 4 Page number of the left child. Omitted if leaf flag is set.
++** var Number of bytes of data. Omitted if the zerodata flag is set.
++** var Number of bytes of key. Or the key itself if intkey flag is set.
++** * Payload
++** 4 First page of the overflow chain. Omitted if no overflow
++**
++** Overflow pages form a linked list. Each page except the last is completely
++** filled with data (pagesize - 4 bytes). The last page can have as little
++** as 1 byte of data.
++**
++** SIZE DESCRIPTION
++** 4 Page number of next overflow page
++** * Data
++**
++** Freelist pages come in two subtypes: trunk pages and leaf pages. The
++** file header points to the first in a linked list of trunk page. Each trunk
++** page points to multiple leaf pages. The content of a leaf page is
++** unspecified. A trunk page looks like this:
++**
++** SIZE DESCRIPTION
++** 4 Page number of next trunk page
++** 4 Number of leaf pointers on this page
++** * zero or more pages numbers of leaves
++*/
++
++
++/* The following value is the maximum cell size assuming a maximum page
++** size give above.
++*/
++#define MX_CELL_SIZE(pBt) (pBt->pageSize-8)
++
++/* The maximum number of cells on a single page of the database. This
++** assumes a minimum cell size of 6 bytes (4 bytes for the cell itself
++** plus 2 bytes for the index to the cell in the page header). Such
++** small cells will be rare, but they are possible.
++*/
++#define MX_CELL(pBt) ((pBt->pageSize-8)/6)
++
++/* Forward declarations */
++typedef struct MemPage MemPage;
++typedef struct BtLock BtLock;
++
++/*
++** This is a magic string that appears at the beginning of every
++** SQLite database in order to identify the file as a real database.
++**
++** You can change this value at compile-time by specifying a
++** -DSQLITE_FILE_HEADER="..." on the compiler command-line. The
++** header must be exactly 16 bytes including the zero-terminator so
++** the string itself should be 15 characters long. If you change
++** the header, then your custom library will not be able to read
++** databases generated by the standard tools and the standard tools
++** will not be able to read databases created by your custom library.
++*/
++#ifndef SQLITE_FILE_HEADER /* 123456789 123456 */
++# define SQLITE_FILE_HEADER "SQLite format 3"
++#endif
++
++/*
++** Page type flags. An ORed combination of these flags appear as the
++** first byte of on-disk image of every BTree page.
++*/
++#define PTF_INTKEY 0x01
++#define PTF_ZERODATA 0x02
++#define PTF_LEAFDATA 0x04
++#define PTF_LEAF 0x08
++
++/*
++** As each page of the file is loaded into memory, an instance of the following
++** structure is appended and initialized to zero. This structure stores
++** information about the page that is decoded from the raw file page.
++**
++** The pParent field points back to the parent page. This allows us to
++** walk up the BTree from any leaf to the root. Care must be taken to
++** unref() the parent page pointer when this page is no longer referenced.
++** The pageDestructor() routine handles that chore.
++**
++** Access to all fields of this structure is controlled by the mutex
++** stored in MemPage.pBt->mutex.
++*/
++struct MemPage {
++ u8 isInit; /* True if previously initialized. MUST BE FIRST! */
++ u8 nOverflow; /* Number of overflow cell bodies in aCell[] */
++ u8 intKey; /* True if intkey flag is set */
++ u8 leaf; /* True if leaf flag is set */
++ u8 hasData; /* True if this page stores data */
++ u8 hdrOffset; /* 100 for page 1. 0 otherwise */
++ u8 childPtrSize; /* 0 if leaf==1. 4 if leaf==0 */
++ u16 maxLocal; /* Copy of BtShared.maxLocal or BtShared.maxLeaf */
++ u16 minLocal; /* Copy of BtShared.minLocal or BtShared.minLeaf */
++ u16 cellOffset; /* Index in aData of first cell pointer */
++ u16 nFree; /* Number of free bytes on the page */
++ u16 nCell; /* Number of cells on this page, local and ovfl */
++ u16 maskPage; /* Mask for page offset */
++ struct _OvflCell { /* Cells that will not fit on aData[] */
++ u8 *pCell; /* Pointers to the body of the overflow cell */
++ u16 idx; /* Insert this cell before idx-th non-overflow cell */
++ } aOvfl[5];
++ BtShared *pBt; /* Pointer to BtShared that this page is part of */
++ u8 *aData; /* Pointer to disk image of the page data */
++ DbPage *pDbPage; /* Pager page handle */
++ Pgno pgno; /* Page number for this page */
++};
++
++/*
++** The in-memory image of a disk page has the auxiliary information appended
++** to the end. EXTRA_SIZE is the number of bytes of space needed to hold
++** that extra information.
++*/
++#define EXTRA_SIZE sizeof(MemPage)
++
++/*
++** A linked list of the following structures is stored at BtShared.pLock.
++** Locks are added (or upgraded from READ_LOCK to WRITE_LOCK) when a cursor
++** is opened on the table with root page BtShared.iTable. Locks are removed
++** from this list when a transaction is committed or rolled back, or when
++** a btree handle is closed.
++*/
++struct BtLock {
++ Btree *pBtree; /* Btree handle holding this lock */
++ Pgno iTable; /* Root page of table */
++ u8 eLock; /* READ_LOCK or WRITE_LOCK */
++ BtLock *pNext; /* Next in BtShared.pLock list */
++};
++
++/* Candidate values for BtLock.eLock */
++#define READ_LOCK 1
++#define WRITE_LOCK 2
++
++/* A Btree handle
++**
++** A database connection contains a pointer to an instance of
++** this object for every database file that it has open. This structure
++** is opaque to the database connection. The database connection cannot
++** see the internals of this structure and only deals with pointers to
++** this structure.
++**
++** For some database files, the same underlying database cache might be
++** shared between multiple connections. In that case, each connection
++** has it own instance of this object. But each instance of this object
++** points to the same BtShared object. The database cache and the
++** schema associated with the database file are all contained within
++** the BtShared object.
++**
++** All fields in this structure are accessed under sqlite3.mutex.
++** The pBt pointer itself may not be changed while there exists cursors
++** in the referenced BtShared that point back to this Btree since those
++** cursors have to do go through this Btree to find their BtShared and
++** they often do so without holding sqlite3.mutex.
++*/
++struct Btree {
++ sqlite3 *db; /* The database connection holding this btree */
++ BtShared *pBt; /* Sharable content of this btree */
++ u8 inTrans; /* TRANS_NONE, TRANS_READ or TRANS_WRITE */
++ u8 sharable; /* True if we can share pBt with another db */
++ u8 locked; /* True if db currently has pBt locked */
++ int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */
++ int nBackup; /* Number of backup operations reading this btree */
++ Btree *pNext; /* List of other sharable Btrees from the same db */
++ Btree *pPrev; /* Back pointer of the same list */
++#ifndef SQLITE_OMIT_SHARED_CACHE
++ BtLock lock; /* Object used to lock page 1 */
++#endif
++};
++
++/*
++** Btree.inTrans may take one of the following values.
++**
++** If the shared-data extension is enabled, there may be multiple users
++** of the Btree structure. At most one of these may open a write transaction,
++** but any number may have active read transactions.
++*/
++#define TRANS_NONE 0
++#define TRANS_READ 1
++#define TRANS_WRITE 2
++
++/*
++** An instance of this object represents a single database file.
++**
++** A single database file can be in use as the same time by two
++** or more database connections. When two or more connections are
++** sharing the same database file, each connection has it own
++** private Btree object for the file and each of those Btrees points
++** to this one BtShared object. BtShared.nRef is the number of
++** connections currently sharing this database file.
++**
++** Fields in this structure are accessed under the BtShared.mutex
++** mutex, except for nRef and pNext which are accessed under the
++** global SQLITE_MUTEX_STATIC_MASTER mutex. The pPager field
++** may not be modified once it is initially set as long as nRef>0.
++** The pSchema field may be set once under BtShared.mutex and
++** thereafter is unchanged as long as nRef>0.
++**
++** isPending:
++**
++** If a BtShared client fails to obtain a write-lock on a database
++** table (because there exists one or more read-locks on the table),
++** the shared-cache enters 'pending-lock' state and isPending is
++** set to true.
++**
++** The shared-cache leaves the 'pending lock' state when either of
++** the following occur:
++**
++** 1) The current writer (BtShared.pWriter) concludes its transaction, OR
++** 2) The number of locks held by other connections drops to zero.
++**
++** while in the 'pending-lock' state, no connection may start a new
++** transaction.
++**
++** This feature is included to help prevent writer-starvation.
++*/
++struct BtShared {
++ Pager *pPager; /* The page cache */
++ sqlite3 *db; /* Database connection currently using this Btree */
++ BtCursor *pCursor; /* A list of all open cursors */
++ MemPage *pPage1; /* First page of the database */
++ u8 readOnly; /* True if the underlying file is readonly */
++ u8 pageSizeFixed; /* True if the page size can no longer be changed */
++ u8 secureDelete; /* True if secure_delete is enabled */
++ u8 initiallyEmpty; /* Database is empty at start of transaction */
++#ifndef SQLITE_OMIT_AUTOVACUUM
++ u8 autoVacuum; /* True if auto-vacuum is enabled */
++ u8 incrVacuum; /* True if incr-vacuum is enabled */
++#endif
++ u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */
++ u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */
++ u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */
++ u16 minLeaf; /* Minimum local payload in a LEAFDATA table */
++ u8 inTransaction; /* Transaction state */
++ u8 doNotUseWAL; /* If true, do not open write-ahead-log file */
++ u32 pageSize; /* Total number of bytes on a page */
++ u32 usableSize; /* Number of usable bytes on each page */
++ int nTransaction; /* Number of open transactions (read + write) */
++ u32 nPage; /* Number of pages in the database */
++ void *pSchema; /* Pointer to space allocated by sqlite3BtreeSchema() */
++ void (*xFreeSchema)(void*); /* Destructor for BtShared.pSchema */
++ sqlite3_mutex *mutex; /* Non-recursive mutex required to access this struct */
++ Bitvec *pHasContent; /* Set of pages moved to free-list this transaction */
++#ifndef SQLITE_OMIT_SHARED_CACHE
++ int nRef; /* Number of references to this structure */
++ BtShared *pNext; /* Next on a list of sharable BtShared structs */
++ BtLock *pLock; /* List of locks held on this shared-btree struct */
++ Btree *pWriter; /* Btree with currently open write transaction */
++ u8 isExclusive; /* True if pWriter has an EXCLUSIVE lock on the db */
++ u8 isPending; /* If waiting for read-locks to clear */
++#endif
++ u8 *pTmpSpace; /* BtShared.pageSize bytes of space for tmp use */
++};
++
++/*
++** An instance of the following structure is used to hold information
++** about a cell. The parseCellPtr() function fills in this structure
++** based on information extract from the raw disk page.
++*/
++typedef struct CellInfo CellInfo;
++struct CellInfo {
++ u8 *pCell; /* Pointer to the start of cell content */
++ i64 nKey; /* The key for INTKEY tables, or number of bytes in key */
++ u32 nData; /* Number of bytes of data */
++ u32 nPayload; /* Total amount of payload */
++ u16 nHeader; /* Size of the cell content header in bytes */
++ u16 nLocal; /* Amount of payload held locally */
++ u16 iOverflow; /* Offset to overflow page number. Zero if no overflow */
++ u16 nSize; /* Size of the cell content on the main b-tree page */
++};
++
++/*
++** Maximum depth of an SQLite B-Tree structure. Any B-Tree deeper than
++** this will be declared corrupt. This value is calculated based on a
++** maximum database size of 2^31 pages a minimum fanout of 2 for a
++** root-node and 3 for all other internal nodes.
++**
++** If a tree that appears to be taller than this is encountered, it is
++** assumed that the database is corrupt.
++*/
++#define BTCURSOR_MAX_DEPTH 20
++
++/*
++** A cursor is a pointer to a particular entry within a particular
++** b-tree within a database file.
++**
++** The entry is identified by its MemPage and the index in
++** MemPage.aCell[] of the entry.
++**
++** A single database file can shared by two more database connections,
++** but cursors cannot be shared. Each cursor is associated with a
++** particular database connection identified BtCursor.pBtree.db.
++**
++** Fields in this structure are accessed under the BtShared.mutex
++** found at self->pBt->mutex.
++*/
++struct BtCursor {
++ Btree *pBtree; /* The Btree to which this cursor belongs */
++ BtShared *pBt; /* The BtShared this cursor points to */
++ BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */
++ struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */
++ Pgno pgnoRoot; /* The root page of this tree */
++ sqlite3_int64 cachedRowid; /* Next rowid cache. 0 means not valid */
++ CellInfo info; /* A parse of the cell we are pointing at */
++ u8 wrFlag; /* True if writable */
++ u8 atLast; /* Cursor pointing to the last entry */
++ u8 validNKey; /* True if info.nKey is valid */
++ u8 eState; /* One of the CURSOR_XXX constants (see below) */
++ void *pKey; /* Saved key that was cursor's last known position */
++ i64 nKey; /* Size of pKey, or last integer key */
++ int skipNext; /* Prev() is noop if negative. Next() is noop if positive */
++#ifndef SQLITE_OMIT_INCRBLOB
++ u8 isIncrblobHandle; /* True if this cursor is an incr. io handle */
++ Pgno *aOverflow; /* Cache of overflow page locations */
++#endif
++ i16 iPage; /* Index of current page in apPage */
++ MemPage *apPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */
++ u16 aiIdx[BTCURSOR_MAX_DEPTH]; /* Current index in apPage[i] */
++};
++
++/*
++** Potential values for BtCursor.eState.
++**
++** CURSOR_VALID:
++** Cursor points to a valid entry. getPayload() etc. may be called.
++**
++** CURSOR_INVALID:
++** Cursor does not point to a valid entry. This can happen (for example)
++** because the table is empty or because BtreeCursorFirst() has not been
++** called.
++**
++** CURSOR_REQUIRESEEK:
++** The table that this cursor was opened on still exists, but has been
++** modified since the cursor was last used. The cursor position is saved
++** in variables BtCursor.pKey and BtCursor.nKey. When a cursor is in
++** this state, restoreCursorPosition() can be called to attempt to
++** seek the cursor to the saved position.
++**
++** CURSOR_FAULT:
++** A unrecoverable error (an I/O error or a malloc failure) has occurred
++** on a different connection that shares the BtShared cache with this
++** cursor. The error has left the cache in an inconsistent state.
++** Do nothing else with this cursor. Any attempt to use the cursor
++** should return the error code stored in BtCursor.skip
++*/
++#define CURSOR_INVALID 0
++#define CURSOR_VALID 1
++#define CURSOR_REQUIRESEEK 2
++#define CURSOR_FAULT 3
++
++/*
++** The database page the PENDING_BYTE occupies. This page is never used.
++*/
++# define PENDING_BYTE_PAGE(pBt) PAGER_MJ_PGNO(pBt)
++
++/*
++** These macros define the location of the pointer-map entry for a
++** database page. The first argument to each is the number of usable
++** bytes on each page of the database (often 1024). The second is the
++** page number to look up in the pointer map.
++**
++** PTRMAP_PAGENO returns the database page number of the pointer-map
++** page that stores the required pointer. PTRMAP_PTROFFSET returns
++** the offset of the requested map entry.
++**
++** If the pgno argument passed to PTRMAP_PAGENO is a pointer-map page,
++** then pgno is returned. So (pgno==PTRMAP_PAGENO(pgsz, pgno)) can be
++** used to test if pgno is a pointer-map page. PTRMAP_ISPAGE implements
++** this test.
++*/
++#define PTRMAP_PAGENO(pBt, pgno) ptrmapPageno(pBt, pgno)
++#define PTRMAP_PTROFFSET(pgptrmap, pgno) (5*(pgno-pgptrmap-1))
++#define PTRMAP_ISPAGE(pBt, pgno) (PTRMAP_PAGENO((pBt),(pgno))==(pgno))
++
++/*
++** The pointer map is a lookup table that identifies the parent page for
++** each child page in the database file. The parent page is the page that
++** contains a pointer to the child. Every page in the database contains
++** 0 or 1 parent pages. (In this context 'database page' refers
++** to any page that is not part of the pointer map itself.) Each pointer map
++** entry consists of a single byte 'type' and a 4 byte parent page number.
++** The PTRMAP_XXX identifiers below are the valid types.
++**
++** The purpose of the pointer map is to facility moving pages from one
++** position in the file to another as part of autovacuum. When a page
++** is moved, the pointer in its parent must be updated to point to the
++** new location. The pointer map is used to locate the parent page quickly.
++**
++** PTRMAP_ROOTPAGE: The database page is a root-page. The page-number is not
++** used in this case.
++**
++** PTRMAP_FREEPAGE: The database page is an unused (free) page. The page-number
++** is not used in this case.
++**
++** PTRMAP_OVERFLOW1: The database page is the first page in a list of
++** overflow pages. The page number identifies the page that
++** contains the cell with a pointer to this overflow page.
++**
++** PTRMAP_OVERFLOW2: The database page is the second or later page in a list of
++** overflow pages. The page-number identifies the previous
++** page in the overflow page list.
++**
++** PTRMAP_BTREE: The database page is a non-root btree page. The page number
++** identifies the parent page in the btree.
++*/
++#define PTRMAP_ROOTPAGE 1
++#define PTRMAP_FREEPAGE 2
++#define PTRMAP_OVERFLOW1 3
++#define PTRMAP_OVERFLOW2 4
++#define PTRMAP_BTREE 5
++
++/* A bunch of assert() statements to check the transaction state variables
++** of handle p (type Btree*) are internally consistent.
++*/
++#define btreeIntegrity(p) \
++ assert( p->pBt->inTransaction!=TRANS_NONE || p->pBt->nTransaction==0 ); \
++ assert( p->pBt->inTransaction>=p->inTrans );
++
++
++/*
++** The ISAUTOVACUUM macro is used within balance_nonroot() to determine
++** if the database supports auto-vacuum or not. Because it is used
++** within an expression that is an argument to another macro
++** (sqliteMallocRaw), it is not possible to use conditional compilation.
++** So, this macro is defined instead.
++*/
++#ifndef SQLITE_OMIT_AUTOVACUUM
++#define ISAUTOVACUUM (pBt->autoVacuum)
++#else
++#define ISAUTOVACUUM 0
++#endif
++
++
++/*
++** This structure is passed around through all the sanity checking routines
++** in order to keep track of some global state information.
++*/
++typedef struct IntegrityCk IntegrityCk;
++struct IntegrityCk {
++ BtShared *pBt; /* The tree being checked out */
++ Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */
++ Pgno nPage; /* Number of pages in the database */
++ int *anRef; /* Number of times each page is referenced */
++ int mxErr; /* Stop accumulating errors when this reaches zero */
++ int nErr; /* Number of messages written to zErrMsg so far */
++ int mallocFailed; /* A memory allocation error has occurred */
++ StrAccum errMsg; /* Accumulate the error message text here */
++};
++
++/*
++** Read or write a two- and four-byte big-endian integer values.
++*/
++#define get2byte(x) ((x)[0]<<8 | (x)[1])
++#define put2byte(p,v) ((p)[0] = (u8)((v)>>8), (p)[1] = (u8)(v))
++#define get4byte sqlite3Get4byte
++#define put4byte sqlite3Put4byte
++
++/************** End of btreeInt.h ********************************************/
++/************** Continuing where we left off in crypto.c *********************/
++/************** Include crypto.h in the middle of crypto.c *******************/
++/************** Begin file crypto.h ******************************************/
++/*
++** SQLCipher
++** crypto.h developed by Stephen Lombardo (Zetetic LLC)
++** sjlombardo at zetetic dot net
++** http://zetetic.net
++**
++** Copyright (c) 2008, ZETETIC LLC
++** All rights reserved.
++**
++** Redistribution and use in source and binary forms, with or without
++** modification, are permitted provided that the following conditions are met:
++** * Redistributions of source code must retain the above copyright
++** notice, this list of conditions and the following disclaimer.
++** * Redistributions in binary form must reproduce the above copyright
++** notice, this list of conditions and the following disclaimer in the
++** documentation and/or other materials provided with the distribution.
++** * Neither the name of the ZETETIC LLC nor the
++** names of its contributors may be used to endorse or promote products
++** derived from this software without specific prior written permission.
++**
++** THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY
++** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
++** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
++** DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY
++** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
++** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
++** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
++** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++**
++*/
++/* BEGIN CRYPTO */
++#ifdef SQLITE_HAS_CODEC
++#ifndef CRYPTO_H
++#define CRYPTO_H
++
++#define FILE_HEADER_SZ 16
++
++#ifndef CIPHER
++#define CIPHER "aes-256-cbc"
++#endif
++
++#define CIPHER_DECRYPT 0
++#define CIPHER_ENCRYPT 1
++
++#ifndef PBKDF2_ITER
++#define PBKDF2_ITER 4000
++#endif
++
++SQLITE_PRIVATE void sqlite3pager_get_codec(Pager *pPager, void **ctx);
++SQLITE_PRIVATE int sqlite3pager_is_mj_pgno(Pager *pPager, Pgno pgno);
++SQLITE_PRIVATE sqlite3_file *sqlite3Pager_get_fd(Pager *pPager);
++SQLITE_PRIVATE void sqlite3pager_sqlite3PagerSetCodec(
++ Pager *pPager,
++ void *(*xCodec)(void*,void*,Pgno,int),
++ void (*xCodecSizeChng)(void*,int,int),
++ void (*xCodecFree)(void*),
++ void *pCodec
++);
++
++#endif
++#endif
++/* END CRYPTO */
++
++/************** End of crypto.h **********************************************/
++/************** Continuing where we left off in crypto.c *********************/
++
++#ifdef CODEC_DEBUG
++#define CODEC_TRACE(X) {printf X;fflush(stdout);}
++#else
++#define CODEC_TRACE(X)
++#endif
++
++SQLITE_PRIVATE void sqlite3FreeCodecArg(void *pCodecArg);
++
++typedef struct {
++ int derive_key;
++ EVP_CIPHER *evp_cipher;
++ int kdf_iter;
++ int key_sz;
++ int iv_sz;
++ int pass_sz;
++ unsigned char *key;
++ char *pass;
++} cipher_ctx;
++
++typedef struct {
++ int kdf_salt_sz;
++ int mode_rekey;
++ unsigned char *kdf_salt;
++ unsigned char *buffer;
++ Btree *pBt;
++ cipher_ctx *read_ctx;
++ cipher_ctx *write_ctx;
++} codec_ctx;
++
++static void activate_openssl() {
++ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
++ if(EVP_get_cipherbyname(CIPHER) == NULL) {
++ OpenSSL_add_all_algorithms();
++ }
++ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
++}
++
++/*
++** Simple routines for converting hex char strings to binary data
++ */
++static int cipher_hex2int(char c) {
++ return (c>='0' && c<='9') ? (c)-'0' :
++ (c>='A' && c<='F') ? (c)-'A'+10 :
++ (c>='a' && c<='f') ? (c)-'a'+10 : 0;
++}
++
++static void cipher_hex2bin(const char *hex, int sz, unsigned char *out){
++ int i;
++ for(i = 0; i < sz; i += 2){
++ out[i/2] = (cipher_hex2int(hex[i])<<4) | cipher_hex2int(hex[i+1]);
++ }
++}
++
++
++/**
++ * Free and wipe memory
++ * If ptr is not null memory will be freed.
++ * If sz is greater than zero, the memory will be overwritten with zero before it is freed
++ */
++static void codec_free(void *ptr, int sz) {
++ if(ptr) {
++ if(sz > 0) memset(ptr, 0, sz); // FIXME - require buffer size
++ sqlite3_free(ptr);
++ }
++}
++
++/**
++ * Set the raw password / key data for a cipher context
++ *
++ * returns SQLITE_OK if assignment was successfull
++ * returns SQLITE_NOMEM if an error occured allocating memory
++ * returns SQLITE_ERROR if the key couldn't be set because the pass was null or size was zero
++ */
++static int cipher_ctx_set_pass(cipher_ctx *ctx, const void *zKey, int nKey) {
++ codec_free(ctx->pass, ctx->pass_sz);
++ ctx->pass_sz = nKey;
++ if(zKey && nKey) {
++ ctx->pass = sqlite3Malloc(nKey);
++ if(ctx->pass == NULL) return SQLITE_NOMEM;
++ memcpy(ctx->pass, zKey, nKey);
++ return SQLITE_OK;
++ }
++ return SQLITE_ERROR;
++}
++
++/**
++ * Initialize a a new cipher_ctx struct. This function will allocate memory
++ * for the cipher context and for the key
++ *
++ * returns SQLITE_OK if initialization was successful
++ * returns SQLITE_NOMEM if an error occured allocating memory
++ */
++static int cipher_ctx_init(cipher_ctx **iCtx) {
++ cipher_ctx *ctx;
++ *iCtx = sqlite3Malloc(sizeof(cipher_ctx));
++ ctx = *iCtx;
++ if(ctx == NULL) return SQLITE_NOMEM;
++ memset(ctx, 0, sizeof(cipher_ctx));
++ ctx->key = sqlite3Malloc(EVP_MAX_KEY_LENGTH);
++ if(ctx->key == NULL) return SQLITE_NOMEM;
++ return SQLITE_OK;
++}
++
++/**
++ * Free and wipe memory associated with a cipher_ctx
++ */
++static void cipher_ctx_free(cipher_ctx **iCtx) {
++ cipher_ctx *ctx = *iCtx;
++ CODEC_TRACE(("cipher_ctx_free: entered iCtx=%d\n", iCtx));
++ codec_free(ctx->key, ctx->key_sz);
++ codec_free(ctx->pass, ctx->pass_sz);
++ codec_free(ctx, sizeof(cipher_ctx));
++}
++
++/**
++ * Copy one cipher_ctx to another. For instance, assuming that read_ctx is a
++ * fully initialized context, you could copy it to write_ctx and all yet data
++ * and pass information across
++ *
++ * returns SQLITE_OK if initialization was successful
++ * returns SQLITE_NOMEM if an error occured allocating memory
++ */
++static int cipher_ctx_copy(cipher_ctx *target, cipher_ctx *source) {
++ void *key = target->key;
++ CODEC_TRACE(("cipher_ctx_copy: entered target=%d, source=%d\n", target, source));
++ codec_free(target->pass, target->pass_sz);
++ memcpy(target, source, sizeof(cipher_ctx));
++
++ target->key = key; //restore pointer to previously allocated key data
++ memcpy(target->key, source->key, EVP_MAX_KEY_LENGTH);
++ target->pass = sqlite3Malloc(source->pass_sz);
++ if(target->pass == NULL) return SQLITE_NOMEM;
++ memcpy(target->pass, source->pass, source->pass_sz);
++ return SQLITE_OK;
++}
++
++/**
++ * Compare one cipher_ctx to another.
++ *
++ * returns 0 if all the parameters (except the derived key data) are the same
++ * returns 1 otherwise
++ */
++static int cipher_ctx_cmp(cipher_ctx *c1, cipher_ctx *c2) {
++ CODEC_TRACE(("cipher_ctx_cmp: entered c1=%d c2=%d\n", c1, c2));
++
++ if(
++ c1->evp_cipher == c2->evp_cipher
++ && c1->iv_sz == c2->iv_sz
++ && c1->kdf_iter == c2->kdf_iter
++ && c1->key_sz == c2->key_sz
++ && c1->pass_sz == c2->pass_sz
++ && (
++ c1->pass == c2->pass
++ || !memcmp(c1->pass, c2->pass, c1->pass_sz)
++ )
++ ) return 0;
++ return 1;
++}
++
++/**
++ * Free and wipe memory associated with a cipher_ctx, including the allocated
++ * read_ctx and write_ctx.
++ */
++static void codec_ctx_free(codec_ctx **iCtx) {
++ codec_ctx *ctx = *iCtx;
++ CODEC_TRACE(("codec_ctx_free: entered iCtx=%d\n", iCtx));
++ codec_free(ctx->kdf_salt, ctx->kdf_salt_sz);
++ codec_free(ctx->buffer, 0);
++ cipher_ctx_free(&ctx->read_ctx);
++ cipher_ctx_free(&ctx->write_ctx);
++ codec_free(ctx, sizeof(codec_ctx));
++}
++
++/**
++ * Derive an encryption key for a cipher contex key based on the raw password.
++ *
++ * If the raw key data is formated as x'hex' and there are exactly enough hex chars to fill
++ * the key space (i.e 64 hex chars for a 256 bit key) then the key data will be used directly.
++ *
++ * Otherwise, a key data will be derived using PBKDF2
++ *
++ * returns SQLITE_OK if initialization was successful
++ * returns SQLITE_NOMEM if the key could't be derived (for instance if pass is NULL or pass_sz is 0)
++ */
++static int codec_key_derive(codec_ctx *ctx, cipher_ctx *c_ctx) {
++ CODEC_TRACE(("codec_key_derive: entered c_ctx->pass=%s, c_ctx->pass_sz=%d ctx->kdf_salt=%d ctx->kdf_salt_sz=%d c_ctx->kdf_iter=%d c_ctx->key_sz=%d\n",
++ c_ctx->pass, c_ctx->pass_sz, ctx->kdf_salt, ctx->kdf_salt_sz, c_ctx->kdf_iter, c_ctx->key_sz));
++
++ if(c_ctx->pass && c_ctx->pass_sz) { // if pass is not null
++ if (c_ctx->pass_sz == ((c_ctx->key_sz*2)+3) && sqlite3StrNICmp(c_ctx->pass ,"x'", 2) == 0) {
++ int n = c_ctx->pass_sz - 3; /* adjust for leading x' and tailing ' */
++ const char *z = c_ctx->pass + 2; /* adjust lead offset of x' */
++ CODEC_TRACE(("codec_key_derive: deriving key from hex\n"));
++ cipher_hex2bin(z, n, c_ctx->key);
++ } else {
++ CODEC_TRACE(("codec_key_derive: deriving key using PBKDF2\n"));
++ PKCS5_PBKDF2_HMAC_SHA1(c_ctx->pass, c_ctx->pass_sz, ctx->kdf_salt, ctx->kdf_salt_sz, c_ctx->kdf_iter, c_ctx->key_sz, c_ctx->key);
++ }
++ return SQLITE_OK;
++ };
++ return SQLITE_ERROR;
++}
++
++/*
++ * ctx - codec context
++ * pgno - page number in database
++ * size - size in bytes of input and output buffers
++ * mode - 1 to encrypt, 0 to decrypt
++ * in - pointer to input bytes
++ * out - pouter to output bytes
++ */
++static int codec_cipher(cipher_ctx *ctx, Pgno pgno, int mode, int size, unsigned char *in, unsigned char *out) {
++ EVP_CIPHER_CTX ectx;
++ unsigned char *iv;
++ int tmp_csz, csz;
++
++ CODEC_TRACE(("codec_cipher:entered pgno=%d, mode=%d, size=%d\n", pgno, mode, size));
++
++ /* just copy raw data from in to out when key size is 0
++ * i.e. during a rekey of a plaintext database */
++ if(ctx->key_sz == 0) {
++ memcpy(out, in, size);
++ return SQLITE_OK;
++ }
++
++ // FIXME - only run if using an IV
++ size = size - ctx->iv_sz; /* adjust size to useable size and memset reserve at end of page */
++ iv = out + size;
++ if(mode == CIPHER_ENCRYPT) {
++ RAND_pseudo_bytes(iv, ctx->iv_sz);
++ } else {
++ memcpy(iv, in+size, ctx->iv_sz);
++ }
++
++ EVP_CipherInit(&ectx, ctx->evp_cipher, NULL, NULL, mode);
++ EVP_CIPHER_CTX_set_padding(&ectx, 0);
++ EVP_CipherInit(&ectx, NULL, ctx->key, iv, mode);
++ EVP_CipherUpdate(&ectx, out, &tmp_csz, in, size);
++ csz = tmp_csz;
++ out += tmp_csz;
++ EVP_CipherFinal(&ectx, out, &tmp_csz);
++ csz += tmp_csz;
++ EVP_CIPHER_CTX_cleanup(&ectx);
++ assert(size == csz);
++
++ return SQLITE_OK;
++}
++
++int codec_set_kdf_iter(sqlite3* db, int nDb, int kdf_iter, int for_ctx) {
++ struct Db *pDb = &db->aDb[nDb];
++ CODEC_TRACE(("codec_set_kdf_iter: entered db=%d nDb=%d kdf_iter=%d for_ctx=%d\n", db, nDb, kdf_iter, for_ctx));
++
++ if(pDb->pBt) {
++ codec_ctx *ctx;
++ cipher_ctx *c_ctx;
++ sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx);
++ c_ctx = for_ctx ? ctx->write_ctx : ctx->read_ctx;
++
++ c_ctx->kdf_iter = kdf_iter;
++ c_ctx->derive_key = 1;
++
++ if(for_ctx == 2) cipher_ctx_copy( for_ctx ? ctx->read_ctx : ctx->write_ctx, c_ctx);
++ return SQLITE_OK;
++ }
++ return SQLITE_ERROR;
++}
++
++/**
++ *
++ * when for_ctx == 0 then it will change for read
++ * when for_ctx == 1 then it will change for write
++ * when for_ctx == 2 then it will change for both
++ */
++int codec_set_cipher_name(sqlite3* db, int nDb, const char *cipher_name, int for_ctx) {
++ struct Db *pDb = &db->aDb[nDb];
++ CODEC_TRACE(("codec_set_cipher_name: entered db=%d nDb=%d cipher_name=%s for_ctx=%d\n", db, nDb, cipher_name, for_ctx));
++
++ if(pDb->pBt) {
++ codec_ctx *ctx;
++ cipher_ctx *c_ctx;
++ sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx);
++ c_ctx = for_ctx ? ctx->write_ctx : ctx->read_ctx;
++
++ c_ctx->evp_cipher = (EVP_CIPHER *) EVP_get_cipherbyname(cipher_name);
++ c_ctx->key_sz = EVP_CIPHER_key_length(c_ctx->evp_cipher);
++ c_ctx->iv_sz = EVP_CIPHER_iv_length(c_ctx->evp_cipher);
++ c_ctx->derive_key = 1;
++
++ if(for_ctx == 2) cipher_ctx_copy( for_ctx ? ctx->read_ctx : ctx->write_ctx, c_ctx);
++ return SQLITE_OK;
++ }
++ return SQLITE_ERROR;
++}
++
++int codec_set_pass_key(sqlite3* db, int nDb, const void *zKey, int nKey, int for_ctx) {
++ struct Db *pDb = &db->aDb[nDb];
++ CODEC_TRACE(("codec_set_pass_key: entered db=%d nDb=%d cipher_name=%s nKey=%d for_ctx=%d\n", db, nDb, zKey, nKey, for_ctx));
++ if(pDb->pBt) {
++ codec_ctx *ctx;
++ cipher_ctx *c_ctx;
++ sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx);
++ c_ctx = for_ctx ? ctx->write_ctx : ctx->read_ctx;
++
++ cipher_ctx_set_pass(c_ctx, zKey, nKey);
++ c_ctx->derive_key = 1;
++
++ if(for_ctx == 2) cipher_ctx_copy( for_ctx ? ctx->read_ctx : ctx->write_ctx, c_ctx);
++ return SQLITE_OK;
++ }
++ return SQLITE_ERROR;
++}
++
++/*
++ * sqlite3Codec can be called in multiple modes.
++ * encrypt mode - expected to return a pointer to the
++ * encrypted data without altering pData.
++ * decrypt mode - expected to return a pointer to pData, with
++ * the data decrypted in the input buffer
++ */
++void* sqlite3Codec(void *iCtx, void *data, Pgno pgno, int mode) {
++ codec_ctx *ctx = (codec_ctx *) iCtx;
++ int pg_sz = sqlite3BtreeGetPageSize(ctx->pBt);
++ int offset = 0;
++ unsigned char *pData = (unsigned char *) data;
++
++ CODEC_TRACE(("sqlite3Codec: entered pgno=%d, mode=%d, ctx->mode_rekey=%d, pg_sz=%d\n", pgno, mode, ctx->mode_rekey, pg_sz));
++
++ /* derive key on first use if necessary */
++ if(ctx->read_ctx->derive_key) {
++ codec_key_derive(ctx, ctx->read_ctx);
++ ctx->read_ctx->derive_key = 0;
++ }
++
++ if(ctx->write_ctx->derive_key) {
++ if(cipher_ctx_cmp(ctx->write_ctx, ctx->read_ctx) == 0) {
++ cipher_ctx_copy(ctx->write_ctx, ctx->read_ctx); // the relevant parameters are the same, just copy read key
++ } else {
++ codec_key_derive(ctx, ctx->write_ctx);
++ ctx->write_ctx->derive_key = 0;
++ }
++ }
++
++
++ if(pgno == 1) offset = FILE_HEADER_SZ; /* adjust starting pointers in data page for header offset on first page*/
++
++ CODEC_TRACE(("sqlite3Codec: switch mode=%d offset=%d\n", mode, offset));
++ switch(mode) {
++ case 0: /* decrypt */
++ case 2:
++ case 3:
++ if(pgno == 1) memcpy(ctx->buffer, SQLITE_FILE_HEADER, FILE_HEADER_SZ); /* copy file header to the first 16 bytes of the page */
++ codec_cipher(ctx->read_ctx, pgno, CIPHER_DECRYPT, pg_sz - offset, pData + offset, ctx->buffer + offset);
++ memcpy(pData, ctx->buffer, pg_sz); /* copy buffer data back to pData and return */
++ return pData;
++ break;
++ case 6: /* encrypt */
++ if(pgno == 1) memcpy(ctx->buffer, ctx->kdf_salt, FILE_HEADER_SZ); /* copy salt to output buffer */
++ codec_cipher(ctx->write_ctx, pgno, CIPHER_ENCRYPT, pg_sz - offset, pData + offset, ctx->buffer + offset);
++ return ctx->buffer; /* return persistent buffer data, pData remains intact */
++ break;
++ case 7:
++ if(pgno == 1) memcpy(ctx->buffer, ctx->kdf_salt, FILE_HEADER_SZ); /* copy salt to output buffer */
++ codec_cipher(ctx->read_ctx, pgno, CIPHER_ENCRYPT, pg_sz - offset, pData + offset, ctx->buffer + offset);
++ return ctx->buffer; /* return persistent buffer data, pData remains intact */
++ break;
++ default:
++ return pData;
++ break;
++ }
++}
++
++
++SQLITE_PRIVATE int sqlite3CodecAttach(sqlite3* db, int nDb, const void *zKey, int nKey) {
++ struct Db *pDb = &db->aDb[nDb];
++
++ CODEC_TRACE(("sqlite3CodecAttach: entered nDb=%d zKey=%s, nKey=%d\n", nDb, zKey, nKey));
++ activate_openssl();
++
++ if(nKey && zKey && pDb->pBt) {
++ codec_ctx *ctx;
++ int rc;
++ Pager *pPager = pDb->pBt->pBt->pPager;
++ sqlite3_file *fd;
++
++ ctx = sqlite3Malloc(sizeof(codec_ctx));
++ if(ctx == NULL) return SQLITE_NOMEM;
++ memset(ctx, 0, sizeof(codec_ctx)); /* initialize all pointers and values to 0 */
++
++ ctx->pBt = pDb->pBt; /* assign pointer to database btree structure */
++
++ if((rc = cipher_ctx_init(&ctx->read_ctx)) != SQLITE_OK) return rc;
++ if((rc = cipher_ctx_init(&ctx->write_ctx)) != SQLITE_OK) return rc;
++
++ /* pre-allocate a page buffer of PageSize bytes. This will
++ be used as a persistent buffer for encryption and decryption
++ operations to avoid overhead of multiple memory allocations*/
++ ctx->buffer = sqlite3Malloc(sqlite3BtreeGetPageSize(ctx->pBt));
++ if(ctx->buffer == NULL) return SQLITE_NOMEM;
++
++ /* allocate space for salt data. Then read the first 16 bytes
++ directly off the database file. This is the salt for the
++ key derivation function. If we get a short read allocate
++ a new random salt value */
++ ctx->kdf_salt_sz = FILE_HEADER_SZ;
++ ctx->kdf_salt = sqlite3Malloc(ctx->kdf_salt_sz);
++ if(ctx->kdf_salt == NULL) return SQLITE_NOMEM;
++
++ fd = sqlite3Pager_get_fd(pPager);
++ if(fd == NULL || sqlite3OsRead(fd, ctx->kdf_salt, FILE_HEADER_SZ, 0) != SQLITE_OK) {
++ /* if unable to read the bytes, generate random salt */
++ RAND_pseudo_bytes(ctx->kdf_salt, FILE_HEADER_SZ);
++ }
++
++ sqlite3pager_sqlite3PagerSetCodec(sqlite3BtreePager(pDb->pBt), sqlite3Codec, NULL, sqlite3FreeCodecArg, (void *) ctx);
++
++ codec_set_cipher_name(db, nDb, CIPHER, 0);
++ codec_set_kdf_iter(db, nDb, PBKDF2_ITER, 0);
++ codec_set_pass_key(db, nDb, zKey, nKey, 0);
++ cipher_ctx_copy(ctx->write_ctx, ctx->read_ctx);
++
++ sqlite3BtreeSetPageSize(ctx->pBt, sqlite3BtreeGetPageSize(ctx->pBt), EVP_MAX_IV_LENGTH, 0);
++ }
++ return SQLITE_OK;
++}
++
++SQLITE_PRIVATE void sqlite3FreeCodecArg(void *pCodecArg) {
++ codec_ctx *ctx = (codec_ctx *) pCodecArg;
++ if(pCodecArg == NULL) return;
++ codec_ctx_free(&ctx); // wipe and free allocated memory for the context
++}
++
++SQLITE_API void sqlite3_activate_see(const char* in) {
++ /* do nothing, security enhancements are always active */
++}
++
++SQLITE_API int sqlite3_key(sqlite3 *db, const void *pKey, int nKey) {
++ CODEC_TRACE(("sqlite3_key: entered db=%d pKey=%s nKey=%d\n", db, pKey, nKey));
++ /* attach key if db and pKey are not null and nKey is > 0 */
++ if(db && pKey && nKey) {
++ sqlite3CodecAttach(db, 0, pKey, nKey); // operate only on the main db
++ return SQLITE_OK;
++ }
++ return SQLITE_ERROR;
++}
++
++/* sqlite3_rekey
++** Given a database, this will reencrypt the database using a new key.
++** There are two possible modes of operation. The first is rekeying
++** an existing database that was not previously encrypted. The second
++** is to change the key on an existing database.
++**
++** The proposed logic for this function follows:
++** 1. Determine if there is already a key present
++** 2. If there is NOT already a key present, create one and attach a codec (key would be null)
++** 3. Initialize a ctx->rekey parameter of the codec
++**
++** Note: this will require modifications to the sqlite3Codec to support rekey
++**
++*/
++SQLITE_API int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey) {
++ CODEC_TRACE(("sqlite3_rekey: entered db=%d pKey=%s, nKey=%d\n", db, pKey, nKey));
++ activate_openssl();
++ if(db && pKey && nKey) {
++ struct Db *pDb = &db->aDb[0];
++ CODEC_TRACE(("sqlite3_rekey: database pDb=%d\n", pDb));
++ if(pDb->pBt) {
++ codec_ctx *ctx;
++ int rc, page_count;
++ Pgno pgno;
++ PgHdr *page;
++ Pager *pPager = pDb->pBt->pBt->pPager;
++
++ sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx);
++
++ if(ctx == NULL) {
++ CODEC_TRACE(("sqlite3_rekey: no codec attached to db, attaching now\n"));
++ /* there was no codec attached to this database,so attach one now with a null password */
++ sqlite3CodecAttach(db, 0, pKey, nKey);
++ sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx);
++
++ /* prepare this setup as if it had already been initialized */
++ RAND_pseudo_bytes(ctx->kdf_salt, ctx->kdf_salt_sz);
++ ctx->read_ctx->key_sz = ctx->read_ctx->iv_sz = ctx->read_ctx->pass_sz = 0;
++ }
++
++ if(ctx->read_ctx->iv_sz != ctx->write_ctx->iv_sz) {
++ char *error;
++ CODEC_TRACE(("sqlite3_rekey: updating page size for iv_sz change from %d to %d\n", ctx->read_ctx->iv_sz, ctx->write_ctx->iv_sz));
++ db->nextPagesize = sqlite3BtreeGetPageSize(pDb->pBt);
++ pDb->pBt->pBt->pageSizeFixed = 0; /* required for sqlite3BtreeSetPageSize to modify pagesize setting */
++ sqlite3BtreeSetPageSize(pDb->pBt, db->nextPagesize, EVP_MAX_IV_LENGTH, 0);
++ sqlite3RunVacuum(&error, db);
++ }
++
++ codec_set_pass_key(db, 0, pKey, nKey, 1);
++ ctx->mode_rekey = 1;
++
++ /* do stuff here to rewrite the database
++ ** 1. Create a transaction on the database
++ ** 2. Iterate through each page, reading it and then writing it.
++ ** 3. If that goes ok then commit and put ctx->rekey into ctx->key
++ ** note: don't deallocate rekey since it may be used in a subsequent iteration
++ */
++ rc = sqlite3BtreeBeginTrans(pDb->pBt, 1); /* begin write transaction */
++ sqlite3PagerPagecount(pPager, &page_count);
++ for(pgno = 1; rc == SQLITE_OK && pgno <= page_count; pgno++) { /* pgno's start at 1 see pager.c:pagerAcquire */
++ if(!sqlite3pager_is_mj_pgno(pPager, pgno)) { /* skip this page (see pager.c:pagerAcquire for reasoning) */
++ rc = sqlite3PagerGet(pPager, pgno, &page);
++ if(rc == SQLITE_OK) { /* write page see pager_incr_changecounter for example */
++ rc = sqlite3PagerWrite(page);
++ //printf("sqlite3PagerWrite(%d)\n", pgno);
++ if(rc == SQLITE_OK) {
++ sqlite3PagerUnref(page);
++ }
++ }
++ }
++ }
++
++ /* if commit was successful commit and copy the rekey data to current key, else rollback to release locks */
++ if(rc == SQLITE_OK) {
++ CODEC_TRACE(("sqlite3_rekey: committing\n"));
++ db->nextPagesize = sqlite3BtreeGetPageSize(pDb->pBt);
++ rc = sqlite3BtreeCommit(pDb->pBt);
++ cipher_ctx_copy(ctx->read_ctx, ctx->write_ctx);
++ } else {
++ CODEC_TRACE(("sqlite3_rekey: rollback\n"));
++ sqlite3BtreeRollback(pDb->pBt);
++ }
++
++ ctx->mode_rekey = 0;
++ }
++ return SQLITE_OK;
++ }
++ return SQLITE_ERROR;
++}
++
++SQLITE_PRIVATE void sqlite3CodecGetKey(sqlite3* db, int nDb, void **zKey, int *nKey) {
++ struct Db *pDb = &db->aDb[nDb];
++ CODEC_TRACE(("sqlite3CodecGetKey: entered db=%d, nDb=%d\n", db, nDb));
++
++ if( pDb->pBt ) {
++ codec_ctx *ctx;
++ sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx);
++
++ if(ctx) { /* if the codec has an attached codec_context user the raw key data */
++ *zKey = ctx->read_ctx->pass;
++ *nKey = ctx->read_ctx->pass_sz;
++ } else {
++ *zKey = NULL;
++ *nKey = 0;
++ }
++ }
++}
++
++
++/* END CRYPTO */
++#endif
++
++/************** End of crypto.c **********************************************/
+ /************** Begin file global.c ******************************************/
+ /*
+ ** 2008 June 13
+@@ -40807,11 +42121,40 @@
+ CODEC2(pPg->pPager, pPg->pData, pPg->pgno, 6, return 0, aData);
+ return aData;
+ }
+-#endif /* SQLITE_HAS_CODEC */
++#endif /* SQLITE_HAS_CODEC */
++
++#endif /* !SQLITE_OMIT_WAL */
++
++#endif /* SQLITE_OMIT_DISKIO */
++
++/* BEGIN CRYPTO */
++#ifdef SQLITE_HAS_CODEC
++SQLITE_PRIVATE void sqlite3pager_get_codec(Pager *pPager, void **ctx) {
++ *ctx = pPager->pCodec;
++}
++
++SQLITE_PRIVATE int sqlite3pager_is_mj_pgno(Pager *pPager, Pgno pgno) {
++ return (PAGER_MJ_PGNO(pPager) == pgno) ? 1 : 0;
++}
+
+-#endif /* !SQLITE_OMIT_WAL */
++SQLITE_PRIVATE sqlite3_file *sqlite3Pager_get_fd(Pager *pPager) {
++ return (isOpen(pPager->fd)) ? pPager->fd : NULL;
++}
++
++SQLITE_PRIVATE void sqlite3pager_sqlite3PagerSetCodec(
++ Pager *pPager,
++ void *(*xCodec)(void*,void*,Pgno,int),
++ void (*xCodecSizeChng)(void*,int,int),
++ void (*xCodecFree)(void*),
++ void *pCodec
++){
++ sqlite3PagerSetCodec(pPager, xCodec, xCodecSizeChng, xCodecFree, pCodec);
++}
++
++
++#endif
++/* END CRYPTO */
+
+-#endif /* SQLITE_OMIT_DISKIO */
+
+ /************** End of pager.c ***********************************************/
+ /************** Begin file wal.c *********************************************/
+@@ -43340,854 +44683,208 @@
+ #if defined(SQLITE_HAS_CODEC)
+ if( (pData = sqlite3PagerCodec(pLast))==0 ) return SQLITE_NOMEM;
+ #else
+- pData = pLast->pData;
+-#endif
+- walEncodeFrame(pWal, pLast->pgno, nTruncate, pData, aFrame);
+- /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */
+- rc = sqlite3OsWrite(pWal->pWalFd, aFrame, sizeof(aFrame), iOffset);
+- if( rc!=SQLITE_OK ){
+- return rc;
+- }
+- iOffset += WAL_FRAME_HDRSIZE;
+- rc = sqlite3OsWrite(pWal->pWalFd, pData, szPage, iOffset);
+- if( rc!=SQLITE_OK ){
+- return rc;
+- }
+- nLast++;
+- iOffset += szPage;
+- }
+-
+- rc = sqlite3OsSync(pWal->pWalFd, sync_flags);
+- }
+-
+- /* Append data to the wal-index. It is not necessary to lock the
+- ** wal-index to do this as the SQLITE_SHM_WRITE lock held on the wal-index
+- ** guarantees that there are no other writers, and no data that may
+- ** be in use by existing readers is being overwritten.
+- */
+- iFrame = pWal->hdr.mxFrame;
+- for(p=pList; p && rc==SQLITE_OK; p=p->pDirty){
+- iFrame++;
+- rc = walIndexAppend(pWal, iFrame, p->pgno);
+- }
+- while( nLast>0 && rc==SQLITE_OK ){
+- iFrame++;
+- nLast--;
+- rc = walIndexAppend(pWal, iFrame, pLast->pgno);
+- }
+-
+- if( rc==SQLITE_OK ){
+- /* Update the private copy of the header. */
+- pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16));
+- testcase( szPage<=32768 );
+- testcase( szPage>=65536 );
+- pWal->hdr.mxFrame = iFrame;
+- if( isCommit ){
+- pWal->hdr.iChange++;
+- pWal->hdr.nPage = nTruncate;
+- }
+- /* If this is a commit, update the wal-index header too. */
+- if( isCommit ){
+- walIndexWriteHdr(pWal);
+- pWal->iCallback = iFrame;
+- }
+- }
+-
+- WALTRACE(("WAL%p: frame write %s\n", pWal, rc ? "failed" : "ok"));
+- return rc;
+-}
+-
+-/*
+-** This routine is called to implement sqlite3_wal_checkpoint() and
+-** related interfaces.
+-**
+-** Obtain a CHECKPOINT lock and then backfill as much information as
+-** we can from WAL into the database.
+-*/
+-SQLITE_PRIVATE int sqlite3WalCheckpoint(
+- Wal *pWal, /* Wal connection */
+- int sync_flags, /* Flags to sync db file with (or 0) */
+- int nBuf, /* Size of temporary buffer */
+- u8 *zBuf /* Temporary buffer to use */
+-){
+- int rc; /* Return code */
+- int isChanged = 0; /* True if a new wal-index header is loaded */
+-
+- assert( pWal->ckptLock==0 );
+-
+- WALTRACE(("WAL%p: checkpoint begins\n", pWal));
+- rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
+- if( rc ){
+- /* Usually this is SQLITE_BUSY meaning that another thread or process
+- ** is already running a checkpoint, or maybe a recovery. But it might
+- ** also be SQLITE_IOERR. */
+- return rc;
+- }
+- pWal->ckptLock = 1;
+-
+- /* Copy data from the log to the database file. */
+- rc = walIndexReadHdr(pWal, &isChanged);
+- if( rc==SQLITE_OK ){
+- rc = walCheckpoint(pWal, sync_flags, nBuf, zBuf);
+- }
+- if( isChanged ){
+- /* If a new wal-index header was loaded before the checkpoint was
+- ** performed, then the pager-cache associated with pWal is now
+- ** out of date. So zero the cached wal-index header to ensure that
+- ** next time the pager opens a snapshot on this database it knows that
+- ** the cache needs to be reset.
+- */
+- memset(&pWal->hdr, 0, sizeof(WalIndexHdr));
+- }
+-
+- /* Release the locks. */
+- walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1);
+- pWal->ckptLock = 0;
+- WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok"));
+- return rc;
+-}
+-
+-/* Return the value to pass to a sqlite3_wal_hook callback, the
+-** number of frames in the WAL at the point of the last commit since
+-** sqlite3WalCallback() was called. If no commits have occurred since
+-** the last call, then return 0.
+-*/
+-SQLITE_PRIVATE int sqlite3WalCallback(Wal *pWal){
+- u32 ret = 0;
+- if( pWal ){
+- ret = pWal->iCallback;
+- pWal->iCallback = 0;
+- }
+- return (int)ret;
+-}
+-
+-/*
+-** This function is called to change the WAL subsystem into or out
+-** of locking_mode=EXCLUSIVE.
+-**
+-** If op is zero, then attempt to change from locking_mode=EXCLUSIVE
+-** into locking_mode=NORMAL. This means that we must acquire a lock
+-** on the pWal->readLock byte. If the WAL is already in locking_mode=NORMAL
+-** or if the acquisition of the lock fails, then return 0. If the
+-** transition out of exclusive-mode is successful, return 1. This
+-** operation must occur while the pager is still holding the exclusive
+-** lock on the main database file.
+-**
+-** If op is one, then change from locking_mode=NORMAL into
+-** locking_mode=EXCLUSIVE. This means that the pWal->readLock must
+-** be released. Return 1 if the transition is made and 0 if the
+-** WAL is already in exclusive-locking mode - meaning that this
+-** routine is a no-op. The pager must already hold the exclusive lock
+-** on the main database file before invoking this operation.
+-**
+-** If op is negative, then do a dry-run of the op==1 case but do
+-** not actually change anything. The pager uses this to see if it
+-** should acquire the database exclusive lock prior to invoking
+-** the op==1 case.
+-*/
+-SQLITE_PRIVATE int sqlite3WalExclusiveMode(Wal *pWal, int op){
+- int rc;
+- assert( pWal->writeLock==0 );
+-
+- /* pWal->readLock is usually set, but might be -1 if there was a
+- ** prior error while attempting to acquire are read-lock. This cannot
+- ** happen if the connection is actually in exclusive mode (as no xShmLock
+- ** locks are taken in this case). Nor should the pager attempt to
+- ** upgrade to exclusive-mode following such an error.
+- */
+- assert( pWal->readLock>=0 || pWal->lockError );
+- assert( pWal->readLock>=0 || (op<=0 && pWal->exclusiveMode==0) );
+-
+- if( op==0 ){
+- if( pWal->exclusiveMode ){
+- pWal->exclusiveMode = 0;
+- if( walLockShared(pWal, WAL_READ_LOCK(pWal->readLock))!=SQLITE_OK ){
+- pWal->exclusiveMode = 1;
+- }
+- rc = pWal->exclusiveMode==0;
+- }else{
+- /* Already in locking_mode=NORMAL */
+- rc = 0;
+- }
+- }else if( op>0 ){
+- assert( pWal->exclusiveMode==0 );
+- assert( pWal->readLock>=0 );
+- walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock));
+- pWal->exclusiveMode = 1;
+- rc = 1;
+- }else{
+- rc = pWal->exclusiveMode==0;
+- }
+- return rc;
+-}
+-
+-#endif /* #ifndef SQLITE_OMIT_WAL */
+-
+-/************** End of wal.c *************************************************/
+-/************** Begin file btmutex.c *****************************************/
+-/*
+-** 2007 August 27
+-**
+-** The author disclaims copyright to this source code. In place of
+-** a legal notice, here is a blessing:
+-**
+-** May you do good and not evil.
+-** May you find forgiveness for yourself and forgive others.
+-** May you share freely, never taking more than you give.
+-**
+-*************************************************************************
+-**
+-** This file contains code used to implement mutexes on Btree objects.
+-** This code really belongs in btree.c. But btree.c is getting too
+-** big and we want to break it down some. This packaged seemed like
+-** a good breakout.
+-*/
+-/************** Include btreeInt.h in the middle of btmutex.c ****************/
+-/************** Begin file btreeInt.h ****************************************/
+-/*
+-** 2004 April 6
+-**
+-** The author disclaims copyright to this source code. In place of
+-** a legal notice, here is a blessing:
+-**
+-** May you do good and not evil.
+-** May you find forgiveness for yourself and forgive others.
+-** May you share freely, never taking more than you give.
+-**
+-*************************************************************************
+-** This file implements a external (disk-based) database using BTrees.
+-** For a detailed discussion of BTrees, refer to
+-**
+-** Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
+-** "Sorting And Searching", pages 473-480. Addison-Wesley
+-** Publishing Company, Reading, Massachusetts.
+-**
+-** The basic idea is that each page of the file contains N database
+-** entries and N+1 pointers to subpages.
+-**
+-** ----------------------------------------------------------------
+-** | Ptr(0) | Key(0) | Ptr(1) | Key(1) | ... | Key(N-1) | Ptr(N) |
+-** ----------------------------------------------------------------
+-**
+-** All of the keys on the page that Ptr(0) points to have values less
+-** than Key(0). All of the keys on page Ptr(1) and its subpages have
+-** values greater than Key(0) and less than Key(1). All of the keys
+-** on Ptr(N) and its subpages have values greater than Key(N-1). And
+-** so forth.
+-**
+-** Finding a particular key requires reading O(log(M)) pages from the
+-** disk where M is the number of entries in the tree.
+-**
+-** In this implementation, a single file can hold one or more separate
+-** BTrees. Each BTree is identified by the index of its root page. The
+-** key and data for any entry are combined to form the "payload". A
+-** fixed amount of payload can be carried directly on the database
+-** page. If the payload is larger than the preset amount then surplus
+-** bytes are stored on overflow pages. The payload for an entry
+-** and the preceding pointer are combined to form a "Cell". Each
+-** page has a small header which contains the Ptr(N) pointer and other
+-** information such as the size of key and data.
+-**
+-** FORMAT DETAILS
+-**
+-** The file is divided into pages. The first page is called page 1,
+-** the second is page 2, and so forth. A page number of zero indicates
+-** "no such page". The page size can be any power of 2 between 512 and 65536.
+-** Each page can be either a btree page, a freelist page, an overflow
+-** page, or a pointer-map page.
+-**
+-** The first page is always a btree page. The first 100 bytes of the first
+-** page contain a special header (the "file header") that describes the file.
+-** The format of the file header is as follows:
+-**
+-** OFFSET SIZE DESCRIPTION
+-** 0 16 Header string: "SQLite format 3\000"
+-** 16 2 Page size in bytes.
+-** 18 1 File format write version
+-** 19 1 File format read version
+-** 20 1 Bytes of unused space at the end of each page
+-** 21 1 Max embedded payload fraction
+-** 22 1 Min embedded payload fraction
+-** 23 1 Min leaf payload fraction
+-** 24 4 File change counter
+-** 28 4 Reserved for future use
+-** 32 4 First freelist page
+-** 36 4 Number of freelist pages in the file
+-** 40 60 15 4-byte meta values passed to higher layers
+-**
+-** 40 4 Schema cookie
+-** 44 4 File format of schema layer
+-** 48 4 Size of page cache
+-** 52 4 Largest root-page (auto/incr_vacuum)
+-** 56 4 1=UTF-8 2=UTF16le 3=UTF16be
+-** 60 4 User version
+-** 64 4 Incremental vacuum mode
+-** 68 4 unused
+-** 72 4 unused
+-** 76 4 unused
+-**
+-** All of the integer values are big-endian (most significant byte first).
+-**
+-** The file change counter is incremented when the database is changed
+-** This counter allows other processes to know when the file has changed
+-** and thus when they need to flush their cache.
+-**
+-** The max embedded payload fraction is the amount of the total usable
+-** space in a page that can be consumed by a single cell for standard
+-** B-tree (non-LEAFDATA) tables. A value of 255 means 100%. The default
+-** is to limit the maximum cell size so that at least 4 cells will fit
+-** on one page. Thus the default max embedded payload fraction is 64.
+-**
+-** If the payload for a cell is larger than the max payload, then extra
+-** payload is spilled to overflow pages. Once an overflow page is allocated,
+-** as many bytes as possible are moved into the overflow pages without letting
+-** the cell size drop below the min embedded payload fraction.
+-**
+-** The min leaf payload fraction is like the min embedded payload fraction
+-** except that it applies to leaf nodes in a LEAFDATA tree. The maximum
+-** payload fraction for a LEAFDATA tree is always 100% (or 255) and it
+-** not specified in the header.
+-**
+-** Each btree pages is divided into three sections: The header, the
+-** cell pointer array, and the cell content area. Page 1 also has a 100-byte
+-** file header that occurs before the page header.
+-**
+-** |----------------|
+-** | file header | 100 bytes. Page 1 only.
+-** |----------------|
+-** | page header | 8 bytes for leaves. 12 bytes for interior nodes
+-** |----------------|
+-** | cell pointer | | 2 bytes per cell. Sorted order.
+-** | array | | Grows downward
+-** | | v
+-** |----------------|
+-** | unallocated |
+-** | space |
+-** |----------------| ^ Grows upwards
+-** | cell content | | Arbitrary order interspersed with freeblocks.
+-** | area | | and free space fragments.
+-** |----------------|
+-**
+-** The page headers looks like this:
+-**
+-** OFFSET SIZE DESCRIPTION
+-** 0 1 Flags. 1: intkey, 2: zerodata, 4: leafdata, 8: leaf
+-** 1 2 byte offset to the first freeblock
+-** 3 2 number of cells on this page
+-** 5 2 first byte of the cell content area
+-** 7 1 number of fragmented free bytes
+-** 8 4 Right child (the Ptr(N) value). Omitted on leaves.
+-**
+-** The flags define the format of this btree page. The leaf flag means that
+-** this page has no children. The zerodata flag means that this page carries
+-** only keys and no data. The intkey flag means that the key is a integer
+-** which is stored in the key size entry of the cell header rather than in
+-** the payload area.
+-**
+-** The cell pointer array begins on the first byte after the page header.
+-** The cell pointer array contains zero or more 2-byte numbers which are
+-** offsets from the beginning of the page to the cell content in the cell
+-** content area. The cell pointers occur in sorted order. The system strives
+-** to keep free space after the last cell pointer so that new cells can
+-** be easily added without having to defragment the page.
+-**
+-** Cell content is stored at the very end of the page and grows toward the
+-** beginning of the page.
+-**
+-** Unused space within the cell content area is collected into a linked list of
+-** freeblocks. Each freeblock is at least 4 bytes in size. The byte offset
+-** to the first freeblock is given in the header. Freeblocks occur in
+-** increasing order. Because a freeblock must be at least 4 bytes in size,
+-** any group of 3 or fewer unused bytes in the cell content area cannot
+-** exist on the freeblock chain. A group of 3 or fewer free bytes is called
+-** a fragment. The total number of bytes in all fragments is recorded.
+-** in the page header at offset 7.
+-**
+-** SIZE DESCRIPTION
+-** 2 Byte offset of the next freeblock
+-** 2 Bytes in this freeblock
+-**
+-** Cells are of variable length. Cells are stored in the cell content area at
+-** the end of the page. Pointers to the cells are in the cell pointer array
+-** that immediately follows the page header. Cells is not necessarily
+-** contiguous or in order, but cell pointers are contiguous and in order.
+-**
+-** Cell content makes use of variable length integers. A variable
+-** length integer is 1 to 9 bytes where the lower 7 bits of each
+-** byte are used. The integer consists of all bytes that have bit 8 set and
+-** the first byte with bit 8 clear. The most significant byte of the integer
+-** appears first. A variable-length integer may not be more than 9 bytes long.
+-** As a special case, all 8 bytes of the 9th byte are used as data. This
+-** allows a 64-bit integer to be encoded in 9 bytes.
+-**
+-** 0x00 becomes 0x00000000
+-** 0x7f becomes 0x0000007f
+-** 0x81 0x00 becomes 0x00000080
+-** 0x82 0x00 becomes 0x00000100
+-** 0x80 0x7f becomes 0x0000007f
+-** 0x8a 0x91 0xd1 0xac 0x78 becomes 0x12345678
+-** 0x81 0x81 0x81 0x81 0x01 becomes 0x10204081
+-**
+-** Variable length integers are used for rowids and to hold the number of
+-** bytes of key and data in a btree cell.
+-**
+-** The content of a cell looks like this:
+-**
+-** SIZE DESCRIPTION
+-** 4 Page number of the left child. Omitted if leaf flag is set.
+-** var Number of bytes of data. Omitted if the zerodata flag is set.
+-** var Number of bytes of key. Or the key itself if intkey flag is set.
+-** * Payload
+-** 4 First page of the overflow chain. Omitted if no overflow
+-**
+-** Overflow pages form a linked list. Each page except the last is completely
+-** filled with data (pagesize - 4 bytes). The last page can have as little
+-** as 1 byte of data.
+-**
+-** SIZE DESCRIPTION
+-** 4 Page number of next overflow page
+-** * Data
+-**
+-** Freelist pages come in two subtypes: trunk pages and leaf pages. The
+-** file header points to the first in a linked list of trunk page. Each trunk
+-** page points to multiple leaf pages. The content of a leaf page is
+-** unspecified. A trunk page looks like this:
+-**
+-** SIZE DESCRIPTION
+-** 4 Page number of next trunk page
+-** 4 Number of leaf pointers on this page
+-** * zero or more pages numbers of leaves
+-*/
+-
+-
+-/* The following value is the maximum cell size assuming a maximum page
+-** size give above.
+-*/
+-#define MX_CELL_SIZE(pBt) (pBt->pageSize-8)
+-
+-/* The maximum number of cells on a single page of the database. This
+-** assumes a minimum cell size of 6 bytes (4 bytes for the cell itself
+-** plus 2 bytes for the index to the cell in the page header). Such
+-** small cells will be rare, but they are possible.
+-*/
+-#define MX_CELL(pBt) ((pBt->pageSize-8)/6)
+-
+-/* Forward declarations */
+-typedef struct MemPage MemPage;
+-typedef struct BtLock BtLock;
+-
+-/*
+-** This is a magic string that appears at the beginning of every
+-** SQLite database in order to identify the file as a real database.
+-**
+-** You can change this value at compile-time by specifying a
+-** -DSQLITE_FILE_HEADER="..." on the compiler command-line. The
+-** header must be exactly 16 bytes including the zero-terminator so
+-** the string itself should be 15 characters long. If you change
+-** the header, then your custom library will not be able to read
+-** databases generated by the standard tools and the standard tools
+-** will not be able to read databases created by your custom library.
+-*/
+-#ifndef SQLITE_FILE_HEADER /* 123456789 123456 */
+-# define SQLITE_FILE_HEADER "SQLite format 3"
+-#endif
+-
+-/*
+-** Page type flags. An ORed combination of these flags appear as the
+-** first byte of on-disk image of every BTree page.
+-*/
+-#define PTF_INTKEY 0x01
+-#define PTF_ZERODATA 0x02
+-#define PTF_LEAFDATA 0x04
+-#define PTF_LEAF 0x08
+-
+-/*
+-** As each page of the file is loaded into memory, an instance of the following
+-** structure is appended and initialized to zero. This structure stores
+-** information about the page that is decoded from the raw file page.
+-**
+-** The pParent field points back to the parent page. This allows us to
+-** walk up the BTree from any leaf to the root. Care must be taken to
+-** unref() the parent page pointer when this page is no longer referenced.
+-** The pageDestructor() routine handles that chore.
+-**
+-** Access to all fields of this structure is controlled by the mutex
+-** stored in MemPage.pBt->mutex.
+-*/
+-struct MemPage {
+- u8 isInit; /* True if previously initialized. MUST BE FIRST! */
+- u8 nOverflow; /* Number of overflow cell bodies in aCell[] */
+- u8 intKey; /* True if intkey flag is set */
+- u8 leaf; /* True if leaf flag is set */
+- u8 hasData; /* True if this page stores data */
+- u8 hdrOffset; /* 100 for page 1. 0 otherwise */
+- u8 childPtrSize; /* 0 if leaf==1. 4 if leaf==0 */
+- u16 maxLocal; /* Copy of BtShared.maxLocal or BtShared.maxLeaf */
+- u16 minLocal; /* Copy of BtShared.minLocal or BtShared.minLeaf */
+- u16 cellOffset; /* Index in aData of first cell pointer */
+- u16 nFree; /* Number of free bytes on the page */
+- u16 nCell; /* Number of cells on this page, local and ovfl */
+- u16 maskPage; /* Mask for page offset */
+- struct _OvflCell { /* Cells that will not fit on aData[] */
+- u8 *pCell; /* Pointers to the body of the overflow cell */
+- u16 idx; /* Insert this cell before idx-th non-overflow cell */
+- } aOvfl[5];
+- BtShared *pBt; /* Pointer to BtShared that this page is part of */
+- u8 *aData; /* Pointer to disk image of the page data */
+- DbPage *pDbPage; /* Pager page handle */
+- Pgno pgno; /* Page number for this page */
+-};
++ pData = pLast->pData;
++#endif
++ walEncodeFrame(pWal, pLast->pgno, nTruncate, pData, aFrame);
++ /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */
++ rc = sqlite3OsWrite(pWal->pWalFd, aFrame, sizeof(aFrame), iOffset);
++ if( rc!=SQLITE_OK ){
++ return rc;
++ }
++ iOffset += WAL_FRAME_HDRSIZE;
++ rc = sqlite3OsWrite(pWal->pWalFd, pData, szPage, iOffset);
++ if( rc!=SQLITE_OK ){
++ return rc;
++ }
++ nLast++;
++ iOffset += szPage;
++ }
+
+-/*
+-** The in-memory image of a disk page has the auxiliary information appended
+-** to the end. EXTRA_SIZE is the number of bytes of space needed to hold
+-** that extra information.
+-*/
+-#define EXTRA_SIZE sizeof(MemPage)
++ rc = sqlite3OsSync(pWal->pWalFd, sync_flags);
++ }
+
+-/*
+-** A linked list of the following structures is stored at BtShared.pLock.
+-** Locks are added (or upgraded from READ_LOCK to WRITE_LOCK) when a cursor
+-** is opened on the table with root page BtShared.iTable. Locks are removed
+-** from this list when a transaction is committed or rolled back, or when
+-** a btree handle is closed.
+-*/
+-struct BtLock {
+- Btree *pBtree; /* Btree handle holding this lock */
+- Pgno iTable; /* Root page of table */
+- u8 eLock; /* READ_LOCK or WRITE_LOCK */
+- BtLock *pNext; /* Next in BtShared.pLock list */
+-};
++ /* Append data to the wal-index. It is not necessary to lock the
++ ** wal-index to do this as the SQLITE_SHM_WRITE lock held on the wal-index
++ ** guarantees that there are no other writers, and no data that may
++ ** be in use by existing readers is being overwritten.
++ */
++ iFrame = pWal->hdr.mxFrame;
++ for(p=pList; p && rc==SQLITE_OK; p=p->pDirty){
++ iFrame++;
++ rc = walIndexAppend(pWal, iFrame, p->pgno);
++ }
++ while( nLast>0 && rc==SQLITE_OK ){
++ iFrame++;
++ nLast--;
++ rc = walIndexAppend(pWal, iFrame, pLast->pgno);
++ }
+
+-/* Candidate values for BtLock.eLock */
+-#define READ_LOCK 1
+-#define WRITE_LOCK 2
++ if( rc==SQLITE_OK ){
++ /* Update the private copy of the header. */
++ pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16));
++ testcase( szPage<=32768 );
++ testcase( szPage>=65536 );
++ pWal->hdr.mxFrame = iFrame;
++ if( isCommit ){
++ pWal->hdr.iChange++;
++ pWal->hdr.nPage = nTruncate;
++ }
++ /* If this is a commit, update the wal-index header too. */
++ if( isCommit ){
++ walIndexWriteHdr(pWal);
++ pWal->iCallback = iFrame;
++ }
++ }
+
+-/* A Btree handle
+-**
+-** A database connection contains a pointer to an instance of
+-** this object for every database file that it has open. This structure
+-** is opaque to the database connection. The database connection cannot
+-** see the internals of this structure and only deals with pointers to
+-** this structure.
+-**
+-** For some database files, the same underlying database cache might be
+-** shared between multiple connections. In that case, each connection
+-** has it own instance of this object. But each instance of this object
+-** points to the same BtShared object. The database cache and the
+-** schema associated with the database file are all contained within
+-** the BtShared object.
+-**
+-** All fields in this structure are accessed under sqlite3.mutex.
+-** The pBt pointer itself may not be changed while there exists cursors
+-** in the referenced BtShared that point back to this Btree since those
+-** cursors have to do go through this Btree to find their BtShared and
+-** they often do so without holding sqlite3.mutex.
+-*/
+-struct Btree {
+- sqlite3 *db; /* The database connection holding this btree */
+- BtShared *pBt; /* Sharable content of this btree */
+- u8 inTrans; /* TRANS_NONE, TRANS_READ or TRANS_WRITE */
+- u8 sharable; /* True if we can share pBt with another db */
+- u8 locked; /* True if db currently has pBt locked */
+- int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */
+- int nBackup; /* Number of backup operations reading this btree */
+- Btree *pNext; /* List of other sharable Btrees from the same db */
+- Btree *pPrev; /* Back pointer of the same list */
+-#ifndef SQLITE_OMIT_SHARED_CACHE
+- BtLock lock; /* Object used to lock page 1 */
+-#endif
+-};
++ WALTRACE(("WAL%p: frame write %s\n", pWal, rc ? "failed" : "ok"));
++ return rc;
++}
+
+-/*
+-** Btree.inTrans may take one of the following values.
++/*
++** This routine is called to implement sqlite3_wal_checkpoint() and
++** related interfaces.
+ **
+-** If the shared-data extension is enabled, there may be multiple users
+-** of the Btree structure. At most one of these may open a write transaction,
+-** but any number may have active read transactions.
++** Obtain a CHECKPOINT lock and then backfill as much information as
++** we can from WAL into the database.
+ */
+-#define TRANS_NONE 0
+-#define TRANS_READ 1
+-#define TRANS_WRITE 2
++SQLITE_PRIVATE int sqlite3WalCheckpoint(
++ Wal *pWal, /* Wal connection */
++ int sync_flags, /* Flags to sync db file with (or 0) */
++ int nBuf, /* Size of temporary buffer */
++ u8 *zBuf /* Temporary buffer to use */
++){
++ int rc; /* Return code */
++ int isChanged = 0; /* True if a new wal-index header is loaded */
+
+-/*
+-** An instance of this object represents a single database file.
+-**
+-** A single database file can be in use as the same time by two
+-** or more database connections. When two or more connections are
+-** sharing the same database file, each connection has it own
+-** private Btree object for the file and each of those Btrees points
+-** to this one BtShared object. BtShared.nRef is the number of
+-** connections currently sharing this database file.
+-**
+-** Fields in this structure are accessed under the BtShared.mutex
+-** mutex, except for nRef and pNext which are accessed under the
+-** global SQLITE_MUTEX_STATIC_MASTER mutex. The pPager field
+-** may not be modified once it is initially set as long as nRef>0.
+-** The pSchema field may be set once under BtShared.mutex and
+-** thereafter is unchanged as long as nRef>0.
+-**
+-** isPending:
+-**
+-** If a BtShared client fails to obtain a write-lock on a database
+-** table (because there exists one or more read-locks on the table),
+-** the shared-cache enters 'pending-lock' state and isPending is
+-** set to true.
+-**
+-** The shared-cache leaves the 'pending lock' state when either of
+-** the following occur:
+-**
+-** 1) The current writer (BtShared.pWriter) concludes its transaction, OR
+-** 2) The number of locks held by other connections drops to zero.
+-**
+-** while in the 'pending-lock' state, no connection may start a new
+-** transaction.
+-**
+-** This feature is included to help prevent writer-starvation.
+-*/
+-struct BtShared {
+- Pager *pPager; /* The page cache */
+- sqlite3 *db; /* Database connection currently using this Btree */
+- BtCursor *pCursor; /* A list of all open cursors */
+- MemPage *pPage1; /* First page of the database */
+- u8 readOnly; /* True if the underlying file is readonly */
+- u8 pageSizeFixed; /* True if the page size can no longer be changed */
+- u8 secureDelete; /* True if secure_delete is enabled */
+- u8 initiallyEmpty; /* Database is empty at start of transaction */
+-#ifndef SQLITE_OMIT_AUTOVACUUM
+- u8 autoVacuum; /* True if auto-vacuum is enabled */
+- u8 incrVacuum; /* True if incr-vacuum is enabled */
+-#endif
+- u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */
+- u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */
+- u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */
+- u16 minLeaf; /* Minimum local payload in a LEAFDATA table */
+- u8 inTransaction; /* Transaction state */
+- u8 doNotUseWAL; /* If true, do not open write-ahead-log file */
+- u32 pageSize; /* Total number of bytes on a page */
+- u32 usableSize; /* Number of usable bytes on each page */
+- int nTransaction; /* Number of open transactions (read + write) */
+- u32 nPage; /* Number of pages in the database */
+- void *pSchema; /* Pointer to space allocated by sqlite3BtreeSchema() */
+- void (*xFreeSchema)(void*); /* Destructor for BtShared.pSchema */
+- sqlite3_mutex *mutex; /* Non-recursive mutex required to access this struct */
+- Bitvec *pHasContent; /* Set of pages moved to free-list this transaction */
+-#ifndef SQLITE_OMIT_SHARED_CACHE
+- int nRef; /* Number of references to this structure */
+- BtShared *pNext; /* Next on a list of sharable BtShared structs */
+- BtLock *pLock; /* List of locks held on this shared-btree struct */
+- Btree *pWriter; /* Btree with currently open write transaction */
+- u8 isExclusive; /* True if pWriter has an EXCLUSIVE lock on the db */
+- u8 isPending; /* If waiting for read-locks to clear */
+-#endif
+- u8 *pTmpSpace; /* BtShared.pageSize bytes of space for tmp use */
+-};
++ assert( pWal->ckptLock==0 );
+
+-/*
+-** An instance of the following structure is used to hold information
+-** about a cell. The parseCellPtr() function fills in this structure
+-** based on information extract from the raw disk page.
+-*/
+-typedef struct CellInfo CellInfo;
+-struct CellInfo {
+- u8 *pCell; /* Pointer to the start of cell content */
+- i64 nKey; /* The key for INTKEY tables, or number of bytes in key */
+- u32 nData; /* Number of bytes of data */
+- u32 nPayload; /* Total amount of payload */
+- u16 nHeader; /* Size of the cell content header in bytes */
+- u16 nLocal; /* Amount of payload held locally */
+- u16 iOverflow; /* Offset to overflow page number. Zero if no overflow */
+- u16 nSize; /* Size of the cell content on the main b-tree page */
+-};
++ WALTRACE(("WAL%p: checkpoint begins\n", pWal));
++ rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
++ if( rc ){
++ /* Usually this is SQLITE_BUSY meaning that another thread or process
++ ** is already running a checkpoint, or maybe a recovery. But it might
++ ** also be SQLITE_IOERR. */
++ return rc;
++ }
++ pWal->ckptLock = 1;
+
+-/*
+-** Maximum depth of an SQLite B-Tree structure. Any B-Tree deeper than
+-** this will be declared corrupt. This value is calculated based on a
+-** maximum database size of 2^31 pages a minimum fanout of 2 for a
+-** root-node and 3 for all other internal nodes.
+-**
+-** If a tree that appears to be taller than this is encountered, it is
+-** assumed that the database is corrupt.
+-*/
+-#define BTCURSOR_MAX_DEPTH 20
++ /* Copy data from the log to the database file. */
++ rc = walIndexReadHdr(pWal, &isChanged);
++ if( rc==SQLITE_OK ){
++ rc = walCheckpoint(pWal, sync_flags, nBuf, zBuf);
++ }
++ if( isChanged ){
++ /* If a new wal-index header was loaded before the checkpoint was
++ ** performed, then the pager-cache associated with pWal is now
++ ** out of date. So zero the cached wal-index header to ensure that
++ ** next time the pager opens a snapshot on this database it knows that
++ ** the cache needs to be reset.
++ */
++ memset(&pWal->hdr, 0, sizeof(WalIndexHdr));
++ }
+
+-/*
+-** A cursor is a pointer to a particular entry within a particular
+-** b-tree within a database file.
+-**
+-** The entry is identified by its MemPage and the index in
+-** MemPage.aCell[] of the entry.
+-**
+-** A single database file can shared by two more database connections,
+-** but cursors cannot be shared. Each cursor is associated with a
+-** particular database connection identified BtCursor.pBtree.db.
+-**
+-** Fields in this structure are accessed under the BtShared.mutex
+-** found at self->pBt->mutex.
++ /* Release the locks. */
++ walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1);
++ pWal->ckptLock = 0;
++ WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok"));
++ return rc;
++}
++
++/* Return the value to pass to a sqlite3_wal_hook callback, the
++** number of frames in the WAL at the point of the last commit since
++** sqlite3WalCallback() was called. If no commits have occurred since
++** the last call, then return 0.
+ */
+-struct BtCursor {
+- Btree *pBtree; /* The Btree to which this cursor belongs */
+- BtShared *pBt; /* The BtShared this cursor points to */
+- BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */
+- struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */
+- Pgno pgnoRoot; /* The root page of this tree */
+- sqlite3_int64 cachedRowid; /* Next rowid cache. 0 means not valid */
+- CellInfo info; /* A parse of the cell we are pointing at */
+- u8 wrFlag; /* True if writable */
+- u8 atLast; /* Cursor pointing to the last entry */
+- u8 validNKey; /* True if info.nKey is valid */
+- u8 eState; /* One of the CURSOR_XXX constants (see below) */
+- void *pKey; /* Saved key that was cursor's last known position */
+- i64 nKey; /* Size of pKey, or last integer key */
+- int skipNext; /* Prev() is noop if negative. Next() is noop if positive */
+-#ifndef SQLITE_OMIT_INCRBLOB
+- u8 isIncrblobHandle; /* True if this cursor is an incr. io handle */
+- Pgno *aOverflow; /* Cache of overflow page locations */
+-#endif
+- i16 iPage; /* Index of current page in apPage */
+- MemPage *apPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */
+- u16 aiIdx[BTCURSOR_MAX_DEPTH]; /* Current index in apPage[i] */
+-};
++SQLITE_PRIVATE int sqlite3WalCallback(Wal *pWal){
++ u32 ret = 0;
++ if( pWal ){
++ ret = pWal->iCallback;
++ pWal->iCallback = 0;
++ }
++ return (int)ret;
++}
+
+ /*
+-** Potential values for BtCursor.eState.
+-**
+-** CURSOR_VALID:
+-** Cursor points to a valid entry. getPayload() etc. may be called.
++** This function is called to change the WAL subsystem into or out
++** of locking_mode=EXCLUSIVE.
+ **
+-** CURSOR_INVALID:
+-** Cursor does not point to a valid entry. This can happen (for example)
+-** because the table is empty or because BtreeCursorFirst() has not been
+-** called.
++** If op is zero, then attempt to change from locking_mode=EXCLUSIVE
++** into locking_mode=NORMAL. This means that we must acquire a lock
++** on the pWal->readLock byte. If the WAL is already in locking_mode=NORMAL
++** or if the acquisition of the lock fails, then return 0. If the
++** transition out of exclusive-mode is successful, return 1. This
++** operation must occur while the pager is still holding the exclusive
++** lock on the main database file.
+ **
+-** CURSOR_REQUIRESEEK:
+-** The table that this cursor was opened on still exists, but has been
+-** modified since the cursor was last used. The cursor position is saved
+-** in variables BtCursor.pKey and BtCursor.nKey. When a cursor is in
+-** this state, restoreCursorPosition() can be called to attempt to
+-** seek the cursor to the saved position.
++** If op is one, then change from locking_mode=NORMAL into
++** locking_mode=EXCLUSIVE. This means that the pWal->readLock must
++** be released. Return 1 if the transition is made and 0 if the
++** WAL is already in exclusive-locking mode - meaning that this
++** routine is a no-op. The pager must already hold the exclusive lock
++** on the main database file before invoking this operation.
+ **
+-** CURSOR_FAULT:
+-** A unrecoverable error (an I/O error or a malloc failure) has occurred
+-** on a different connection that shares the BtShared cache with this
+-** cursor. The error has left the cache in an inconsistent state.
+-** Do nothing else with this cursor. Any attempt to use the cursor
+-** should return the error code stored in BtCursor.skip
++** If op is negative, then do a dry-run of the op==1 case but do
++** not actually change anything. The pager uses this to see if it
++** should acquire the database exclusive lock prior to invoking
++** the op==1 case.
+ */
+-#define CURSOR_INVALID 0
+-#define CURSOR_VALID 1
+-#define CURSOR_REQUIRESEEK 2
+-#define CURSOR_FAULT 3
++SQLITE_PRIVATE int sqlite3WalExclusiveMode(Wal *pWal, int op){
++ int rc;
++ assert( pWal->writeLock==0 );
+
+-/*
+-** The database page the PENDING_BYTE occupies. This page is never used.
+-*/
+-# define PENDING_BYTE_PAGE(pBt) PAGER_MJ_PGNO(pBt)
++ /* pWal->readLock is usually set, but might be -1 if there was a
++ ** prior error while attempting to acquire are read-lock. This cannot
++ ** happen if the connection is actually in exclusive mode (as no xShmLock
++ ** locks are taken in this case). Nor should the pager attempt to
++ ** upgrade to exclusive-mode following such an error.
++ */
++ assert( pWal->readLock>=0 || pWal->lockError );
++ assert( pWal->readLock>=0 || (op<=0 && pWal->exclusiveMode==0) );
+
+-/*
+-** These macros define the location of the pointer-map entry for a
+-** database page. The first argument to each is the number of usable
+-** bytes on each page of the database (often 1024). The second is the
+-** page number to look up in the pointer map.
+-**
+-** PTRMAP_PAGENO returns the database page number of the pointer-map
+-** page that stores the required pointer. PTRMAP_PTROFFSET returns
+-** the offset of the requested map entry.
+-**
+-** If the pgno argument passed to PTRMAP_PAGENO is a pointer-map page,
+-** then pgno is returned. So (pgno==PTRMAP_PAGENO(pgsz, pgno)) can be
+-** used to test if pgno is a pointer-map page. PTRMAP_ISPAGE implements
+-** this test.
+-*/
+-#define PTRMAP_PAGENO(pBt, pgno) ptrmapPageno(pBt, pgno)
+-#define PTRMAP_PTROFFSET(pgptrmap, pgno) (5*(pgno-pgptrmap-1))
+-#define PTRMAP_ISPAGE(pBt, pgno) (PTRMAP_PAGENO((pBt),(pgno))==(pgno))
++ if( op==0 ){
++ if( pWal->exclusiveMode ){
++ pWal->exclusiveMode = 0;
++ if( walLockShared(pWal, WAL_READ_LOCK(pWal->readLock))!=SQLITE_OK ){
++ pWal->exclusiveMode = 1;
++ }
++ rc = pWal->exclusiveMode==0;
++ }else{
++ /* Already in locking_mode=NORMAL */
++ rc = 0;
++ }
++ }else if( op>0 ){
++ assert( pWal->exclusiveMode==0 );
++ assert( pWal->readLock>=0 );
++ walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock));
++ pWal->exclusiveMode = 1;
++ rc = 1;
++ }else{
++ rc = pWal->exclusiveMode==0;
++ }
++ return rc;
++}
++
++#endif /* #ifndef SQLITE_OMIT_WAL */
+
++/************** End of wal.c *************************************************/
++/************** Begin file btmutex.c *****************************************/
+ /*
+-** The pointer map is a lookup table that identifies the parent page for
+-** each child page in the database file. The parent page is the page that
+-** contains a pointer to the child. Every page in the database contains
+-** 0 or 1 parent pages. (In this context 'database page' refers
+-** to any page that is not part of the pointer map itself.) Each pointer map
+-** entry consists of a single byte 'type' and a 4 byte parent page number.
+-** The PTRMAP_XXX identifiers below are the valid types.
+-**
+-** The purpose of the pointer map is to facility moving pages from one
+-** position in the file to another as part of autovacuum. When a page
+-** is moved, the pointer in its parent must be updated to point to the
+-** new location. The pointer map is used to locate the parent page quickly.
+-**
+-** PTRMAP_ROOTPAGE: The database page is a root-page. The page-number is not
+-** used in this case.
++** 2007 August 27
+ **
+-** PTRMAP_FREEPAGE: The database page is an unused (free) page. The page-number
+-** is not used in this case.
++** The author disclaims copyright to this source code. In place of
++** a legal notice, here is a blessing:
+ **
+-** PTRMAP_OVERFLOW1: The database page is the first page in a list of
+-** overflow pages. The page number identifies the page that
+-** contains the cell with a pointer to this overflow page.
++** May you do good and not evil.
++** May you find forgiveness for yourself and forgive others.
++** May you share freely, never taking more than you give.
+ **
+-** PTRMAP_OVERFLOW2: The database page is the second or later page in a list of
+-** overflow pages. The page-number identifies the previous
+-** page in the overflow page list.
++*************************************************************************
+ **
+-** PTRMAP_BTREE: The database page is a non-root btree page. The page number
+-** identifies the parent page in the btree.
+-*/
+-#define PTRMAP_ROOTPAGE 1
+-#define PTRMAP_FREEPAGE 2
+-#define PTRMAP_OVERFLOW1 3
+-#define PTRMAP_OVERFLOW2 4
+-#define PTRMAP_BTREE 5
+-
+-/* A bunch of assert() statements to check the transaction state variables
+-** of handle p (type Btree*) are internally consistent.
+-*/
+-#define btreeIntegrity(p) \
+- assert( p->pBt->inTransaction!=TRANS_NONE || p->pBt->nTransaction==0 ); \
+- assert( p->pBt->inTransaction>=p->inTrans );
+-
+-
+-/*
+-** The ISAUTOVACUUM macro is used within balance_nonroot() to determine
+-** if the database supports auto-vacuum or not. Because it is used
+-** within an expression that is an argument to another macro
+-** (sqliteMallocRaw), it is not possible to use conditional compilation.
+-** So, this macro is defined instead.
+-*/
+-#ifndef SQLITE_OMIT_AUTOVACUUM
+-#define ISAUTOVACUUM (pBt->autoVacuum)
+-#else
+-#define ISAUTOVACUUM 0
+-#endif
+-
+-
+-/*
+-** This structure is passed around through all the sanity checking routines
+-** in order to keep track of some global state information.
+-*/
+-typedef struct IntegrityCk IntegrityCk;
+-struct IntegrityCk {
+- BtShared *pBt; /* The tree being checked out */
+- Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */
+- Pgno nPage; /* Number of pages in the database */
+- int *anRef; /* Number of times each page is referenced */
+- int mxErr; /* Stop accumulating errors when this reaches zero */
+- int nErr; /* Number of messages written to zErrMsg so far */
+- int mallocFailed; /* A memory allocation error has occurred */
+- StrAccum errMsg; /* Accumulate the error message text here */
+-};
+-
+-/*
+-** Read or write a two- and four-byte big-endian integer values.
++** This file contains code used to implement mutexes on Btree objects.
++** This code really belongs in btree.c. But btree.c is getting too
++** big and we want to break it down some. This packaged seemed like
++** a good breakout.
+ */
+-#define get2byte(x) ((x)[0]<<8 | (x)[1])
+-#define put2byte(p,v) ((p)[0] = (u8)((v)>>8), (p)[1] = (u8)(v))
+-#define get4byte sqlite3Get4byte
+-#define put4byte sqlite3Put4byte
+-
+-/************** End of btreeInt.h ********************************************/
+-/************** Continuing where we left off in btmutex.c ********************/
+ #ifndef SQLITE_OMIT_SHARED_CACHE
+ #if SQLITE_THREADSAFE
+
+@@ -85120,60 +85817,6 @@
+
+ #ifndef SQLITE_OMIT_SCHEMA_PRAGMAS
+ /*
+- ** PRAGMA proc_list
+- **
+- ** Return a single row for each procedure, the returned data set are:
+- **
+- ** name: Procedure name
+- ** is_aggregate: True is procedure is an aggregate
+- ** nargs: Number of arguments of the procedure, or -1 if unlimited
+- ** spe_name: Specific name (unique procedure name)
+- */
+- if( sqlite3StrICmp(zLeft, "proc_list")==0 ){
+- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+-
+- sqlite3VdbeSetNumCols(v, 4);
+- pParse->nMem = 4;
+- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "name", SQLITE_STATIC);
+- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "is_aggregate", SQLITE_STATIC);
+- sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "nargs", SQLITE_STATIC);
+- sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "spe_name", SQLITE_STATIC);
+- int j;
+- for(j=0; j<ArraySize(db->aFunc.a); j++){
+- FuncDef *func;
+- for (func =db->aFunc.a[j]; func; func = func->pNext) {
+- char *sname;
+- int size;
+- size = strlen (func->zName) + 25;
+- sname = sqlite3_malloc (sizeof (char) * size);
+- snprintf (sname, size-1, "%s_%d_%d", func->zName, func->nArg, func->iPrefEnc);
+- sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, func->zName, 0);
+- sqlite3VdbeAddOp2(v, OP_Integer, func->xFinalize ? 1 : 0, 2);
+- sqlite3VdbeAddOp2(v, OP_Integer, func->nArg, 3);
+- sqlite3VdbeAddOp4(v, OP_String8, 0, 4, 0, sname, 0);
+- sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 4);
+- sqlite3_free (sname);
+- }
+- }
+- for(j=0; j<ArraySize(sqlite3GlobalFunctions.a); j++){
+- FuncDef *func;
+- for (func =sqlite3GlobalFunctions.a[j]; func; func = func->pNext) {
+- char *sname;
+- int size;
+- size = strlen (func->zName) + 25;
+- sname = sqlite3_malloc (sizeof (char) * size);
+- snprintf (sname, size-1, "%s_%d_%d", func->zName, func->nArg, func->iPrefEnc);
+- sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, func->zName, 0);
+- sqlite3VdbeAddOp2(v, OP_Integer, func->xFinalize ? 1 : 0, 2);
+- sqlite3VdbeAddOp2(v, OP_Integer, func->nArg, 3);
+- sqlite3VdbeAddOp4(v, OP_String8, 0, 4, 0, sname, 0);
+- sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 4);
+- sqlite3_free (sname);
+- }
+- }
+- }else
+-
+- /*
+ ** PRAGMA table_info(<table>)
+ **
+ ** Return a single row for each column of the named table. The columns of
+@@ -85795,6 +86438,24 @@
+ sqlite3_rekey(db, zKey, i/2);
+ }
+ }else
++/** BEGIN CRYPTO **/
++ if( sqlite3StrICmp(zLeft, "cipher")==0 && zRight ){
++ extern int codec_set_cipher_name(sqlite3*, int, const char *, int);
++ codec_set_cipher_name(db,0,zRight,2); // change cipher for both
++ }else
++ if( sqlite3StrICmp(zLeft, "rekey_cipher")==0 && zRight ){
++ extern int codec_set_cipher_name(sqlite3*, int, const char *, int);
++ codec_set_cipher_name(db,0,zRight,1); // change write cipher only
++ }else
++ if( sqlite3StrICmp(zLeft, "kdf_iter")==0 && zRight ){
++ extern int codec_set_kdf_iter(sqlite3*, int, int, int);
++ codec_set_kdf_iter(db,0,atoi(zRight),2); // change cipher for both
++ }else
++ if( sqlite3StrICmp(zLeft, "rekey_kdf_iter")==0 && zRight ){
++ extern int codec_set_kdf_iter(sqlite3*, int, int, int);
++ codec_set_kdf_iter(db,0,atoi(zRight),1); // change write cipher only
++ }else
++/** END CRYPTO **/
+ #endif
+ #if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD)
+ if( sqlite3StrICmp(zLeft, "activate_extensions")==0 ){
diff --git a/providers/sqlcipher/sqlcipher_specs_add_column.xml.in b/providers/sqlcipher/sqlcipher_specs_add_column.xml.in
new file mode 120000
index 0000000..a255afa
--- /dev/null
+++ b/providers/sqlcipher/sqlcipher_specs_add_column.xml.in
@@ -0,0 +1 @@
+../sqlite/sqlite_specs_add_column.xml.in
\ No newline at end of file
diff --git a/providers/sqlcipher/sqlcipher_specs_auth.xml.in b/providers/sqlcipher/sqlcipher_specs_auth.xml.in
new file mode 100644
index 0000000..bd7df0f
--- /dev/null
+++ b/providers/sqlcipher/sqlcipher_specs_auth.xml.in
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<data-set-spec>
+ <parameters>
+ <parameter id="PASSWORD" _name="Passphrase" _descr="Encryption passphrase" gdatype="gchararray" nullok="TRUE" plugin="string:HIDDEN=true"/>
+ </parameters>
+</data-set-spec>
diff --git a/providers/sqlcipher/sqlcipher_specs_create_db.xml.in b/providers/sqlcipher/sqlcipher_specs_create_db.xml.in
new file mode 120000
index 0000000..c8f1a76
--- /dev/null
+++ b/providers/sqlcipher/sqlcipher_specs_create_db.xml.in
@@ -0,0 +1 @@
+../sqlite/sqlite_specs_create_db.xml.in
\ No newline at end of file
diff --git a/providers/sqlcipher/sqlcipher_specs_create_index.xml.in b/providers/sqlcipher/sqlcipher_specs_create_index.xml.in
new file mode 120000
index 0000000..6c95c53
--- /dev/null
+++ b/providers/sqlcipher/sqlcipher_specs_create_index.xml.in
@@ -0,0 +1 @@
+../sqlite/sqlite_specs_create_index.xml.in
\ No newline at end of file
diff --git a/providers/sqlcipher/sqlcipher_specs_create_table.xml.in b/providers/sqlcipher/sqlcipher_specs_create_table.xml.in
new file mode 120000
index 0000000..3cef9c3
--- /dev/null
+++ b/providers/sqlcipher/sqlcipher_specs_create_table.xml.in
@@ -0,0 +1 @@
+../sqlite/sqlite_specs_create_table.xml.in
\ No newline at end of file
diff --git a/providers/sqlcipher/sqlcipher_specs_create_view.xml.in b/providers/sqlcipher/sqlcipher_specs_create_view.xml.in
new file mode 120000
index 0000000..cdfeefc
--- /dev/null
+++ b/providers/sqlcipher/sqlcipher_specs_create_view.xml.in
@@ -0,0 +1 @@
+../sqlite/sqlite_specs_create_view.xml.in
\ No newline at end of file
diff --git a/providers/sqlcipher/sqlcipher_specs_drop_db.xml.in b/providers/sqlcipher/sqlcipher_specs_drop_db.xml.in
new file mode 120000
index 0000000..3a6f43d
--- /dev/null
+++ b/providers/sqlcipher/sqlcipher_specs_drop_db.xml.in
@@ -0,0 +1 @@
+../sqlite/sqlite_specs_drop_db.xml.in
\ No newline at end of file
diff --git a/providers/sqlcipher/sqlcipher_specs_drop_index.xml.in b/providers/sqlcipher/sqlcipher_specs_drop_index.xml.in
new file mode 120000
index 0000000..2c6daab
--- /dev/null
+++ b/providers/sqlcipher/sqlcipher_specs_drop_index.xml.in
@@ -0,0 +1 @@
+../sqlite/sqlite_specs_drop_index.xml.in
\ No newline at end of file
diff --git a/providers/sqlcipher/sqlcipher_specs_drop_table.xml.in b/providers/sqlcipher/sqlcipher_specs_drop_table.xml.in
new file mode 120000
index 0000000..57b73f4
--- /dev/null
+++ b/providers/sqlcipher/sqlcipher_specs_drop_table.xml.in
@@ -0,0 +1 @@
+../sqlite/sqlite_specs_drop_table.xml.in
\ No newline at end of file
diff --git a/providers/sqlcipher/sqlcipher_specs_drop_view.xml.in b/providers/sqlcipher/sqlcipher_specs_drop_view.xml.in
new file mode 120000
index 0000000..ba154ff
--- /dev/null
+++ b/providers/sqlcipher/sqlcipher_specs_drop_view.xml.in
@@ -0,0 +1 @@
+../sqlite/sqlite_specs_drop_view.xml.in
\ No newline at end of file
diff --git a/providers/sqlcipher/sqlcipher_specs_dsn.xml.in b/providers/sqlcipher/sqlcipher_specs_dsn.xml.in
new file mode 120000
index 0000000..5b59709
--- /dev/null
+++ b/providers/sqlcipher/sqlcipher_specs_dsn.xml.in
@@ -0,0 +1 @@
+../sqlite/sqlite_specs_dsn.xml.in
\ No newline at end of file
diff --git a/providers/sqlcipher/sqlcipher_specs_rename_table.xml.in b/providers/sqlcipher/sqlcipher_specs_rename_table.xml.in
new file mode 120000
index 0000000..63874b7
--- /dev/null
+++ b/providers/sqlcipher/sqlcipher_specs_rename_table.xml.in
@@ -0,0 +1 @@
+../sqlite/sqlite_specs_rename_table.xml.in
\ No newline at end of file
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]