Imported Upstream version 0.48
[platform/upstream/libical.git] / src / libicalss / icalbdbset.c
1 /* -*- Mode: C -*-
2   ======================================================================
3   FILE: icalbdbset.c
4  ======================================================================*/
5
6
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
10
11 #include "icalbdbset.h"
12 #include "icalgauge.h"
13 #include <errno.h>
14 #include <sys/stat.h> /* for stat */
15 #include <stdio.h>
16
17 #ifndef WIN32
18 #include <unistd.h> /* for stat, getpid, unlink */
19 #include <fcntl.h> /* for fcntl */
20 #else
21 #define S_IRUSR S_IREAD         /* R for owner */
22 #define S_IWUSR S_IWRITE        /* W for owner */
23 #endif
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "icalbdbsetimpl.h"
28
29 #define STRBUF_LEN 255
30 #define MAX_RETRY 5
31
32 extern int errno;
33
34
35
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);
40
41 static int _compare_keys(DB *dbp, const DBT *a, const DBT *b);
42
43     
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};
46
47
48 static DB_ENV *ICAL_DB_ENV = 0;
49
50 /** Initialize the db environment */
51
52 int icalbdbset_init_dbenv(char *db_env_dir, void (*logDbFunc)(const char*, char*)) {
53   int ret;
54   int flags;
55
56   if (db_env_dir) {
57     struct stat env_dir_sb;
58     
59     if (stat(db_env_dir, &env_dir_sb)) {
60       fprintf(stderr, "The directory '%s' is missing, please create it.\n", db_env_dir);
61       return EINVAL;
62     }
63   }
64   
65   ret = db_env_create(&ICAL_DB_ENV, 0);
66
67   if (ret) {
68     /* some kind of error... */
69     return ret;
70   }
71
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");
76     return ret;
77   }    
78
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);
82   
83   if (ret) {
84     char * foo = db_strerror(ret);
85     ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "dbenv->open");
86     return ret;
87   }
88
89   /* display additional error messages */
90   if (logDbFunc != NULL) {
91      ICAL_DB_ENV->set_errcall(ICAL_DB_ENV, logDbFunc);
92   }
93
94   return ret;
95 }
96
97 void icalbdbset_checkpoint(void)
98 {
99   int ret;
100   char *err;
101
102   switch (ret = ICAL_DB_ENV->txn_checkpoint(ICAL_DB_ENV, 0,0,0)) {
103   case 0:
104   case DB_INCOMPLETE:
105     break;
106   default:
107     err = db_strerror(ret);
108     ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "checkpoint failed");
109     abort();
110   }
111 }
112
113 void icalbdbset_rmdbLog(void)
114 {
115     int      ret = 0;
116     char**   listp;
117
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) {
120        if (listp != NULL) {
121           int ii = 0;
122           while (listp[ii] != NULL) {
123               ret = unlink(listp[ii]);
124               ii++;
125           }
126           free(listp);
127        }
128     }
129 }
130
131 int icalbdbset_cleanup(void)
132 {
133   int ret = 0;
134
135   /* one last checkpoint.. */
136   icalbdbset_checkpoint();
137
138     /* remove logs that are not needed anymore */
139     icalbdbset_rmdbLog();
140
141   if (ICAL_DB_ENV) 
142      ret = ICAL_DB_ENV->close(ICAL_DB_ENV, 0);
143
144   return ret;
145 }
146
147 DB_ENV *icalbdbset_get_env(void) {
148   return ICAL_DB_ENV;
149 }
150
151
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.
157  */
158
159 icalset* icalbdbset_init(icalset* set, const char* dsn, void* options_in)
160 {
161     icalbdbset *bset = (icalbdbset*)set;
162     icalbdbset_options *options = options_in;
163     int ret;
164     DB *cal_db;
165     char *subdb_name;
166
167     if (options == NULL) 
168       *options = icalbdbset_options_default;
169
170     switch (options->subdb) {
171     case ICALBDB_CALENDARS:
172       subdb_name = "calendars";
173       break;
174     case ICALBDB_EVENTS:
175       subdb_name = "events";
176       break;
177     case ICALBDB_TODOS:
178       subdb_name = "todos";
179       break;
180     case ICALBDB_REMINDERS:
181       subdb_name = "reminders";
182       break;
183     }
184   
185     cal_db = icalbdbset_bdb_open(set->dsn, 
186                                  subdb_name, 
187                                  options->dbtype, 
188                                  options->mode,
189                                  options->flag);
190     if (cal_db == NULL)
191       return NULL;
192
193     bset->dbp = cal_db;
194     bset->sdbp = NULL;
195     bset->gauge = 0;
196     bset->cluster = 0;
197   
198     if ((ret = icalbdbset_read_database(bset, options->pfunc)) != ICAL_NO_ERROR) {
199       return NULL;
200     }
201
202     return (icalset *)bset;
203 }
204
205
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
209  */
210
211 icalset* icalbdbset_new(const char* database_filename, 
212                         icalbdbset_subdb_type subdb_type,
213                         int dbtype, int flag)
214 {
215   icalbdbset_options options = icalbdbset_options_default;
216
217   options.subdb = subdb_type;
218   options.dbtype = dbtype;
219   options.flag = flag;
220   
221   /* this will in turn call icalbdbset_init */
222   return icalset_new(ICAL_BDB_SET, database_filename, &options);
223 }
224
225 /**
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.
230  */
231
232 DB * icalbdbset_bdb_open_secondary(DB *dbp,
233                                const char *database,
234                                const char *sub_database, 
235                                int (*callback) (DB *db, 
236                                                 const DBT *dbt1, 
237                                                 const DBT *dbt2, 
238                                                 DBT *dbt3),
239                                int type)
240 {
241   int ret;
242   int flags;
243     DB  *sdbp = NULL;
244
245   if (!sub_database) 
246         return NULL;
247
248   if (!ICAL_DB_ENV)
249         icalbdbset_init_dbenv(NULL, NULL);
250
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);
254         return NULL;
255   }
256
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);
259         return NULL;
260   }
261
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)
266             abort();
267         else
268             return NULL;
269   }
270
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);
274         return NULL;
275   }
276
277   return sdbp;
278 }
279
280 DB* icalbdbset_bdb_open(const char* path, 
281                                   const char *subdb, 
282                                 int dbtype, 
283                                 mode_t mode,
284                                 int flag)
285 {
286     DB  *dbp = NULL;
287   int ret;
288   int flags;
289
290   /* Initialize the correct set of db subsystems (see capdb.c) */
291   flags =  DB_CREATE | DB_THREAD;
292
293   /* should just abort here instead of opening an env in the current dir..  */
294   if (!ICAL_DB_ENV)
295         icalbdbset_init_dbenv(NULL, NULL);
296
297   /* Create and initialize database object, open the database. */
298   if ((ret = db_create(&dbp, ICAL_DB_ENV, 0)) != 0) {
299         return (NULL);
300   }
301
302   /* set comparison function, if BTREE */
303   if (dbtype == DB_BTREE)
304     dbp->set_bt_compare(dbp, _compare_keys);
305
306   /* set DUP, DUPSORT */
307   if (flag != 0)
308     dbp->set_flags(dbp, flag);
309
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)
313     abort();
314         else
315             return NULL;
316   }
317
318   return (dbp);
319 }
320
321     
322 /* icalbdbset_parse_data -- parses using pfunc to unpack data. */
323 char *icalbdbset_parse_data(DBT *dbt, char *(*pfunc)(const DBT *dbt)) 
324 {
325   char *ret;
326
327   if(pfunc) {
328     ret = (char *)pfunc(dbt);
329   } else {
330     ret = (char *) dbt->data;
331   }
332
333   return (ret);
334 }
335
336 /* This populates a cluster with the entire contents of a database */
337 icalerrorenum icalbdbset_read_database(icalbdbset* bset, char *(*pfunc)(const DBT *dbt))
338 {
339
340   DB *dbp;
341   DBC *dbcp;
342   DBT key, data;
343   char *str, *szpstr;
344   int ret;
345   char keystore[256];
346   char datastore[1024];
347   char *more_mem = NULL;
348   DB_TXN *tid;
349
350   memset(&key, 0, sizeof(DBT));
351   memset(&data, 0, sizeof(DBT));
352
353   if (bset->sdbp) { dbp = bset->sdbp; }
354   else { dbp = bset->dbp; }
355      
356   if(!dbp) { goto err1; }
357
358   bset->cluster = icalcomponent_new(ICAL_XROOT_COMPONENT);
359
360   if ((ret = ICAL_DB_ENV->txn_begin(ICAL_DB_ENV, NULL, &tid, 0)) != 0) {
361         char *foo = db_strerror(ret);
362         abort();
363   }
364
365   /* acquire a cursor for the database */
366   if ((ret = dbp->cursor(dbp, tid, &dbcp, 0)) != 0) {
367     dbp->err(dbp, ret, "primary index");
368     goto err1;
369   }
370
371   key.flags = DB_DBT_USERMEM;
372   key.data = keystore;
373   key.ulen = sizeof(keystore);
374
375   data.flags= DB_DBT_USERMEM;
376   data.data = datastore;
377   data.ulen = sizeof(datastore);
378
379
380   /* fetch the key/data pair */
381   while (1) {
382     ret = dbcp->c_get(dbcp, &key, &data, DB_NEXT);
383     if (ret == DB_NOTFOUND) {
384       break;
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 */
393     } else if (ret) {
394       char *foo = db_strerror(ret);
395       /* some other weird-ass error  */
396       dbp->err(dbp, ret, "cursor");
397       abort();
398     } else {
399       icalcomponent *cl;
400       
401       /* this prevents an array read bounds error */
402       if((str = (char *)calloc(data.size + 1, sizeof(char)))==NULL)
403         goto err2;
404       memcpy(str, (char *)data.data, data.size);
405       
406       cl = icalparser_parse_string(str);
407       
408       icalcomponent_add_component(bset->cluster, cl);
409       free(str);
410     }
411   }
412   if(ret != DB_NOTFOUND) {
413       goto err2;
414   }
415
416
417   if (more_mem) {
418       free(more_mem);
419       more_mem = NULL;
420   }
421
422   if ((ret = dbcp->c_close(dbcp)) != 0) {
423         char * foo = db_strerror(ret);
424         abort(); /* should retry in case of DB_LOCK_DEADLOCK */
425   }
426
427     if ((ret = tid->commit(tid, 0)) != 0) {
428         char * foo = db_strerror(ret);
429         abort();
430     }
431
432   return ICAL_NO_ERROR;
433
434  err2:
435   if (more_mem) free(more_mem);
436   dbcp->c_close(dbcp);
437   abort(); /* should retry in case of DB_LOCK_DEADLOCK */
438   return ICAL_INTERNAL_ERROR;
439
440  err1:
441   dbp->err(dbp, ret, "cursor index");
442   abort();
443   return (ICAL_FILE_ERROR);
444 }
445
446
447 /* XXX add more to this */
448 void icalbdbset_free(icalset* set)
449 {
450     icalbdbset *bset = (icalbdbset*)set;
451     int ret;
452
453     icalerror_check_arg_rv((bset!=0),"bset");
454
455     if (bset->cluster != 0){
456         icalbdbset_commit(set);
457         icalcomponent_free(bset->cluster);
458         bset->cluster=0;
459     }
460
461     if(bset->gauge !=0){
462         icalgauge_free(bset->gauge);
463     }
464
465     if(bset->path != 0){
466         free((char *)bset->path);
467         bset->path = 0;
468     }
469
470     if(bset->sindex != 0) {
471       free((char *)bset->sindex);
472       bset->sindex = 0;
473     }
474
475     if (bset->dbp && 
476         ((ret = bset->dbp->close(bset->dbp, 0)) != 0)) {
477     }
478     bset->dbp = NULL;
479 }
480
481 /* return cursor is in rdbcp */
482 int icalbdbset_acquire_cursor(DB *dbp, DB_TXN *tid, DBC **rdbcp) {
483   int ret=0;
484
485   if((ret = dbp->cursor(dbp, tid, rdbcp, 0)) != 0) {
486     dbp->err(dbp, ret, "couldn't open cursor");
487     goto err1;
488   }
489
490   return ICAL_NO_ERROR;
491
492  err1:
493   return ICAL_FILE_ERROR;
494
495 }
496
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);
500 }
501
502 int icalbdbset_get_next(DBC *dbcp, DBT *key, DBT *data) {
503   return icalbdbset_cget(dbcp, key, data, DB_NEXT);
504 }
505
506 int icalbdbset_get_last(DBC *dbcp, DBT *key, DBT *data) {
507   return icalbdbset_cget(dbcp, key, data, DB_LAST);
508 }
509
510 int icalbdbset_get_key(DBC *dbcp, DBT *key, DBT *data) {
511   return icalbdbset_cget(dbcp, key, data, DB_SET);
512 }
513
514 int icalbdbset_delete(DB *dbp, DBT *key) {
515   DB_TXN *tid;
516   int ret;
517     int done = 0;
518     int retry = 0;
519
520     while ((retry < MAX_RETRY) && !done) {
521
522   if ((ret = ICAL_DB_ENV->txn_begin(ICAL_DB_ENV, NULL, &tid, 0)) != 0) {
523             if (ret == DB_LOCK_DEADLOCK) {
524                 retry++;
525                 continue;
526             }
527             else {
528         char *foo = db_strerror(ret);
529         abort();
530   }
531         }
532
533         if ((ret = dbp->del(dbp, tid, key, 0)) != 0) {
534             if (ret == DB_NOTFOUND) {
535                 /* do nothing - not an error condition */
536             }
537             else if (ret == DB_LOCK_DEADLOCK) {
538                 tid->abort(tid);
539                 retry++;
540                 continue;
541             }
542             else {
543                 char *strError = db_strerror(ret);
544                 icalerror_warn("icalbdbset_delete faild: ");
545                 icalerror_warn(strError);
546                 tid->abort(tid);
547                 return ICAL_FILE_ERROR;
548             }
549   }
550
551   if ((ret = tid->commit(tid, 0)) != 0) {
552             if (ret == DB_LOCK_DEADLOCK) {
553                 tid->abort(tid);
554                 retry++;
555                 continue;
556             }
557             else {
558         char * foo = db_strerror(ret);
559         abort();
560   }
561        }
562
563        done = 1;   /* all is well */
564     }
565
566     if (!done) {
567         if (tid != NULL) tid->abort(tid);
568     }
569
570   return ret;
571 }
572
573 int icalbdbset_cget(DBC *dbcp, DBT *key, DBT *data, int access_method) {
574   int ret=0;
575
576   key->flags |= DB_DBT_MALLOC; /* change these to DB_DBT_USERMEM */
577   data->flags |= DB_DBT_MALLOC;
578
579   /* fetch the key/data pair */
580   if((ret = dbcp->c_get(dbcp, key, data, access_method)) != 0) {
581     goto err1;
582   }
583
584   return ICAL_NO_ERROR;
585
586  err1:
587   return ICAL_FILE_ERROR;
588 }
589
590
591 int icalbdbset_cput(DBC *dbcp, DBT *key, DBT *data, int access_method) {
592   int ret=0;
593
594   key->flags |= DB_DBT_MALLOC; /* change these to DB_DBT_USERMEM */
595   data->flags |= DB_DBT_MALLOC;
596
597   /* fetch the key/data pair */
598   if((ret = dbcp->c_put(dbcp, key, data, 0)) != 0) {
599     goto err1;
600   }
601
602   return ICAL_NO_ERROR;
603
604  err1:
605   return ICAL_FILE_ERROR;
606 }
607
608
609 int icalbdbset_put(DB *dbp, DBT *key, DBT *data, int access_method)
610 {
611     int     ret   = 0;
612     DB_TXN *tid   = NULL;
613     int     retry = 0;
614     int     done  = 0;
615
616     while ((retry < MAX_RETRY) && !done) {
617
618   if ((ret = ICAL_DB_ENV->txn_begin(ICAL_DB_ENV, NULL, &tid, 0)) != 0) {
619             if (ret == DB_LOCK_DEADLOCK) {
620                 retry++;
621                 continue;
622             }
623             else {
624         char *foo = db_strerror(ret);
625         abort();
626   }
627         }
628   
629         if ((ret = dbp->put(dbp, tid, key, data, access_method)) != 0) {
630             if (ret == DB_LOCK_DEADLOCK) {
631                 tid->abort(tid);
632                 retry++;
633                 continue;
634             }
635             else {
636     char *strError = db_strerror(ret);
637     icalerror_warn("icalbdbset_put faild: ");
638     icalerror_warn(strError);
639     tid->abort(tid);
640     return ICAL_FILE_ERROR;
641   }
642         }
643
644   if ((ret = tid->commit(tid, 0)) != 0) {
645             if (ret == DB_LOCK_DEADLOCK) {
646                 tid->abort(tid);
647                 retry++;
648                 continue;
649             }
650             else {
651         char * foo = db_strerror(ret);
652         abort();
653   }
654        }
655
656        done = 1;   /* all is well */
657     }
658
659     if (!done) {
660         if (tid != NULL) tid->abort(tid);
661         return ICAL_FILE_ERROR;
662     }
663     else
664   return ICAL_NO_ERROR;
665 }
666
667 int icalbdbset_get(DB *dbp, DB_TXN *tid, DBT *key, DBT *data, int flags)
668 {
669     return (dbp->get(dbp, tid, key, data, flags));
670 }
671
672 /** Return the path of the database file **/
673
674 const char* icalbdbset_path(icalset* set)
675 {
676     icalerror_check_arg_rz((set!=0),"set");
677
678     return set->dsn;
679 }
680
681 const char* icalbdbset_subdb(icalset* set)
682 {
683     icalbdbset *bset = (icalbdbset*)set;
684     icalerror_check_arg_rz((bset!=0),"bset");
685
686     return bset->subdb;
687 }
688
689
690 /** Write changes out to the database file.
691  */
692
693 icalerrorenum icalbdbset_commit(icalset *set) {
694   DB *dbp;
695   DBC *dbcp;
696   DBT key, data;
697   icalcomponent *c;
698   char *str;
699   int ret=0;
700     int           reterr = ICAL_NO_ERROR;
701   char keystore[256];
702     char          uidbuf[256];
703   char datastore[1024];
704   char *more_mem = NULL;
705     DB_TXN        *tid = NULL;
706   icalbdbset *bset = (icalbdbset*)set;
707   int bad_uid_counter = 0;
708     int           retry = 0, done = 0, completed = 0, deadlocked = 0;
709
710   icalerror_check_arg_re((bset!=0),"bset",ICAL_BADARG_ERROR);  
711
712   dbp = bset->dbp;
713   icalerror_check_arg_re((dbp!=0),"dbp is invalid",ICAL_BADARG_ERROR);
714
715   if (bset->changed == 0)
716     return ICAL_NO_ERROR;
717
718   memset(&key, 0, sizeof(key));
719   memset(&data, 0, sizeof(data));
720
721   key.flags = DB_DBT_USERMEM; 
722   key.data = keystore;
723   key.ulen = sizeof(keystore);
724
725   data.flags = DB_DBT_USERMEM;
726   data.data = datastore;
727   data.ulen = sizeof(datastore);
728   
729   if (!ICAL_DB_ENV)
730         icalbdbset_init_dbenv(NULL, NULL);
731
732     while ((retry < MAX_RETRY) && !done) {
733
734   if ((ret = ICAL_DB_ENV->txn_begin(ICAL_DB_ENV, NULL, &tid, 0)) != 0) {
735             if (ret ==  DB_LOCK_DEADLOCK) {
736                 retry++;
737                 continue;
738             }
739             else if (ret == DB_RUNRECOVERY) {
740                 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "icalbdbset_commit: txn_begin failed");
741         abort();
742   }
743             else {
744                 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "icalbdbset_commit");
745                 return ICAL_INTERNAL_ERROR;
746             }
747         }
748
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) {
751             tid->abort(tid);
752             if (ret == DB_LOCK_DEADLOCK) {
753                 retry++;
754                 continue;
755             }
756             else if (ret == DB_RUNRECOVERY) {
757                 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "curor failed");
758     abort();
759             }
760             else {
761                 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "curor failed");
762     /* leave bset->changed set to true */
763     return ICAL_INTERNAL_ERROR;
764   }
765         }
766
767   /* fetch the key/data pair, then delete it */
768         completed = 0;
769         while (!completed && !deadlocked) {
770     ret = dbcp->c_get(dbcp, &key, &data, DB_NEXT);
771     if (ret == DB_NOTFOUND) {
772                 completed = 1;
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) {
779                 deadlocked = 1;
780             } else if (ret == DB_RUNRECOVERY) {
781                 tid->abort(tid);
782                 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "c_get failed.");
783       abort();
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) {
791                         deadlocked = 1;
792          } else {
793             char *foo = db_strerror(ret);
794             abort();
795          }
796        }
797             } else {  /* some other non-fatal error */
798                 dbcp->c_close(dbcp);
799                 tid->abort(tid);
800                 if (more_mem) {
801                     free(more_mem);
802                     more_mem = NULL;
803                 }
804                 return ICAL_INTERNAL_ERROR;
805     }
806   }
807
808   if (more_mem) {
809           free(more_mem);
810           more_mem = NULL;
811   }
812
813         if (deadlocked) {
814             dbcp->c_close(dbcp);
815             tid->abort(tid);
816             retry++;
817             continue;  /* next retry */
818         }
819
820         deadlocked = 0;
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)) {
824
825             memset(&key, 0, sizeof(key));
826             memset(&data, 0, sizeof(data));
827
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++);
834                     key.data = uidbuf;
835       } else {
836                     key.data = uidstr;
837       }
838     } else {
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++);
843                     key.data = uidbuf;
844       } else {
845                     key.data = relcalid;
846       }
847     }
848     key.size = strlen(key.data);
849
850     str = icalcomponent_as_ical_string_r(c);
851     data.data = str;
852     data.size = strlen(str);
853
854     if ((ret = dbcp->c_put(dbcp, &key, &data, DB_KEYLAST)) != 0) {
855                 if (ret == DB_LOCK_DEADLOCK) {
856                     deadlocked = 1;
857                 }
858                 else if (ret == DB_RUNRECOVERY) {
859                     ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "c_put failed.");
860                     abort();
861                 }
862                 else {
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;
866                 }
867             }
868     }
869
870     free(str);
871
872         if (deadlocked) {
873             dbcp->c_close(dbcp);
874             tid->abort(tid);
875             retry++;
876             continue;
877   } 
878
879   if ((ret = dbcp->c_close(dbcp)) != 0) {
880             tid->abort(tid);
881             if (ret == DB_LOCK_DEADLOCK) {
882                 retry++;
883                 continue;
884             }
885             else if (ret == DB_RUNRECOVERY) {
886                 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "c_closed failed.");
887                 abort();
888             }
889             else {
890                 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "c_closed failed.");
891                 reterr = ICAL_INTERNAL_ERROR;
892             }
893   }
894
895   if ((ret = tid->commit(tid, 0)) != 0) {
896             tid->abort(tid);
897             if (ret == DB_LOCK_DEADLOCK) {
898                 retry++;
899                 continue;
900             }
901             else if (ret == DB_RUNRECOVERY) {
902                 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "commit failed.");
903     abort();
904   }
905             else {
906                 ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "commit failed.");
907                 reterr = ICAL_INTERNAL_ERROR;
908             }
909         }
910
911         done = 1;
912     }
913
914   bset->changed = 0;    
915     return reterr;
916
917
918
919 void icalbdbset_mark(icalset* set)
920 {
921     icalbdbset *bset = (icalbdbset*)set;
922     icalerror_check_arg_rv((bset!=0),"bset");
923
924     bset->changed = 1;
925 }
926
927
928 icalcomponent* icalbdbset_get_component(icalset* set)
929 {
930     icalbdbset *bset = (icalbdbset*)set;
931     icalerror_check_arg_rz((bset!=0),"bset");
932
933     return bset->cluster;
934 }
935
936
937 /* manipulate the components in the cluster */
938
939 icalerrorenum icalbdbset_add_component(icalset *set,
940                                         icalcomponent* child)
941 {
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);
945
946     icalcomponent_add_component(bset->cluster,child);
947
948     icalbdbset_mark(set);
949
950     return ICAL_NO_ERROR;
951 }
952
953
954 icalerrorenum icalbdbset_remove_component(icalset *set,
955                                            icalcomponent* child)
956 {
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);
960
961     icalcomponent_remove_component(bset->cluster,child);
962
963     icalbdbset_mark(set);
964
965     return ICAL_NO_ERROR;
966 }
967
968
969 int icalbdbset_count_components(icalset *set,
970                                  icalcomponent_kind kind)
971 {
972     icalbdbset *bset = (icalbdbset*)set;
973
974     if(set == 0){
975         icalerror_set_errno(ICAL_BADARG_ERROR);
976         return -1;
977     }
978
979     return icalcomponent_count_components(bset->cluster,kind);
980 }
981
982
983 /** Set the gauge **/
984
985 icalerrorenum icalbdbset_select(icalset* set, icalgauge* gauge)
986 {
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);
990
991     bset->gauge = gauge;
992
993     return ICAL_NO_ERROR;
994 }
995
996     
997 /** Clear the gauge **/
998
999 void icalbdbset_clear(icalset* set)
1000 {
1001   icalbdbset *bset = (icalbdbset*)set;
1002   icalerror_check_arg_rv((bset!=0),"bset");
1003     
1004   bset->gauge = 0;
1005 }
1006
1007
1008 icalcomponent* icalbdbset_fetch(icalset* set, icalcomponent_kind kind, const char* uid)
1009 {
1010     icalcompiter i;    
1011   icalbdbset *bset = (icalbdbset*)set;
1012   icalerror_check_arg_rz((bset!=0),"bset");
1013     
1014   for(i = icalcomponent_begin_component(bset->cluster, kind);
1015         icalcompiter_deref(&i)!= 0; icalcompiter_next(&i)){
1016         
1017         icalcomponent *this = icalcompiter_deref(&i);
1018         icalproperty  *p = NULL;
1019         const char    *this_uid = NULL;
1020
1021         if (this != 0){
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);
1025             } else {
1026                 p = icalcomponent_get_first_property(this,ICAL_UID_PROPERTY);
1027                 if (p != NULL) this_uid = icalproperty_get_uid(p);
1028             }
1029
1030             if(this_uid==NULL){
1031                 icalerror_warn("icalbdbset_fetch found a component with no UID");
1032                 continue;
1033             }
1034
1035             if (strcmp(uid,this_uid)==0){
1036                 return this;
1037             }
1038         }
1039     }
1040
1041     return 0;
1042 }
1043
1044
1045 int icalbdbset_has_uid(icalset* store,const char* uid)
1046 {
1047     assert(0); /* HACK, not implemented */
1048     return 0;
1049 }
1050
1051
1052 /******* support routines for icalbdbset_fetch_match *********/
1053
1054 struct icalbdbset_id {
1055     char* uid;
1056     char* recurrence_id;
1057     int sequence;
1058 };
1059
1060 void icalbdbset_id_free(struct icalbdbset_id *id)
1061 {
1062     if(id->recurrence_id != 0){
1063         free(id->recurrence_id);
1064     }
1065
1066     if(id->uid != 0){
1067         free(id->uid);
1068     }
1069
1070 }
1071
1072 struct icalbdbset_id icalbdbset_get_id(icalcomponent* comp)
1073 {
1074
1075     icalcomponent *inner;
1076     struct icalbdbset_id id;
1077     icalproperty *p;
1078
1079     inner = icalcomponent_get_first_real_component(comp);
1080     
1081     p = icalcomponent_get_first_property(inner, ICAL_UID_PROPERTY);
1082
1083     assert(p!= 0);
1084
1085     id.uid = strdup(icalproperty_get_uid(p));
1086
1087     p = icalcomponent_get_first_property(inner, ICAL_SEQUENCE_PROPERTY);
1088
1089     if(p == 0) {
1090         id.sequence = 0;
1091     } else { 
1092         id.sequence = icalproperty_get_sequence(p);
1093     }
1094
1095     p = icalcomponent_get_first_property(inner, ICAL_RECURRENCEID_PROPERTY);
1096
1097     if (p == 0){
1098         id.recurrence_id = 0;
1099     } else {
1100         icalvalue *v;
1101         v = icalproperty_get_value(p);
1102         id.recurrence_id = icalvalue_as_ical_string_r(v);
1103
1104         assert(id.recurrence_id != 0);
1105     }
1106
1107     return id;
1108 }
1109
1110 /* Find the component that is related to the given
1111    component. Currently, it just matches based on UID and
1112    RECURRENCE-ID */
1113
1114 icalcomponent* icalbdbset_fetch_match(icalset* set, icalcomponent *comp)
1115 {
1116     icalbdbset *bset = (icalbdbset*)set;
1117     icalcompiter i;    
1118     struct icalbdbset_id comp_id, match_id;
1119     
1120     icalerror_check_arg_rz((bset!=0),"bset");
1121     comp_id = icalbdbset_get_id(comp);
1122
1123     for(i = icalcomponent_begin_component(bset->cluster,ICAL_ANY_COMPONENT);
1124         icalcompiter_deref(&i)!= 0; icalcompiter_next(&i)){
1125         
1126         icalcomponent *match = icalcompiter_deref(&i);
1127
1128         match_id = icalbdbset_get_id(match);
1129
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 )){
1133
1134             /* HACK. What to do with SEQUENCE? */
1135
1136             icalbdbset_id_free(&match_id);
1137             icalbdbset_id_free(&comp_id);
1138             return match;
1139             
1140         }
1141         
1142         icalbdbset_id_free(&match_id);
1143     }
1144
1145     icalbdbset_id_free(&comp_id);
1146     return 0;
1147
1148 }
1149
1150
1151 icalerrorenum icalbdbset_modify(icalset* set, icalcomponent *old,
1152                                  icalcomponent *newc)
1153 {
1154     assert(0); /* HACK, not implemented */
1155     return ICAL_NO_ERROR;
1156 }
1157
1158 /* caller is responsible to cal icalbdbset_free_cluster first */
1159 icalerrorenum  icalbdbset_set_cluster(icalset* set, icalcomponent* cluster)
1160 {
1161     icalbdbset *bset = (icalbdbset*)set;
1162     icalerror_check_arg_rz((bset!=0),"bset"); 
1163
1164     bset->cluster = cluster;
1165 }
1166
1167 icalerrorenum  icalbdbset_free_cluster(icalset* set)
1168 {
1169     icalbdbset *bset = (icalbdbset*)set;
1170     icalerror_check_arg_rz((bset!=0),"bset");
1171
1172     if (bset->cluster != NULL) icalcomponent_free(bset->cluster);
1173 }
1174
1175 icalcomponent* icalbdbset_get_cluster(icalset* set)
1176 {
1177     icalbdbset *bset = (icalbdbset*)set;
1178     icalerror_check_arg_rz((bset!=0),"bset");
1179
1180     return (bset->cluster);
1181 }
1182
1183
1184 /** Iterate through components. */
1185 icalcomponent* icalbdbset_get_current_component (icalset* set)
1186 {
1187     icalbdbset *bset = (icalbdbset*)set;
1188
1189     icalerror_check_arg_rz((bset!=0),"bset");
1190
1191     return icalcomponent_get_current_component(bset->cluster);
1192 }
1193
1194
1195 icalcomponent* icalbdbset_get_first_component(icalset* set)
1196 {
1197     icalbdbset *bset = (icalbdbset*)set;
1198     icalcomponent *c=0;
1199
1200     icalerror_check_arg_rz((bset!=0),"bset");
1201
1202     do {
1203         if (c == 0)
1204             c = icalcomponent_get_first_component(bset->cluster,
1205                                                   ICAL_ANY_COMPONENT);
1206         else
1207             c = icalcomponent_get_next_component(bset->cluster,
1208                                                  ICAL_ANY_COMPONENT);
1209
1210              if(c != 0 && (bset->gauge == 0 ||
1211                       icalgauge_compare(bset->gauge,c) == 1)){
1212             return c;
1213         }
1214
1215       } while (c!=0);
1216
1217     return 0;
1218 }
1219
1220
1221 icalsetiter icalbdbset_begin_component(icalset* set, icalcomponent_kind kind, icalgauge* gauge, const char* tzid)
1222 {
1223     icalsetiter itr = icalsetiter_null;
1224     icalcomponent* comp = NULL;
1225     icalcompiter citr;
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;
1231     int g = 0;
1232     int orig_time_was_utc = 0;
1233
1234     icalerror_check_arg_re((set!=0), "set", icalsetiter_null);
1235
1236     itr.gauge = gauge;
1237     itr.tzid = tzid;
1238
1239     citr = icalcomponent_begin_component(bset->cluster, kind);
1240     comp = icalcompiter_deref(&citr);
1241
1242     if (gauge == 0) {
1243         itr.iter = citr;
1244         return itr;
1245     }
1246
1247     /* if there is a gauge, the first matched component is returned */
1248     while (comp != 0) {
1249
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);
1254
1255         if (rrule != 0
1256             && g == 1) {
1257
1258             /* it is a recurring event */
1259
1260             u_zone = icaltimezone_get_builtin_timezone(itr.tzid);
1261
1262             /* use UTC, if that's all we have. */
1263             if (!u_zone)
1264                 u_zone = icaltimezone_get_utc_timezone();
1265
1266
1267             recur = icalproperty_get_rrule(rrule);
1268
1269             if (icalcomponent_isa(comp) == ICAL_VEVENT_COMPONENT) {
1270                 dtstart = icalcomponent_get_first_property(comp, ICAL_DTSTART_PROPERTY);
1271                 if (dtstart)
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);
1275                     if (due)
1276                         start = icalproperty_get_due(due);
1277             }
1278
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;
1284             }
1285
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;
1290             }
1291             else {
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);
1296                     itr.ritr = NULL;
1297                     /* no matched occurence */
1298                     goto getNextComp;
1299                 } else {
1300                     itr.last_component = comp;
1301                 }
1302             }
1303
1304             /* if it is excluded, do next one */
1305             if (icalproperty_recurrence_is_excluded(comp, &start, &next)) {
1306                 icalrecur_iterator_decrement_count(itr.ritr);
1307                 continue;
1308             }
1309
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);
1313             if (prop == 0)
1314                 icalcomponent_add_property(comp, icalproperty_new_recurrenceid(next));
1315             else
1316                 icalproperty_set_recurrenceid(prop, next);
1317
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());
1321
1322         } /* end of a recurring event */
1323
1324         if (gauge == 0 || icalgauge_compare(itr.gauge, comp) == 1) {
1325             /* find a matched and return it */
1326             itr.iter = citr;
1327             return itr;
1328         }
1329        
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 */
1333 getNextComp: 
1334         if ((rrule != NULL && itr.last_component == NULL) ||
1335            (rrule == NULL)) {
1336             comp =  icalcompiter_next(&citr);
1337             comp = icalcompiter_deref(&citr);
1338     }
1339     } /* while */
1340
1341     /* no matched component has found */
1342     return icalsetiter_null;
1343 }
1344
1345 icalcomponent* icalbdbset_form_a_matched_recurrence_component(icalsetiter* itr)
1346 {
1347     icalcomponent* comp = NULL;
1348     struct icaltimetype start, next, end;
1349     icalproperty *dtstart, *rrule, *prop, *due;
1350     struct icalrecurrencetype recur;
1351     icaltimezone *u_zone;
1352     int g = 0;
1353     int orig_time_was_utc = 0;
1354
1355     comp = itr->last_component;
1356
1357     if (comp == NULL || itr->gauge == NULL) {
1358         return NULL;
1359     }
1360
1361
1362     rrule = icalcomponent_get_first_property(comp, ICAL_RRULE_PROPERTY);
1363     /* if there is no RRULE, simply return to the caller */
1364     if (rrule == NULL)
1365         return NULL;
1366
1367     u_zone = icaltimezone_get_builtin_timezone(itr->tzid);
1368
1369     /* use UTC, if that's all we have. */
1370     if (!u_zone)
1371         u_zone = icaltimezone_get_utc_timezone();
1372
1373     recur = icalproperty_get_rrule(rrule);
1374
1375     if (icalcomponent_isa(comp) == ICAL_VEVENT_COMPONENT) {
1376         dtstart = icalcomponent_get_first_property(comp, ICAL_DTSTART_PROPERTY);
1377         if (dtstart)
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);
1381         if (due)
1382             start = icalproperty_get_due(due);
1383     }
1384
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;
1390     }
1391
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;
1396     } else {
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);
1402             itr->ritr = NULL;
1403             /* no more pending matched occurence,
1404              * all the pending matched occurences have been returned */
1405             return NULL;
1406         } else {
1407             itr->last_component = comp;
1408         }
1409     }
1410
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);
1414        return NULL; 
1415     }
1416
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);
1420     if (prop == 0)
1421         icalcomponent_add_property(comp, icalproperty_new_recurrenceid(next));
1422     else
1423         icalproperty_set_recurrenceid(prop, next);
1424
1425     if (orig_time_was_utc) {
1426         next = icaltime_convert_to_zone(next, icaltimezone_get_utc_timezone());
1427     }
1428
1429
1430      if (itr->gauge == 0 || icalgauge_compare(itr->gauge, comp) == 1) {
1431          /* find a matched and return it */
1432          return comp;
1433      } 
1434
1435      /* not matched */
1436      return NULL;
1437
1438 }
1439
1440 icalcomponent* icalbdbsetiter_to_next(icalset *set, icalsetiter* i)
1441 {
1442
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;
1449     int g = 0;
1450     int orig_time_was_utc = 0;
1451
1452     do {
1453
1454         /* no pending occurence, read the next component */
1455         if (i->last_component == NULL) {
1456             comp = icalcompiter_next(&(i->iter));
1457         }
1458         else {
1459             comp = i->last_component;
1460         }
1461
1462         /* no next component, simply return */
1463         if (comp == 0) return NULL;
1464         if (i->gauge == 0) return comp;
1465
1466         /* finding the next matched component and return it to the caller */
1467
1468         rrule = icalcomponent_get_first_property(comp, ICAL_RRULE_PROPERTY);
1469         g = icalgauge_get_expand(i->gauge);
1470
1471         /* a recurring component with expand query */
1472         if (rrule != 0
1473             && g == 1) {
1474
1475             u_zone = icaltimezone_get_builtin_timezone(i->tzid);
1476
1477             /* use UTC, if that's all we have. */
1478             if (!u_zone)
1479                 u_zone = icaltimezone_get_utc_timezone();
1480
1481             recur = icalproperty_get_rrule(rrule);
1482
1483             if (icalcomponent_isa(comp) == ICAL_VEVENT_COMPONENT) {
1484                 dtstart = icalcomponent_get_first_property(comp, ICAL_DTSTART_PROPERTY);
1485                 if (dtstart)
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);
1489                 if (due)
1490                     start = icalproperty_get_due(due);
1491             }
1492
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;
1498             }
1499
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;
1504             } else {
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);
1509                     i->ritr = NULL;
1510                     /* no more occurence, should go to get next component */
1511                     continue;
1512                 } else {
1513                     i->last_component = comp;
1514                 }
1515             }
1516
1517             /* if it is excluded, do next one */
1518             if (icalproperty_recurrence_is_excluded(comp, &start, &next)) {
1519                 icalrecur_iterator_decrement_count(i->ritr);
1520                 continue;
1521             }
1522
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);
1526             if (prop == 0)
1527                icalcomponent_add_property(comp, icalproperty_new_recurrenceid(next));
1528             else
1529                icalproperty_set_recurrenceid(prop, next);
1530
1531             if (orig_time_was_utc) {
1532                 next = icaltime_convert_to_zone(next, icaltimezone_get_utc_timezone());
1533             }
1534  
1535         } /* end of recurring event with expand query */
1536
1537         if(comp != 0 && (i->gauge == 0 ||
1538             icalgauge_compare(i->gauge, comp) == 1)){
1539            /* found a matched, return it */ 
1540             return comp;
1541         }
1542     } while (comp != 0);
1543
1544     return 0;
1545
1546 }
1547
1548 icalcomponent* icalbdbset_get_next_component(icalset* set)
1549 {
1550     icalbdbset *bset = (icalbdbset*)set;
1551     icalcomponent *c=0;
1552
1553     struct icaltimetype start, next;
1554     icalproperty *dtstart, *rrule, *prop, *due;
1555     struct icalrecurrencetype recur;
1556     int g = 0;
1557
1558     icalerror_check_arg_rz((bset!=0),"bset");
1559     
1560     do {
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)){
1565                     return c;
1566                 }
1567
1568     } while(c != 0);
1569     
1570     return 0;
1571 }
1572
1573 int icalbdbset_begin_transaction(DB_TXN* parent_tid, DB_TXN** tid)
1574 {
1575     return (ICAL_DB_ENV->txn_begin(ICAL_DB_ENV, parent_tid, tid, 0));
1576 }
1577
1578 int icalbdbset_commit_transaction(DB_TXN* txnid)
1579 {
1580     return (txnid->commit(txnid, 0));
1581 }
1582
1583
1584 static int _compare_keys(DB *dbp, const DBT *a, const DBT *b)
1585 {
1586 /* 
1587  * Returns: 
1588  * < 0 if a < b 
1589  * = 0 if a = b 
1590  * > 0 if a > b 
1591  */ 
1592         
1593     char*  ac = (char*)a->data;
1594     char*  bc = (char*)b->data;
1595     return (strncmp(ac, bc, a->size));
1596 }
1597
1598
1599