$darkmode
Libical API Documentation 4.0 STABLE VERSION Visit the v3.0 documentation
icalbdbset.c
Go to the documentation of this file.
1 /*======================================================================
2  FILE: icalbdbset.c
3 
4  SPDX-FileCopyrightText: 2001, Critical Path
5  SPDX-License-Identifier: LGPL-2.1-only OR MPL-2.0
6 ======================================================================*/
7 
15 #ifdef HAVE_CONFIG_H
16 #include <config.h>
17 #endif
18 
19 #include "icalbdbset.h"
20 #include "icalbdbsetimpl.h"
21 
22 #include "icalerror_p.h"
23 #include "icalparser.h"
24 #include "icaltimezone.h"
25 #include "icalvalue.h"
26 
27 #include <errno.h>
28 #include <stdlib.h>
29 
30 #define MAX_RETRY 5
31 
32 #if !defined(DB_VERSION_MAJOR)
33 #define DB_VERSION_MAJOR 1 //assume ancient version
34 #endif
35 
36 static int _compare_ids(const char *compid, const char *matchid);
37 #if DB_VERSION_MAJOR > 5
38 static int _compare_keys(DB *dbp, const DBT *a, const DBT *b, size_t *locp);
39 #else
40 static int _compare_keys(DB *dbp, const DBT *a, const DBT *b);
41 #endif
42 
44 static icalbdbset_options icalbdbset_options_default =
45  {
46  ICALBDB_EVENTS, DB_BTREE, 0644, 0, NULL, NULL};
47 
48 static DB_ENV *ICAL_DB_ENV = 0;
49 
52 int icalbdbset_init_dbenv(char *db_env_dir,
53  void (*logDbFunc)(const DB_ENV *, const char *, const char *))
54 {
55  int ret;
56  u_int32_t flags;
57 
58  if (db_env_dir) {
59  struct stat env_dir_sb;
60 
61  if (stat(db_env_dir, &env_dir_sb)) {
62  fprintf(stderr, "The directory '%s' is missing, please create it.\n", db_env_dir);
63  return EINVAL;
64  }
65  }
66 
67  ret = db_env_create(&ICAL_DB_ENV, 0);
68 
69  if (ret) {
70  /* some kind of error... */
71  return ret;
72  }
73 
74  /* Do deadlock detection internally */
75  if ((ret = ICAL_DB_ENV->set_lk_detect(ICAL_DB_ENV, DB_LOCK_DEFAULT)) != 0) {
76  /*char *foo = db_strerror(ret); */
77  fprintf(stderr, "Could not initialize the database locking environment\n");
78  return ret;
79  }
80 
81  flags = (u_int32_t)(DB_INIT_LOCK | DB_INIT_TXN | DB_CREATE | DB_THREAD |
82  DB_RECOVER | DB_INIT_LOG | DB_INIT_MPOOL);
83 #if defined(_WIN32) //krazy:exclude=cpp
84  ret = ICAL_DB_ENV->open(ICAL_DB_ENV, db_env_dir, flags, 0 /*ignored on Windows*/);
85 #else
86  ret = ICAL_DB_ENV->open(ICAL_DB_ENV, db_env_dir, flags, S_IRUSR | S_IWUSR);
87 #endif
88 
89  if (ret) {
90  /*char *foo = db_strerror(ret); */
91  ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "dbenv->open");
92  return ret;
93  }
94 
95  /* display additional error messages */
96  if (logDbFunc != NULL) {
97  ICAL_DB_ENV->set_errcall(ICAL_DB_ENV, logDbFunc);
98  }
99 
100  return ret;
101 }
102 
103 void icalbdbset_checkpoint(void)
104 {
105  int ret;
106 
107  switch (ret = ICAL_DB_ENV->txn_checkpoint(ICAL_DB_ENV, 0, 0, 0)) {
108  case 0:
109  break;
110  default:
111  ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "checkpoint failed");
112  abort();
113  }
114 }
115 
116 void icalbdbset_rmdbLog(void)
117 {
118  char **listp;
119 
120  /* remove log files that are can be archived (ie. no longer needed) */
121  if (ICAL_DB_ENV->log_archive(ICAL_DB_ENV, &listp, DB_ARCH_ABS) == 0) {
122  if (*listp != NULL) {
123  int ii = 0;
124 
125  while (listp[ii] != NULL) {
126  (void)unlink(listp[ii]);
127  ii++;
128  }
129  free((void *)listp);
130  }
131  }
132 }
133 
134 int icalbdbset_cleanup(void)
135 {
136  int ret = 0;
137 
138  /* one last checkpoint.. */
139  icalbdbset_checkpoint();
140 
141  /* remove logs that are not needed anymore */
142  icalbdbset_rmdbLog();
143 
144  if (ICAL_DB_ENV) {
145  ret = ICAL_DB_ENV->close(ICAL_DB_ENV, 0);
146  }
147 
148  return ret;
149 }
150 
151 DB_ENV *icalbdbset_get_env(void)
152 {
153  return ICAL_DB_ENV;
154 }
155 
163 /* This populates a cluster with the entire contents of a database */
164 static icalerrorenum icalbdbset_read_database(icalbdbset *bset, char *(*pfunc)(const DBT *dbt))
165 {
166  DB *dbp;
167  DBC *dbcp;
168  DBT key, data;
169  char *str;
170  int ret = EINVAL;
171  char keystore[256];
172  char datastore[1024];
173  char *more_mem = NULL;
174  DB_TXN *tid;
175 
176  _unused(pfunc);
177 
178  memset(&key, 0, sizeof(DBT));
179  memset(&data, 0, sizeof(DBT));
180 
181  if (bset->sdbp) {
182  dbp = bset->sdbp;
183  } else {
184  dbp = bset->dbp;
185  }
186 
187  if (!dbp) {
188  return ICAL_FILE_ERROR;
189  }
190 
191  bset->cluster = icalcomponent_new(ICAL_XROOT_COMPONENT);
192 
193  if (ICAL_DB_ENV->txn_begin(ICAL_DB_ENV, NULL, &tid, 0) != 0) {
194  abort();
195  }
196 
197  /* acquire a cursor for the database */
198  if ((ret = dbp->cursor(dbp, tid, &dbcp, 0)) != 0) {
199  dbp->err(dbp, ret, "primary index");
200  goto err1;
201  }
202 
203  key.flags = DB_DBT_USERMEM;
204  key.data = keystore;
205  key.ulen = (u_int32_t)sizeof(keystore);
206 
207  data.flags = DB_DBT_USERMEM;
208  data.data = datastore;
209  data.ulen = (u_int32_t)sizeof(datastore);
210 
211  /* fetch the key/data pair */
212  while (1) {
213  ret = dbcp->c_get(dbcp, &key, &data, DB_NEXT);
214  if (ret == DB_NOTFOUND) {
215  break;
216  } else if (ret == ENOMEM) {
217  if (more_mem) {
218  free(more_mem);
219  }
220  more_mem = malloc(data.ulen + 1024);
221  data.data = more_mem;
222  data.ulen = data.ulen + 1024;
223  } else if (ret == DB_LOCK_DEADLOCK) {
224  /*char *foo = db_strerror(ret); */
225  abort(); /* should retry in case of DB_LOCK_DEADLOCK */
226  } else if (ret) {
227  /*char *foo = db_strerror(ret); */
228  /* some other weird-ass error */
229  dbp->err(dbp, ret, "cursor");
230  abort();
231  } else {
232  icalcomponent *cl;
233 
234  /* this prevents an array read bounds error */
235  if ((str = (char *)calloc(data.size + 1, sizeof(char))) == NULL) {
236  goto err2;
237  }
238  memcpy(str, (char *)data.data, data.size);
239 
240  cl = icalparser_parse_string(str);
241 
242  icalcomponent_add_component(bset->cluster, cl);
243  free(str);
244  }
245  }
246  if (ret != DB_NOTFOUND) {
247  goto err2;
248  }
249 
250  if (more_mem) {
251  free(more_mem);
252  more_mem = NULL;
253  }
254 
255  if (dbcp->c_close(dbcp) != 0) {
256  abort(); /* should retry in case of DB_LOCK_DEADLOCK */
257  }
258 
259  if (tid->commit(tid, 0) != 0) {
260  abort();
261  }
262 
263  return ICAL_NO_ERROR;
264 
265 err2:
266  if (more_mem) {
267  free(more_mem);
268  }
269  dbcp->c_close(dbcp);
270  abort(); /* should retry in case of DB_LOCK_DEADLOCK */
271  return ICAL_INTERNAL_ERROR;
272 
273 err1:
274  dbp->err(dbp, ret, "cursor index");
275  abort();
276  return ICAL_FILE_ERROR;
277 }
278 
279 icalset *icalbdbset_init(icalset *set, const char *dsn, void *options_in)
280 {
281  icalbdbset *bset = (icalbdbset *)set;
282  icalbdbset_options *options = (icalbdbset_options *)options_in;
283  DB *cal_db;
284  const char *subdb_name = NULL;
285 
286  _unused(dsn);
287 
288  if (options == (icalbdbset_options *)NULL) {
289  options = &icalbdbset_options_default;
290  }
291 
292  switch (options->subdb) {
293  case ICALBDB_CALENDARS:
294  subdb_name = "calendars";
295  break;
296  case ICALBDB_EVENTS:
297  subdb_name = "events";
298  break;
299  case ICALBDB_TODOS:
300  subdb_name = "todos";
301  break;
302  case ICALBDB_REMINDERS:
303  subdb_name = "reminders";
304  break;
305  }
306 
307  cal_db = icalbdbset_bdb_open(set->dsn,
308  subdb_name, options->dbtype, options->mode, options->flag);
309  if (cal_db == NULL) {
310  return NULL;
311  }
312 
313  bset->dbp = cal_db;
314  bset->sdbp = NULL;
315  bset->gauge = 0;
316  bset->cluster = 0;
317 
318  if (icalbdbset_read_database(bset, options->pfunc) != ICAL_NO_ERROR) {
319  return NULL;
320  }
321 
322  return (icalset *)bset;
323 }
324 
330 icalset *icalbdbset_new(const char *database_filename, icalbdbset_subdb_type subdb_type, int dbtype,
331  u_int32_t flag)
332 {
333  icalbdbset_options options = icalbdbset_options_default;
334 
335  options.subdb = subdb_type;
336  options.dbtype = dbtype;
337  options.flag = flag;
338 
339  /* this will in turn call icalbdbset_init */
340  return icalset_new(ICAL_BDB_SET, database_filename, &options);
341 }
342 
350 DB *icalbdbset_bdb_open_secondary(DB *dbp, const char *database, const char *sub_database,
351  int (*callback)(DB *db, const DBT *dbt1, const DBT *dbt2,
352  DBT *dbt3),
353  int type)
354 {
355  int ret;
356  u_int32_t flags;
357  DB *sdbp = NULL;
358 
359  if (!sub_database) {
360  return NULL;
361  }
362 
363  if (!ICAL_DB_ENV) {
364  if (icalbdbset_init_dbenv(NULL, NULL) != 0) {
365  return NULL;
366  }
367  }
368 
369  /* Open/create secondary */
370  if ((ret = db_create(&sdbp, ICAL_DB_ENV, 0)) != 0) {
371  ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "secondary index: %s", sub_database);
372  return NULL;
373  }
374 
375  if ((ret = sdbp->set_flags(sdbp, (u_int32_t)(DB_DUP | DB_DUPSORT))) != 0) {
376  ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "set_flags error for secondary index: %s", sub_database);
377  return NULL;
378  }
379 
380  flags = (u_int32_t)(DB_CREATE | DB_THREAD);
381  ret = sdbp->open(sdbp, NULL, database, sub_database, (DBTYPE)type, (u_int32_t)flags, 0644);
382  if (ret != 0) {
383  ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "failed to open secondary index: %s", sub_database);
384  if (ret == DB_RUNRECOVERY) {
385  abort();
386  } else {
387  return NULL;
388  }
389  }
390 
391  /* Associate the primary index with a secondary */
392  if ((ret = dbp->associate(dbp, NULL, sdbp, callback, 0)) != 0) {
393  ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "failed to associate secondary index: %s", sub_database);
394  return NULL;
395  }
396 
397  return sdbp;
398 }
399 
400 DB *icalbdbset_bdb_open(const char *path, const char *subdb, int dbtype, int mode, u_int32_t flag)
401 {
402  DB *dbp = NULL;
403  int ret;
404  u_int32_t flags;
405 
406  /* Initialize the correct set of db subsystems (see capdb.c) */
407  flags = (u_int32_t)(DB_CREATE | DB_THREAD);
408 
409  /* should just abort here instead of opening an env in the current dir.. */
410  if (!ICAL_DB_ENV) {
411  if (icalbdbset_init_dbenv(NULL, NULL) != 0) {
412  return NULL;
413  }
414  }
415 
416  /* Create and initialize database object, open the database. */
417  if (db_create(&dbp, ICAL_DB_ENV, 0) != 0) {
418  return NULL;
419  }
420 
421  /* set comparison function, if BTREE */
422  if (dbtype == DB_BTREE) {
423  dbp->set_bt_compare(dbp, _compare_keys);
424  }
425 
426  /* set DUP, DUPSORT */
427  if (flag != 0) {
428  dbp->set_flags(dbp, flag);
429  }
430 
431  if ((ret = dbp->open(dbp, NULL, path, subdb, (DBTYPE)dbtype, flags, mode)) != 0) {
432  ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "%s (database: %s): open failed.", path, subdb);
433  if (ret == DB_RUNRECOVERY) {
434  abort();
435  } else {
436  return NULL;
437  }
438  }
439 
440  return dbp;
441 }
442 
443 /* icalbdbset_parse_data -- parses using pfunc to unpack data. */
444 char *icalbdbset_parse_data(DBT *dbt, char *(*pfunc)(const DBT *dbt))
445 {
446  char *ret;
447 
448  if (pfunc) {
449  ret = (char *)pfunc(dbt);
450  } else {
451  ret = (char *)dbt->data;
452  }
453 
454  return ret;
455 }
456 
457 /* XXX add more to this */
458 void icalbdbset_free(icalset *set)
459 {
460  icalbdbset *bset = (icalbdbset *)set;
461 
462  icalerror_check_arg_rv((bset != 0), "bset");
463 
464  if (bset->cluster != 0) {
465  (void)icalbdbset_commit(set);
466  icalcomponent_free(bset->cluster);
467  bset->cluster = 0;
468  }
469 
470  if (bset->gauge != 0) {
471  icalgauge_free(bset->gauge);
472  }
473 
474  if (bset->path != 0) {
475  free((char *)bset->path);
476  bset->path = 0;
477  }
478 
479  if (bset->sindex != 0) {
480  free((char *)bset->sindex);
481  bset->sindex = 0;
482  }
483 
484  if (bset->dbp && (bset->dbp->close(bset->dbp, 0) != 0)) {
485  /* ?? */
486  }
487  bset->dbp = NULL;
488 }
489 
490 /* return cursor is in rdbcp */
491 int icalbdbset_acquire_cursor(DB *dbp, DB_TXN *tid, DBC **rdbcp)
492 {
493  int ret = 0;
494 
495  if ((ret = dbp->cursor(dbp, tid, rdbcp, 0)) != 0) {
496  dbp->err(dbp, ret, "couldn't open cursor");
497  goto err1;
498  }
499 
500  return ICAL_NO_ERROR;
501 
502 err1:
503  return ICAL_FILE_ERROR;
504 }
505 
506 /* returns key/data in arguments */
507 int icalbdbset_get_first(DBC *dbcp, DBT *key, DBT *data)
508 {
509  return icalbdbset_cget(dbcp, key, data, DB_FIRST);
510 }
511 
512 int icalbdbset_get_next(DBC *dbcp, DBT *key, DBT *data)
513 {
514  return icalbdbset_cget(dbcp, key, data, DB_NEXT);
515 }
516 
517 int icalbdbset_get_last(DBC *dbcp, DBT *key, DBT *data)
518 {
519  return icalbdbset_cget(dbcp, key, data, DB_LAST);
520 }
521 
522 int icalbdbset_get_key(DBC *dbcp, DBT *key, DBT *data)
523 {
524  return icalbdbset_cget(dbcp, key, data, DB_SET);
525 }
526 
527 int icalbdbset_delete(DB *dbp, DBT *key)
528 {
529  DB_TXN *tid;
530  int ret = 0;
531  int done = 0;
532  int retry = 0;
533 
534  while ((retry < MAX_RETRY) && !done) {
535  if ((ret = ICAL_DB_ENV->txn_begin(ICAL_DB_ENV, NULL, &tid, 0)) != 0) {
536  if (ret == DB_LOCK_DEADLOCK) {
537  retry++;
538  continue;
539  } else {
540  /*char *foo = db_strerror(ret); */
541  abort();
542  }
543  }
544 
545  if ((ret = dbp->del(dbp, tid, key, 0)) != 0) {
546  if (ret == DB_NOTFOUND) {
547  /* do nothing - not an error condition */
548  } else if (ret == DB_LOCK_DEADLOCK) {
549  tid->abort(tid);
550  retry++;
551  continue;
552  } else {
553  char *strError = db_strerror(ret);
554 
555  icalerror_warn("icalbdbset_delete failed: ");
556  icalerror_warn(strError);
557  tid->abort(tid);
558  return ICAL_FILE_ERROR;
559  }
560  }
561 
562  if ((ret = tid->commit(tid, 0)) != 0) {
563  if (ret == DB_LOCK_DEADLOCK) {
564  tid->abort(tid);
565  retry++;
566  continue;
567  } else {
568  /*char *foo = db_strerror(ret); */
569  abort();
570  }
571  }
572 
573  done = 1; /* all is well */
574  }
575 
576  if (!done) {
577  if (tid != NULL) {
578  tid->abort(tid);
579  }
580  }
581 
582  return ret;
583 }
584 
585 int icalbdbset_cget(DBC *dbcp, DBT *key, DBT *data, u_int32_t access_method)
586 {
587  key->flags |= DB_DBT_MALLOC; /* change these to DB_DBT_USERMEM */
588  data->flags |= DB_DBT_MALLOC;
589 
590  /* fetch the key/data pair */
591  if (dbcp->c_get(dbcp, key, data, access_method) != 0) {
592  goto err1;
593  }
594 
595  return ICAL_NO_ERROR;
596 
597 err1:
598  return ICAL_FILE_ERROR;
599 }
600 
601 int icalbdbset_cput(DBC *dbcp, DBT *key, DBT *data, u_int32_t access_method)
602 {
603  _unused(access_method);
604 
605  key->flags |= DB_DBT_MALLOC; /* change these to DB_DBT_USERMEM */
606  data->flags |= DB_DBT_MALLOC;
607 
608  /* fetch the key/data pair */
609  if (dbcp->c_put(dbcp, key, data, 0) != 0) {
610  goto err1;
611  }
612 
613  return ICAL_NO_ERROR;
614 
615 err1:
616  return ICAL_FILE_ERROR;
617 }
618 
619 int icalbdbset_put(DB *dbp, DBT *key, DBT *data, u_int32_t access_method)
620 {
621  DB_TXN *tid = NULL;
622  int retry = 0;
623  int done = 0;
624 
625  while ((retry < MAX_RETRY) && !done) {
626  int ret;
627  if ((ret = ICAL_DB_ENV->txn_begin(ICAL_DB_ENV, NULL, &tid, 0)) != 0) {
628  if (ret == DB_LOCK_DEADLOCK) {
629  retry++;
630  continue;
631  } else {
632  /*char *foo = db_strerror(ret); */
633  abort();
634  }
635  }
636 
637  if ((ret = dbp->put(dbp, tid, key, data, access_method)) != 0) {
638  if (ret == DB_LOCK_DEADLOCK) {
639  tid->abort(tid);
640  retry++;
641  continue;
642  } else {
643  char *strError = db_strerror(ret);
644 
645  icalerror_warn("icalbdbset_put failed: ");
646  icalerror_warn(strError);
647  tid->abort(tid);
648  return ICAL_FILE_ERROR;
649  }
650  }
651 
652  if ((ret = tid->commit(tid, 0)) != 0) {
653  if (ret == DB_LOCK_DEADLOCK) {
654  tid->abort(tid);
655  retry++;
656  continue;
657  } else {
658  /*char *foo = db_strerror(ret); */
659  abort();
660  }
661  }
662 
663  done = 1; /* all is well */
664  }
665 
666  if (!done) {
667  if (tid != NULL) {
668  tid->abort(tid);
669  }
670  return ICAL_FILE_ERROR;
671  } else {
672  return ICAL_NO_ERROR;
673  }
674 }
675 
676 int icalbdbset_get(DB *dbp, DB_TXN *tid, DBT *key, DBT *data, u_int32_t flags)
677 {
678  return dbp->get(dbp, tid, key, data, flags);
679 }
680 
683 /* cppcheck-suppress constParameterPointer */
684 const char *icalbdbset_path(icalset *set)
685 {
686  icalerror_check_arg_rz((set != 0), "set");
687 
688  return set->dsn;
689 }
690 
691 const char *icalbdbset_subdb(icalset *set)
692 {
693  const icalbdbset *bset = (icalbdbset *)set;
694 
695  icalerror_check_arg_rz((bset != 0), "bset");
696 
697  return bset->subdb;
698 }
699 
704 {
705  DB *dbp;
706  DBC *dbcp;
707  DBT key, data;
708  icalcomponent *c;
709  char *str = NULL;
710  icalerrorenum reterr = ICAL_NO_ERROR;
711  char keystore[256];
712  char uidbuf[256];
713  char datastore[1024];
714  char *more_mem = NULL;
715  DB_TXN *tid = NULL;
716  icalbdbset *bset = (icalbdbset *)set;
717  int bad_uid_counter = 0;
718  int retry = 0, done = 0, completed = 0, deadlocked = 0;
719 
720  icalerror_check_arg_re((bset != 0), "bset", ICAL_BADARG_ERROR);
721 
722  dbp = bset->dbp;
723  icalerror_check_arg_re((dbp != 0), "dbp is invalid", ICAL_BADARG_ERROR);
724 
725  if (bset->changed == 0) {
726  return ICAL_NO_ERROR;
727  }
728 
729  memset(&key, 0, sizeof(key));
730  memset(&data, 0, sizeof(data));
731 
732  key.flags = DB_DBT_USERMEM;
733  key.data = keystore;
734  key.ulen = (u_int32_t)sizeof(keystore);
735 
736  data.flags = DB_DBT_USERMEM;
737  data.data = datastore;
738  data.ulen = (u_int32_t)sizeof(datastore);
739 
740  if (!ICAL_DB_ENV) {
741  if (icalbdbset_init_dbenv(NULL, NULL) != 0) {
742  return ICAL_INTERNAL_ERROR;
743  }
744  }
745 
746  while ((retry < MAX_RETRY) && !done) {
747  int ret;
748  if ((ret = ICAL_DB_ENV->txn_begin(ICAL_DB_ENV, NULL, &tid, 0)) != 0) {
749  if (ret == DB_LOCK_DEADLOCK) {
750  retry++;
751  continue;
752  } else if (ret == DB_RUNRECOVERY) {
753  ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "icalbdbset_commit: txn_begin failed");
754  abort();
755  } else {
756  ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "icalbdbset_commit");
757  return ICAL_INTERNAL_ERROR;
758  }
759  }
760 
761  /* first delete everything in the database, because there could be removed components */
762  if ((ret = dbp->cursor(dbp, tid, &dbcp, DB_DIRTY_READ)) != 0) {
763  tid->abort(tid);
764  if (ret == DB_LOCK_DEADLOCK) {
765  retry++;
766  continue;
767  } else if (ret == DB_RUNRECOVERY) {
768  ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "curor failed");
769  abort();
770  } else {
771  ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "curor failed");
772  /* leave bset->changed set to true */
773  return ICAL_INTERNAL_ERROR;
774  }
775  }
776 
777  /* fetch the key/data pair, then delete it */
778  completed = 0;
779  while (!completed && !deadlocked) {
780  ret = dbcp->c_get(dbcp, &key, &data, DB_NEXT);
781  if (ret == DB_NOTFOUND) {
782  completed = 1;
783  } else if (ret == ENOMEM) {
784  if (more_mem) {
785  free(more_mem);
786  }
787  more_mem = malloc(data.ulen + 1024);
788  data.data = more_mem;
789  data.ulen = data.ulen + 1024;
790  } else if (ret == DB_LOCK_DEADLOCK) {
791  deadlocked = 1;
792  } else if (ret == DB_RUNRECOVERY) {
793  tid->abort(tid);
794  ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "c_get failed.");
795  abort();
796  } else if (ret == 0) {
797  if ((ret = dbcp->c_del(dbcp, 0)) != 0) {
798  dbp->err(dbp, ret, "cursor");
799  if (ret == DB_KEYEMPTY) {
800  /* never actually created, continue onward.. */
801  /* do nothing - break; */
802  } else if (ret == DB_LOCK_DEADLOCK) {
803  deadlocked = 1;
804  } else {
805  /*char *foo = db_strerror(ret); */
806  abort();
807  }
808  }
809  } else { /* some other non-fatal error */
810  dbcp->c_close(dbcp);
811  tid->abort(tid);
812  if (more_mem) {
813  free(more_mem);
814  more_mem = NULL;
815  }
816  return ICAL_INTERNAL_ERROR;
817  }
818  }
819 
820  if (more_mem) {
821  free(more_mem);
822  more_mem = NULL;
823  }
824 
825  if (deadlocked) {
826  dbcp->c_close(dbcp);
827  tid->abort(tid);
828  retry++;
829  continue; /* next retry */
830  }
831 
832  deadlocked = 0;
833  for (c = icalcomponent_get_first_component(bset->cluster, ICAL_ANY_COMPONENT);
834  c != 0 && !deadlocked;
836  memset(&key, 0, sizeof(key));
837  memset(&data, 0, sizeof(data));
838 
839  /* Note that we're always inserting into a primary index. */
841  char *uidstr = (char *)icalcomponent_get_uid(c);
842 
843  if (!uidstr) { /* this shouldn't happen */
844  /* no uid string, we need to add one */
845  snprintf(uidbuf, 256, "baduid%d-%d", getpid(), bad_uid_counter++);
846  key.data = uidbuf;
847  } else {
848  key.data = uidstr;
849  }
850  } else {
851  char *relcalid = NULL;
852 
853  relcalid = (char *)icalcomponent_get_relcalid(c);
854  if (relcalid == NULL) {
855  snprintf(uidbuf, 256, "baduid%d-%d", getpid(), bad_uid_counter++);
856  key.data = uidbuf;
857  } else {
858  key.data = relcalid;
859  }
860  }
861  key.size = (u_int32_t)strlen(key.data);
862 
864  data.data = str;
865  data.size = (u_int32_t)strlen(str);
866 
867  if ((ret = dbcp->c_put(dbcp, &key, &data, DB_KEYLAST)) != 0) {
868  if (ret == DB_LOCK_DEADLOCK) {
869  deadlocked = 1;
870  } else if (ret == DB_RUNRECOVERY) {
871  ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "c_put failed.");
872  abort();
873  } else {
874  ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "c_put failed %s.", str);
875  /* continue to try to put as many icalcomponent as possible */
876  reterr = ICAL_INTERNAL_ERROR;
877  }
878  }
879  }
880 
881  if (deadlocked) {
882  dbcp->c_close(dbcp);
883  tid->abort(tid);
884  retry++;
885  continue;
886  }
887 
888  if ((ret = dbcp->c_close(dbcp)) != 0) {
889  tid->abort(tid);
890  if (ret == DB_LOCK_DEADLOCK) {
891  retry++;
892  continue;
893  } else if (ret == DB_RUNRECOVERY) {
894  ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "c_closed failed.");
895  abort();
896  } else {
897  ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "c_closed failed.");
898  reterr = ICAL_INTERNAL_ERROR;
899  }
900  }
901 
902  if ((ret = tid->commit(tid, 0)) != 0) {
903  tid->abort(tid);
904  if (ret == DB_LOCK_DEADLOCK) {
905  retry++;
906  continue;
907  } else if (ret == DB_RUNRECOVERY) {
908  ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "commit failed.");
909  abort();
910  } else {
911  ICAL_DB_ENV->err(ICAL_DB_ENV, ret, "commit failed.");
912  reterr = ICAL_INTERNAL_ERROR;
913  }
914  }
915 
916  done = 1;
917  }
918 
919  bset->changed = 0;
920  return reterr;
921 }
922 
923 void icalbdbset_mark(icalset *set)
924 {
925  icalbdbset *bset = (icalbdbset *)set;
926 
927  icalerror_check_arg_rv((bset != 0), "bset");
928 
929  bset->changed = 1;
930 }
931 
932 icalcomponent *icalbdbset_get_component(icalset *set)
933 {
934  const icalbdbset *bset = (icalbdbset *)set;
935 
936  icalerror_check_arg_rz((bset != 0), "bset");
937 
938  return bset->cluster;
939 }
940 
941 /* manipulate the components in the cluster */
942 
943 icalerrorenum icalbdbset_add_component(icalset *set, icalcomponent *child)
944 {
945  icalbdbset *bset = (icalbdbset *)set;
946 
947  icalerror_check_arg_re((bset != 0), "bset", ICAL_BADARG_ERROR);
948  icalerror_check_arg_re((child != 0), "child", ICAL_BADARG_ERROR);
949 
950  icalcomponent_add_component(bset->cluster, child);
951 
952  icalbdbset_mark(set);
953 
954  return ICAL_NO_ERROR;
955 }
956 
957 icalerrorenum icalbdbset_remove_component(icalset *set, icalcomponent *child)
958 {
959  icalbdbset *bset = (icalbdbset *)set;
960 
961  icalerror_check_arg_re((bset != 0), "bset", ICAL_BADARG_ERROR);
962  icalerror_check_arg_re((child != 0), "child", ICAL_BADARG_ERROR);
963 
964  icalcomponent_remove_component(bset->cluster, child);
965 
966  icalbdbset_mark(set);
967 
968  return ICAL_NO_ERROR;
969 }
970 
971 int icalbdbset_count_components(icalset *set, icalcomponent_kind kind)
972 {
973  icalbdbset *bset;
974 
975  if (set == 0) {
977  return -1;
978  }
979 
980  bset = (icalbdbset *)set;
981  return icalcomponent_count_components(bset->cluster, kind);
982 }
983 
986 icalerrorenum icalbdbset_select(icalset *set, icalgauge *gauge)
987 {
988  icalbdbset *bset = (icalbdbset *)set;
989 
990  icalerror_check_arg_re((bset != 0), "bset", ICAL_BADARG_ERROR);
991  icalerror_check_arg_re(gauge != 0, "gauge", ICAL_BADARG_ERROR);
992 
993  bset->gauge = gauge;
994 
995  return ICAL_NO_ERROR;
996 }
997 
1000 void icalbdbset_clear(icalset *set)
1001 {
1002  icalbdbset *bset = (icalbdbset *)set;
1003 
1004  icalerror_check_arg_rv((bset != 0), "bset");
1005 
1006  bset->gauge = 0;
1007 }
1008 
1009 icalcomponent *icalbdbset_fetch(icalset *set, icalcomponent_kind kind, const char *uid)
1010 {
1011  icalcompiter i;
1012  icalbdbset *bset = (icalbdbset *)set;
1013 
1014  icalerror_check_arg_rz((bset != 0), "bset");
1015 
1016  for (i = icalcomponent_begin_component(bset->cluster, kind);
1017  icalcompiter_deref(&i) != 0; icalcompiter_next(&i)) {
1018  icalcomponent *this = icalcompiter_deref(&i);
1019 
1020  if (this != 0) {
1021  const char *this_uid = NULL;
1022  if (kind == ICAL_VAGENDA_COMPONENT) {
1023  icalproperty *p = icalcomponent_get_first_property(this, ICAL_RELCALID_PROPERTY);
1024  if (p != NULL) {
1025  this_uid = icalproperty_get_relcalid(p);
1026  }
1027  } else {
1028  icalproperty *p = icalcomponent_get_first_property(this, ICAL_UID_PROPERTY);
1029  if (p != NULL) {
1030  this_uid = icalproperty_get_uid(p);
1031  }
1032  }
1033 
1034  if (this_uid == NULL) {
1035  icalerror_warn("icalbdbset_fetch found a component with no UID");
1036  continue;
1037  }
1038 
1039  if (strcmp(uid, this_uid) == 0) {
1040  return this;
1041  }
1042  }
1043  }
1044 
1045  return 0;
1046 }
1047 
1048 int icalbdbset_has_uid(icalset *set, const char *uid) // return an int for icalset_bdbset_init
1049 {
1050  _unused(set);
1051  _unused(uid);
1052  assert(0); /* HACK, not implemented */
1053  return 0;
1054 }
1055 
1056 /******* support routines for icalbdbset_fetch_match *********/
1057 
1058 struct icalbdbset_id {
1059  char *uid;
1060  char *recurrence_id;
1061  int sequence;
1062 };
1063 
1064 static void icalbdbset_id_free(struct icalbdbset_id *id)
1065 {
1066  if (id->recurrence_id != 0) {
1067  free(id->recurrence_id);
1068  }
1069 
1070  if (id->uid != 0) {
1071  free(id->uid);
1072  }
1073 }
1074 
1075 struct icalbdbset_id icalbdbset_get_id(const icalcomponent *comp)
1076 {
1077  icalcomponent *inner;
1078  struct icalbdbset_id id;
1079  icalproperty *p;
1080 
1082 
1083  p = icalcomponent_get_first_property(inner, ICAL_UID_PROPERTY);
1084 
1085  assert(p != 0);
1086 
1087  id.uid = strdup(icalproperty_get_uid(p));
1088 
1089  p = icalcomponent_get_first_property(inner, ICAL_SEQUENCE_PROPERTY);
1090 
1091  if (p == 0) {
1092  id.sequence = 0;
1093  } else {
1094  id.sequence = icalproperty_get_sequence(p);
1095  }
1096 
1097  p = icalcomponent_get_first_property(inner, ICAL_RECURRENCEID_PROPERTY);
1098 
1099  if (p == 0) {
1100  id.recurrence_id = NULL;
1101  } else {
1102  icalvalue *v;
1103 
1104  v = icalproperty_get_value(p);
1105  id.recurrence_id = icalvalue_as_ical_string_r(v);
1106 
1107  assert(id.recurrence_id != 0);
1108  }
1109 
1110  return id;
1111 }
1112 
1113 /* Find the component that is related to the given
1114  component. Currently, it just matches based on UID and
1115  RECURRENCE-ID */
1116 
1117 static int _compare_ids(const char *compid, const char *matchid)
1118 {
1119  if (compid != NULL && matchid != NULL) {
1120  if (strcmp(compid, matchid) == 0) {
1121  return 1;
1122  }
1123  }
1124 
1125  if (compid == NULL && matchid == NULL) {
1126  return 1;
1127  }
1128 
1129  return 0;
1130 }
1131 
1132 icalcomponent *icalbdbset_fetch_match(icalset *set, const icalcomponent *comp)
1133 {
1134  icalbdbset *bset = (icalbdbset *)set;
1135  icalcompiter i;
1136  struct icalbdbset_id comp_id, match_id;
1137 
1138  icalerror_check_arg_rz((bset != 0), "bset");
1139  comp_id = icalbdbset_get_id(comp);
1140 
1141  for (i = icalcomponent_begin_component(bset->cluster, ICAL_ANY_COMPONENT);
1142  icalcompiter_deref(&i) != 0; icalcompiter_next(&i)) {
1143  icalcomponent *match = icalcompiter_deref(&i);
1144 
1145  match_id = icalbdbset_get_id(match);
1146 
1147  if (_compare_ids(comp_id.uid, match_id.uid) &&
1148  _compare_ids(comp_id.recurrence_id, match_id.recurrence_id)) {
1149  /* HACK. What to do with SEQUENCE? */
1150 
1151  icalbdbset_id_free(&match_id);
1152  icalbdbset_id_free(&comp_id);
1153  return match;
1154  }
1155 
1156  icalbdbset_id_free(&match_id);
1157  }
1158 
1159  icalbdbset_id_free(&comp_id);
1160  return 0;
1161 }
1162 
1163 icalerrorenum icalbdbset_modify(icalset *set, icalcomponent *old, icalcomponent *newc)
1164 {
1165  _unused(set);
1166  _unused(old);
1167  _unused(newc);
1168  assert(0); /* HACK, not implemented */
1169  return ICAL_NO_ERROR;
1170 }
1171 
1172 /* caller is responsible to cal icalbdbset_free_cluster first */
1173 icalerrorenum icalbdbset_set_cluster(icalset *set, icalcomponent *cluster)
1174 {
1175  icalbdbset *bset = (icalbdbset *)set;
1176 
1177  icalerror_check_arg_rz((bset != 0), "bset");
1178 
1179  bset->cluster = cluster;
1180  return ICAL_NO_ERROR;
1181 }
1182 
1183 icalerrorenum icalbdbset_free_cluster(icalset *set)
1184 {
1185  icalbdbset *bset = (icalbdbset *)set;
1186 
1187  icalerror_check_arg_rz((bset != 0), "bset");
1188 
1189  if (bset->cluster != NULL) {
1190  icalcomponent_free(bset->cluster);
1191  }
1192  return ICAL_NO_ERROR;
1193 }
1194 
1195 icalcomponent *icalbdbset_get_cluster(icalset *set)
1196 {
1197  const icalbdbset *bset = (icalbdbset *)set;
1198 
1199  icalerror_check_arg_rz((bset != 0), "bset");
1200 
1201  return bset->cluster;
1202 }
1203 
1205 icalcomponent *icalbdbset_get_current_component(icalset *set)
1206 {
1207  icalbdbset *bset = (icalbdbset *)set;
1208 
1209  icalerror_check_arg_rz((bset != 0), "bset");
1210 
1211  return icalcomponent_get_current_component(bset->cluster);
1212 }
1213 
1214 icalcomponent *icalbdbset_get_first_component(icalset *set)
1215 {
1216  icalbdbset *bset = (icalbdbset *)set;
1217  icalcomponent *c = 0;
1218 
1219  icalerror_check_arg_rz((bset != 0), "bset");
1220 
1221  do {
1222  if (c == 0) {
1224  } else {
1226  }
1227 
1228  if (c != 0 && (bset->gauge == 0 || icalgauge_compare(bset->gauge, c) == 1)) {
1229  return c;
1230  }
1231 
1232  } while (c != 0);
1233 
1234  return 0;
1235 }
1236 
1237 icalsetiter icalbdbset_begin_component(icalset *set, icalcomponent_kind kind,
1238  icalgauge *gauge, const char *tzid)
1239 {
1241  icalcomponent *comp = NULL;
1242  icalcompiter citr;
1243  icalbdbset *bset;
1244  struct icaltimetype start, next;
1245  icalproperty *dtstart, *rrule, *prop, *due;
1246  struct icalrecurrencetype *recur;
1247  icaltimezone *u_zone;
1248  int g;
1249  int orig_time_was_utc = 0;
1250 
1251  icalerror_check_arg_re((set != 0), "set", icalsetiter_null);
1252  bset = (icalbdbset *)set;
1253 
1254  itr.gauge = gauge;
1255  itr.tzid = tzid;
1256 
1257  citr = icalcomponent_begin_component(bset->cluster, kind);
1258  comp = icalcompiter_deref(&citr);
1259 
1260  if (gauge == 0) {
1261  itr.iter = citr;
1262  return itr;
1263  }
1264 
1265  /* if there is a gauge, the first matched component is returned */
1266  while (comp != 0) {
1267  /* check if it is a recurring component and with gauge expand, if so
1268  * we need to add recurrence-id property to the given component */
1269  rrule = icalcomponent_get_first_property(comp, ICAL_RRULE_PROPERTY);
1270  recur = rrule ? icalproperty_get_rrule(rrule) : NULL;
1271  g = icalgauge_get_expand(gauge);
1272 
1273  if (recur != 0 && g == 1) {
1274  /* it is a recurring event */
1275 
1276  u_zone = icaltimezone_get_builtin_timezone(itr.tzid);
1277 
1278  /* use UTC, if that's all we have. */
1279  if (!u_zone) {
1280  u_zone = icaltimezone_get_utc_timezone();
1281  }
1282 
1283  start = icaltime_from_timet_with_zone(time(0), 0, NULL);
1284 
1286  dtstart = icalcomponent_get_first_property(comp, ICAL_DTSTART_PROPERTY);
1287  if (dtstart) {
1288  start = icalproperty_get_dtstart(dtstart);
1289  }
1290  } else if (icalcomponent_isa(comp) == ICAL_VTODO_COMPONENT) {
1291  due = icalcomponent_get_first_property(comp, ICAL_DUE_PROPERTY);
1292  if (due) {
1293  start = icalproperty_get_due(due);
1294  }
1295  }
1296 
1297  /* Convert to the user's timezone in order to be able to compare
1298  * the results from the rrule iterator. */
1299  if (icaltime_is_utc(start)) {
1300  start = icaltime_convert_to_zone(start, u_zone);
1301  orig_time_was_utc = 1;
1302  }
1303 
1304  if (itr.last_component == NULL) {
1305  itr.ritr = icalrecur_iterator_new(recur, start);
1306  next = icalrecur_iterator_next(itr.ritr);
1307  itr.last_component = comp;
1308  } else {
1309  next = icalrecur_iterator_next(itr.ritr);
1310  if (icaltime_is_null_time(next)) {
1311  itr.last_component = NULL;
1312  icalrecur_iterator_free(itr.ritr);
1313  itr.ritr = NULL;
1314  /* no matched occurrence */
1315  goto getNextComp;
1316  } else {
1317  itr.last_component = comp;
1318  }
1319  }
1320 
1321  /* if it is excluded, do next one */
1322  if (icalproperty_recurrence_is_excluded(comp, &start, &next)) {
1323  next = icalrecur_iterator_next(itr.ritr);
1324  continue;
1325  }
1326 
1327  /* add recurrence-id value to the property if the property already exist;
1328  * add the recurrence id property and the value if the property does not exist */
1329  prop = icalcomponent_get_first_property(comp, ICAL_RECURRENCEID_PROPERTY);
1330  if (prop == 0) {
1331  icalcomponent_add_property(comp, icalproperty_new_recurrenceid(next));
1332  } else {
1333  icalproperty_set_recurrenceid(prop, next);
1334  }
1335 
1336  /* convert the next recurrence time into the user's timezone */
1337  if (orig_time_was_utc) {
1339  }
1340  }
1341  /* end of a recurring event */
1342  if (gauge == 0 || icalgauge_compare(itr.gauge, comp) == 1) {
1343  /* find a matched and return it */
1344  itr.iter = citr;
1345  return itr;
1346  }
1347 
1348  /* if it is a recurring but no matched occurrence has been found OR
1349  * it is not a recurring and no matched component has been found,
1350  * read the next component to find out */
1351  getNextComp:
1352  if ((rrule != NULL && itr.last_component == NULL) || (rrule == NULL)) {
1353  (void)icalcompiter_next(&citr);
1354  comp = icalcompiter_deref(&citr);
1355  }
1356  } /* while */
1357 
1358  /* no matched component has found */
1359  return icalsetiter_null;
1360 }
1361 
1362 icalcomponent *icalbdbset_form_a_matched_recurrence_component(icalsetiter *itr)
1363 {
1364  icalcomponent *comp = NULL;
1365  struct icaltimetype start, next;
1366  icalproperty *rrule, *prop;
1367  struct icalrecurrencetype *recur;
1368  icaltimezone *u_zone;
1369  int orig_time_was_utc = 0;
1370 
1371  comp = itr->last_component;
1372 
1373  if (comp == NULL || itr->gauge == NULL) {
1374  return NULL;
1375  }
1376 
1377  rrule = icalcomponent_get_first_property(comp, ICAL_RRULE_PROPERTY);
1378  recur = rrule ? icalproperty_get_rrule(rrule) : NULL;
1379  /* if there is no RRULE, simply return to the caller */
1380  if (recur == NULL) {
1381  return NULL;
1382  }
1383 
1384  u_zone = icaltimezone_get_builtin_timezone(itr->tzid);
1385 
1386  /* use UTC, if that's all we have. */
1387  if (!u_zone) {
1388  u_zone = icaltimezone_get_utc_timezone();
1389  }
1390 
1391  start = icaltime_from_timet_with_zone(time(0), 0, NULL);
1392 
1394  icalproperty *dtstart = icalcomponent_get_first_property(comp, ICAL_DTSTART_PROPERTY);
1395  if (dtstart) {
1396  start = icalproperty_get_dtstart(dtstart);
1397  }
1398  } else if (icalcomponent_isa(comp) == ICAL_VTODO_COMPONENT) {
1399  icalproperty *due = icalcomponent_get_first_property(comp, ICAL_DUE_PROPERTY);
1400  if (due) {
1401  start = icalproperty_get_due(due);
1402  }
1403  }
1404 
1405  /* Convert to the user's timezone in order to be able to compare the results
1406  * from the rrule iterator. */
1407  if (icaltime_is_utc(start)) {
1408  start = icaltime_convert_to_zone(start, u_zone);
1409  orig_time_was_utc = 1;
1410  }
1411 
1412  if (itr->ritr == NULL) {
1413  itr->ritr = icalrecur_iterator_new(recur, start);
1414  next = icalrecur_iterator_next(itr->ritr);
1415  itr->last_component = comp;
1416  } else {
1417  next = icalrecur_iterator_next(itr->ritr);
1418  if (icaltime_is_null_time(next)) {
1419  /* no more recurrence, returns */
1420  itr->last_component = NULL;
1421  icalrecur_iterator_free(itr->ritr);
1422  itr->ritr = NULL;
1423  /* no more pending matched occurrence,
1424  * all the pending matched occurrences have been returned */
1425  return NULL;
1426  } else {
1427  itr->last_component = comp;
1428  }
1429  }
1430 
1431  /* if it is excluded, return NULL to the caller */
1432  if (icalproperty_recurrence_is_excluded(comp, &start, &next)) {
1433  (void)icalrecur_iterator_next(itr->ritr);
1434  return NULL;
1435  }
1436 
1437  /* set recurrence-id value to the property if the property already exist;
1438  * add the recurrence id property and the value if the property does not exist */
1439  prop = icalcomponent_get_first_property(comp, ICAL_RECURRENCEID_PROPERTY);
1440  if (prop == 0) {
1441  icalcomponent_add_property(comp, icalproperty_new_recurrenceid(next));
1442  } else {
1443  icalproperty_set_recurrenceid(prop, next);
1444  }
1445 
1446  if (orig_time_was_utc) {
1448  }
1449 
1450  if (itr->gauge == 0 || icalgauge_compare(itr->gauge, comp) == 1) {
1451  /* find a matched and return it */
1452  return comp;
1453  }
1454 
1455  /* not matched */
1456  return NULL;
1457 }
1458 
1459 icalcomponent *icalbdbsetiter_to_next(icalset *set, icalsetiter *i)
1460 {
1461  icalcomponent *comp = NULL;
1462  struct icaltimetype start, next;
1463  icalproperty *rrule, *prop;
1464  int orig_time_was_utc = 0;
1465 
1466  _unused(set);
1467 
1468  do {
1469  /* no pending occurrence, read the next component */
1470  if (i->last_component == NULL) {
1471  comp = icalcompiter_next(&(i->iter));
1472  } else {
1473  comp = i->last_component;
1474  }
1475 
1476  /* no next component, simply return */
1477  if (comp == 0) {
1478  return NULL;
1479  }
1480 
1481  if (i->gauge == 0) {
1482  return comp;
1483  }
1484 
1485  /* finding the next matched component and return it to the caller */
1486 
1487  rrule = icalcomponent_get_first_property(comp, ICAL_RRULE_PROPERTY);
1488  struct icalrecurrencetype *recur = rrule ? icalproperty_get_rrule(rrule) : NULL;
1489  int g = icalgauge_get_expand(i->gauge);
1490 
1491  /* a recurring component with expand query */
1492  if (recur != 0 && g == 1) {
1494 
1495  /* use UTC, if that's all we have. */
1496  if (!u_zone) {
1497  u_zone = icaltimezone_get_utc_timezone();
1498  }
1499 
1500  start = icaltime_from_timet_with_zone(time(0), 0, NULL);
1501 
1503  icalproperty *dtstart = icalcomponent_get_first_property(comp, ICAL_DTSTART_PROPERTY);
1504  if (dtstart) {
1505  start = icalproperty_get_dtstart(dtstart);
1506  }
1507  } else if (icalcomponent_isa(comp) == ICAL_VTODO_COMPONENT) {
1508  icalproperty *due = icalcomponent_get_first_property(comp, ICAL_DUE_PROPERTY);
1509  if (due) {
1510  start = icalproperty_get_due(due);
1511  }
1512  }
1513 
1514  /* Convert to the user's timezone in order to be able to compare
1515  * the results from the rrule iterator. */
1516  if (icaltime_is_utc(start)) {
1517  start = icaltime_convert_to_zone(start, u_zone);
1518  orig_time_was_utc = 1;
1519  }
1520 
1521  if (i->ritr == NULL) {
1522  i->ritr = icalrecur_iterator_new(recur, start);
1523  next = icalrecur_iterator_next(i->ritr);
1524  i->last_component = comp;
1525  } else {
1526  next = icalrecur_iterator_next(i->ritr);
1527  if (icaltime_is_null_time(next)) {
1528  i->last_component = NULL;
1529  icalrecur_iterator_free(i->ritr);
1530  i->ritr = NULL;
1531  /* no more occurrence, should go to get next component */
1532  continue;
1533  } else {
1534  i->last_component = comp;
1535  }
1536  }
1537 
1538  /* if it is excluded, do next one */
1539  if (icalproperty_recurrence_is_excluded(comp, &start, &next)) {
1540  next = icalrecur_iterator_next(i->ritr);
1541  continue;
1542  }
1543 
1544  /* set recurrence-id value to the property if the property already exist;
1545  * add the recurrence id property and the value if the property does not exist */
1546  prop = icalcomponent_get_first_property(comp, ICAL_RECURRENCEID_PROPERTY);
1547  if (prop == 0) {
1548  icalcomponent_add_property(comp, icalproperty_new_recurrenceid(next));
1549  } else {
1550  icalproperty_set_recurrenceid(prop, next);
1551  }
1552 
1553  if (orig_time_was_utc) {
1555  }
1556  }
1557  /* end of recurring event with expand query */
1558  if ((i->gauge == 0 || icalgauge_compare(i->gauge, comp) == 1)) {
1559  /* found a matched, return it */
1560  return comp;
1561  }
1562  } while (comp != 0);
1563 
1564  /* coverity[dead_error_line] */
1565  return NULL; /*unreachable */
1566 }
1567 
1568 icalcomponent *icalbdbset_get_next_component(icalset *set)
1569 {
1570  icalbdbset *bset = (icalbdbset *)set;
1571  icalcomponent *c = 0;
1572 
1573  icalerror_check_arg_rz((bset != 0), "bset");
1574 
1575  do {
1577  if (c != 0 && (bset->gauge == 0 || icalgauge_compare(bset->gauge, c) == 1)) {
1578  return c;
1579  }
1580 
1581  } while (c != 0);
1582 
1583  return 0;
1584 }
1585 
1586 int icalbdbset_begin_transaction(DB_TXN *parent_id, DB_TXN **txnid)
1587 {
1588  return ICAL_DB_ENV->txn_begin(ICAL_DB_ENV, parent_id, txnid, 0);
1589 }
1590 
1591 int icalbdbset_commit_transaction(DB_TXN *txnid)
1592 {
1593  return txnid->commit(txnid, 0);
1594 }
1595 
1596 #if DB_VERSION_MAJOR > 5
1597 static int _compare_keys(DB *dbp, const DBT *a, const DBT *b, size_t *locp)
1598 #else
1599 static int _compare_keys(DB *dbp, const DBT *a, const DBT *b)
1600 #endif
1601 {
1602 #if DB_VERSION_MAJOR > 5
1603  _unused(locp);
1604 #endif
1605  /*
1606  * Returns:
1607  * < 0 if a < b
1608  * = 0 if a = b
1609  * > 0 if a > b
1610  */
1611 
1612  const char *ac = a->data;
1613  const char *bc = b->data;
1614 
1615  _unused(dbp);
1616  return strncmp(ac, bc, a->size);
1617 }
icalcomponent * icalcomponent_get_first_component(icalcomponent *c, icalcomponent_kind kind)
icalcomponent * icalparser_parse_string(const char *str)
Parses a string and returns the parsed icalcomponent.
Definition: icalparser.c:1331
char * icalvalue_as_ical_string_r(const icalvalue *value)
Definition: icalvalue.c:1226
bool icalgauge_compare(icalgauge *gauge, icalcomponent *comp)
Definition: icalgauge.c:216
Line-oriented parsing.
icalvalue * icalproperty_get_value(const icalproperty *prop)
Definition: icalproperty.c:817
icalcomponent * icalcompiter_next(icalcompiter *i)
const char * icalcomponent_get_uid(icalcomponent *comp)
icalcomponent * icalcomponent_get_current_component(icalcomponent *component)
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)
Definition: icalbdbset.c:350
Timezone handling routines.
Defines the data structure representing iCalendar parameter values.
int icalcomponent_count_components(icalcomponent *component, icalcomponent_kind kind)
void icalcomponent_remove_component(icalcomponent *parent, icalcomponent *child)
void icalcomponent_add_component(icalcomponent *parent, icalcomponent *child)
bool icaltime_is_utc(const struct icaltimetype t)
Definition: icaltime.c:625
bool icalproperty_recurrence_is_excluded(icalcomponent *comp, struct icaltimetype *dtstart, struct icaltimetype *recurtime)
Decides if a recurrence is acceptable.
icalcomponent * icalcomponent_get_first_real_component(const icalcomponent *c)
void icalbdbset_clear(icalset *set)
Definition: icalbdbset.c:1000
icalsetiter icalsetiter_null
Definition: icalset.c:413
struct icaltimetype icalrecur_iterator_next(icalrecur_iterator *impl)
Definition: icalrecur.c:3694
void icalerror_set_errno(icalerrorenum x)
Sets the icalerrno to a given error.
Definition: icalerror.c:90
icalerrorenum
Represents the different types of errors that can be triggered in libical.
Definition: icalerror.h:41
const char * icalcomponent_get_relcalid(icalcomponent *comp)
icalerrorenum icalbdbset_select(icalset *set, icalgauge *gauge)
Definition: icalbdbset.c:986
int icalbdbset_init_dbenv(char *db_env_dir, void(*logDbFunc)(const DB_ENV *, const char *, const char *))
Definition: icalbdbset.c:52
icalset * icalbdbset_new(const char *database_filename, icalbdbset_subdb_type subdb_type, int dbtype, u_int32_t flag)
Definition: icalbdbset.c:330
icaltimezone * icaltimezone_get_builtin_timezone(const char *location)
char * icalcomponent_as_ical_string_r(const icalcomponent *component)
struct icaltimetype icaltime_convert_to_zone(const struct icaltimetype tt, icaltimezone *zone)
Definition: icaltime.c:846
struct _icaltimezone icaltimezone
Definition: icaltimezone.h:29
icalcomponent * icalcomponent_get_next_component(icalcomponent *c, icalcomponent_kind kind)
void icalrecur_iterator_free(icalrecur_iterator *impl)
Definition: icalrecur.c:2451
const char * icalbdbset_path(icalset *set)
Definition: icalbdbset.c:684
icaltimezone * icaltimezone_get_utc_timezone(void)
icalcomponent * icalcomponent_new(icalcomponent_kind kind)
void icalgauge_free(icalgauge *gauge)
Definition: icalgauge.c:72
struct icaltimetype icaltime_from_timet_with_zone(const icaltime_t tm, const bool is_date, const icaltimezone *zone)
Constructor.
Definition: icaltime.c:209
icalcomponent_kind
Definition: icalenums.h:28
icalcomponent * icalcompiter_deref(icalcompiter *i)
icalcomponent_kind icalcomponent_isa(const icalcomponent *component)
icalcomponent * icalbdbset_get_current_component(icalset *set)
Definition: icalbdbset.c:1205
bool icaltime_is_null_time(const struct icaltimetype t)
Definition: icaltime.c:630
icalerrorenum icalbdbset_commit(icalset *set)
Definition: icalbdbset.c:703
int icalgauge_get_expand(const icalgauge *gauge)
Definition: icalgauge.c:66
icalrecur_iterator * icalrecur_iterator_new(struct icalrecurrencetype *rule, struct icaltimetype dtstart)
Definition: icalrecur.c:2323
void icalcomponent_free(icalcomponent *c)
void icalcomponent_add_property(icalcomponent *component, icalproperty *property)
icalset * icalset_new(icalset_kind kind, const char *dsn, void *options)
Definition: icalset.c:193
Manages a Berkeley database of ical components and offers interfaces for reading, writing and searchi...
icalcompiter icalcomponent_begin_component(icalcomponent *component, icalcomponent_kind kind)
icalproperty * icalcomponent_get_first_property(icalcomponent *c, icalproperty_kind kind)