diff --git a/HACKING b/HACKING index 43308fd6..ecb66125 100644 --- a/HACKING +++ b/HACKING @@ -140,10 +140,12 @@ Building a Release - upload new files to sf.net - remove files from www.estamos.de so that downloads access sf.net - update entries on the web about the release: - http://maemo.org/downloads/product/syncevolution/ + http://maemo.org/downloads/product/OS2006/syncevolution/ + http://maemo.org/downloads/product/OS2007/syncevolution/ http://www.modmyiphone.com/ (?) http://www.estamos.de/blog/wp-admin http://www.estamos.de/projects/SyncEvolution/Roadmap.html + http://freshmeat.net/projects/syncevolution/ Compiling for Maemo @@ -195,6 +197,13 @@ Compiling for Mac OS X /configure --with-sync4j-src= --enable-addressbook make CFLAGS="-isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch ppc -arch i386" \ LDFLAGS="-Wl,-syslibroot,/Developer/SDKs/MacOSX10.4u.sdk -arch ppc -arch i386" + + ./configure --enable-addressbook \ + CXXFLAGS="-O -g -arch i386 -arch ppc" \ + CFLAGS="-O -g -arch i386 -arch ppc" \ + LDFLAGS="-arch i386 -arch ppc" --disable-dependency-tracking + + TODO: --disable-dependency-tracking an client-api weiterreichen Fine-grained memory checking: MallocStackLogging=1 MallocStackLoggingNoCompact=1 \ diff --git a/configure.in b/configure.in index 427432de..e4420950 100644 --- a/configure.in +++ b/configure.in @@ -240,21 +240,21 @@ AC_ARG_ENABLE(addressbook, AS_HELP_STRING([--enable-addressbook], [enable access enable_evo="no" enable_any="no" if test "$enable_ebook" = "yes"; then - test "x${EBOOKFOUND}" == "xyes" || AC_MSG_ERROR(["--enable-ebook requires pkg-config information for libebook, which was not found"]) + test "x${EBOOKFOUND}" == "xyes" || AC_MSG_ERROR([--enable-ebook requires pkg-config information for libebook, which was not found]) AC_DEFINE(ENABLE_EBOOK, 1, [libebook available]) enable_evo="yes" enable_any="yes" fi if test "$enable_ecal" = "yes"; then - test "x${ECALFOUND}" == "xyes" || AC_MSG_ERROR(["--enable-ecal requires pkg-config information for libecal, which was not found"]) + test "x${ECALFOUND}" == "xyes" || AC_MSG_ERROR([--enable-ecal requires pkg-config information for libecal, which was not found"]) AC_DEFINE(ENABLE_ECAL, 1, [libecal available]) enable_evo="yes" enable_any="yes" fi if test "$enable_sqlite" = "yes"; then - test "x${SQLITEFOUND}" == "xyes" || AC_MSG_ERROR(["--enable-sqlite requires pkg-config information for sqlite, which was not found"]) + test "x${SQLITEFOUND}" == "xyes" || AC_MSG_ERROR([--enable-sqlite requires pkg-config information for sqlite3, which was not found]) AC_DEFINE(ENABLE_SQLITE, 1, [sqlite available]) enable_any="yes" fi diff --git a/src/Makefile.am b/src/Makefile.am index bf1c700e..621e3f28 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -62,8 +62,8 @@ SYNCEBOOK_SOURCES = \ EvolutionContactSource.cpp SYNCSQLITE_SOURCES = \ - SQLiteSyncSource.h \ - SQLiteSyncSource.cpp \ + SQLiteUtil.h \ + SQLiteUtil.cpp \ SQLiteContactSource.h \ SQLiteContactSource.cpp diff --git a/src/SQLiteContactSource.cpp b/src/SQLiteContactSource.cpp index 212b2b60..94a509c7 100644 --- a/src/SQLiteContactSource.cpp +++ b/src/SQLiteContactSource.cpp @@ -32,28 +32,6 @@ using namespace vocl; -const char *SQLiteContactSource::getDefaultSchema() -{ - return - "BEGIN TRANSACTION;" - "CREATE TABLE ABMultiValue (UID INTEGER PRIMARY KEY, record_id INTEGER, property INTEGER, identifier INTEGER, label INTEGER, value TEXT);" - "CREATE TABLE ABMultiValueEntry (parent_id INTEGER, key INTEGER, value TEXT, UNIQUE(parent_id, key));" - "CREATE TABLE ABMultiValueEntryKey (value TEXT, UNIQUE(value));" - "CREATE TABLE ABMultiValueLabel (value TEXT, UNIQUE(value));" - "CREATE TABLE ABPerson (ROWID INTEGER PRIMARY KEY AUTOINCREMENT, First TEXT, Last TEXT, Middle TEXT, FirstPhonetic TEXT, MiddlePhonetic TEXT, LastPhonetic TEXT, Organization TEXT, Department TEXT, Note TEXT, Kind INTEGER, Birthday TEXT, JobTitle TEXT, Nickname TEXT, Prefix TEXT, Suffix TEXT, FirstSort TEXT, LastSort TEXT, CreationDate INTEGER, ModificationDate INTEGER, CompositeNameFallback TEXT);" - "INSERT INTO ABMultiValueLabel VALUES('_$!!$_');" - "INSERT INTO ABMultiValueLabel VALUES('_$!!$_');" - "INSERT INTO ABMultiValueLabel VALUES('_$!!$_');" - "INSERT INTO ABMultiValueEntryKey VALUES('CountryCode');" - "INSERT INTO ABMultiValueEntryKey VALUES('City');" - "INSERT INTO ABMultiValueEntryKey VALUES('Street');" - "INSERT INTO ABMultiValueEntryKey VALUES('State');" - "INSERT INTO ABMultiValueEntryKey VALUES('ZIP');" - "INSERT INTO ABMultiValueEntryKey VALUES('Country');" - "COMMIT;" - ; -} - enum { PERSON_LAST, PERSON_MIDDLE, @@ -72,9 +50,9 @@ enum { LAST_COL }; -const SQLiteSyncSource::Mapping *SQLiteContactSource::getConstMapping() +void SQLiteContactSource::open() { - static const Mapping mapping[LAST_COL] = { + static const SQLiteUtil::Mapping mapping[LAST_COL + 1] = { { "Last", "ABPerson" }, { "Middle", "ABPerson" }, { "First", "ABPerson" }, @@ -87,44 +65,64 @@ const SQLiteSyncSource::Mapping *SQLiteContactSource::getConstMapping() { "Note", "ABPerson", "NOTE" }, { "Birthday", "ABPerson", "BIRTHDAY" }, { "JobTitle", "ABPerson", "ROLE" }, - { "Nickname", "ABPerson", "NICKNAME" } + { "Nickname", "ABPerson", "NICKNAME" }, + { NULL } }; + static const char *schema = + "BEGIN TRANSACTION;" + "CREATE TABLE ABMultiValue (UID INTEGER PRIMARY KEY, record_id INTEGER, property INTEGER, identifier INTEGER, label INTEGER, value TEXT);" + "CREATE TABLE ABMultiValueEntry (parent_id INTEGER, key INTEGER, value TEXT, UNIQUE(parent_id, key));" + "CREATE TABLE ABMultiValueEntryKey (value TEXT, UNIQUE(value));" + "CREATE TABLE ABMultiValueLabel (value TEXT, UNIQUE(value));" + "CREATE TABLE ABPerson (ROWID INTEGER PRIMARY KEY AUTOINCREMENT, First TEXT, Last TEXT, Middle TEXT, FirstPhonetic TEXT, MiddlePhonetic TEXT, LastPhonetic TEXT, Organization TEXT, Department TEXT, Note TEXT, Kind INTEGER, Birthday TEXT, JobTitle TEXT, Nickname TEXT, Prefix TEXT, Suffix TEXT, FirstSort TEXT, LastSort TEXT, CreationDate INTEGER, ModificationDate INTEGER, CompositeNameFallback TEXT);" + "INSERT INTO ABMultiValueLabel VALUES('_$!!$_');" + "INSERT INTO ABMultiValueLabel VALUES('_$!!$_');" + "INSERT INTO ABMultiValueLabel VALUES('_$!!$_');" + "INSERT INTO ABMultiValueEntryKey VALUES('CountryCode');" + "INSERT INTO ABMultiValueEntryKey VALUES('City');" + "INSERT INTO ABMultiValueEntryKey VALUES('Street');" + "INSERT INTO ABMultiValueEntryKey VALUES('State');" + "INSERT INTO ABMultiValueEntryKey VALUES('ZIP');" + "INSERT INTO ABMultiValueEntryKey VALUES('Country');" + "COMMIT;"; - return mapping; -} - -void SQLiteContactSource::open() -{ - SQLiteSyncSource::open(); + m_sqlite.open(getName(), + m_id, + mapping, + schema); // query database for certain constant indices - m_addrCountryCode = findKey("ABMultiValueEntryKey", "value", "CountryCode"); - m_addrCity = findKey("ABMultiValueEntryKey", "value", "City"); - m_addrStreet = findKey("ABMultiValueEntryKey", "value", "Street"); - m_addrState = findKey("ABMultiValueEntryKey", "value", "State"); - m_addrZIP = findKey("ABMultiValueEntryKey", "value", "ZIP"); - m_typeMobile = findKey("ABMultiValueLabel", "value", "_$!!$_"); - m_typeHome = findKey("ABMultiValueLabel", "value", "_$!!$_"); - m_typeWork = findKey("ABMultiValueLabel", "value", "_$!!$_"); + m_addrCountryCode = m_sqlite.findKey("ABMultiValueEntryKey", "value", "CountryCode"); + m_addrCity = m_sqlite.findKey("ABMultiValueEntryKey", "value", "City"); + m_addrStreet = m_sqlite.findKey("ABMultiValueEntryKey", "value", "Street"); + m_addrState = m_sqlite.findKey("ABMultiValueEntryKey", "value", "State"); + m_addrZIP = m_sqlite.findKey("ABMultiValueEntryKey", "value", "ZIP"); + m_typeMobile = m_sqlite.findKey("ABMultiValueLabel", "value", "_$!!$_"); + m_typeHome = m_sqlite.findKey("ABMultiValueLabel", "value", "_$!!$_"); + m_typeWork = m_sqlite.findKey("ABMultiValueLabel", "value", "_$!!$_"); } +void SQLiteContactSource::close() +{ + m_sqlite.close(); +} void SQLiteContactSource::beginSyncThrow(bool needAll, bool needPartial, bool deleteLocal) { - syncml_time_t lastSyncTime = anchorToTimestamp(getLastAnchor()); + SQLiteUtil::syncml_time_t lastSyncTime = anchorToTimestamp(getLastAnchor()); - eptr all(prepareSQL("SELECT ROWID, CreationDate, ModificationDate FROM ABPerson;")); - while (checkSQL(sqlite3_step(all)) == SQLITE_ROW) { - string uid = toString(SQLITE3_COLUMN_KEY(all, 0)); + eptr all(m_sqlite.prepareSQL("SELECT ROWID, CreationDate, ModificationDate FROM ABPerson;")); + while (m_sqlite.checkSQL(sqlite3_step(all)) == SQLITE_ROW) { + string uid = m_sqlite.toString(SQLITE3_COLUMN_KEY(all, 0)); m_allItems.addItem(uid); // find new and updated items by comparing their creation resp. modification time stamp // against the end of the last sync if (needPartial) { - syncml_time_t creationTime = getTimeColumn(all, 1); - syncml_time_t modTime = getTimeColumn(all, 2); + SQLiteUtil::syncml_time_t creationTime = m_sqlite.getTimeColumn(all, 1); + SQLiteUtil::syncml_time_t modTime = m_sqlite.getTimeColumn(all, 2); if (creationTime > lastSyncTime) { m_newItems.addItem(uid); @@ -143,8 +141,8 @@ void SQLiteContactSource::beginSyncThrow(bool needAll, // TODO: currently syncevolution resets the last anchor in case of // a failure and thus forces a slow sync - avoid that for SQLite // database sources - eptr start(prepareSQL("BEGIN TRANSACTION;")); - checkSQL(sqlite3_step(start)); + eptr start(m_sqlite.prepareSQL("BEGIN TRANSACTION;")); + m_sqlite.checkSQL(sqlite3_step(start)); if (deleteLocal) { @@ -160,15 +158,15 @@ void SQLiteContactSource::beginSyncThrow(bool needAll, void SQLiteContactSource::endSyncThrow() { // complete the transaction started in beginSyncThrow() - eptr end(prepareSQL("COMMIT;")); - checkSQL(sqlite3_step(end)); + eptr end(m_sqlite.prepareSQL("COMMIT;")); + m_sqlite.checkSQL(sqlite3_step(end)); } void SQLiteContactSource::exportData(ostream &out) { - eptr all(prepareSQL("SELECT ROWID FROM ABPerson;")); - while (checkSQL(sqlite3_step(all)) == SQLITE_ROW) { - string uid = toString(SQLITE3_COLUMN_KEY(all, 1)); + eptr all(m_sqlite.prepareSQL("SELECT ROWID FROM ABPerson;")); + while (m_sqlite.checkSQL(sqlite3_step(all)) == SQLITE_ROW) { + string uid = m_sqlite.toString(SQLITE3_COLUMN_KEY(all, 1)); auto_ptr item(createItem(uid, SYNC_STATE_NONE)); out << item->getData(); @@ -180,8 +178,8 @@ SyncItem *SQLiteContactSource::createItem( const string &uid, SyncState state ) { logItem(uid, "extracting from database"); - eptr contact(prepareSQL("SELECT * FROM ABPerson WHERE ROWID = '%s';", uid.c_str())); - if (checkSQL(sqlite3_step(contact)) != SQLITE_ROW) { + eptr contact(m_sqlite.prepareSQL("SELECT * FROM ABPerson WHERE ROWID = '%s';", uid.c_str())); + if (m_sqlite.checkSQL(sqlite3_step(contact)) != SQLITE_ROW) { throw runtime_error(string(getName()) + ": contact not found: " + uid); } @@ -193,26 +191,24 @@ SyncItem *SQLiteContactSource::createItem( const string &uid, SyncState state ) vobj.addProperty("VERSION", "2.1"); vobj.setVersion("2.1"); - tmp = getTextColumn(contact, m_mapping[PERSON_LAST].colindex); + tmp = m_sqlite.getTextColumn(contact, m_sqlite.getMapping(PERSON_LAST).colindex); tmp += VObject::SEMICOLON_REPLACEMENT; - tmp += getTextColumn(contact, m_mapping[PERSON_MIDDLE].colindex); + tmp += m_sqlite.getTextColumn(contact, m_sqlite.getMapping(PERSON_MIDDLE).colindex); tmp += VObject::SEMICOLON_REPLACEMENT; - tmp += getTextColumn(contact, m_mapping[PERSON_FIRST].colindex); + tmp += m_sqlite.getTextColumn(contact, m_sqlite.getMapping(PERSON_FIRST).colindex); if (tmp.size() > 2) { vobj.addProperty("N", tmp.c_str()); } - tmp = getTextColumn(contact, m_mapping[PERSON_ORGANIZATION].colindex); + tmp = m_sqlite.getTextColumn(contact, m_sqlite.getMapping(PERSON_ORGANIZATION).colindex); tmp += VObject::SEMICOLON_REPLACEMENT; - tmp += getTextColumn(contact, m_mapping[PERSON_DEPARTMENT].colindex); + tmp += m_sqlite.getTextColumn(contact, m_sqlite.getMapping(PERSON_DEPARTMENT).colindex); if (tmp.size() > 1) { vobj.addProperty("ORG", tmp.c_str()); } - rowToVObject(contact, vobj); - + m_sqlite.rowToVObject(contact, vobj); vobj.addProperty("END", "VCARD"); - vobj.fromNativeEncoding(); arrayptr finalstr(vobj.toString(), "VOCL string"); @@ -237,7 +233,7 @@ int SQLiteContactSource::updateItemThrow(SyncItem& item) // Make sure that there is no contact with this uid, // then insert the new data. If there was no such uid, // then this behaves like an add. - string creationTime = findColumn("ABPerson", "ROWID", item.getKey(), "CreationDate", ""); + string creationTime = m_sqlite.findColumn("ABPerson", "ROWID", item.getKey(), "CreationDate", ""); deleteItemThrow(item.getKey()); return insertItemThrow(item, item.getKey(), creationTime); } @@ -279,11 +275,11 @@ int SQLiteContactSource::insertItemThrow(SyncItem &item, const char *uid, const suffix = fn.substr(sep4 + 1); } } - cols << m_mapping[PERSON_FIRST].colname << ", " << - m_mapping[PERSON_MIDDLE].colname << ", " << - m_mapping[PERSON_LAST].colname << ", " << - m_mapping[PERSON_LASTSORT].colname << ", " << - m_mapping[PERSON_FIRSTSORT].colname; + cols << m_sqlite.getMapping(PERSON_FIRST).colname << ", " << + m_sqlite.getMapping(PERSON_MIDDLE).colname << ", " << + m_sqlite.getMapping(PERSON_LAST).colname << ", " << + m_sqlite.getMapping(PERSON_LASTSORT).colname << ", " << + m_sqlite.getMapping(PERSON_FIRSTSORT).colname; values << "?, ?, ?, ?, ?"; // synthesize sort keys: upper case with specific order of first/last name @@ -300,31 +296,31 @@ int SQLiteContactSource::insertItemThrow(SyncItem &item, const char *uid, const cols << ", CreationDate, ModificationDate"; values << ", ?, ?"; - eptr insert(prepareSQL("INSERT INTO ABPerson( %s ) " + eptr insert(m_sqlite.prepareSQL("INSERT INTO ABPerson( %s ) " "VALUES( %s );", cols.str().c_str(), values.str().c_str())); // now bind parameter values in the same order as the columns specification above int param = 1; - checkSQL(sqlite3_bind_text(insert, param++, first.c_str(), -1, SQLITE_TRANSIENT)); - checkSQL(sqlite3_bind_text(insert, param++, middle.c_str(), -1, SQLITE_TRANSIENT)); - checkSQL(sqlite3_bind_text(insert, param++, last.c_str(), -1, SQLITE_TRANSIENT)); - checkSQL(sqlite3_bind_text(insert, param++, lastsort.c_str(), -1, SQLITE_TRANSIENT)); - checkSQL(sqlite3_bind_text(insert, param++, firstsort.c_str(), -1, SQLITE_TRANSIENT)); + m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, first.c_str(), -1, SQLITE_TRANSIENT)); + m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, middle.c_str(), -1, SQLITE_TRANSIENT)); + m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, last.c_str(), -1, SQLITE_TRANSIENT)); + m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, lastsort.c_str(), -1, SQLITE_TRANSIENT)); + m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, firstsort.c_str(), -1, SQLITE_TRANSIENT)); if (uid) { - checkSQL(sqlite3_bind_text(insert, param++, uid, -1, SQLITE_TRANSIENT)); - checkSQL(sqlite3_bind_text(insert, param++, creationTime.c_str(), -1, SQLITE_TRANSIENT)); + m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, uid, -1, SQLITE_TRANSIENT)); + m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, creationTime.c_str(), -1, SQLITE_TRANSIENT)); } else { - checkSQL(sqlite3_bind_int64(insert, param++, (long long)time(NULL))); + m_sqlite.checkSQL(sqlite3_bind_int64(insert, param++, (long long)time(NULL))); } - checkSQL(sqlite3_bind_int64(insert, param++, (long long)time(NULL))); + m_sqlite.checkSQL(sqlite3_bind_int64(insert, param++, (long long)time(NULL))); - checkSQL(sqlite3_step(insert)); + m_sqlite.checkSQL(sqlite3_step(insert)); if (!uid) { // figure out which UID was assigned to the new contact - string newuid = findColumn("SQLITE_SEQUENCE", "NAME", "ABPerson", "SEQ", ""); + string newuid = m_sqlite.findColumn("SQLITE_SEQUENCE", "NAME", "ABPerson", "SEQ", ""); item.setKey(newuid.c_str()); } @@ -337,24 +333,24 @@ int SQLiteContactSource::deleteItemThrow(const string &uid) int status = STC_OK; // delete address field members of contact - eptr del(prepareSQL("DELETE FROM ABMultiValueEntry " - "WHERE ABMultiValueEntry.parent_id IN " - "(SELECT ABMultiValue.uid FROM ABMultiValue WHERE " - " ABMultiValue.record_id = ?);")); - checkSQL(sqlite3_bind_text(del, 1, uid.c_str(), -1, SQLITE_TRANSIENT)); - checkSQL(sqlite3_step(del)); + eptr del(m_sqlite.prepareSQL("DELETE FROM ABMultiValueEntry " + "WHERE ABMultiValueEntry.parent_id IN " + "(SELECT ABMultiValue.uid FROM ABMultiValue WHERE " + " ABMultiValue.record_id = ?);")); + m_sqlite.checkSQL(sqlite3_bind_text(del, 1, uid.c_str(), -1, SQLITE_TRANSIENT)); + m_sqlite.checkSQL(sqlite3_step(del)); // delete addresses and emails of contact - del.set(prepareSQL("DELETE FROM ABMultiValue WHERE " - "ABMultiValue.record_id = ?;")); - checkSQL(sqlite3_bind_text(del, 1, uid.c_str(), -1, SQLITE_TRANSIENT)); - checkSQL(sqlite3_step(del)); + del.set(m_sqlite.prepareSQL("DELETE FROM ABMultiValue WHERE " + "ABMultiValue.record_id = ?;")); + m_sqlite.checkSQL(sqlite3_bind_text(del, 1, uid.c_str(), -1, SQLITE_TRANSIENT)); + m_sqlite.checkSQL(sqlite3_step(del)); // now delete the contact itself - del.set(prepareSQL("DELETE FROM ABPerson WHERE " - "ABPerson.ROWID = ?;")); - checkSQL(sqlite3_bind_text(del, 1, uid.c_str(), -1, SQLITE_TRANSIENT)); - checkSQL(sqlite3_step(del)); + del.set(m_sqlite.prepareSQL("DELETE FROM ABPerson WHERE " + "ABPerson.ROWID = ?;")); + m_sqlite.checkSQL(sqlite3_bind_text(del, 1, uid.c_str(), -1, SQLITE_TRANSIENT)); + m_sqlite.checkSQL(sqlite3_step(del)); return status; } @@ -369,11 +365,11 @@ void SQLiteContactSource::logItem(const string &uid, const string &info, bool de if (LOG.getLevel() >= (debug ? LOG_LEVEL_DEBUG : LOG_LEVEL_INFO)) { (LOG.*(debug ? &Log::debug : &Log::info))("%s: %s %s", getName(), - findColumn("ABPerson", - "ROWID", - uid.c_str(), - "FirstSort", - uid.c_str()).c_str(), + m_sqlite.findColumn("ABPerson", + "ROWID", + uid.c_str(), + "FirstSort", + uid.c_str()).c_str(), info.c_str()); } } diff --git a/src/SQLiteContactSource.h b/src/SQLiteContactSource.h index 2e6a5f2c..9c9ea9e7 100644 --- a/src/SQLiteContactSource.h +++ b/src/SQLiteContactSource.h @@ -20,25 +20,30 @@ #ifndef INCL_SQLITECONTACTSOURCE #define INCL_SQLITECONTACTSOURCE -#include "SQLiteSyncSource.h" +#include "EvolutionSyncSource.h" +#include "SQLiteUtil.h" #ifdef ENABLE_SQLITE /** - * Specialization of SQLiteSyncSource for contacts + * Uses SQLiteUtil for contacts * with a schema as used by Mac OS X. + * The schema has hierarchical tables, which is not + * supported by SQLiteUtil, so only the properties which + * have a 1:1 mapping are currently stored. */ -class SQLiteContactSource : public SQLiteSyncSource +class SQLiteContactSource : public EvolutionSyncSource { public: SQLiteContactSource( const string name, SyncSourceConfig *sc, const string &changeId, const string &id ) : - SQLiteSyncSource( name, sc, changeId, id) + EvolutionSyncSource(name, sc, changeId, id) {} - virtual ~SQLiteContactSource() {} protected: /* implementation of EvolutionSyncSource interface */ virtual void open(); + virtual void close(); + virtual sources getSyncBackends() { return sources(); } virtual SyncItem *createItem( const string &uid, SyncState state ); virtual void exportData(ostream &out); virtual string fileSuffix() { return "vcf"; } @@ -58,11 +63,10 @@ class SQLiteContactSource : public SQLiteSyncSource virtual void logItem(const string &uid, const string &info, bool debug = false); virtual void logItem(SyncItem &item, const string &info, bool debug = false); - /* implementation of SQLiteSyncSource interface */ - virtual const char *getDefaultSchema(); - virtual const Mapping *getConstMapping(); - private: + /** encapsulates access to database */ + SQLiteUtil m_sqlite; + /** constant key values defined by tables in the database, queried during open() */ key_t m_addrCountryCode, m_addrCity, diff --git a/src/SQLiteSyncSource.cpp b/src/SQLiteUtil.cpp similarity index 79% rename from src/SQLiteSyncSource.cpp rename to src/SQLiteUtil.cpp index 0aecaa50..a846ee92 100644 --- a/src/SQLiteSyncSource.cpp +++ b/src/SQLiteUtil.cpp @@ -21,15 +21,15 @@ #ifdef ENABLE_SQLITE -#include "SQLiteSyncSource.h" +#include "SQLiteUtil.h" #include "base/util/StringBuffer.h" #include "vocl/VConverter.h" #include -void SQLiteSyncSource::throwError(const string &operation) +void SQLiteUtil::throwError(const string &operation) { - string descr = string(getName()) + ": '" + m_id + "': " + operation + " failed"; + string descr = m_name + ": '" + m_fileid + "': " + operation + " failed"; if (m_db) { const char *error = sqlite3_errmsg(m_db); @@ -40,7 +40,7 @@ void SQLiteSyncSource::throwError(const string &operation) throw runtime_error(descr); } -sqlite3_stmt *SQLiteSyncSource::prepareSQLWrapper(const char *sql, const char **nextsql) +sqlite3_stmt *SQLiteUtil::prepareSQLWrapper(const char *sql, const char **nextsql) { sqlite3_stmt *stmt = NULL; @@ -48,7 +48,7 @@ sqlite3_stmt *SQLiteSyncSource::prepareSQLWrapper(const char *sql, const char ** return stmt; } -sqlite3_stmt *SQLiteSyncSource::prepareSQL(const char *sqlfmt, ...) +sqlite3_stmt *SQLiteUtil::prepareSQL(const char *sqlfmt, ...) { StringBuffer sql; va_list ap; @@ -61,7 +61,7 @@ sqlite3_stmt *SQLiteSyncSource::prepareSQL(const char *sqlfmt, ...) } -SQLiteSyncSource::key_t SQLiteSyncSource::findKey(const char *database, const char *keyname, const char *key) +SQLiteUtil::key_t SQLiteUtil::findKey(const char *database, const char *keyname, const char *key) { eptr query(prepareSQL("SELECT ROWID FROM %s WHERE %s = '%s';", database, keyname, key)); @@ -73,7 +73,7 @@ SQLiteSyncSource::key_t SQLiteSyncSource::findKey(const char *database, const ch } } -string SQLiteSyncSource::findColumn(const char *database, const char *keyname, const char *key, const char *column, const char *def) +string SQLiteUtil::findColumn(const char *database, const char *keyname, const char *key, const char *column, const char *def) { eptr query(prepareSQL("SELECT %s FROM %s WHERE %s = '%s';", column, database, keyname, key)); @@ -87,19 +87,19 @@ string SQLiteSyncSource::findColumn(const char *database, const char *keyname, c } } -string SQLiteSyncSource::getTextColumn(sqlite3_stmt *stmt, int col, const char *def) +string SQLiteUtil::getTextColumn(sqlite3_stmt *stmt, int col, const char *def) { const unsigned char *text = sqlite3_column_text(stmt, col); return text ? (const char *)text : def; } -SQLiteSyncSource::syncml_time_t SQLiteSyncSource::getTimeColumn(sqlite3_stmt *stmt, int col) +SQLiteUtil::syncml_time_t SQLiteUtil::getTimeColumn(sqlite3_stmt *stmt, int col) { // assumes that the database stores the result of time() directly return sqlite3_column_int64(stmt, col); } -void SQLiteSyncSource::rowToVObject(sqlite3_stmt *stmt, vocl::VObject &vobj) +void SQLiteUtil::rowToVObject(sqlite3_stmt *stmt, vocl::VObject &vobj) { const unsigned char *text; const char *tablename; @@ -121,14 +121,21 @@ void SQLiteSyncSource::rowToVObject(sqlite3_stmt *stmt, vocl::VObject &vobj) } } -void SQLiteSyncSource::open() +void SQLiteUtil::open(const string &name, + const string &fileid, + const SQLiteUtil::Mapping *mapping, + const char *schema) { + close(); + m_name = name; + m_fileid = fileid; + const string prefix("file://"); - bool create = m_id.substr(0, prefix.size()) == prefix; - string filename = create ? m_id.substr(prefix.size()) : m_id; + bool create = fileid.substr(0, prefix.size()) == prefix; + string filename = create ? fileid.substr(prefix.size()) : fileid; if (!create && access(filename.c_str(), F_OK)) { - throw runtime_error(string(getName()) + ": no such database: '" + filename + "'"); + throw runtime_error(m_name + ": no such database: '" + filename + "'"); } sqlite3 *db; @@ -144,7 +151,6 @@ void SQLiteSyncSource::open() break; case SQLITE_DONE: { // empty - const char *schema = getDefaultSchema(); const char *nextsql = schema; while (nextsql && *nextsql) { const char *sql = nextsql; @@ -168,7 +174,6 @@ void SQLiteSyncSource::open() } // query database schema to find columns we need - const Mapping *mapping = getConstMapping(); int i; for (i = 0; mapping[i].colname; i++) ; m_mapping.set(new Mapping[i + 1]); @@ -196,8 +201,10 @@ void SQLiteSyncSource::open() memset(&m_mapping[i], 0, sizeof(m_mapping[i])); } -void SQLiteSyncSource::close() +void SQLiteUtil::close() { + m_db = NULL; } + #endif /* ENABLE_SQLITE */ diff --git a/src/SQLiteSyncSource.h b/src/SQLiteUtil.h similarity index 71% rename from src/SQLiteSyncSource.h rename to src/SQLiteUtil.h index 8d706300..b59885eb 100644 --- a/src/SQLiteSyncSource.h +++ b/src/SQLiteUtil.h @@ -20,13 +20,14 @@ #ifndef INCL_SQLITESYNCSOURCE #define INCL_SQLITESYNCSOURCE -#include "EvolutionSyncSource.h" - #ifdef ENABLE_SQLITE #include #include "EvolutionSmartPtr.h" +#include +using namespace std; + namespace vocl { class VObject; } @@ -37,34 +38,40 @@ void inline unref(sqlite3_stmt *stmt) { sqlite3_finalize(stmt); } * This class implements access to SQLite database files: * - opening the database file * - error reporting - * - creating a database file in debugging mode + * - creating a database file + * - converting to and from a VObject via a simple property<->column name mapping */ -class SQLiteSyncSource : public EvolutionSyncSource +class SQLiteUtil { public: + /** information about the database mapping */ + struct Mapping { + const char *colname; /**< column name in SQL table */ + const char *tablename; /**< name of the SQL table which has this column */ + const char *propname; /**< optional: vcard/vcalendar property which corresponds to this */ + int colindex; /**< determined dynamically in open(): index of the column, -1 if not present */ + }; + + const Mapping &getMapping(int i) { return m_mapping[i]; } + /** - * Creates a new Evolution sync source. - * - * @param name the named needed by SyncSource - * @param sc obligatory config for this source, must remain valid throughout the lifetime of the source - * @param changeId is used to track changes in the Evolution backend - * @param id identifies the backend; not specifying it makes this instance - * unusable for anything but listing backend databases + * @param name a name for the data source, used for error messages + * @param fileid a descriptor which identifies the file to be opened: + * currently valid syntax is file:// followed by path + * @param mapping array with database mapping, terminated by NULL colname + * @param schema database schema to use when creating new databases, may be NULL */ - SQLiteSyncSource( const string name, SyncSourceConfig *sc, const string &changeId, const string &id ) : - EvolutionSyncSource( name, sc, changeId, id), - m_db(NULL) - {} - virtual ~SQLiteSyncSource() {} + void open(const string &name, + const string &fileid, + const Mapping *mapping, + const char *schema); - /* implementation of EvolutionSyncSource interface */ - virtual sources getSyncBackends() { return sources(); /* we cannot list available databases */ } - virtual void open(); - virtual void close(); + void close(); - - protected: - /** throw error for a specific sqlite3 operation on m_db */ + /** + * throw error for a specific sqlite3 operation on m_db + * @param operation a description of the operation which failed + */ void throwError(const string &operation); /** @@ -114,27 +121,13 @@ class SQLiteSyncSource : public EvolutionSyncSource /** copies all columns which directly map to a property into the vobj */ void rowToVObject(sqlite3_stmt *stmt, vocl::VObject &vobj); - /** database schema to use when creating new databases, may be NULL */ - virtual const char *getDefaultSchema() = 0; - - /** information about the database mapping */ - struct Mapping { - const char *colname; /**< column name in SQL table */ - const char *tablename; /**< name of the SQL table which has this column */ - const char *propname; /**< optional: vcard/vcalendar property which corresponds to this */ - int colindex; /**< determined dynamically in open(): index of the column, -1 if not present */ - }; - - /** - * array with database mapping, terminated by NULL colname: - * variable fields are stored in a copy maintained by the SQLiteSyncSource class - */ - virtual const Mapping *getConstMapping() = 0; - - /** filled in by SQLiteSyncSource::open() */ + private: + /* copy of open() parameters */ arrayptr m_mapping; + string m_name; + string m_fileid; - /** after opening: current databse */ + /** current database */ eptr m_db; };