2 ======================================================================
4 ======================================================================*/
11 #include "icalbdbset.h"
12 #include "icalgauge.h"
14 #include <sys/stat.h> /* for stat */
18 #include <unistd.h> /* for stat, getpid, unlink */
19 #include <fcntl.h> /* for fcntl */
21 #define S_IRUSR S_IREAD /* R for owner */
22 #define S_IWUSR S_IWRITE /* W for owner */
27 #include "icalbdbsetimpl.h"
29 #define STRBUF_LEN 255
36 /* these are just stub functions */
37 icalerrorenum icalbdbset_read_database(icalbdbset* bset, char *(*pfunc)(const DBT *dbt));
38 icalerrorenum icalbdbset_create_cluster(const char *path);
39 int icalbdbset_cget(DBC *dbcp, DBT *key, DBT *data, int access_method);
41 static int _compare_keys(DB *dbp, const DBT *a, const DBT *b);
44 /** Default options used when NULL is passed to icalset_new() **/
45 icalbdbset_options icalbdbset_options_default = {ICALBDB_EVENTS, DB_BTREE, 0644, 0, NULL, NULL};
48 static DB_ENV *ICAL_DB_ENV = 0;
50 /** Initialize the db environment */
52 int icalbdbset_init_dbenv(char *db_env_dir, void (*logDbFunc)(const char*, char*)) {
57 struct stat env_dir_sb;
59 if (stat(db_env_dir, &env_dir_sb)) {
60 fprintf(stderr, "The directory '%s' is missing, please create it.\n", db_env_dir);
65 ret = db_env_create(&ICAL_DB_ENV, 0);
68 /* some kind of error... */
72 /* Do deadlock detection internally */
73 if ((ret = ICAL_DB_ENV->set_lk_detect(ICAL_DB_ENV, DB_LOCK_DEFAULT)) != 0) {
74 char * foo = db_strerror(ret);
75 fprintf(stderr, "Could not initialize the database locking environment\n");
79 flags = DB_INIT_LOCK | DB_INIT_TXN | DB_CREATE | DB_THREAD | \
80 DB_RECOVER | DB_INIT_LOG | DB_INIT_MPOOL;
81 ret = ICAL_DB_ENV->open(ICAL_DB_ENV, db_env_dir, flags, S_IRUSR|S_IWUSR);
84 char * foo = db_strerror(ret);
85 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "dbenv->open");
89 /* display additional error messages */
90 if (logDbFunc != NULL) {
91 ICAL_DB_ENV->set_errcall(ICAL_DB_ENV, logDbFunc);
97 void icalbdbset_checkpoint(void)
102 switch (ret = ICAL_DB_ENV->txn_checkpoint(ICAL_DB_ENV, 0,0,0)) {
107 err = db_strerror(ret);
108 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "checkpoint failed");
113 void icalbdbset_rmdbLog(void)
118 /* remove log files that are archivable (ie. no longer needed) */
119 if (ICAL_DB_ENV->log_archive(ICAL_DB_ENV, &listp, DB_ARCH_ABS) == 0) {
122 while (listp[ii] != NULL) {
123 ret = unlink(listp[ii]);
131 int icalbdbset_cleanup(void)
135 /* one last checkpoint.. */
136 icalbdbset_checkpoint();
138 /* remove logs that are not needed anymore */
139 icalbdbset_rmdbLog();
142 ret = ICAL_DB_ENV->close(ICAL_DB_ENV, 0);
147 DB_ENV *icalbdbset_get_env(void) {
152 /** Initialize an icalbdbset. Also attempts to populate from the
153 * database (primary if only dbp is given, secondary if sdbp is
154 * given) and creates an empty object if retrieval is unsuccessful.
155 * pfunc is used to unpack data from the database. If not given, we
156 * assume data is a string.
159 icalset* icalbdbset_init(icalset* set, const char* dsn, void* options_in)
161 icalbdbset *bset = (icalbdbset*)set;
162 icalbdbset_options *options = options_in;
168 *options = icalbdbset_options_default;
170 switch (options->subdb) {
171 case ICALBDB_CALENDARS:
172 subdb_name = "calendars";
175 subdb_name = "events";
178 subdb_name = "todos";
180 case ICALBDB_REMINDERS:
181 subdb_name = "reminders";
185 cal_db = icalbdbset_bdb_open(set->dsn,
198 if ((ret = icalbdbset_read_database(bset, options->pfunc)) != ICAL_NO_ERROR) {
202 return (icalset *)bset;
206 /** open a database and return a reference to it. Used only for
207 opening the primary index.
208 flag = set_flag() DUP | DUP_SORT
211 icalset* icalbdbset_new(const char* database_filename,
212 icalbdbset_subdb_type subdb_type,
213 int dbtype, int flag)
215 icalbdbset_options options = icalbdbset_options_default;
217 options.subdb = subdb_type;
218 options.dbtype = dbtype;
221 /* this will in turn call icalbdbset_init */
222 return icalset_new(ICAL_BDB_SET, database_filename, &options);
226 * Open a secondary database, used for accessing secondary indices.
227 * The callback function tells icalbdbset how to associate secondary
228 * key information with primary data. See the BerkeleyDB reference
229 * guide for more information.
232 DB * icalbdbset_bdb_open_secondary(DB *dbp,
233 const char *database,
234 const char *sub_database,
235 int (*callback) (DB *db,
249 icalbdbset_init_dbenv(NULL, NULL);
251 /* Open/create secondary */
252 if((ret = db_create(&sdbp, ICAL_DB_ENV, 0)) != 0) {
253 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "secondary index: %s", sub_database);
257 if ((ret = sdbp->set_flags(sdbp, DB_DUP | DB_DUPSORT)) != 0) {
258 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "set_flags error for secondary index: %s", sub_database);
262 flags = DB_CREATE | DB_THREAD;
263 if ((ret = sdbp->open(sdbp, database, sub_database, type, (u_int32_t) flags, 0644)) != 0) {
264 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "failed to open secondary index: %s", sub_database);
265 if (ret == DB_RUNRECOVERY)
271 /* Associate the primary index with a secondary */
272 if((ret = dbp->associate(dbp, sdbp, callback, 0)) != 0) {
273 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "failed to associate secondary index: %s", sub_database);
280 DB* icalbdbset_bdb_open(const char* path,
290 /* Initialize the correct set of db subsystems (see capdb.c) */
291 flags = DB_CREATE | DB_THREAD;
293 /* should just abort here instead of opening an env in the current dir.. */
295 icalbdbset_init_dbenv(NULL, NULL);
297 /* Create and initialize database object, open the database. */
298 if ((ret = db_create(&dbp, ICAL_DB_ENV, 0)) != 0) {
302 /* set comparison function, if BTREE */
303 if (dbtype == DB_BTREE)
304 dbp->set_bt_compare(dbp, _compare_keys);
306 /* set DUP, DUPSORT */
308 dbp->set_flags(dbp, flag);
310 if ((ret = dbp->open(dbp, path, subdb, dbtype, flags, mode)) != 0) {
311 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "%s (database: %s): open failed.", path, subdb);
312 if (ret == DB_RUNRECOVERY)
322 /* icalbdbset_parse_data -- parses using pfunc to unpack data. */
323 char *icalbdbset_parse_data(DBT *dbt, char *(*pfunc)(const DBT *dbt))
328 ret = (char *)pfunc(dbt);
330 ret = (char *) dbt->data;
336 /* This populates a cluster with the entire contents of a database */
337 icalerrorenum icalbdbset_read_database(icalbdbset* bset, char *(*pfunc)(const DBT *dbt))
346 char datastore[1024];
347 char *more_mem = NULL;
350 memset(&key, 0, sizeof(DBT));
351 memset(&data, 0, sizeof(DBT));
353 if (bset->sdbp) { dbp = bset->sdbp; }
354 else { dbp = bset->dbp; }
356 if(!dbp) { goto err1; }
358 bset->cluster = icalcomponent_new(ICAL_XROOT_COMPONENT);
360 if ((ret = ICAL_DB_ENV->txn_begin(ICAL_DB_ENV, NULL, &tid, 0)) != 0) {
361 char *foo = db_strerror(ret);
365 /* acquire a cursor for the database */
366 if ((ret = dbp->cursor(dbp, tid, &dbcp, 0)) != 0) {
367 dbp->err(dbp, ret, "primary index");
371 key.flags = DB_DBT_USERMEM;
373 key.ulen = sizeof(keystore);
375 data.flags= DB_DBT_USERMEM;
376 data.data = datastore;
377 data.ulen = sizeof(datastore);
380 /* fetch the key/data pair */
382 ret = dbcp->c_get(dbcp, &key, &data, DB_NEXT);
383 if (ret == DB_NOTFOUND) {
385 } else if (ret == ENOMEM) {
386 if (more_mem) free (more_mem);
387 more_mem = malloc(data.ulen+1024);
388 data.data = more_mem;
389 data.ulen = data.ulen+1024;
390 } else if (ret == DB_LOCK_DEADLOCK) {
391 char *foo = db_strerror(ret);
392 abort(); /* should retry in case of DB_LOCK_DEADLOCK */
394 char *foo = db_strerror(ret);
395 /* some other weird-ass error */
396 dbp->err(dbp, ret, "cursor");
401 /* this prevents an array read bounds error */
402 if((str = (char *)calloc(data.size + 1, sizeof(char)))==NULL)
404 memcpy(str, (char *)data.data, data.size);
406 cl = icalparser_parse_string(str);
408 icalcomponent_add_component(bset->cluster, cl);
412 if(ret != DB_NOTFOUND) {
422 if ((ret = dbcp->c_close(dbcp)) != 0) {
423 char * foo = db_strerror(ret);
424 abort(); /* should retry in case of DB_LOCK_DEADLOCK */
427 if ((ret = tid->commit(tid, 0)) != 0) {
428 char * foo = db_strerror(ret);
432 return ICAL_NO_ERROR;
435 if (more_mem) free(more_mem);
437 abort(); /* should retry in case of DB_LOCK_DEADLOCK */
438 return ICAL_INTERNAL_ERROR;
441 dbp->err(dbp, ret, "cursor index");
443 return (ICAL_FILE_ERROR);
447 /* XXX add more to this */
448 void icalbdbset_free(icalset* set)
450 icalbdbset *bset = (icalbdbset*)set;
453 icalerror_check_arg_rv((bset!=0),"bset");
455 if (bset->cluster != 0){
456 icalbdbset_commit(set);
457 icalcomponent_free(bset->cluster);
462 icalgauge_free(bset->gauge);
466 free((char *)bset->path);
470 if(bset->sindex != 0) {
471 free((char *)bset->sindex);
476 ((ret = bset->dbp->close(bset->dbp, 0)) != 0)) {
481 /* return cursor is in rdbcp */
482 int icalbdbset_acquire_cursor(DB *dbp, DB_TXN *tid, DBC **rdbcp) {
485 if((ret = dbp->cursor(dbp, tid, rdbcp, 0)) != 0) {
486 dbp->err(dbp, ret, "couldn't open cursor");
490 return ICAL_NO_ERROR;
493 return ICAL_FILE_ERROR;
497 /* returns key/data in arguments */
498 int icalbdbset_get_first(DBC *dbcp, DBT *key, DBT *data) {
499 return icalbdbset_cget(dbcp, key, data, DB_FIRST);
502 int icalbdbset_get_next(DBC *dbcp, DBT *key, DBT *data) {
503 return icalbdbset_cget(dbcp, key, data, DB_NEXT);
506 int icalbdbset_get_last(DBC *dbcp, DBT *key, DBT *data) {
507 return icalbdbset_cget(dbcp, key, data, DB_LAST);
510 int icalbdbset_get_key(DBC *dbcp, DBT *key, DBT *data) {
511 return icalbdbset_cget(dbcp, key, data, DB_SET);
514 int icalbdbset_delete(DB *dbp, DBT *key) {
520 while ((retry < MAX_RETRY) && !done) {
522 if ((ret = ICAL_DB_ENV->txn_begin(ICAL_DB_ENV, NULL, &tid, 0)) != 0) {
523 if (ret == DB_LOCK_DEADLOCK) {
528 char *foo = db_strerror(ret);
533 if ((ret = dbp->del(dbp, tid, key, 0)) != 0) {
534 if (ret == DB_NOTFOUND) {
535 /* do nothing - not an error condition */
537 else if (ret == DB_LOCK_DEADLOCK) {
543 char *strError = db_strerror(ret);
544 icalerror_warn("icalbdbset_delete faild: ");
545 icalerror_warn(strError);
547 return ICAL_FILE_ERROR;
551 if ((ret = tid->commit(tid, 0)) != 0) {
552 if (ret == DB_LOCK_DEADLOCK) {
558 char * foo = db_strerror(ret);
563 done = 1; /* all is well */
567 if (tid != NULL) tid->abort(tid);
573 int icalbdbset_cget(DBC *dbcp, DBT *key, DBT *data, int access_method) {
576 key->flags |= DB_DBT_MALLOC; /* change these to DB_DBT_USERMEM */
577 data->flags |= DB_DBT_MALLOC;
579 /* fetch the key/data pair */
580 if((ret = dbcp->c_get(dbcp, key, data, access_method)) != 0) {
584 return ICAL_NO_ERROR;
587 return ICAL_FILE_ERROR;
591 int icalbdbset_cput(DBC *dbcp, DBT *key, DBT *data, int access_method) {
594 key->flags |= DB_DBT_MALLOC; /* change these to DB_DBT_USERMEM */
595 data->flags |= DB_DBT_MALLOC;
597 /* fetch the key/data pair */
598 if((ret = dbcp->c_put(dbcp, key, data, 0)) != 0) {
602 return ICAL_NO_ERROR;
605 return ICAL_FILE_ERROR;
609 int icalbdbset_put(DB *dbp, DBT *key, DBT *data, int access_method)
616 while ((retry < MAX_RETRY) && !done) {
618 if ((ret = ICAL_DB_ENV->txn_begin(ICAL_DB_ENV, NULL, &tid, 0)) != 0) {
619 if (ret == DB_LOCK_DEADLOCK) {
624 char *foo = db_strerror(ret);
629 if ((ret = dbp->put(dbp, tid, key, data, access_method)) != 0) {
630 if (ret == DB_LOCK_DEADLOCK) {
636 char *strError = db_strerror(ret);
637 icalerror_warn("icalbdbset_put faild: ");
638 icalerror_warn(strError);
640 return ICAL_FILE_ERROR;
644 if ((ret = tid->commit(tid, 0)) != 0) {
645 if (ret == DB_LOCK_DEADLOCK) {
651 char * foo = db_strerror(ret);
656 done = 1; /* all is well */
660 if (tid != NULL) tid->abort(tid);
661 return ICAL_FILE_ERROR;
664 return ICAL_NO_ERROR;
667 int icalbdbset_get(DB *dbp, DB_TXN *tid, DBT *key, DBT *data, int flags)
669 return (dbp->get(dbp, tid, key, data, flags));
672 /** Return the path of the database file **/
674 const char* icalbdbset_path(icalset* set)
676 icalerror_check_arg_rz((set!=0),"set");
681 const char* icalbdbset_subdb(icalset* set)
683 icalbdbset *bset = (icalbdbset*)set;
684 icalerror_check_arg_rz((bset!=0),"bset");
690 /** Write changes out to the database file.
693 icalerrorenum icalbdbset_commit(icalset *set) {
700 int reterr = ICAL_NO_ERROR;
703 char datastore[1024];
704 char *more_mem = NULL;
706 icalbdbset *bset = (icalbdbset*)set;
707 int bad_uid_counter = 0;
708 int retry = 0, done = 0, completed = 0, deadlocked = 0;
710 icalerror_check_arg_re((bset!=0),"bset",ICAL_BADARG_ERROR);
713 icalerror_check_arg_re((dbp!=0),"dbp is invalid",ICAL_BADARG_ERROR);
715 if (bset->changed == 0)
716 return ICAL_NO_ERROR;
718 memset(&key, 0, sizeof(key));
719 memset(&data, 0, sizeof(data));
721 key.flags = DB_DBT_USERMEM;
723 key.ulen = sizeof(keystore);
725 data.flags = DB_DBT_USERMEM;
726 data.data = datastore;
727 data.ulen = sizeof(datastore);
730 icalbdbset_init_dbenv(NULL, NULL);
732 while ((retry < MAX_RETRY) && !done) {
734 if ((ret = ICAL_DB_ENV->txn_begin(ICAL_DB_ENV, NULL, &tid, 0)) != 0) {
735 if (ret == DB_LOCK_DEADLOCK) {
739 else if (ret == DB_RUNRECOVERY) {
740 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "icalbdbset_commit: txn_begin failed");
744 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "icalbdbset_commit");
745 return ICAL_INTERNAL_ERROR;
749 /* first delete everything in the database, because there could be removed components */
750 if ((ret = dbp->cursor(dbp, tid, &dbcp, DB_DIRTY_READ)) != 0) {
752 if (ret == DB_LOCK_DEADLOCK) {
756 else if (ret == DB_RUNRECOVERY) {
757 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "curor failed");
761 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "curor failed");
762 /* leave bset->changed set to true */
763 return ICAL_INTERNAL_ERROR;
767 /* fetch the key/data pair, then delete it */
769 while (!completed && !deadlocked) {
770 ret = dbcp->c_get(dbcp, &key, &data, DB_NEXT);
771 if (ret == DB_NOTFOUND) {
773 } else if (ret == ENOMEM) {
774 if (more_mem) free(more_mem);
775 more_mem = malloc(data.ulen+1024);
776 data.data = more_mem;
777 data.ulen = data.ulen+1024;
778 } else if (ret == DB_LOCK_DEADLOCK) {
780 } else if (ret == DB_RUNRECOVERY) {
782 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "c_get failed.");
784 } else if (ret == 0) {
785 if ((ret = dbcp->c_del(dbcp,0))!=0) {
786 dbp->err(dbp, ret, "cursor");
787 if (ret == DB_KEYEMPTY) {
788 /* never actually created, continue onward.. */
789 /* do nothing - break; */
790 } else if (ret == DB_LOCK_DEADLOCK) {
793 char *foo = db_strerror(ret);
797 } else { /* some other non-fatal error */
804 return ICAL_INTERNAL_ERROR;
817 continue; /* next retry */
821 for (c = icalcomponent_get_first_component(bset->cluster,ICAL_ANY_COMPONENT);
822 c != 0 && !deadlocked;
823 c = icalcomponent_get_next_component(bset->cluster,ICAL_ANY_COMPONENT)) {
825 memset(&key, 0, sizeof(key));
826 memset(&data, 0, sizeof(data));
828 /* Note that we're always inserting into a primary index. */
829 if (icalcomponent_isa(c) != ICAL_VAGENDA_COMPONENT) {
830 char *uidstr = (char *)icalcomponent_get_uid(c);
831 if (!uidstr) { /* this shouldn't happen */
832 /* no uid string, we need to add one */
833 snprintf(uidbuf, 256, "baduid%d-%d", getpid(), bad_uid_counter++);
839 char *relcalid = NULL;
840 relcalid = (char*)icalcomponent_get_relcalid(c);
841 if (relcalid == NULL) {
842 snprintf(uidbuf, 256, "baduid%d-%d", getpid(), bad_uid_counter++);
848 key.size = strlen(key.data);
850 str = icalcomponent_as_ical_string_r(c);
852 data.size = strlen(str);
854 if ((ret = dbcp->c_put(dbcp, &key, &data, DB_KEYLAST)) != 0) {
855 if (ret == DB_LOCK_DEADLOCK) {
858 else if (ret == DB_RUNRECOVERY) {
859 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "c_put failed.");
863 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "c_put failed %s.", str);
864 /* continue to try to put as many icalcomponent as possible */
865 reterr = ICAL_INTERNAL_ERROR;
879 if ((ret = dbcp->c_close(dbcp)) != 0) {
881 if (ret == DB_LOCK_DEADLOCK) {
885 else if (ret == DB_RUNRECOVERY) {
886 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "c_closed failed.");
890 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "c_closed failed.");
891 reterr = ICAL_INTERNAL_ERROR;
895 if ((ret = tid->commit(tid, 0)) != 0) {
897 if (ret == DB_LOCK_DEADLOCK) {
901 else if (ret == DB_RUNRECOVERY) {
902 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "commit failed.");
906 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "commit failed.");
907 reterr = ICAL_INTERNAL_ERROR;
919 void icalbdbset_mark(icalset* set)
921 icalbdbset *bset = (icalbdbset*)set;
922 icalerror_check_arg_rv((bset!=0),"bset");
928 icalcomponent* icalbdbset_get_component(icalset* set)
930 icalbdbset *bset = (icalbdbset*)set;
931 icalerror_check_arg_rz((bset!=0),"bset");
933 return bset->cluster;
937 /* manipulate the components in the cluster */
939 icalerrorenum icalbdbset_add_component(icalset *set,
940 icalcomponent* child)
942 icalbdbset *bset = (icalbdbset*)set;
943 icalerror_check_arg_re((bset!=0),"bset", ICAL_BADARG_ERROR);
944 icalerror_check_arg_re((child!=0),"child",ICAL_BADARG_ERROR);
946 icalcomponent_add_component(bset->cluster,child);
948 icalbdbset_mark(set);
950 return ICAL_NO_ERROR;
954 icalerrorenum icalbdbset_remove_component(icalset *set,
955 icalcomponent* child)
957 icalbdbset *bset = (icalbdbset*)set;
958 icalerror_check_arg_re((bset!=0),"bset", ICAL_BADARG_ERROR);
959 icalerror_check_arg_re((child!=0),"child",ICAL_BADARG_ERROR);
961 icalcomponent_remove_component(bset->cluster,child);
963 icalbdbset_mark(set);
965 return ICAL_NO_ERROR;
969 int icalbdbset_count_components(icalset *set,
970 icalcomponent_kind kind)
972 icalbdbset *bset = (icalbdbset*)set;
975 icalerror_set_errno(ICAL_BADARG_ERROR);
979 return icalcomponent_count_components(bset->cluster,kind);
983 /** Set the gauge **/
985 icalerrorenum icalbdbset_select(icalset* set, icalgauge* gauge)
987 icalbdbset *bset = (icalbdbset*)set;
988 icalerror_check_arg_re((bset!=0),"bset", ICAL_BADARG_ERROR);
989 icalerror_check_arg_re(gauge!=0,"gauge",ICAL_BADARG_ERROR);
993 return ICAL_NO_ERROR;
997 /** Clear the gauge **/
999 void icalbdbset_clear(icalset* set)
1001 icalbdbset *bset = (icalbdbset*)set;
1002 icalerror_check_arg_rv((bset!=0),"bset");
1008 icalcomponent* icalbdbset_fetch(icalset* set, icalcomponent_kind kind, const char* uid)
1011 icalbdbset *bset = (icalbdbset*)set;
1012 icalerror_check_arg_rz((bset!=0),"bset");
1014 for(i = icalcomponent_begin_component(bset->cluster, kind);
1015 icalcompiter_deref(&i)!= 0; icalcompiter_next(&i)){
1017 icalcomponent *this = icalcompiter_deref(&i);
1018 icalproperty *p = NULL;
1019 const char *this_uid = NULL;
1022 if (kind == ICAL_VAGENDA_COMPONENT) {
1023 p = icalcomponent_get_first_property(this,ICAL_RELCALID_PROPERTY);
1024 if (p != NULL) this_uid = icalproperty_get_relcalid(p);
1026 p = icalcomponent_get_first_property(this,ICAL_UID_PROPERTY);
1027 if (p != NULL) this_uid = icalproperty_get_uid(p);
1031 icalerror_warn("icalbdbset_fetch found a component with no UID");
1035 if (strcmp(uid,this_uid)==0){
1045 int icalbdbset_has_uid(icalset* store,const char* uid)
1047 assert(0); /* HACK, not implemented */
1052 /******* support routines for icalbdbset_fetch_match *********/
1054 struct icalbdbset_id {
1056 char* recurrence_id;
1060 void icalbdbset_id_free(struct icalbdbset_id *id)
1062 if(id->recurrence_id != 0){
1063 free(id->recurrence_id);
1072 struct icalbdbset_id icalbdbset_get_id(icalcomponent* comp)
1075 icalcomponent *inner;
1076 struct icalbdbset_id id;
1079 inner = icalcomponent_get_first_real_component(comp);
1081 p = icalcomponent_get_first_property(inner, ICAL_UID_PROPERTY);
1085 id.uid = strdup(icalproperty_get_uid(p));
1087 p = icalcomponent_get_first_property(inner, ICAL_SEQUENCE_PROPERTY);
1092 id.sequence = icalproperty_get_sequence(p);
1095 p = icalcomponent_get_first_property(inner, ICAL_RECURRENCEID_PROPERTY);
1098 id.recurrence_id = 0;
1101 v = icalproperty_get_value(p);
1102 id.recurrence_id = icalvalue_as_ical_string_r(v);
1104 assert(id.recurrence_id != 0);
1110 /* Find the component that is related to the given
1111 component. Currently, it just matches based on UID and
1114 icalcomponent* icalbdbset_fetch_match(icalset* set, icalcomponent *comp)
1116 icalbdbset *bset = (icalbdbset*)set;
1118 struct icalbdbset_id comp_id, match_id;
1120 icalerror_check_arg_rz((bset!=0),"bset");
1121 comp_id = icalbdbset_get_id(comp);
1123 for(i = icalcomponent_begin_component(bset->cluster,ICAL_ANY_COMPONENT);
1124 icalcompiter_deref(&i)!= 0; icalcompiter_next(&i)){
1126 icalcomponent *match = icalcompiter_deref(&i);
1128 match_id = icalbdbset_get_id(match);
1130 if(strcmp(comp_id.uid, match_id.uid) == 0 &&
1131 ( comp_id.recurrence_id ==0 ||
1132 strcmp(comp_id.recurrence_id, match_id.recurrence_id) ==0 )){
1134 /* HACK. What to do with SEQUENCE? */
1136 icalbdbset_id_free(&match_id);
1137 icalbdbset_id_free(&comp_id);
1142 icalbdbset_id_free(&match_id);
1145 icalbdbset_id_free(&comp_id);
1151 icalerrorenum icalbdbset_modify(icalset* set, icalcomponent *old,
1152 icalcomponent *newc)
1154 assert(0); /* HACK, not implemented */
1155 return ICAL_NO_ERROR;
1158 /* caller is responsible to cal icalbdbset_free_cluster first */
1159 icalerrorenum icalbdbset_set_cluster(icalset* set, icalcomponent* cluster)
1161 icalbdbset *bset = (icalbdbset*)set;
1162 icalerror_check_arg_rz((bset!=0),"bset");
1164 bset->cluster = cluster;
1167 icalerrorenum icalbdbset_free_cluster(icalset* set)
1169 icalbdbset *bset = (icalbdbset*)set;
1170 icalerror_check_arg_rz((bset!=0),"bset");
1172 if (bset->cluster != NULL) icalcomponent_free(bset->cluster);
1175 icalcomponent* icalbdbset_get_cluster(icalset* set)
1177 icalbdbset *bset = (icalbdbset*)set;
1178 icalerror_check_arg_rz((bset!=0),"bset");
1180 return (bset->cluster);
1184 /** Iterate through components. */
1185 icalcomponent* icalbdbset_get_current_component (icalset* set)
1187 icalbdbset *bset = (icalbdbset*)set;
1189 icalerror_check_arg_rz((bset!=0),"bset");
1191 return icalcomponent_get_current_component(bset->cluster);
1195 icalcomponent* icalbdbset_get_first_component(icalset* set)
1197 icalbdbset *bset = (icalbdbset*)set;
1200 icalerror_check_arg_rz((bset!=0),"bset");
1204 c = icalcomponent_get_first_component(bset->cluster,
1205 ICAL_ANY_COMPONENT);
1207 c = icalcomponent_get_next_component(bset->cluster,
1208 ICAL_ANY_COMPONENT);
1210 if(c != 0 && (bset->gauge == 0 ||
1211 icalgauge_compare(bset->gauge,c) == 1)){
1221 icalsetiter icalbdbset_begin_component(icalset* set, icalcomponent_kind kind, icalgauge* gauge, const char* tzid)
1223 icalsetiter itr = icalsetiter_null;
1224 icalcomponent* comp = NULL;
1226 icalbdbset *bset = (icalbdbset*) set;
1227 struct icaltimetype start, next, end;
1228 icalproperty *dtstart, *rrule, *prop, *due;
1229 struct icalrecurrencetype recur;
1230 icaltimezone *u_zone;
1232 int orig_time_was_utc = 0;
1234 icalerror_check_arg_re((set!=0), "set", icalsetiter_null);
1239 citr = icalcomponent_begin_component(bset->cluster, kind);
1240 comp = icalcompiter_deref(&citr);
1247 /* if there is a gauge, the first matched component is returned */
1250 /* check if it is a recurring component and with guage expand, if so
1251 * we need to add recurrence-id property to the given component */
1252 rrule = icalcomponent_get_first_property(comp, ICAL_RRULE_PROPERTY);
1253 g = icalgauge_get_expand(gauge);
1258 /* it is a recurring event */
1260 u_zone = icaltimezone_get_builtin_timezone(itr.tzid);
1262 /* use UTC, if that's all we have. */
1264 u_zone = icaltimezone_get_utc_timezone();
1267 recur = icalproperty_get_rrule(rrule);
1269 if (icalcomponent_isa(comp) == ICAL_VEVENT_COMPONENT) {
1270 dtstart = icalcomponent_get_first_property(comp, ICAL_DTSTART_PROPERTY);
1272 start = icalproperty_get_dtstart(dtstart);
1273 } else if (icalcomponent_isa(comp) == ICAL_VTODO_COMPONENT) {
1274 due = icalcomponent_get_first_property(comp, ICAL_DUE_PROPERTY);
1276 start = icalproperty_get_due(due);
1279 /* Convert to the user's timezone in order to be able to compare
1280 * the results from the rrule iterator. */
1281 if (icaltime_is_utc(start)) {
1282 start = icaltime_convert_to_zone(start, u_zone);
1283 orig_time_was_utc = 1;
1286 if (itr.last_component == NULL) {
1287 itr.ritr = icalrecur_iterator_new(recur, start);
1288 next = icalrecur_iterator_next(itr.ritr);
1289 itr.last_component = comp;
1292 next = icalrecur_iterator_next(itr.ritr);
1293 if (icaltime_is_null_time(next)){
1294 itr.last_component = NULL;
1295 icalrecur_iterator_free(itr.ritr);
1297 /* no matched occurence */
1300 itr.last_component = comp;
1304 /* if it is excluded, do next one */
1305 if (icalproperty_recurrence_is_excluded(comp, &start, &next)) {
1306 icalrecur_iterator_decrement_count(itr.ritr);
1310 /* add recurrence-id value to the property if the property already exist;
1311 * add the recurrence id property and the value if the property does not exist */
1312 prop = icalcomponent_get_first_property(comp, ICAL_RECURRENCEID_PROPERTY);
1314 icalcomponent_add_property(comp, icalproperty_new_recurrenceid(next));
1316 icalproperty_set_recurrenceid(prop, next);
1318 /* convert the next recurrence time into the user's timezone */
1319 if (orig_time_was_utc)
1320 next = icaltime_convert_to_zone(next, icaltimezone_get_utc_timezone());
1322 } /* end of a recurring event */
1324 if (gauge == 0 || icalgauge_compare(itr.gauge, comp) == 1) {
1325 /* find a matched and return it */
1330 /* if it is a recurring but no matched occurrence has been found OR
1331 * it is not a recurring and no matched component has been found,
1332 * read the next component to find out */
1334 if ((rrule != NULL && itr.last_component == NULL) ||
1336 comp = icalcompiter_next(&citr);
1337 comp = icalcompiter_deref(&citr);
1341 /* no matched component has found */
1342 return icalsetiter_null;
1345 icalcomponent* icalbdbset_form_a_matched_recurrence_component(icalsetiter* itr)
1347 icalcomponent* comp = NULL;
1348 struct icaltimetype start, next, end;
1349 icalproperty *dtstart, *rrule, *prop, *due;
1350 struct icalrecurrencetype recur;
1351 icaltimezone *u_zone;
1353 int orig_time_was_utc = 0;
1355 comp = itr->last_component;
1357 if (comp == NULL || itr->gauge == NULL) {
1362 rrule = icalcomponent_get_first_property(comp, ICAL_RRULE_PROPERTY);
1363 /* if there is no RRULE, simply return to the caller */
1367 u_zone = icaltimezone_get_builtin_timezone(itr->tzid);
1369 /* use UTC, if that's all we have. */
1371 u_zone = icaltimezone_get_utc_timezone();
1373 recur = icalproperty_get_rrule(rrule);
1375 if (icalcomponent_isa(comp) == ICAL_VEVENT_COMPONENT) {
1376 dtstart = icalcomponent_get_first_property(comp, ICAL_DTSTART_PROPERTY);
1378 start = icalproperty_get_dtstart(dtstart);
1379 } else if (icalcomponent_isa(comp) == ICAL_VTODO_COMPONENT) {
1380 due = icalcomponent_get_first_property(comp, ICAL_DUE_PROPERTY);
1382 start = icalproperty_get_due(due);
1385 /* Convert to the user's timezone in order to be able to compare the results
1386 * from the rrule iterator. */
1387 if (icaltime_is_utc(start)) {
1388 start = icaltime_convert_to_zone(start, u_zone);
1389 orig_time_was_utc = 1;
1392 if (itr->ritr == NULL) {
1393 itr->ritr = icalrecur_iterator_new(recur, start);
1394 next = icalrecur_iterator_next(itr->ritr);
1395 itr->last_component = comp;
1397 next = icalrecur_iterator_next(itr->ritr);
1398 if (icaltime_is_null_time(next)){
1399 /* no more recurrence, returns */
1400 itr->last_component = NULL;
1401 icalrecur_iterator_free(itr->ritr);
1403 /* no more pending matched occurence,
1404 * all the pending matched occurences have been returned */
1407 itr->last_component = comp;
1411 /* if it is excluded, return NULL to the caller */
1412 if (icalproperty_recurrence_is_excluded(comp, &start, &next)) {
1413 icalrecur_iterator_decrement_count(itr->ritr);
1417 /* set recurrence-id value to the property if the property already exist;
1418 * add the recurrence id property and the value if the property does not exist */
1419 prop = icalcomponent_get_first_property(comp, ICAL_RECURRENCEID_PROPERTY);
1421 icalcomponent_add_property(comp, icalproperty_new_recurrenceid(next));
1423 icalproperty_set_recurrenceid(prop, next);
1425 if (orig_time_was_utc) {
1426 next = icaltime_convert_to_zone(next, icaltimezone_get_utc_timezone());
1430 if (itr->gauge == 0 || icalgauge_compare(itr->gauge, comp) == 1) {
1431 /* find a matched and return it */
1440 icalcomponent* icalbdbsetiter_to_next(icalset *set, icalsetiter* i)
1443 icalcomponent* comp = NULL;
1444 icalbdbset *bset = (icalbdbset*) set;
1445 struct icaltimetype start, next, end;
1446 icalproperty *dtstart, *rrule, *prop, *due;
1447 struct icalrecurrencetype recur;
1448 icaltimezone *u_zone;
1450 int orig_time_was_utc = 0;
1454 /* no pending occurence, read the next component */
1455 if (i->last_component == NULL) {
1456 comp = icalcompiter_next(&(i->iter));
1459 comp = i->last_component;
1462 /* no next component, simply return */
1463 if (comp == 0) return NULL;
1464 if (i->gauge == 0) return comp;
1466 /* finding the next matched component and return it to the caller */
1468 rrule = icalcomponent_get_first_property(comp, ICAL_RRULE_PROPERTY);
1469 g = icalgauge_get_expand(i->gauge);
1471 /* a recurring component with expand query */
1475 u_zone = icaltimezone_get_builtin_timezone(i->tzid);
1477 /* use UTC, if that's all we have. */
1479 u_zone = icaltimezone_get_utc_timezone();
1481 recur = icalproperty_get_rrule(rrule);
1483 if (icalcomponent_isa(comp) == ICAL_VEVENT_COMPONENT) {
1484 dtstart = icalcomponent_get_first_property(comp, ICAL_DTSTART_PROPERTY);
1486 start = icalproperty_get_dtstart(dtstart);
1487 } else if (icalcomponent_isa(comp) == ICAL_VTODO_COMPONENT) {
1488 due = icalcomponent_get_first_property(comp, ICAL_DUE_PROPERTY);
1490 start = icalproperty_get_due(due);
1493 /* Convert to the user's timezone in order to be able to compare
1494 * the results from the rrule iterator. */
1495 if (icaltime_is_utc(start)) {
1496 start = icaltime_convert_to_zone(start, u_zone);
1497 orig_time_was_utc = 1;
1500 if (i->ritr == NULL) {
1501 i->ritr = icalrecur_iterator_new(recur, start);
1502 next = icalrecur_iterator_next(i->ritr);
1503 i->last_component = comp;
1505 next = icalrecur_iterator_next(i->ritr);
1506 if (icaltime_is_null_time(next)) {
1507 i->last_component = NULL;
1508 icalrecur_iterator_free(i->ritr);
1510 /* no more occurence, should go to get next component */
1513 i->last_component = comp;
1517 /* if it is excluded, do next one */
1518 if (icalproperty_recurrence_is_excluded(comp, &start, &next)) {
1519 icalrecur_iterator_decrement_count(i->ritr);
1523 /* set recurrence-id value to the property if the property already exist;
1524 * add the recurrence id property and the value if the property does not exist */
1525 prop = icalcomponent_get_first_property(comp, ICAL_RECURRENCEID_PROPERTY);
1527 icalcomponent_add_property(comp, icalproperty_new_recurrenceid(next));
1529 icalproperty_set_recurrenceid(prop, next);
1531 if (orig_time_was_utc) {
1532 next = icaltime_convert_to_zone(next, icaltimezone_get_utc_timezone());
1535 } /* end of recurring event with expand query */
1537 if(comp != 0 && (i->gauge == 0 ||
1538 icalgauge_compare(i->gauge, comp) == 1)){
1539 /* found a matched, return it */
1542 } while (comp != 0);
1548 icalcomponent* icalbdbset_get_next_component(icalset* set)
1550 icalbdbset *bset = (icalbdbset*)set;
1553 struct icaltimetype start, next;
1554 icalproperty *dtstart, *rrule, *prop, *due;
1555 struct icalrecurrencetype recur;
1558 icalerror_check_arg_rz((bset!=0),"bset");
1561 c = icalcomponent_get_next_component(bset->cluster,
1562 ICAL_ANY_COMPONENT);
1563 if(c != 0 && (bset->gauge == 0 ||
1564 icalgauge_compare(bset->gauge,c) == 1)){
1573 int icalbdbset_begin_transaction(DB_TXN* parent_tid, DB_TXN** tid)
1575 return (ICAL_DB_ENV->txn_begin(ICAL_DB_ENV, parent_tid, tid, 0));
1578 int icalbdbset_commit_transaction(DB_TXN* txnid)
1580 return (txnid->commit(txnid, 0));
1584 static int _compare_keys(DB *dbp, const DBT *a, const DBT *b)
1593 char* ac = (char*)a->data;
1594 char* bc = (char*)b->data;
1595 return (strncmp(ac, bc, a->size));