00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "config.h"
00020 #include "EvolutionSyncSource.h"
00021
00022 #ifdef ENABLE_SQLITE
00023
00024 #include "SQLiteContactSource.h"
00025
00026 #include <common/base/Log.h>
00027 #include "vocl/VConverter.h"
00028
00029 #include <algorithm>
00030 #include <cctype>
00031 #include <sstream>
00032
00033 using namespace vocl;
00034
00035 #include <boost/algorithm/string/case_conv.hpp>
00036
00037 enum {
00038 PERSON_LAST,
00039 PERSON_MIDDLE,
00040 PERSON_FIRST,
00041 PERSON_PREFIX,
00042 PERSON_SUFFIX,
00043 PERSON_LASTSORT,
00044 PERSON_FIRSTSORT,
00045 PERSON_ORGANIZATION,
00046 PERSON_DEPARTMENT,
00047 PERSON_UNIT,
00048 PERSON_NOTE,
00049 PERSON_BIRTHDAY,
00050 PERSON_JOBTITLE,
00051 PERSON_TITLE,
00052 PERSON_NICKNAME,
00053 PERSON_FULLNAME,
00054 PERSON_CATEGORIES,
00055
00056 PERSON_AIM,
00057 PERSON_GROUPWISE,
00058 PERSON_ICQ,
00059 PERSON_YAHOO,
00060
00061 PERSON_FILEAS,
00062 PERSON_ANNIVERSARY,
00063
00064 PERSON_ASSISTANT,
00065 PERSON_MANAGER,
00066 PERSON_SPOUSE,
00067
00068 PERSON_URL,
00069 PERSON_BLOG_URL,
00070 PERSON_VIDEO_URL,
00071
00072 LAST_COL
00073 };
00074
00075 void SQLiteContactSource::open()
00076 {
00077 static const SQLiteUtil::Mapping mapping[LAST_COL + 1] = {
00078 { "Last", "ABPerson" },
00079 { "Middle", "ABPerson" },
00080 { "First", "ABPerson" },
00081 { "Prefix", "ABPerson" },
00082 { "Suffix", "ABPerson" },
00083 { "FirstSort", "ABPerson" },
00084 { "LastSort", "ABPerson" },
00085 { "Organization", "ABPerson" },
00086 { "Department", "ABPerson" },
00087 { "Unit", "ABPerson" },
00088 { "Note", "ABPerson", "NOTE" },
00089 { "Birthday", "ABPerson", "BDAY" },
00090 { "JobTitle", "ABPerson", "ROLE" },
00091 { "Title", "ABPerson", "TITLE" },
00092 { "Nickname", "ABPerson", "NICKNAME" },
00093 { "CompositeNameFallback", "ABPerson", "FN" },
00094 { "Categories", "ABPerson", "CATEGORIES" },
00095
00096 { "AIM", "ABPerson", "X-AIM" },
00097 { "Groupwise", "ABPerson", "X-GROUPWISE" },
00098 { "ICQ", "ABPerson", "X-ICQ" },
00099 { "Yahoo", "ABPerson", "X-YAHOO" },
00100
00101 { "FileAs", "ABPerson", "X-EVOLUTION-FILE-AS" },
00102 { "Anniversary", "ABPerson", "X-EVOLUTION-ANNIVERSARY" },
00103
00104 { "Assistant", "ABPerson", "X-EVOLUTION-ASSISTANT" },
00105 { "Manager", "ABPerson", "X-EVOLUTION-MANAGER" },
00106 { "Spouse", "ABPerson", "X-EVOLUTION-SPOUSE" },
00107
00108
00109 { "URL", "ABPerson", "URL" },
00110 { "BlogURL", "ABPerson", "X-EVOLUTION-BLOG-URL" },
00111 { "VideoURL", "ABPerson", "X-EVOLUTION-VIDEO-URL" },
00112
00113 { NULL }
00114 };
00115 static const char *schema =
00116 "BEGIN TRANSACTION;"
00117 "CREATE TABLE ABPerson (ROWID INTEGER PRIMARY KEY AUTOINCREMENT, "
00118 "First TEXT, "
00119 "Last TEXT, "
00120 "Middle TEXT, "
00121 "FirstPhonetic TEXT, "
00122 "MiddlePhonetic TEXT, "
00123 "LastPhonetic TEXT, "
00124 "Organization TEXT, "
00125 "Department TEXT, "
00126 "Unit TEXT, "
00127 "Note TEXT, "
00128 "Kind INTEGER, "
00129 "Birthday TEXT, "
00130 "JobTitle TEXT, "
00131 "Title TEXT, "
00132 "Nickname TEXT, "
00133 "Prefix TEXT, "
00134 "Suffix TEXT, "
00135 "FirstSort TEXT, "
00136 "LastSort TEXT, "
00137 "CreationDate INTEGER, "
00138 "ModificationDate INTEGER, "
00139 "CompositeNameFallback TEXT, "
00140 "Categories TEXT, "
00141 "AIM TEXT, "
00142 "Groupwise TEXT, "
00143 "ICQ Text, "
00144 "Yahoo TEXT, "
00145 "Anniversary TEXT, "
00146 "Assistant TEXT, "
00147 "Manager TEXT, "
00148 "Spouse TEXT, "
00149 "URL TEXT, "
00150 "BlogURL TEXT, "
00151 "VideoURL TEXT, "
00152 "FileAs TEXT);"
00153 "COMMIT;";
00154
00155 string id = getDatabaseID();
00156 m_sqlite.open(getName(),
00157 id.c_str(),
00158 mapping,
00159 schema);
00160 }
00161
00162 void SQLiteContactSource::close()
00163 {
00164
00165
00166
00167
00168
00169 sleepSinceModification(1);
00170
00171 m_sqlite.close();
00172 }
00173
00174 SQLiteContactSource::Databases SQLiteContactSource::getDatabases()
00175 {
00176 Databases result;
00177
00178 result.push_back(Database("select database via file path",
00179 "file:///<absolute path>"));
00180 return result;
00181 }
00182
00183 void SQLiteContactSource::listAllItems(RevisionMap_t &revisions)
00184 {
00185 sqliteptr all(m_sqlite.prepareSQL("SELECT ROWID, CreationDate, ModificationDate FROM ABPerson;"));
00186 while (m_sqlite.checkSQL(sqlite3_step(all)) == SQLITE_ROW) {
00187 string uid = m_sqlite.toString(SQLITE3_COLUMN_KEY(all, 0));
00188 string modTime = m_sqlite.time2str(m_sqlite.getTimeColumn(all, 2));
00189 revisions.insert(RevisionMap_t::value_type(uid, modTime));
00190 }
00191 }
00192
00193 SyncItem *SQLiteContactSource::createItem(const string &uid)
00194 {
00195 logItem(uid, "extracting from database", true);
00196
00197 sqliteptr contact(m_sqlite.prepareSQL("SELECT * FROM ABPerson WHERE ROWID = '%s';", uid.c_str()));
00198 if (m_sqlite.checkSQL(sqlite3_step(contact)) != SQLITE_ROW) {
00199 throw runtime_error(string(getName()) + ": contact not found: " + uid);
00200 }
00201
00202 VObject vobj;
00203 string tmp;
00204
00205 vobj.addProperty("BEGIN", "VCARD");
00206 vobj.addProperty("VERSION", "2.1");
00207 vobj.setVersion("2.1");
00208
00209 tmp = m_sqlite.getTextColumn(contact, m_sqlite.getMapping(PERSON_LAST).colindex);
00210 tmp += VObject::SEMICOLON_REPLACEMENT;
00211 tmp += m_sqlite.getTextColumn(contact, m_sqlite.getMapping(PERSON_MIDDLE).colindex);
00212 tmp += VObject::SEMICOLON_REPLACEMENT;
00213 tmp += m_sqlite.getTextColumn(contact, m_sqlite.getMapping(PERSON_FIRST).colindex);
00214 tmp += VObject::SEMICOLON_REPLACEMENT;
00215 tmp += m_sqlite.getTextColumn(contact, m_sqlite.getMapping(PERSON_PREFIX).colindex);
00216 tmp += VObject::SEMICOLON_REPLACEMENT;
00217 tmp += m_sqlite.getTextColumn(contact, m_sqlite.getMapping(PERSON_SUFFIX).colindex);
00218 if (tmp.size() > 4) {
00219 vobj.addProperty("N", tmp.c_str());
00220 }
00221
00222 tmp = m_sqlite.getTextColumn(contact, m_sqlite.getMapping(PERSON_ORGANIZATION).colindex);
00223 tmp += VObject::SEMICOLON_REPLACEMENT;
00224 tmp += m_sqlite.getTextColumn(contact, m_sqlite.getMapping(PERSON_DEPARTMENT).colindex);
00225 tmp += VObject::SEMICOLON_REPLACEMENT;
00226 tmp += m_sqlite.getTextColumn(contact, m_sqlite.getMapping(PERSON_UNIT).colindex);
00227 if (tmp.size() > 2) {
00228 vobj.addProperty("ORG", tmp.c_str());
00229 }
00230
00231 m_sqlite.rowToVObject(contact, vobj);
00232 vobj.addProperty("END", "VCARD");
00233 vobj.fromNativeEncoding();
00234
00235 arrayptr<char> finalstr(vobj.toString(), "VOCL string");
00236 LOG.debug("%s", (char *)finalstr);
00237
00238 cxxptr<SyncItem> item( new SyncItem( uid.c_str() ) );
00239 item->setData( (char *)finalstr, strlen(finalstr) );
00240 item->setDataType( getMimeType() );
00241 item->setModificationTime( 0 );
00242
00243 return item.release();
00244 }
00245
00246 TrackingSyncSource::InsertItemResult SQLiteContactSource::insertItem(const string &uid, const SyncItem &item)
00247 {
00248 string newuid = uid;
00249 string creationTime;
00250 std::auto_ptr<VObject> vobj(VConverter::parse((char *)((SyncItem &)item).getData()));
00251 if (vobj.get() == 0) {
00252 throwError(string("parsing contact ") + ((SyncItem &)item).getKey());
00253 }
00254 vobj->toNativeEncoding();
00255
00256 int numparams = 0;
00257 stringstream cols;
00258 stringstream values;
00259 VProperty *prop;
00260
00261
00262 prop = vobj->getProperty("ORG");
00263 string organization, department, unit;
00264 if (prop && prop->getValue()) {
00265 string fn = prop->getValue();
00266 size_t sep1 = fn.find(VObject::SEMICOLON_REPLACEMENT);
00267 size_t sep2 = sep1 == fn.npos ? fn.npos : fn.find(VObject::SEMICOLON_REPLACEMENT, sep1 + 1);
00268
00269 organization = fn.substr(0, sep1);
00270 if (sep1 != fn.npos) {
00271 department = fn.substr(sep1 + 1, (sep2 == fn.npos) ? fn.npos : sep2 - sep1 - 1);
00272 }
00273 if (sep2 != fn.npos) {
00274 unit = fn.substr(sep2 + 1);
00275 }
00276 }
00277 cols << m_sqlite.getMapping(PERSON_ORGANIZATION).colname << ", " <<
00278 m_sqlite.getMapping(PERSON_DEPARTMENT).colname << ", " <<
00279 m_sqlite.getMapping(PERSON_UNIT).colname << ", ";
00280 values << "?, ?, ?, ";
00281 numparams += 3;
00282
00283
00284 prop = vobj->getProperty("N");
00285 string first, middle, last, prefix, suffix, firstsort, lastsort;
00286 if (prop && prop->getValue()) {
00287 string fn = prop->getValue();
00288 size_t sep1 = fn.find(VObject::SEMICOLON_REPLACEMENT);
00289 size_t sep2 = sep1 == fn.npos ? fn.npos : fn.find(VObject::SEMICOLON_REPLACEMENT, sep1 + 1);
00290 size_t sep3 = sep2 == fn.npos ? fn.npos : fn.find(VObject::SEMICOLON_REPLACEMENT, sep2 + 1);
00291 size_t sep4 = sep3 == fn.npos ? fn.npos : fn.find(VObject::SEMICOLON_REPLACEMENT, sep3 + 1);
00292
00293 last = fn.substr(0, sep1);
00294 if (sep1 != fn.npos) {
00295 middle = fn.substr(sep1 + 1, (sep2 == fn.npos) ? fn.npos : sep2 - sep1 - 1);
00296 }
00297 if (sep2 != fn.npos) {
00298 first = fn.substr(sep2 + 1, (sep3 == fn.npos) ? fn.npos : sep3 - sep2 - 1);
00299 }
00300 if (sep3 != fn.npos) {
00301 prefix = fn.substr(sep3 + 1, (sep4 == fn.npos) ? fn.npos : sep4 - sep3 - 1);
00302 }
00303 if (sep4 != fn.npos) {
00304 suffix = fn.substr(sep4 + 1);
00305 }
00306 }
00307 cols << m_sqlite.getMapping(PERSON_FIRST).colname << ", " <<
00308 m_sqlite.getMapping(PERSON_MIDDLE).colname << ", " <<
00309 m_sqlite.getMapping(PERSON_LAST).colname << ", " <<
00310 m_sqlite.getMapping(PERSON_PREFIX).colname << ", " <<
00311 m_sqlite.getMapping(PERSON_SUFFIX).colname << ", " <<
00312 m_sqlite.getMapping(PERSON_LASTSORT).colname << ", " <<
00313 m_sqlite.getMapping(PERSON_FIRSTSORT).colname;
00314 values << "?, ?, ?, ?, ?, ?, ?";
00315 numparams += 7;
00316
00317
00318
00319 firstsort = first + " " + last;
00320 boost::to_upper(firstsort);
00321 lastsort = last + " " + first;
00322 boost::to_upper(lastsort);
00323
00324
00325 if (uid.size()) {
00326 creationTime = m_sqlite.findColumn("ABPerson", "ROWID", uid.c_str(), "CreationDate", "");
00327 cols << ", ROWID";
00328 values << ", ?";
00329 numparams++;
00330 }
00331 cols << ", CreationDate, ModificationDate";
00332 values << ", ?, ?";
00333 numparams += 2;
00334
00335
00336 if (uid.size()) {
00337 sqliteptr remove(m_sqlite.prepareSQL("DELETE FROM ABPerson WHERE ROWID == ?;"));
00338 m_sqlite.checkSQL(sqlite3_bind_text(remove, 1, uid.c_str(), -1, SQLITE_TRANSIENT));
00339 m_sqlite.checkSQL(sqlite3_step(remove));
00340 }
00341
00342 string cols_str = cols.str();
00343 string values_str = values.str();
00344 sqliteptr insert(m_sqlite.vObjectToRow(*vobj,
00345 "ABPerson",
00346 numparams,
00347 cols.str(),
00348 values.str()));
00349
00350
00351 int param = 1;
00352 m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, organization.c_str(), -1, SQLITE_TRANSIENT));
00353 m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, department.c_str(), -1, SQLITE_TRANSIENT));
00354 m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, unit.c_str(), -1, SQLITE_TRANSIENT));
00355 m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, first.c_str(), -1, SQLITE_TRANSIENT));
00356 m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, middle.c_str(), -1, SQLITE_TRANSIENT));
00357 m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, last.c_str(), -1, SQLITE_TRANSIENT));
00358 m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, prefix.c_str(), -1, SQLITE_TRANSIENT));
00359 m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, suffix.c_str(), -1, SQLITE_TRANSIENT));
00360 m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, lastsort.c_str(), -1, SQLITE_TRANSIENT));
00361 m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, firstsort.c_str(), -1, SQLITE_TRANSIENT));
00362 if (uid.size()) {
00363 m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, uid.c_str(), -1, SQLITE_TRANSIENT));
00364 m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, creationTime.c_str(), -1, SQLITE_TRANSIENT));
00365 } else {
00366 m_sqlite.checkSQL(sqlite3_bind_int64(insert, param++, (long long)time(NULL)));
00367 }
00368 SQLiteUtil::syncml_time_t modificationTime = time(NULL);
00369 m_sqlite.checkSQL(sqlite3_bind_int64(insert, param++, modificationTime));
00370
00371 m_sqlite.checkSQL(sqlite3_step(insert));
00372
00373 if (!uid.size()) {
00374
00375 newuid = m_sqlite.findColumn("SQLITE_SEQUENCE", "NAME", "ABPerson", "SEQ", "");
00376 }
00377 return InsertItemResult(newuid,
00378 m_sqlite.time2str(modificationTime),
00379 false);
00380 }
00381
00382
00383 void SQLiteContactSource::deleteItem(const string &uid)
00384 {
00385 sqliteptr del;
00386
00387 del.set(m_sqlite.prepareSQL("DELETE FROM ABPerson WHERE "
00388 "ABPerson.ROWID = ?;"));
00389 m_sqlite.checkSQL(sqlite3_bind_text(del, 1, uid.c_str(), -1, SQLITE_TRANSIENT));
00390 m_sqlite.checkSQL(sqlite3_step(del));
00391 }
00392
00393 void SQLiteContactSource::logItem(const string &uid, const string &info, bool debug)
00394 {
00395 if (LOG.getLevel() >= (debug ? LOG_LEVEL_DEBUG : LOG_LEVEL_INFO)) {
00396 (LOG.*(debug ? &Log::debug : &Log::info))("%s: %s %s",
00397 getName(),
00398 m_sqlite.findColumn("ABPerson",
00399 "ROWID",
00400 uid.c_str(),
00401 "FirstSort",
00402 uid.c_str()).c_str(),
00403 info.c_str());
00404 }
00405 }
00406
00407 void SQLiteContactSource::logItem(const SyncItem &item, const string &info, bool debug)
00408 {
00409 if (!item.getData()) {
00410 logItem(string(item.getKey()), info, debug);
00411 return;
00412 }
00413
00414 if (LOG.getLevel() >= (debug ? LOG_LEVEL_DEBUG : LOG_LEVEL_INFO)) {
00415 string data = (const char *)item.getData();
00416
00417
00418
00419 string name = "???";
00420 string prop = "\nFN:";
00421 size_t start = data.find(prop);
00422 if (start != data.npos) {
00423 start += prop.size();
00424 size_t end = data.find("\n", start);
00425 if (end != data.npos) {
00426 name = data.substr(start, end - start);
00427 }
00428 }
00429
00430 (LOG.*(debug ? &Log::debug : &Log::info))("%s: %s %s",
00431 getName(),
00432 name.c_str(),
00433 info.c_str());
00434 }
00435 }
00436
00437 #endif
00438
00439 #ifdef ENABLE_MODULES
00440 # include "SQLiteContactSourceRegister.cpp"
00441 #endif