diff options
author | Hans Petter <hansp@src.gnome.org> | 2003-09-12 06:04:44 +0800 |
---|---|---|
committer | Hans Petter <hansp@src.gnome.org> | 2003-09-12 06:04:44 +0800 |
commit | 697761cc337aa77a47140c8df50ed84bc25e23f6 (patch) | |
tree | b785830f72e9938cceaa016a419c7b6d9892bada /libical/src/libicalss/icalbdbset.c | |
parent | 733d77e657516f9a59b5c1a7b62acb87b03ec86f (diff) | |
download | gsoc2013-evolution-697761cc337aa77a47140c8df50ed84bc25e23f6.tar gsoc2013-evolution-697761cc337aa77a47140c8df50ed84bc25e23f6.tar.gz gsoc2013-evolution-697761cc337aa77a47140c8df50ed84bc25e23f6.tar.bz2 gsoc2013-evolution-697761cc337aa77a47140c8df50ed84bc25e23f6.tar.lz gsoc2013-evolution-697761cc337aa77a47140c8df50ed84bc25e23f6.tar.xz gsoc2013-evolution-697761cc337aa77a47140c8df50ed84bc25e23f6.tar.zst gsoc2013-evolution-697761cc337aa77a47140c8df50ed84bc25e23f6.zip |
Import new libical from mainline HEAD and make appropriate changes to
Evolution.
svn path=/trunk/; revision=22538
Diffstat (limited to 'libical/src/libicalss/icalbdbset.c')
-rw-r--r-- | libical/src/libicalss/icalbdbset.c | 1598 |
1 files changed, 1598 insertions, 0 deletions
diff --git a/libical/src/libicalss/icalbdbset.c b/libical/src/libicalss/icalbdbset.c new file mode 100644 index 0000000000..6b11e46741 --- /dev/null +++ b/libical/src/libicalss/icalbdbset.c @@ -0,0 +1,1598 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icalbdbset.c + ======================================================================*/ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "icalbdbset.h" +#include "icalgauge.h" +#include <errno.h> +#include <sys/stat.h> /* for stat */ +#include <stdio.h> + +#ifndef WIN32 +#include <unistd.h> /* for stat, getpid, unlink */ +#include <fcntl.h> /* for fcntl */ +#include <unistd.h> /* for fcntl */ +#else +#define S_IRUSR S_IREAD /* R for owner */ +#define S_IWUSR S_IWRITE /* W for owner */ +#endif +#include <stdlib.h> +#include <string.h> + +#include "icalbdbsetimpl.h" + +#define STRBUF_LEN 255 +#define MAX_RETRY 5 + +extern int errno; + + + +/* these are just stub functions */ +icalerrorenum icalbdbset_read_database(icalbdbset* bset, char *(*pfunc)(const DBT *dbt)); +icalerrorenum icalbdbset_create_cluster(const char *path); +int icalbdbset_cget(DBC *dbcp, DBT *key, DBT *data, int access_method); + +static int _compare_keys(DB *dbp, const DBT *a, const DBT *b); + + +/** Default options used when NULL is passed to icalset_new() **/ +icalbdbset_options icalbdbset_options_default = {ICALBDB_EVENTS, DB_BTREE, 0644, 0, NULL, NULL}; + + +static DB_ENV *ICAL_DB_ENV = 0; + +/** Initialize the db environment */ + +int icalbdbset_init_dbenv(char *db_env_dir, void (*logDbFunc)(const char*, char*)) { + int ret; + int flags; + + if (db_env_dir) { + struct stat env_dir_sb; + + if (stat(db_env_dir, &env_dir_sb)) { + fprintf(stderr, "The directory '%s' is missing, please create it.\n", db_env_dir); + return EINVAL; + } + } + + ret = db_env_create(&ICAL_DB_ENV, 0); + + if (ret) { + /* some kind of error... */ + return ret; + } + + /* Do deadlock detection internally */ + if ((ret = ICAL_DB_ENV->set_lk_detect(ICAL_DB_ENV, DB_LOCK_DEFAULT)) != 0) { + char * foo = db_strerror(ret); + fprintf(stderr, "Could not initialize the database locking environment\n"); + return ret; + } + + flags = DB_INIT_LOCK | DB_INIT_TXN | DB_CREATE | DB_THREAD | \ + DB_RECOVER | DB_INIT_LOG | DB_INIT_MPOOL; + ret = ICAL_DB_ENV->open(ICAL_DB_ENV, db_env_dir, flags, S_IRUSR|S_IWUSR); + + if (ret) { + char * foo = db_strerror(ret); + ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "dbenv->open"); + return ret; + } + + /* display additional error messages */ + if (logDbFunc != NULL) { + ICAL_DB_ENV->set_errcall(ICAL_DB_ENV, logDbFunc); + } + + return ret; +} + +void icalbdbset_checkpoint(void) +{ + int ret; + char *err; + + switch (ret = ICAL_DB_ENV->txn_checkpoint(ICAL_DB_ENV, 0,0,0)) { + case 0: + case DB_INCOMPLETE: + break; + default: + err = db_strerror(ret); + ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "checkpoint failed"); + abort(); + } +} + +void icalbdbset_rmdbLog(void) +{ + int ret = 0; + char** listp; + + /* remove log files that are archivable (ie. no longer needed) */ + if (ICAL_DB_ENV->log_archive(ICAL_DB_ENV, &listp, DB_ARCH_ABS) == 0) { + if (listp != NULL) { + int ii = 0; + while (listp[ii] != NULL) { + ret = unlink(listp[ii]); + ii++; + } + free(listp); + } + } +} + +int icalbdbset_cleanup(void) +{ + int ret = 0; + + /* one last checkpoint.. */ + icalbdbset_checkpoint(); + + /* remove logs that are not needed anymore */ + icalbdbset_rmdbLog(); + + if (ICAL_DB_ENV) + ret = ICAL_DB_ENV->close(ICAL_DB_ENV, 0); + + return ret; +} + +DB_ENV *icalbdbset_get_env(void) { + return ICAL_DB_ENV; +} + + +/** Initialize an icalbdbset. Also attempts to populate from the + * database (primary if only dbp is given, secondary if sdbp is + * given) and creates an empty object if retrieval is unsuccessful. + * pfunc is used to unpack data from the database. If not given, we + * assume data is a string. + */ + +icalset* icalbdbset_init(icalset* set, const char* dsn, void* options_in) +{ + icalbdbset *bset = (icalbdbset*)set; + icalbdbset_options *options = options_in; + int ret; + DB *cal_db; + char *subdb_name; + + if (options == NULL) + *options = icalbdbset_options_default; + + switch (options->subdb) { + case ICALBDB_CALENDARS: + subdb_name = "calendars"; + break; + case ICALBDB_EVENTS: + subdb_name = "events"; + break; + case ICALBDB_TODOS: + subdb_name = "todos"; + break; + case ICALBDB_REMINDERS: + subdb_name = "reminders"; + break; + } + + cal_db = icalbdbset_bdb_open(set->dsn, + subdb_name, + options->dbtype, + options->mode, + options->flag); + if (cal_db == NULL) + return NULL; + + bset->dbp = cal_db; + bset->sdbp = NULL; + bset->gauge = 0; + bset->cluster = 0; + + if ((ret = icalbdbset_read_database(bset, options->pfunc)) != ICAL_NO_ERROR) { + return NULL; + } + + return (icalset *)bset; +} + + +/** open a database and return a reference to it. Used only for + opening the primary index. + flag = set_flag() DUP | DUP_SORT + */ + +icalset* icalbdbset_new(const char* database_filename, + icalbdbset_subdb_type subdb_type, + int dbtype, int flag) +{ + icalbdbset_options options = icalbdbset_options_default; + + options.subdb = subdb_type; + options.dbtype = dbtype; + options.flag = flag; + + /* this will in turn call icalbdbset_init */ + return icalset_new(ICAL_BDB_SET, database_filename, &options); +} + +/** + * Open a secondary database, used for accessing secondary indices. + * The callback function tells icalbdbset how to associate secondary + * key information with primary data. See the BerkeleyDB reference + * guide for more information. + */ + +DB * icalbdbset_bdb_open_secondary(DB *dbp, + const char *database, + const char *sub_database, + int (*callback) (DB *db, + const DBT *dbt1, + const DBT *dbt2, + DBT *dbt3), + int type) +{ + int ret; + int flags; + DB *sdbp = NULL; + + if (!sub_database) + return NULL; + + if (!ICAL_DB_ENV) + icalbdbset_init_dbenv(NULL, NULL); + + /* Open/create secondary */ + if((ret = db_create(&sdbp, ICAL_DB_ENV, 0)) != 0) { + ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "secondary index: %s", sub_database); + return NULL; + } + + if ((ret = sdbp->set_flags(sdbp, DB_DUP | DB_DUPSORT)) != 0) { + ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "set_flags error for secondary index: %s", sub_database); + return NULL; + } + + flags = DB_CREATE | DB_THREAD; + if ((ret = sdbp->open(sdbp, database, sub_database, type, (u_int32_t) flags, 0644)) != 0) { + ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "failed to open secondary index: %s", sub_database); + if (ret == DB_RUNRECOVERY) + abort(); + else + return NULL; + } + + /* Associate the primary index with a secondary */ + if((ret = dbp->associate(dbp, sdbp, callback, 0)) != 0) { + ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "failed to associate secondary index: %s", sub_database); + return NULL; + } + + return sdbp; +} + +DB* icalbdbset_bdb_open(const char* path, + const char *subdb, + int dbtype, + mode_t mode, + int flag) +{ + DB *dbp = NULL; + int ret; + int flags; + + /* Initialize the correct set of db subsystems (see capdb.c) */ + flags = DB_CREATE | DB_THREAD; + + /* should just abort here instead of opening an env in the current dir.. */ + if (!ICAL_DB_ENV) + icalbdbset_init_dbenv(NULL, NULL); + + /* Create and initialize database object, open the database. */ + if ((ret = db_create(&dbp, ICAL_DB_ENV, 0)) != 0) { + return (NULL); + } + + /* set comparison function, if BTREE */ + if (dbtype == DB_BTREE) + dbp->set_bt_compare(dbp, _compare_keys); + + /* set DUP, DUPSORT */ + if (flag != 0) + dbp->set_flags(dbp, flag); + + if ((ret = dbp->open(dbp, path, subdb, dbtype, flags, mode)) != 0) { + ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "%s (database: %s): open failed.", path, subdb); + if (ret == DB_RUNRECOVERY) + abort(); + else + return NULL; + } + + return (dbp); +} + + +/* icalbdbset_parse_data -- parses using pfunc to unpack data. */ +char *icalbdbset_parse_data(DBT *dbt, char *(*pfunc)(const DBT *dbt)) +{ + char *ret; + + if(pfunc) { + ret = (char *)pfunc(dbt); + } else { + ret = (char *) dbt->data; + } + + return (ret); +} + +/* This populates a cluster with the entire contents of a database */ +icalerrorenum icalbdbset_read_database(icalbdbset* bset, char *(*pfunc)(const DBT *dbt)) +{ + + DB *dbp; + DBC *dbcp; + DBT key, data; + char *str, *szpstr; + int ret; + char keystore[256]; + char datastore[1024]; + char *more_mem = NULL; + DB_TXN *tid; + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + + if (bset->sdbp) { dbp = bset->sdbp; } + else { dbp = bset->dbp; } + + if(!dbp) { goto err1; } + + bset->cluster = icalcomponent_new(ICAL_XROOT_COMPONENT); + + if ((ret = ICAL_DB_ENV->txn_begin(ICAL_DB_ENV, NULL, &tid, 0)) != 0) { + char *foo = db_strerror(ret); + abort(); + } + + /* acquire a cursor for the database */ + if ((ret = dbp->cursor(dbp, tid, &dbcp, 0)) != 0) { + dbp->err(dbp, ret, "primary index"); + goto err1; + } + + key.flags = DB_DBT_USERMEM; + key.data = keystore; + key.ulen = sizeof(keystore); + + data.flags= DB_DBT_USERMEM; + data.data = datastore; + data.ulen = sizeof(datastore); + + + /* fetch the key/data pair */ + while (1) { + ret = dbcp->c_get(dbcp, &key, &data, DB_NEXT); + if (ret == DB_NOTFOUND) { + break; + } else if (ret == ENOMEM) { + if (more_mem) free (more_mem); + more_mem = malloc(data.ulen+1024); + data.data = more_mem; + data.ulen = data.ulen+1024; + } else if (ret == DB_LOCK_DEADLOCK) { + char *foo = db_strerror(ret); + abort(); /* should retry in case of DB_LOCK_DEADLOCK */ + } else if (ret) { + char *foo = db_strerror(ret); + /* some other weird-ass error */ + dbp->err(dbp, ret, "cursor"); + abort(); + } else { + icalcomponent *cl; + + /* this prevents an array read bounds error */ + if((str = (char *)calloc(data.size + 1, sizeof(char)))==NULL) + goto err2; + memcpy(str, (char *)data.data, data.size); + + cl = icalparser_parse_string(str); + + icalcomponent_add_component(bset->cluster, cl); + free(str); + } + } + if(ret != DB_NOTFOUND) { + goto err2; + } + + + if (more_mem) { + free(more_mem); + more_mem = NULL; + } + + if ((ret = dbcp->c_close(dbcp)) != 0) { + char * foo = db_strerror(ret); + abort(); /* should retry in case of DB_LOCK_DEADLOCK */ + } + + if ((ret = tid->commit(tid, 0)) != 0) { + char * foo = db_strerror(ret); + abort(); + } + + return ICAL_NO_ERROR; + + err2: + if (more_mem) free(more_mem); + dbcp->c_close(dbcp); + abort(); /* should retry in case of DB_LOCK_DEADLOCK */ + return ICAL_INTERNAL_ERROR; + + err1: + dbp->err(dbp, ret, "cursor index"); + abort(); + return (ICAL_FILE_ERROR); +} + + +/* XXX add more to this */ +void icalbdbset_free(icalset* set) +{ + icalbdbset *bset = (icalbdbset*)set; + int ret; + + icalerror_check_arg_rv((bset!=0),"bset"); + + if (bset->cluster != 0){ + icalbdbset_commit(set); + icalcomponent_free(bset->cluster); + bset->cluster=0; + } + + if(bset->gauge !=0){ + icalgauge_free(bset->gauge); + } + + if(bset->path != 0){ + free((char *)bset->path); + bset->path = 0; + } + + if(bset->sindex != 0) { + free((char *)bset->sindex); + bset->sindex = 0; + } + + if (bset->dbp && + ((ret = bset->dbp->close(bset->dbp, 0)) != 0)) { + } + bset->dbp = NULL; +} + +/* return cursor is in rdbcp */ +int icalbdbset_acquire_cursor(DB *dbp, DB_TXN *tid, DBC **rdbcp) { + int ret=0; + + if((ret = dbp->cursor(dbp, tid, rdbcp, 0)) != 0) { + dbp->err(dbp, ret, "couldn't open cursor"); + goto err1; + } + + return ICAL_NO_ERROR; + + err1: + return ICAL_FILE_ERROR; + +} + +/* returns key/data in arguments */ +int icalbdbset_get_first(DBC *dbcp, DBT *key, DBT *data) { + return icalbdbset_cget(dbcp, key, data, DB_FIRST); +} + +int icalbdbset_get_next(DBC *dbcp, DBT *key, DBT *data) { + return icalbdbset_cget(dbcp, key, data, DB_NEXT); +} + +int icalbdbset_get_last(DBC *dbcp, DBT *key, DBT *data) { + return icalbdbset_cget(dbcp, key, data, DB_LAST); +} + +int icalbdbset_get_key(DBC *dbcp, DBT *key, DBT *data) { + return icalbdbset_cget(dbcp, key, data, DB_SET); +} + +int icalbdbset_delete(DB *dbp, DBT *key) { + DB_TXN *tid; + int ret; + int done = 0; + int retry = 0; + + while ((retry < MAX_RETRY) && !done) { + + if ((ret = ICAL_DB_ENV->txn_begin(ICAL_DB_ENV, NULL, &tid, 0)) != 0) { + if (ret == DB_LOCK_DEADLOCK) { + retry++; + continue; + } + else { + char *foo = db_strerror(ret); + abort(); + } + } + + if ((ret = dbp->del(dbp, tid, key, 0)) != 0) { + if (ret == DB_NOTFOUND) { + /* do nothing - not an error condition */ + } + else if (ret == DB_LOCK_DEADLOCK) { + tid->abort(tid); + retry++; + continue; + } + else { + char *strError = db_strerror(ret); + icalerror_warn("icalbdbset_delete faild: "); + icalerror_warn(strError); + tid->abort(tid); + return ICAL_FILE_ERROR; + } + } + + if ((ret = tid->commit(tid, 0)) != 0) { + if (ret == DB_LOCK_DEADLOCK) { + tid->abort(tid); + retry++; + continue; + } + else { + char * foo = db_strerror(ret); + abort(); + } + } + + done = 1; /* all is well */ + } + + if (!done) { + if (tid != NULL) tid->abort(tid); + } + + return ret; +} + +int icalbdbset_cget(DBC *dbcp, DBT *key, DBT *data, int access_method) { + int ret=0; + + key->flags |= DB_DBT_MALLOC; /* change these to DB_DBT_USERMEM */ + data->flags |= DB_DBT_MALLOC; + + /* fetch the key/data pair */ + if((ret = dbcp->c_get(dbcp, key, data, access_method)) != 0) { + goto err1; + } + + return ICAL_NO_ERROR; + + err1: + return ICAL_FILE_ERROR; +} + + +int icalbdbset_cput(DBC *dbcp, DBT *key, DBT *data, int access_method) { + int ret=0; + + key->flags |= DB_DBT_MALLOC; /* change these to DB_DBT_USERMEM */ + data->flags |= DB_DBT_MALLOC; + + /* fetch the key/data pair */ + if((ret = dbcp->c_put(dbcp, key, data, 0)) != 0) { + goto err1; + } + + return ICAL_NO_ERROR; + + err1: + return ICAL_FILE_ERROR; +} + + +int icalbdbset_put(DB *dbp, DBT *key, DBT *data, int access_method) +{ + int ret = 0; + DB_TXN *tid = NULL; + int retry = 0; + int done = 0; + + while ((retry < MAX_RETRY) && !done) { + + if ((ret = ICAL_DB_ENV->txn_begin(ICAL_DB_ENV, NULL, &tid, 0)) != 0) { + if (ret == DB_LOCK_DEADLOCK) { + retry++; + continue; + } + else { + char *foo = db_strerror(ret); + abort(); + } + } + + if ((ret = dbp->put(dbp, tid, key, data, access_method)) != 0) { + if (ret == DB_LOCK_DEADLOCK) { + tid->abort(tid); + retry++; + continue; + } + else { + char *strError = db_strerror(ret); + icalerror_warn("icalbdbset_put faild: "); + icalerror_warn(strError); + tid->abort(tid); + return ICAL_FILE_ERROR; + } + } + + if ((ret = tid->commit(tid, 0)) != 0) { + if (ret == DB_LOCK_DEADLOCK) { + tid->abort(tid); + retry++; + continue; + } + else { + char * foo = db_strerror(ret); + abort(); + } + } + + done = 1; /* all is well */ + } + + if (!done) { + if (tid != NULL) tid->abort(tid); + return ICAL_FILE_ERROR; + } + else + return ICAL_NO_ERROR; +} + +int icalbdbset_get(DB *dbp, DB_TXN *tid, DBT *key, DBT *data, int flags) +{ + return (dbp->get(dbp, tid, key, data, flags)); +} + +/** Return the path of the database file **/ + +const char* icalbdbset_path(icalset* set) +{ + icalerror_check_arg_rz((set!=0),"set"); + + return set->dsn; +} + +const char* icalbdbset_subdb(icalset* set) +{ + icalbdbset *bset = (icalbdbset*)set; + icalerror_check_arg_rz((bset!=0),"bset"); + + return bset->subdb; +} + + +/** Write changes out to the database file. + */ + +icalerrorenum icalbdbset_commit(icalset *set) { + DB *dbp; + DBC *dbcp; + DBT key, data; + icalcomponent *c; + char *str; + int ret=0; + int reterr = ICAL_NO_ERROR; + char keystore[256]; + char uidbuf[256]; + char datastore[1024]; + char *more_mem = NULL; + DB_TXN *tid = NULL; + icalbdbset *bset = (icalbdbset*)set; + int bad_uid_counter = 0; + int retry = 0, done = 0, completed = 0, deadlocked = 0; + + icalerror_check_arg_re((bset!=0),"bset",ICAL_BADARG_ERROR); + + dbp = bset->dbp; + icalerror_check_arg_re((dbp!=0),"dbp is invalid",ICAL_BADARG_ERROR); + + if (bset->changed == 0) + return ICAL_NO_ERROR; + + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + + key.flags = DB_DBT_USERMEM; + key.data = keystore; + key.ulen = sizeof(keystore); + + data.flags = DB_DBT_USERMEM; + data.data = datastore; + data.ulen = sizeof(datastore); + + if (!ICAL_DB_ENV) + icalbdbset_init_dbenv(NULL, NULL); + + while ((retry < MAX_RETRY) && !done) { + + if ((ret = ICAL_DB_ENV->txn_begin(ICAL_DB_ENV, NULL, &tid, 0)) != 0) { + if (ret == DB_LOCK_DEADLOCK) { + retry++; + continue; + } + else if (ret == DB_RUNRECOVERY) { + ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "icalbdbset_commit: txn_begin failed"); + abort(); + } + else { + ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "icalbdbset_commit"); + return ICAL_INTERNAL_ERROR; + } + } + + /* first delete everything in the database, because there could be removed components */ + if ((ret = dbp->cursor(dbp, tid, &dbcp, DB_DIRTY_READ)) != 0) { + tid->abort(tid); + if (ret == DB_LOCK_DEADLOCK) { + retry++; + continue; + } + else if (ret == DB_RUNRECOVERY) { + ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "curor failed"); + abort(); + } + else { + ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "curor failed"); + /* leave bset->changed set to true */ + return ICAL_INTERNAL_ERROR; + } + } + + /* fetch the key/data pair, then delete it */ + completed = 0; + while (!completed && !deadlocked) { + ret = dbcp->c_get(dbcp, &key, &data, DB_NEXT); + if (ret == DB_NOTFOUND) { + completed = 1; + } else if (ret == ENOMEM) { + if (more_mem) free(more_mem); + more_mem = malloc(data.ulen+1024); + data.data = more_mem; + data.ulen = data.ulen+1024; + } else if (ret == DB_LOCK_DEADLOCK) { + deadlocked = 1; + } else if (ret == DB_RUNRECOVERY) { + tid->abort(tid); + ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "c_get failed."); + abort(); + } else if (ret == 0) { + if ((ret = dbcp->c_del(dbcp,0))!=0) { + dbp->err(dbp, ret, "cursor"); + if (ret == DB_KEYEMPTY) { + /* never actually created, continue onward.. */ + /* do nothing - break; */ + } else if (ret == DB_LOCK_DEADLOCK) { + deadlocked = 1; + } else { + char *foo = db_strerror(ret); + abort(); + } + } + } else { /* some other non-fatal error */ + dbcp->c_close(dbcp); + tid->abort(tid); + if (more_mem) { + free(more_mem); + more_mem = NULL; + } + return ICAL_INTERNAL_ERROR; + } + } + + if (more_mem) { + free(more_mem); + more_mem = NULL; + } + + if (deadlocked) { + dbcp->c_close(dbcp); + tid->abort(tid); + retry++; + continue; /* next retry */ + } + + deadlocked = 0; + for (c = icalcomponent_get_first_component(bset->cluster,ICAL_ANY_COMPONENT); + c != 0 && !deadlocked; + c = icalcomponent_get_next_component(bset->cluster,ICAL_ANY_COMPONENT)) { + + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + + /* Note that we're always inserting into a primary index. */ + if (icalcomponent_isa(c) != ICAL_VAGENDA_COMPONENT) { + char *uidstr = (char *)icalcomponent_get_uid(c); + if (!uidstr) { /* this shouldn't happen */ + /* no uid string, we need to add one */ + snprintf(uidbuf, 256, "baduid%d-%d", getpid(), bad_uid_counter++); + key.data = uidbuf; + } else { + key.data = uidstr; + } + } else { + char *relcalid = NULL; + relcalid = (char*)icalcomponent_get_relcalid(c); + if (relcalid == NULL) { + snprintf(uidbuf, 256, "baduid%d-%d", getpid(), bad_uid_counter++); + key.data = uidbuf; + } else { + key.data = relcalid; + } + } + key.size = strlen(key.data); + + str = icalcomponent_as_ical_string(c); + data.data = str; + data.size = strlen(str); + + if ((ret = dbcp->c_put(dbcp, &key, &data, DB_KEYLAST)) != 0) { + if (ret == DB_LOCK_DEADLOCK) { + deadlocked = 1; + } + else if (ret == DB_RUNRECOVERY) { + ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "c_put failed."); + abort(); + } + else { + ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "c_put failed %s.", str); + /* continue to try to put as many icalcomponent as possible */ + reterr = ICAL_INTERNAL_ERROR; + } + } + } + + if (deadlocked) { + dbcp->c_close(dbcp); + tid->abort(tid); + retry++; + continue; + } + + if ((ret = dbcp->c_close(dbcp)) != 0) { + tid->abort(tid); + if (ret == DB_LOCK_DEADLOCK) { + retry++; + continue; + } + else if (ret == DB_RUNRECOVERY) { + ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "c_closed failed."); + abort(); + } + else { + ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "c_closed failed."); + reterr = ICAL_INTERNAL_ERROR; + } + } + + if ((ret = tid->commit(tid, 0)) != 0) { + tid->abort(tid); + if (ret == DB_LOCK_DEADLOCK) { + retry++; + continue; + } + else if (ret == DB_RUNRECOVERY) { + ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "commit failed."); + abort(); + } + else { + ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "commit failed."); + reterr = ICAL_INTERNAL_ERROR; + } + } + + done = 1; + } + + bset->changed = 0; + return reterr; +} + + +void icalbdbset_mark(icalset* set) +{ + icalbdbset *bset = (icalbdbset*)set; + icalerror_check_arg_rv((bset!=0),"bset"); + + bset->changed = 1; +} + + +icalcomponent* icalbdbset_get_component(icalset* set) +{ + icalbdbset *bset = (icalbdbset*)set; + icalerror_check_arg_rz((bset!=0),"bset"); + + return bset->cluster; +} + + +/* manipulate the components in the cluster */ + +icalerrorenum icalbdbset_add_component(icalset *set, + icalcomponent* child) +{ + icalbdbset *bset = (icalbdbset*)set; + icalerror_check_arg_re((bset!=0),"bset", ICAL_BADARG_ERROR); + icalerror_check_arg_re((child!=0),"child",ICAL_BADARG_ERROR); + + icalcomponent_add_component(bset->cluster,child); + + icalbdbset_mark(set); + + return ICAL_NO_ERROR; +} + + +icalerrorenum icalbdbset_remove_component(icalset *set, + icalcomponent* child) +{ + icalbdbset *bset = (icalbdbset*)set; + icalerror_check_arg_re((bset!=0),"bset", ICAL_BADARG_ERROR); + icalerror_check_arg_re((child!=0),"child",ICAL_BADARG_ERROR); + + icalcomponent_remove_component(bset->cluster,child); + + icalbdbset_mark(set); + + return ICAL_NO_ERROR; +} + + +int icalbdbset_count_components(icalset *set, + icalcomponent_kind kind) +{ + icalbdbset *bset = (icalbdbset*)set; + + if(set == 0){ + icalerror_set_errno(ICAL_BADARG_ERROR); + return -1; + } + + return icalcomponent_count_components(bset->cluster,kind); +} + + +/** Set the gauge **/ + +icalerrorenum icalbdbset_select(icalset* set, icalgauge* gauge) +{ + icalbdbset *bset = (icalbdbset*)set; + icalerror_check_arg_re((bset!=0),"bset", ICAL_BADARG_ERROR); + icalerror_check_arg_re(gauge!=0,"gauge",ICAL_BADARG_ERROR); + + bset->gauge = gauge; + + return ICAL_NO_ERROR; +} + + +/** Clear the gauge **/ + +void icalbdbset_clear(icalset* set) +{ + icalbdbset *bset = (icalbdbset*)set; + icalerror_check_arg_rv((bset!=0),"bset"); + + bset->gauge = 0; +} + + +icalcomponent* icalbdbset_fetch(icalset* set, icalcomponent_kind kind, const char* uid) +{ + icalcompiter i; + icalbdbset *bset = (icalbdbset*)set; + icalerror_check_arg_rz((bset!=0),"bset"); + + for(i = icalcomponent_begin_component(bset->cluster, kind); + icalcompiter_deref(&i)!= 0; icalcompiter_next(&i)){ + + icalcomponent *this = icalcompiter_deref(&i); + icalproperty *p = NULL; + const char *this_uid = NULL; + + if (this != 0){ + if (kind == ICAL_VAGENDA_COMPONENT) { + p = icalcomponent_get_first_property(this,ICAL_RELCALID_PROPERTY); + if (p != NULL) this_uid = icalproperty_get_relcalid(p); + } else { + p = icalcomponent_get_first_property(this,ICAL_UID_PROPERTY); + if (p != NULL) this_uid = icalproperty_get_uid(p); + } + + if(this_uid==NULL){ + icalerror_warn("icalbdbset_fetch found a component with no UID"); + continue; + } + + if (strcmp(uid,this_uid)==0){ + return this; + } + } + } + + return 0; +} + + +int icalbdbset_has_uid(icalset* store,const char* uid) +{ + assert(0); /* HACK, not implemented */ + return 0; +} + + +/******* support routines for icalbdbset_fetch_match *********/ + +struct icalbdbset_id { + char* uid; + char* recurrence_id; + int sequence; +}; + +void icalbdbset_id_free(struct icalbdbset_id *id) +{ + if(id->recurrence_id != 0){ + free(id->recurrence_id); + } + + if(id->uid != 0){ + free(id->uid); + } + +} + +struct icalbdbset_id icalbdbset_get_id(icalcomponent* comp) +{ + + icalcomponent *inner; + struct icalbdbset_id id; + icalproperty *p; + + inner = icalcomponent_get_first_real_component(comp); + + p = icalcomponent_get_first_property(inner, ICAL_UID_PROPERTY); + + assert(p!= 0); + + id.uid = strdup(icalproperty_get_uid(p)); + + p = icalcomponent_get_first_property(inner, ICAL_SEQUENCE_PROPERTY); + + if(p == 0) { + id.sequence = 0; + } else { + id.sequence = icalproperty_get_sequence(p); + } + + p = icalcomponent_get_first_property(inner, ICAL_RECURRENCEID_PROPERTY); + + if (p == 0){ + id.recurrence_id = 0; + } else { + icalvalue *v; + v = icalproperty_get_value(p); + id.recurrence_id = strdup(icalvalue_as_ical_string(v)); + + assert(id.recurrence_id != 0); + } + + return id; +} + +/* Find the component that is related to the given + component. Currently, it just matches based on UID and + RECURRENCE-ID */ + +icalcomponent* icalbdbset_fetch_match(icalset* set, icalcomponent *comp) +{ + icalbdbset *bset = (icalbdbset*)set; + icalcompiter i; + struct icalbdbset_id comp_id, match_id; + + icalerror_check_arg_rz((bset!=0),"bset"); + comp_id = icalbdbset_get_id(comp); + + for(i = icalcomponent_begin_component(bset->cluster,ICAL_ANY_COMPONENT); + icalcompiter_deref(&i)!= 0; icalcompiter_next(&i)){ + + icalcomponent *match = icalcompiter_deref(&i); + + match_id = icalbdbset_get_id(match); + + if(strcmp(comp_id.uid, match_id.uid) == 0 && + ( comp_id.recurrence_id ==0 || + strcmp(comp_id.recurrence_id, match_id.recurrence_id) ==0 )){ + + /* HACK. What to do with SEQUENCE? */ + + icalbdbset_id_free(&match_id); + icalbdbset_id_free(&comp_id); + return match; + + } + + icalbdbset_id_free(&match_id); + } + + icalbdbset_id_free(&comp_id); + return 0; + +} + + +icalerrorenum icalbdbset_modify(icalset* set, icalcomponent *old, + icalcomponent *newc) +{ + assert(0); /* HACK, not implemented */ + return ICAL_NO_ERROR; +} + +/* caller is responsible to cal icalbdbset_free_cluster first */ +icalerrorenum icalbdbset_set_cluster(icalset* set, icalcomponent* cluster) +{ + icalbdbset *bset = (icalbdbset*)set; + icalerror_check_arg_rz((bset!=0),"bset"); + + bset->cluster = cluster; +} + +icalerrorenum icalbdbset_free_cluster(icalset* set) +{ + icalbdbset *bset = (icalbdbset*)set; + icalerror_check_arg_rz((bset!=0),"bset"); + + if (bset->cluster != NULL) icalcomponent_free(bset->cluster); +} + +icalcomponent* icalbdbset_get_cluster(icalset* set) +{ + icalbdbset *bset = (icalbdbset*)set; + icalerror_check_arg_rz((bset!=0),"bset"); + + return (bset->cluster); +} + + +/** Iterate through components. */ +icalcomponent* icalbdbset_get_current_component (icalset* set) +{ + icalbdbset *bset = (icalbdbset*)set; + + icalerror_check_arg_rz((bset!=0),"bset"); + + return icalcomponent_get_current_component(bset->cluster); +} + + +icalcomponent* icalbdbset_get_first_component(icalset* set) +{ + icalbdbset *bset = (icalbdbset*)set; + icalcomponent *c=0; + + icalerror_check_arg_rz((bset!=0),"bset"); + + do { + if (c == 0) + c = icalcomponent_get_first_component(bset->cluster, + ICAL_ANY_COMPONENT); + else + c = icalcomponent_get_next_component(bset->cluster, + ICAL_ANY_COMPONENT); + + if(c != 0 && (bset->gauge == 0 || + icalgauge_compare(bset->gauge,c) == 1)){ + return c; + } + + } while (c!=0); + + return 0; +} + + +icalsetiter icalbdbset_begin_component(icalset* set, icalcomponent_kind kind, icalgauge* gauge, const char* tzid) +{ + icalsetiter itr = icalsetiter_null; + icalcomponent* comp = NULL; + icalcompiter citr; + icalbdbset *bset = (icalbdbset*) set; + struct icaltimetype start, next, end; + icalproperty *dtstart, *rrule, *prop, *due; + struct icalrecurrencetype recur; + icaltimezone *u_zone; + int g = 0; + int orig_time_was_utc = 0; + + icalerror_check_arg_re((set!=0), "set", icalsetiter_null); + + itr.gauge = gauge; + itr.tzid = tzid; + + citr = icalcomponent_begin_component(bset->cluster, kind); + comp = icalcompiter_deref(&citr); + + if (gauge == 0) { + itr.iter = citr; + return itr; + } + + /* if there is a gauge, the first matched component is returned */ + while (comp != 0) { + + /* check if it is a recurring component and with guage expand, if so + * we need to add recurrence-id property to the given component */ + rrule = icalcomponent_get_first_property(comp, ICAL_RRULE_PROPERTY); + g = icalgauge_get_expand(gauge); + + if (rrule != 0 + && g == 1) { + + /* it is a recurring event */ + + u_zone = icaltimezone_get_builtin_timezone(itr.tzid); + + /* use UTC, if that's all we have. */ + if (!u_zone) + u_zone = icaltimezone_get_utc_timezone(); + + + recur = icalproperty_get_rrule(rrule); + + if (icalcomponent_isa(comp) == ICAL_VEVENT_COMPONENT) { + dtstart = icalcomponent_get_first_property(comp, ICAL_DTSTART_PROPERTY); + if (dtstart) + start = icalproperty_get_dtstart(dtstart); + } else if (icalcomponent_isa(comp) == ICAL_VTODO_COMPONENT) { + due = icalcomponent_get_first_property(comp, ICAL_DUE_PROPERTY); + if (due) + start = icalproperty_get_due(due); + } + + /* Convert to the user's timezone in order to be able to compare + * the results from the rrule iterator. */ + if (icaltime_is_utc(start)) { + start = icaltime_convert_to_zone(start, u_zone); + orig_time_was_utc = 1; + } + + if (itr.last_component == NULL) { + itr.ritr = icalrecur_iterator_new(recur, start); + next = icalrecur_iterator_next(itr.ritr); + itr.last_component = comp; + } + else { + next = icalrecur_iterator_next(itr.ritr); + if (icaltime_is_null_time(next)){ + itr.last_component = NULL; + icalrecur_iterator_free(itr.ritr); + itr.ritr = NULL; + /* no matched occurence */ + goto getNextComp; + } else { + itr.last_component = comp; + } + } + + /* if it is excluded, do next one */ + if (icalproperty_recurrence_is_excluded(comp, &start, &next)) { + icalrecur_iterator_decrement_count(itr.ritr); + continue; + } + + /* add recurrence-id value to the property if the property already exist; + * add the recurrence id property and the value if the property does not exist */ + prop = icalcomponent_get_first_property(comp, ICAL_RECURRENCEID_PROPERTY); + if (prop == 0) + icalcomponent_add_property(comp, icalproperty_new_recurrenceid(next)); + else + icalproperty_set_recurrenceid(prop, next); + + /* convert the next recurrence time into the user's timezone */ + if (orig_time_was_utc) + next = icaltime_convert_to_zone(next, icaltimezone_get_utc_timezone()); + + } /* end of a recurring event */ + + if (gauge == 0 || icalgauge_compare(itr.gauge, comp) == 1) { + /* find a matched and return it */ + itr.iter = citr; + return itr; + } + + /* if it is a recurring but no matched occurrence has been found OR + * it is not a recurring and no matched component has been found, + * read the next component to find out */ +getNextComp: + if ((rrule != NULL && itr.last_component == NULL) || + (rrule == NULL)) { + comp = icalcompiter_next(&citr); + comp = icalcompiter_deref(&citr); + } + } /* while */ + + /* no matched component has found */ + return icalsetiter_null; +} + +icalcomponent* icalbdbset_form_a_matched_recurrence_component(icalsetiter* itr) +{ + icalcomponent* comp = NULL; + struct icaltimetype start, next, end; + icalproperty *dtstart, *rrule, *prop, *due; + struct icalrecurrencetype recur; + icaltimezone *u_zone; + int g = 0; + int orig_time_was_utc = 0; + + comp = itr->last_component; + + if (comp == NULL || itr->gauge == NULL) { + return NULL; + } + + + rrule = icalcomponent_get_first_property(comp, ICAL_RRULE_PROPERTY); + /* if there is no RRULE, simply return to the caller */ + if (rrule == NULL) + return NULL; + + u_zone = icaltimezone_get_builtin_timezone(itr->tzid); + + /* use UTC, if that's all we have. */ + if (!u_zone) + u_zone = icaltimezone_get_utc_timezone(); + + recur = icalproperty_get_rrule(rrule); + + if (icalcomponent_isa(comp) == ICAL_VEVENT_COMPONENT) { + dtstart = icalcomponent_get_first_property(comp, ICAL_DTSTART_PROPERTY); + if (dtstart) + start = icalproperty_get_dtstart(dtstart); + } else if (icalcomponent_isa(comp) == ICAL_VTODO_COMPONENT) { + due = icalcomponent_get_first_property(comp, ICAL_DUE_PROPERTY); + if (due) + start = icalproperty_get_due(due); + } + + /* Convert to the user's timezone in order to be able to compare the results + * from the rrule iterator. */ + if (icaltime_is_utc(start)) { + start = icaltime_convert_to_zone(start, u_zone); + orig_time_was_utc = 1; + } + + if (itr->ritr == NULL) { + itr->ritr = icalrecur_iterator_new(recur, start); + next = icalrecur_iterator_next(itr->ritr); + itr->last_component = comp; + } else { + next = icalrecur_iterator_next(itr->ritr); + if (icaltime_is_null_time(next)){ + /* no more recurrence, returns */ + itr->last_component = NULL; + icalrecur_iterator_free(itr->ritr); + itr->ritr = NULL; + /* no more pending matched occurence, + * all the pending matched occurences have been returned */ + return NULL; + } else { + itr->last_component = comp; + } + } + + /* if it is excluded, return NULL to the caller */ + if (icalproperty_recurrence_is_excluded(comp, &start, &next)) { + icalrecur_iterator_decrement_count(itr->ritr); + return NULL; + } + + /* set recurrence-id value to the property if the property already exist; + * add the recurrence id property and the value if the property does not exist */ + prop = icalcomponent_get_first_property(comp, ICAL_RECURRENCEID_PROPERTY); + if (prop == 0) + icalcomponent_add_property(comp, icalproperty_new_recurrenceid(next)); + else + icalproperty_set_recurrenceid(prop, next); + + if (orig_time_was_utc) { + next = icaltime_convert_to_zone(next, icaltimezone_get_utc_timezone()); + } + + + if (itr->gauge == 0 || icalgauge_compare(itr->gauge, comp) == 1) { + /* find a matched and return it */ + return comp; + } + + /* not matched */ + return NULL; + +} + +icalcomponent* icalbdbsetiter_to_next(icalset *set, icalsetiter* i) +{ + + icalcomponent* comp = NULL; + icalbdbset *bset = (icalbdbset*) set; + struct icaltimetype start, next, end; + icalproperty *dtstart, *rrule, *prop, *due; + struct icalrecurrencetype recur; + icaltimezone *u_zone; + int g = 0; + int orig_time_was_utc = 0; + + do { + + /* no pending occurence, read the next component */ + if (i->last_component == NULL) { + comp = icalcompiter_next(&(i->iter)); + } + else { + comp = i->last_component; + } + + /* no next component, simply return */ + if (comp == 0) return NULL; + if (i->gauge == 0) return comp; + + /* finding the next matched component and return it to the caller */ + + rrule = icalcomponent_get_first_property(comp, ICAL_RRULE_PROPERTY); + g = icalgauge_get_expand(i->gauge); + + /* a recurring component with expand query */ + if (rrule != 0 + && g == 1) { + + u_zone = icaltimezone_get_builtin_timezone(i->tzid); + + /* use UTC, if that's all we have. */ + if (!u_zone) + u_zone = icaltimezone_get_utc_timezone(); + + recur = icalproperty_get_rrule(rrule); + + if (icalcomponent_isa(comp) == ICAL_VEVENT_COMPONENT) { + dtstart = icalcomponent_get_first_property(comp, ICAL_DTSTART_PROPERTY); + if (dtstart) + start = icalproperty_get_dtstart(dtstart); + } else if (icalcomponent_isa(comp) == ICAL_VTODO_COMPONENT) { + due = icalcomponent_get_first_property(comp, ICAL_DUE_PROPERTY); + if (due) + start = icalproperty_get_due(due); + } + + /* Convert to the user's timezone in order to be able to compare + * the results from the rrule iterator. */ + if (icaltime_is_utc(start)) { + start = icaltime_convert_to_zone(start, u_zone); + orig_time_was_utc = 1; + } + + if (i->ritr == NULL) { + i->ritr = icalrecur_iterator_new(recur, start); + next = icalrecur_iterator_next(i->ritr); + i->last_component = comp; + } else { + next = icalrecur_iterator_next(i->ritr); + if (icaltime_is_null_time(next)) { + i->last_component = NULL; + icalrecur_iterator_free(i->ritr); + i->ritr = NULL; + /* no more occurence, should go to get next component */ + continue; + } else { + i->last_component = comp; + } + } + + /* if it is excluded, do next one */ + if (icalproperty_recurrence_is_excluded(comp, &start, &next)) { + icalrecur_iterator_decrement_count(i->ritr); + continue; + } + + /* set recurrence-id value to the property if the property already exist; + * add the recurrence id property and the value if the property does not exist */ + prop = icalcomponent_get_first_property(comp, ICAL_RECURRENCEID_PROPERTY); + if (prop == 0) + icalcomponent_add_property(comp, icalproperty_new_recurrenceid(next)); + else + icalproperty_set_recurrenceid(prop, next); + + if (orig_time_was_utc) { + next = icaltime_convert_to_zone(next, icaltimezone_get_utc_timezone()); + } + + } /* end of recurring event with expand query */ + + if(comp != 0 && (i->gauge == 0 || + icalgauge_compare(i->gauge, comp) == 1)){ + /* found a matched, return it */ + return comp; + } + } while (comp != 0); + + return 0; + +} + +icalcomponent* icalbdbset_get_next_component(icalset* set) +{ + icalbdbset *bset = (icalbdbset*)set; + icalcomponent *c=0; + + struct icaltimetype start, next; + icalproperty *dtstart, *rrule, *prop, *due; + struct icalrecurrencetype recur; + int g = 0; + + icalerror_check_arg_rz((bset!=0),"bset"); + + do { + c = icalcomponent_get_next_component(bset->cluster, + ICAL_ANY_COMPONENT); + if(c != 0 && (bset->gauge == 0 || + icalgauge_compare(bset->gauge,c) == 1)){ + return c; + } + + } while(c != 0); + + return 0; +} + +int icalbdbset_begin_transaction(DB_TXN* parent_tid, DB_TXN** tid) +{ + return (ICAL_DB_ENV->txn_begin(ICAL_DB_ENV, parent_tid, tid, 0)); +} + +int icalbdbset_commit_transaction(DB_TXN* txnid) +{ + return (txnid->commit(txnid, 0)); +} + + +static int _compare_keys(DB *dbp, const DBT *a, const DBT *b) +{ +/* + * Returns: + * < 0 if a < b + * = 0 if a = b + * > 0 if a > b + */ + + char* ac = (char*)a->data; + char* bc = (char*)b->data; + return (strncmp(ac, bc, a->size)); +} + + + |