--- /dev/null
+/*
+ * Copyright (c) 2008,2011-2012,2014 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+//
+// sqlite++ - C++ interface to SQLite3
+//
+#include "sqlite++.h"
+#include <stdexcept>
+#include <security_utilities/cfutilities.h>
+
+
+//@@@
+// From cssmapple.h - layering break
+// Where should this go?
+//@@@
+#define errSecErrnoBase 100000
+#define errSecErrnoLimit 100255
+
+
+namespace Security {
+namespace SQLite3 {
+
+
+//
+// Our exception object
+//
+void Error::check(int err)
+{
+ if (err != SQLITE_OK)
+ throw Error(err);
+}
+
+Error::Error(Database &db)
+ : error(db.errcode()), message(db.errmsg())
+{
+ SECURITY_EXCEPTION_THROW_SQLITE(this, error, (char*)message.c_str());
+}
+
+void Error::throwMe(int err)
+{
+ throw Error(err);
+}
+
+OSStatus Error::osStatus() const
+{
+ return unixError() + errSecErrnoBase;
+}
+
+int Error::unixError() const
+{
+ switch (error) {
+ case SQLITE_PERM:
+ case SQLITE_READONLY:
+ case SQLITE_AUTH:
+ return EACCES;
+ case SQLITE_BUSY:
+ return EAGAIN;
+ case SQLITE_NOMEM:
+ return ENOMEM;
+ case SQLITE_IOERR:
+ return EIO;
+ case SQLITE_FULL:
+ return ENOSPC;
+ case SQLITE_TOOBIG:
+ return EFBIG;
+ case SQLITE_MISMATCH:
+ case SQLITE_MISUSE:
+ return EINVAL;
+ case SQLITE_NOLFS:
+ return ENOTSUP;
+ case SQLITE_RANGE:
+ return EDOM;
+ default:
+ return -1;
+ }
+}
+
+
+//
+// Database objects
+//
+Database::Database(const char *path, int flags, bool lenient /* = false */)
+ : mMutex(Mutex::recursive)
+{
+ try {
+ int rc = ::sqlite3_open_v2(path, &mDb, flags, NULL);
+ if (rc != SQLITE_OK && lenient) { // silent open failure
+ sqlite3_close(mDb); // ditch useless Db object
+ mDb = NULL; // indicate failure
+ return;
+ }
+ check(rc);
+ check(::sqlite3_extended_result_codes(mDb, true));
+ mOpenFlags = flags;
+ } catch (...) {
+ sqlite3_close(mDb); // allocated even if open fails(!)
+ throw;
+ }
+}
+
+Database::~Database()
+{
+ this->close();
+}
+
+void Database::close()
+{
+ if (mDb)
+ check(::sqlite3_close(mDb));
+}
+
+
+int Database::execute(const char *text, bool strict /* = true */)
+{
+ StLock<Mutex> _(mMutex);
+
+ int rc = ::sqlite3_exec(mDb, text, NULL, NULL, NULL);
+ if (strict)
+ check(rc);
+ return rc;
+}
+
+
+void Database::busyDelay(int ms)
+{
+ StLock<Mutex> _(mMutex);
+
+ check(::sqlite3_busy_timeout(mDb, ms));
+}
+
+
+void Database::check(int err)
+{
+ if (err)
+ throw Error(*this);
+}
+
+
+bool Database::empty()
+{
+ return value("select count(*) from sqlite_master;", 0) == 0;
+}
+
+
+int Database::errcode()
+{
+ StLock<Mutex> _(mMutex);
+
+ return sqlite3_errcode(mDb);
+}
+
+
+
+const char *Database::errmsg()
+{
+ StLock<Mutex> _(mMutex);
+
+ return sqlite3_errmsg(mDb);
+}
+
+
+
+bool Database::inTransaction()
+{
+ StLock<Mutex> _(mMutex);
+
+ return !::sqlite3_get_autocommit(mDb);
+}
+
+
+
+int64 Database::lastInsert()
+{
+ StLock<Mutex> _(mMutex);
+
+ return ::sqlite3_last_insert_rowid(mDb);
+}
+
+int Database::changes()
+{
+ StLock<Mutex> _(mMutex);
+
+ return ::sqlite3_changes(mDb);
+}
+
+void Database::interrupt()
+{
+ StLock<Mutex> _(mMutex);
+
+ ::sqlite3_interrupt(mDb);
+}
+
+//
+// Transaction managers
+//
+Transaction::Transaction(Database &db, Type type, const char *name)
+ : database(db), mName(name ? name : "")
+{
+ switch (type) {
+ case deferred: xactCommand("BEGIN DEFERRED"); break;
+ case immediate: xactCommand("BEGIN IMMEDIATE"); break;
+ case exclusive: xactCommand("BEGIN EXCLUSIVE"); break;
+ }
+}
+
+Transaction::~Transaction()
+{
+ if (database.inTransaction())
+ abort();
+}
+
+void Transaction::commit()
+{
+ xactCommand("COMMIT");
+}
+
+void Transaction::abort()
+{
+ xactCommand("ROLLBACK");
+}
+
+void Transaction::xactCommand(const string &cmd)
+{
+ database.execute(cmd + " TRANSACTION " + mName + ";");
+}
+
+
+//
+// Statement objects
+//
+Statement::Statement(Database &db, const char *text)
+ : StLock<Mutex>(db.mMutex), database(db), mStmt(NULL)
+{
+ this->query(text);
+}
+
+Statement::Statement(Database &db)
+ : StLock<Mutex>(db.mMutex), database(db), mStmt(NULL)
+{ }
+
+void Statement::query(const char *text)
+{
+ this->close();
+ const char *tail;
+ check(::sqlite3_prepare_v2(database.sql(), text, -1, &mStmt, &tail));
+ if (*tail)
+ throw std::logic_error("multiple statements");
+}
+
+void Statement::close()
+{
+ // Sqlite3_finalize will return an error if the Statement (executed and) failed.
+ // So we eat any error code here, since we can't tell "genuine" errors apart from
+ // errors inherited from the Statement execution.
+ if (mStmt)
+ ::sqlite3_finalize(mStmt);
+ mStmt = NULL;
+}
+
+Statement::~Statement()
+{
+ this->close();
+}
+
+
+void Statement::unbind()
+{
+ check(::sqlite3_clear_bindings(mStmt));
+}
+
+void Statement::reset()
+{
+ check(::sqlite3_reset(mStmt));
+}
+
+
+int Statement::step()
+{
+ return ::sqlite3_step(mStmt);
+}
+
+void Statement::execute()
+{
+ switch (int rc = this->step()) {
+ case SQLITE_DONE:
+ case SQLITE_OK:
+ break;
+ default:
+ check(rc);
+ }
+}
+
+bool Statement::nextRow()
+{
+ switch (int rc = this->step()) {
+ case SQLITE_ROW:
+ return true;
+ case SQLITE_DONE:
+ return false;
+ default:
+ check(rc);
+ return false;
+ }
+}
+
+
+//
+// Binding gluons.
+//
+Statement::Binding Statement::bind(const char *name) const
+{
+ if (int ix = ::sqlite3_bind_parameter_index(mStmt, name))
+ return Binding(*this, ix);
+ else
+ throw std::logic_error("unknown parameter name");
+}
+
+void Statement::Binding::null()
+{
+ statement.check(::sqlite3_bind_null(statement.sql(), index));
+}
+
+void Statement::Binding::operator = (const Value &value)
+{
+ statement.check(::sqlite3_bind_value(statement.sql(), index, value.sql()));
+}
+
+void Statement::Binding::operator = (int value)
+{
+ statement.check(::sqlite3_bind_int(statement.sql(), index, value));
+}
+
+void Statement::Binding::operator = (sqlite3_int64 value)
+{
+ statement.check(::sqlite3_bind_int64(statement.sql(), index, value));
+}
+
+void Statement::Binding::integer(sqlite3_int64 value)
+{
+ statement.check(::sqlite3_bind_int64(statement.sql(), index, value));
+}
+
+void Statement::Binding::operator = (double value)
+{
+ statement.check(::sqlite3_bind_double(statement.sql(), index, value));
+}
+
+void Statement::Binding::operator = (const char *value)
+{
+ if (value == NULL)
+ this->null();
+ else
+ statement.check(::sqlite3_bind_text(statement.sql(), index,
+ ::strdup(value), -1, ::free));
+}
+
+void Statement::Binding::operator = (const std::string &value)
+{
+ statement.check(::sqlite3_bind_text(statement.sql(), index,
+ ::strdup(value.c_str()), -1, ::free));
+}
+
+void Statement::Binding::blob(const void *data, size_t length, bool shared /* = false */)
+{
+ if (data == NULL)
+ this->null();
+ else if (shared) {
+ statement.check(::sqlite3_bind_blob(statement.sql(), index, data, (int)length, NULL));
+ } else if (void *copy = ::malloc(length)) {
+ ::memcpy(copy, data, length);
+ statement.check(::sqlite3_bind_blob(statement.sql(), index,
+ copy, (int)length, ::free));
+ } else
+ throw std::bad_alloc();
+}
+
+void Statement::Binding::operator = (CFDataRef data)
+{
+ if (data)
+ this->blob(CFDataGetBytePtr(data), CFDataGetLength(data));
+ else
+ this->null();
+}
+
+void Statement::Binding::operator = (CFStringRef value)
+{
+ if (value)
+ *this = cfString(value).c_str();
+ else
+ this->null();
+}
+
+const char *Statement::Binding::name() const
+{
+ return sqlite3_bind_parameter_name(statement.sql(), index);
+}
+
+
+//
+// Row/column results
+//
+const char *Statement::Result::name() const
+{
+ return sqlite3_column_name(statement.sql(), index);
+}
+
+CFDataRef Statement::Result::data() const
+{
+ switch (this->type()) {
+ case SQLITE_NULL:
+ return NULL;
+ case SQLITE_BLOB:
+ return makeCFData(this->blob(), this->length());
+ default:
+ throw Error(SQLITE_MISMATCH, "Retrieving data() of non-Blob");
+ }
+}
+
+
+} // SQLite3
+} // Security