$darkmode
Libical API Documentation 4.0 STABLE VERSION Visit the v3.0 documentation
icalfileset.c
Go to the documentation of this file.
1 /*======================================================================
2  FILE: icalfileset.c
3  CREATOR: eric 23 December 1999
4 
5  SPDX-FileCopyrightText: 2000, Eric Busboom <eric@civicknowledge.com>
6  SPDX-License-Identifier: LGPL-2.1-only OR MPL-2.0
7 
8  The Original Code is eric. The Initial Developer of the Original
9  Code is Eric Busboom
10 ======================================================================*/
11 //krazy:excludeall=cpp
12 
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22 
23 #include "icalfileset.h"
24 #include "icalfilesetimpl.h"
25 #include "icalerror_p.h"
26 #include "icalparser.h"
27 #include "icalvalue.h"
28 #include "icalmemory.h"
29 
30 #include <errno.h>
31 #include <stdlib.h>
32 
33 #if defined(_MSC_VER)
34 typedef int mode_t;
35 #endif
36 
37 #if defined(_WIN32_WCE)
38 #include <winbase.h>
39 #endif
40 
42 icalfileset_options icalfileset_options_default = {O_RDWR | O_CREAT, 0644, 0, NULL};
43 
44 static int _compare_ids(const char *compid, const char *matchid);
45 
46 static int icalfileset_lock(icalfileset *set);
47 static int icalfileset_unlock(icalfileset *set);
48 static icalerrorenum icalfileset_read_file(icalfileset *set, int mode);
49 static long icalfileset_filesize(icalfileset *set);
50 
51 icalset *icalfileset_new(const char *path)
52 {
53  return icalset_new(ICAL_FILE_SET, path, &icalfileset_options_default);
54 }
55 
56 icalset *icalfileset_new_reader(const char *path)
57 {
59 
60  reader_options.flags = O_RDONLY;
61 
62  return icalset_new(ICAL_FILE_SET, path, &reader_options);
63 }
64 
65 icalset *icalfileset_new_writer(const char *path)
66 {
68 
69  writer_options.flags = O_RDONLY;
70 
71  return icalset_new(ICAL_FILE_SET, path, &writer_options);
72 }
73 
74 icalset *icalfileset_init(icalset *set, const char *path, void *options_in)
75 {
76  const icalfileset_options *options = (options_in) ? options_in : &icalfileset_options_default;
77  icalfileset *fset = (icalfileset *)set;
78  int flags;
79  int mode;
80  long cluster_file_size;
81 
83  icalerror_check_arg_rz((path != 0), "path");
84  icalerror_check_arg_rz((fset != 0), "fset");
85 
86  fset->path = strdup(path);
87  fset->options = *options;
88 
89  flags = options->flags;
90  mode = options->mode;
91 
92  cluster_file_size = icalfileset_filesize(fset);
93 
94  if (cluster_file_size < 0) {
95  icalfileset_free(set);
96  return 0;
97  }
98 
99  fset->fd = open(fset->path, flags, (mode_t)mode);
100 
101  if (fset->fd < 0) {
103  icalfileset_free(set);
104  return 0;
105  }
106 
107  (void)icalfileset_lock(fset);
108 
109  if (cluster_file_size > 0) {
110  if (icalfileset_read_file(fset, mode) != ICAL_NO_ERROR) {
111  icalfileset_free(set);
112  return 0;
113  }
114  }
115 
116  if (options->cluster) {
117  fset->cluster = icalcomponent_clone(icalcluster_get_component(options->cluster));
118  fset->changed = 1;
119  }
120 
121  if (fset->cluster == 0) {
122  fset->cluster = icalcomponent_new(ICAL_XROOT_COMPONENT);
123  }
124 
125  return set;
126 }
127 
128 icalcluster *icalfileset_produce_icalcluster(const char *path)
129 {
130  icalset *fileset;
131  icalcluster *ret;
132 
133  bool errstate = icalerror_get_errors_are_fatal();
134 
136 
137  fileset = icalfileset_new_reader(path);
138 
139  if (fileset == 0 || icalerrno == ICAL_FILE_ERROR) {
140  /* file does not exist */
141  ret = icalcluster_new(path, NULL);
142  } else {
143  ret = icalcluster_new(path, ((icalfileset *)fileset)->cluster);
144  icalfileset_free(fileset);
145  }
146 
149  return ret;
150 }
151 
152 static char *icalfileset_read_from_file(char *s, size_t size, void *d)
153 {
154  char *p;
155  icalfileset *set = d;
156 
157  /* Simulate fgets -- read single characters and stop at '\n' */
158 
159  for (p = s; p < s + size - 1; p++) {
160  if (read(set->fd, p, 1) != 1 || *p == '\n') {
161  p++;
162  break;
163  }
164  }
165 
166  *p = '\0';
167 
168  if (*s == 0) {
169  return 0;
170  } else {
171  return s;
172  }
173 }
174 
175 icalerrorenum icalfileset_read_file(icalfileset *set, int mode)
176 {
177  icalparser *parser;
178 
179  _unused(mode);
180 
181  parser = icalparser_new();
182 
183  icalparser_set_gen_data(parser, set);
184  set->cluster = icalparser_parse(parser, icalfileset_read_from_file);
185  icalparser_free(parser);
186 
187  if (set->cluster == 0 || icalerrno != ICAL_NO_ERROR) {
189  /*return ICAL_PARSE_ERROR; */
190  }
191 
192  if (icalcomponent_isa(set->cluster) != ICAL_XROOT_COMPONENT) {
193  /* The parser got a single component, so it did not put it in
194  an XROOT. */
195  icalcomponent *cl = set->cluster;
196 
197  set->cluster = icalcomponent_new(ICAL_XROOT_COMPONENT);
198  icalcomponent_add_component(set->cluster, cl);
199  }
200 
201  return ICAL_NO_ERROR;
202 }
203 
204 long icalfileset_filesize(icalfileset *fset)
205 {
206  struct stat sbuf;
207 
208  if (!fset || !fset->path || fset->path[0] == '\0') {
209  return -1;
210  }
211 
212  if (stat(fset->path, &sbuf) != 0) {
213  /* A file by the given name does not exist, or there was
214  another error */
215  if (errno == ENOENT) {
216  /* It was because the file does not exist */
217  return 0;
218  } else {
219  /* It was because of another error */
221  return -1;
222  }
223  } else {
224  /* A file by the given name exists, but is it a regular file? */
225 
226  if (!S_ISREG(sbuf.st_mode)) {
227  /* Nope, not a regular file */
229  return -1;
230  } else {
231  /* Let's assume that it is a file of the right type */
232  return (long)sbuf.st_size;
233  }
234  }
235 }
236 
237 void icalfileset_free(icalset *set)
238 {
239  icalfileset *fset;
240 
241  icalerror_check_arg_rv((set != 0), "set");
242 
243  fset = (icalfileset *)set;
244 
245  if (fset->cluster != 0) {
246  (void)icalfileset_commit(set);
247  icalcomponent_free(fset->cluster);
248  fset->cluster = 0;
249  }
250 
251  if (fset->gauge != 0) {
252  icalgauge_free(fset->gauge);
253  fset->gauge = 0;
254  }
255 
256  if (fset->fd > 0) {
257  (void)icalfileset_unlock(fset);
258  close(fset->fd);
259  fset->fd = -1;
260  }
261 
262  if (fset->path != 0) {
263  free(fset->path);
264  fset->path = 0;
265  }
266 }
267 
268 const char *icalfileset_path(icalset *set)
269 {
270  icalerror_check_arg_rz((set != 0), "set");
271 
272  return ((icalfileset *)set)->path;
273 }
274 
275 int icalfileset_lock(icalfileset *set)
276 {
277 #if !defined(_WIN32)
278  struct flock lock;
279  int rtrn;
280 
281  icalerror_check_arg_rz((set->fd > 0), "set->fd");
282  errno = 0;
283  lock.l_type = F_WRLCK; /* F_RDLCK, F_WRLCK, F_UNLCK */
284  lock.l_start = 0; /* byte offset relative to l_whence */
285  lock.l_whence = SEEK_SET; /* SEEK_SET, SEEK_CUR, SEEK_END */
286  lock.l_len = 0; /* #bytes (0 means to EOF) */
287 
288  rtrn = fcntl(set->fd, F_SETLKW, &lock);
289 
290  return rtrn;
291 #else
292  _unused(set);
293  return 0;
294 #endif
295 }
296 
297 int icalfileset_unlock(icalfileset *set)
298 {
299 #if !defined(_WIN32)
300  struct flock lock;
301 
302  icalerror_check_arg_rz((set->fd > 0), "set->fd");
303 
304  lock.l_type = F_WRLCK; /* F_RDLCK, F_WRLCK, F_UNLCK */
305  lock.l_start = 0; /* byte offset relative to l_whence */
306  lock.l_whence = SEEK_SET; /* SEEK_SET, SEEK_CUR, SEEK_END */
307  lock.l_len = 0; /* #bytes (0 means to EOF) */
308 
309  return (fcntl(set->fd, F_UNLCK, &lock));
310 #else
311  _unused(set);
312  return 0;
313 #endif
314 }
315 
316 /* Lifted from https://stackoverflow.com/questions/29079011/copy-file-function-in-c */
317 static int file_copy(const char *fileSource, const char *fileDestination)
318 {
319  char c[1024]; // or any other constant you like
320  FILE *stream_R, *stream_W;
321 
322  if ((stream_R = fopen(fileSource, "r")) == (FILE *)NULL) {
323  return -1;
324  }
325  if ((stream_W = fopen(fileDestination, "w")) == (FILE *)NULL) {
326  fclose(stream_R);
327  return -1;
328  }
329 
330  while (!feof(stream_R) && !ferror(stream_R)) {
331  size_t bytes = fread(c, 1, sizeof(c), stream_R);
332  if (bytes) {
333  (void)fwrite(c, 1, bytes, stream_W);
334  }
335  }
336 
337  //close streams
338  fclose(stream_R);
339  fclose(stream_W);
340 
341  return 0;
342 }
343 
344 icalerrorenum icalfileset_commit(icalset *set)
345 {
346  char backupFile[MAXPATHLEN];
347  char *str;
348  icalcomponent *c;
349 #if !defined(_WIN32)
350  size_t write_size = 0;
351 #endif
352  icalfileset *fset = (icalfileset *)set;
353 
354  icalerror_check_arg_re((fset != 0), "set", ICAL_BADARG_ERROR);
355 
356  icalerror_check_arg_re((fset->fd > 0), "set->fd is invalid", ICAL_INTERNAL_ERROR);
357 
358  if (fset->changed == 0) {
359  return ICAL_NO_ERROR;
360  }
361 
362  if (fset->options.safe_saves == 1) {
363  strncpy(backupFile, fset->path, MAXPATHLEN - 4);
364  strncat(backupFile, ".bak", MAXPATHLEN - 1);
365  if (file_copy(fset->path, backupFile) != 0) {
367  return ICAL_FILE_ERROR;
368  }
369  }
370 
371  if (lseek(fset->fd, 0, SEEK_SET) < 0) {
373  return ICAL_FILE_ERROR;
374  }
375 
376  for (c = icalcomponent_get_first_component(fset->cluster, ICAL_ANY_COMPONENT);
377  c != 0; c = icalcomponent_get_next_component(fset->cluster, ICAL_ANY_COMPONENT)) {
378  IO_SSIZE_T sz;
379 
381 
382  sz = write(fset->fd, str, (IO_SIZE_T)strlen(str));
383  if (sz != (IO_SSIZE_T)strlen(str)) {
384  perror("write");
387  return ICAL_FILE_ERROR;
388  }
389 
391 #if !defined(_WIN32)
392  write_size += (size_t)sz;
393 #endif
394  }
395 
396  fset->changed = 0;
397 
398 #if !defined(_WIN32)
399  if (ftruncate(fset->fd, (off_t)write_size) < 0) {
400  return ICAL_FILE_ERROR;
401  }
402 #else
403 #if !defined(_WIN32_WCE)
404  chsize(fset->fd, tell(fset->fd));
405 #else
406  SetEndOfFile(fset->fd);
407 #endif
408 #endif
409 
410  return ICAL_NO_ERROR;
411 }
412 
413 void icalfileset_mark(icalset *set)
414 {
415  icalerror_check_arg_rv((set != 0), "set");
416 
417  ((icalfileset *)set)->changed = 1;
418 }
419 
420 icalcomponent *icalfileset_get_component(icalset *set)
421 {
422  const icalfileset *fset;
423 
424  icalerror_check_arg_rz((set != 0), "set");
425 
426  fset = (icalfileset *)set;
427  return fset->cluster;
428 }
429 
430 /* manipulate the components in the set */
431 
432 icalerrorenum icalfileset_add_component(icalset *set, icalcomponent *child)
433 {
434  icalfileset *fset;
435 
436  icalerror_check_arg_re((set != 0), "set", ICAL_BADARG_ERROR);
437  icalerror_check_arg_re((child != 0), "child", ICAL_BADARG_ERROR);
438 
439  fset = (icalfileset *)set;
440  icalcomponent_add_component(fset->cluster, child);
441 
442  icalfileset_mark(set);
443 
444  return ICAL_NO_ERROR;
445 }
446 
447 icalerrorenum icalfileset_remove_component(icalset *set, icalcomponent *child)
448 {
449  icalfileset *fset;
450 
451  icalerror_check_arg_re((set != 0), "set", ICAL_BADARG_ERROR);
452  icalerror_check_arg_re((child != 0), "child", ICAL_BADARG_ERROR);
453 
454  fset = (icalfileset *)set;
455  icalcomponent_remove_component(fset->cluster, child);
456 
457  icalfileset_mark(set);
458 
459  return ICAL_NO_ERROR;
460 }
461 
462 int icalfileset_count_components(icalset *set, icalcomponent_kind kind)
463 {
464  icalfileset *fset;
465 
466  if (set == 0) {
468  return -1;
469  }
470 
471  fset = (icalfileset *)set;
472  return icalcomponent_count_components(fset->cluster, kind);
473 }
474 
475 icalerrorenum icalfileset_select(icalset *set, icalgauge *gauge)
476 {
477  icalfileset *fset;
478 
479  icalerror_check_arg_re(gauge != 0, "gauge", ICAL_BADARG_ERROR);
480 
481  fset = (icalfileset *)set;
482  fset->gauge = gauge;
483 
484  return ICAL_NO_ERROR;
485 }
486 
487 void icalfileset_clear(icalset *set)
488 {
489  icalfileset *fset;
490 
491  icalerror_check_arg_rv(set != 0, "set");
492 
493  fset = (icalfileset *)set;
494  fset->gauge = 0;
495 }
496 
497 icalcomponent *icalfileset_fetch(icalset *set, icalcomponent_kind kind, const char *uid)
498 {
499  icalfileset *fset;
500  icalcompiter i;
501 
502  _unused(kind);
503 
504  icalerror_check_arg_rz(set != 0, "set");
505  fset = (icalfileset *)set;
506 
507  for (i = icalcomponent_begin_component(fset->cluster, ICAL_ANY_COMPONENT);
508  icalcompiter_deref(&i) != 0; icalcompiter_next(&i)) {
509  icalcomponent *this = icalcompiter_deref(&i);
510  icalcomponent *inner;
511 
513  inner != 0; inner = icalcomponent_get_next_component(this, ICAL_ANY_COMPONENT)) {
514  icalproperty *p = icalcomponent_get_first_property(inner, ICAL_UID_PROPERTY);
515  if (p) {
516  const char *this_uid = icalproperty_get_uid(p);
517 
518  if (this_uid == 0) {
519  icalerror_warn("icalfileset_fetch found a component with no UID");
520  continue;
521  }
522 
523  if (strcmp(uid, this_uid) == 0) {
524  return this;
525  }
526  }
527  }
528  }
529 
530  return 0;
531 }
532 
533 int icalfileset_has_uid(icalset *set, const char *uid)
534 {
535  _unused(set);
536  _unused(uid);
537  assert(0); /* HACK, not implemented */
538  return 0;
539 }
540 
541 /******* support routines for icalfileset_fetch_match *********/
542 
543 struct icalfileset_id {
544  char *uid;
545  char *recurrence_id;
546  int sequence;
547 };
548 
549 static void icalfileset_id_free(struct icalfileset_id *id)
550 {
551  if (id->recurrence_id != 0) {
552  free(id->recurrence_id);
553  }
554 
555  if (id->uid != 0) {
556  free(id->uid);
557  }
558 }
559 
560 static struct icalfileset_id icalfileset_get_id(const icalcomponent *comp)
561 {
562  icalcomponent *inner;
563  struct icalfileset_id id;
564  icalproperty *p;
565 
567 
568  p = icalcomponent_get_first_property(inner, ICAL_UID_PROPERTY);
569 
570  assert(p != 0);
571 
572  id.uid = strdup(icalproperty_get_uid(p));
573 
574  p = icalcomponent_get_first_property(inner, ICAL_SEQUENCE_PROPERTY);
575 
576  if (p == 0) {
577  id.sequence = 0;
578  } else {
579  id.sequence = icalproperty_get_sequence(p);
580  }
581 
582  p = icalcomponent_get_first_property(inner, ICAL_RECURRENCEID_PROPERTY);
583 
584  if (p == 0) {
585  id.recurrence_id = NULL;
586  } else {
587  icalvalue *v;
588 
589  v = icalproperty_get_value(p);
590  id.recurrence_id = icalvalue_as_ical_string_r(v);
591 
592  assert(id.recurrence_id != 0);
593  }
594 
595  return id;
596 }
597 
598 /* Find the component that is related to the given
599  component. Currently, it just matches based on UID and
600  RECURRENCE-ID */
601 
602 static int _compare_ids(const char *compid, const char *matchid)
603 {
604  if (compid != NULL && matchid != NULL) {
605  if (strcmp(compid, matchid) == 0) {
606  return 1;
607  }
608  }
609 
610  if (compid == NULL && matchid == NULL) {
611  return 1;
612  }
613 
614  return 0;
615 }
616 
617 icalcomponent *icalfileset_fetch_match(icalset *set, const icalcomponent *comp)
618 {
619  icalfileset *fset = (icalfileset *)set;
620  icalcompiter i;
621 
622  struct icalfileset_id comp_id, match_id;
623 
624  comp_id = icalfileset_get_id(comp);
625 
626  for (i = icalcomponent_begin_component(fset->cluster, ICAL_ANY_COMPONENT);
627  icalcompiter_deref(&i) != 0; icalcompiter_next(&i)) {
628  icalcomponent *match = icalcompiter_deref(&i);
629 
630  match_id = icalfileset_get_id(match);
631 
632  if (_compare_ids(comp_id.uid, match_id.uid) &&
633  _compare_ids(comp_id.recurrence_id, match_id.recurrence_id)) {
634  /* HACK. What to do with SEQUENCE? */
635 
636  icalfileset_id_free(&match_id);
637  icalfileset_id_free(&comp_id);
638  return match;
639  }
640 
641  icalfileset_id_free(&match_id);
642  }
643 
644  icalfileset_id_free(&comp_id);
645  return 0;
646 }
647 
648 icalerrorenum icalfileset_modify(icalset *set, icalcomponent *old, icalcomponent *new)
649 {
650  _unused(set);
651  _unused(old);
652  _unused(new);
653  assert(0); /* HACK, not implemented */
654  return ICAL_NO_ERROR;
655 }
656 
657 /* Iterate through components */
658 icalcomponent *icalfileset_get_current_component(icalset *set)
659 {
660  icalfileset *fset;
661 
662  icalerror_check_arg_rz((set != 0), "set");
663 
664  fset = (icalfileset *)set;
665  return icalcomponent_get_current_component(fset->cluster);
666 }
667 
668 icalcomponent *icalfileset_get_first_component(icalset *set)
669 {
670  icalcomponent *c = 0;
671  icalfileset *fset;
672 
673  icalerror_check_arg_rz((set != 0), "set");
674  fset = (icalfileset *)set;
675 
676  do {
677  if (c == 0) {
679  } else {
681  }
682 
683  if (c != 0 && (fset->gauge == 0 || icalgauge_compare(fset->gauge, c) == 1)) {
684  return c;
685  }
686 
687  } while (c != 0);
688 
689  return 0;
690 }
691 
692 icalcomponent *icalfileset_get_next_component(icalset *set)
693 {
694  icalfileset *fset;
695  icalcomponent *c;
696 
697  icalerror_check_arg_rz((set != 0), "set");
698  fset = (icalfileset *)set;
699 
700  do {
702 
703  if (c != 0 && (fset->gauge == 0 || icalgauge_compare(fset->gauge, c) == 1)) {
704  return c;
705  }
706 
707  } while (c != 0);
708 
709  return 0;
710 }
711 
712 /*
713 icalsetiter icalfileset_begin_component(icalset* set, icalcomponent_kind kind, icalgauge* gauge)
714 {
715  icalsetiter itr = icalsetiter_null;
716  icalcomponent* comp = NULL;
717  icalcompiter citr;
718  icalfileset *fset = (icalfileset*)set;
719 
720  icalerror_check_arg_re((set!=0), "set", icalsetiter_null);
721 
722  itr.gauge = gauge;
723 
724  citr = icalcomponent_begin_component(fset->cluster, kind);
725  comp = icalcompiter_deref(&citr);
726 
727  while (comp != 0) {
728  comp = icalcompiter_deref(&citr);
729  if (gauge == 0 || icalgauge_compare(itr.gauge, comp) == 1) {
730  itr.iter = citr;
731  return itr;
732  }
733  comp = icalcompiter_next(&citr);
734  }
735 
736  return icalsetiter_null;
737 }
738 
739 */
740 
741 icalsetiter icalfileset_begin_component(icalset *set, icalcomponent_kind kind, icalgauge *gauge,
742  const char *tzid)
743 {
745  icalcomponent *comp = NULL;
746  icalcompiter citr;
747  icalfileset *fset;
748  struct icaltimetype start, next;
749  icalproperty *rrule;
750 
751  _unused(tzid);
752 
753  icalerror_check_arg_re((set != 0), "set", icalsetiter_null);
754 
755  start = icaltime_from_timet_with_zone(time(0), 0, NULL);
756  itr.gauge = gauge;
757 
758  fset = (icalfileset *)set;
759  citr = icalcomponent_begin_component(fset->cluster, kind);
760  comp = icalcompiter_deref(&citr);
761 
762  if (gauge == 0) {
763  itr.iter = citr;
764  return itr;
765  }
766 
767  while (comp != 0) {
768  /* check if it is a recurring component and with gauge expand, if so
769  we need to add recurrence-id property to the given component */
770  rrule = icalcomponent_get_first_property(comp, ICAL_RRULE_PROPERTY);
771  struct icalrecurrencetype *recur = rrule ? icalproperty_get_rrule(rrule) : NULL;
772  int g = icalgauge_get_expand(gauge);
773  if (recur != 0 && g == 1) {
775  icalproperty *dtstart = icalcomponent_get_first_property(comp, ICAL_DTSTART_PROPERTY);
776  if (dtstart) {
777  start = icalproperty_get_dtstart(dtstart);
778  }
779  } else if (icalcomponent_isa(comp) == ICAL_VTODO_COMPONENT) {
780  icalproperty *due = icalcomponent_get_first_property(comp, ICAL_DUE_PROPERTY);
781  if (due) {
782  start = icalproperty_get_due(due);
783  }
784  }
785 
786  if (itr.last_component == NULL) {
787  itr.ritr = icalrecur_iterator_new(recur, start);
788  next = icalrecur_iterator_next(itr.ritr);
789  itr.last_component = comp;
790  } else {
791  next = icalrecur_iterator_next(itr.ritr);
792  if (icaltime_is_null_time(next)) {
793  itr.last_component = NULL;
794  icalrecur_iterator_free(itr.ritr);
795  itr.ritr = NULL;
796  return icalsetiter_null;
797  } else {
798  itr.last_component = comp;
799  }
800  }
801 
802  /* add recurrence-id to the component
803  if there is a recurrence-id already, remove it, then add the new one */
804  icalproperty *prop = icalcomponent_get_first_property(comp, ICAL_RECURRENCEID_PROPERTY);
805  if (prop) {
806  icalcomponent_remove_property(comp, prop);
807  }
808  icalcomponent_add_property(comp, icalproperty_new_recurrenceid(next));
809  }
810 
811  if (icalgauge_compare(itr.gauge, comp) == 1) {
812  /* matches and returns */
813  itr.iter = citr;
814  return itr;
815  }
816 
817  /* if there is no previous component pending, then get the next component */
818  if (itr.last_component == NULL) {
819  comp = icalcompiter_next(&citr);
820  }
821  }
822 
823  return icalsetiter_null;
824 }
825 
826 icalcomponent *icalfileset_form_a_matched_recurrence_component(icalsetiter *itr)
827 {
828  icalcomponent *comp = NULL;
829  struct icaltimetype start, next;
830  icalproperty *rrule, *prop;
831  struct icalrecurrencetype *recur;
832 
833  start = icaltime_from_timet_with_zone(time(0), 0, NULL);
834  comp = itr->last_component;
835 
836  if (comp == NULL || itr->gauge == NULL) {
837  return NULL;
838  }
839 
840  rrule = icalcomponent_get_first_property(comp, ICAL_RRULE_PROPERTY);
841  recur = rrule ? icalproperty_get_rrule(rrule) : NULL;
842 
843  if (recur == NULL) {
844  return NULL;
845  }
846 
848  icalproperty *dtstart = icalcomponent_get_first_property(comp, ICAL_DTSTART_PROPERTY);
849  if (dtstart) {
850  start = icalproperty_get_dtstart(dtstart);
851  }
852  } else if (icalcomponent_isa(comp) == ICAL_VTODO_COMPONENT) {
853  icalproperty *due = icalcomponent_get_first_property(comp, ICAL_DUE_PROPERTY);
854  if (due) {
855  start = icalproperty_get_due(due);
856  }
857  }
858 
859  if (itr->ritr == NULL) {
860  itr->ritr = icalrecur_iterator_new(recur, start);
861  next = icalrecur_iterator_next(itr->ritr);
862  itr->last_component = comp;
863  } else {
864  next = icalrecur_iterator_next(itr->ritr);
865  if (icaltime_is_null_time(next)) {
866  /* no more recurrence, returns */
867  itr->last_component = NULL;
868  icalrecur_iterator_free(itr->ritr);
869  itr->ritr = NULL;
870  return NULL;
871  } else {
872  itr->last_component = comp;
873  }
874  }
875 
876  /* add recurrence-id to the component
877  * if there is a recurrence-id already, remove it, then add the new one */
878  prop = icalcomponent_get_first_property(comp, ICAL_RECURRENCEID_PROPERTY);
879  if (prop) {
880  icalcomponent_remove_property(comp, prop);
881  }
882  icalcomponent_add_property(comp, icalproperty_new_recurrenceid(next));
883 
884  if (itr->gauge == 0 || icalgauge_compare(itr->gauge, comp) == 1) {
885  /* matches and returns */
886  return comp;
887  }
888  /* not matched */
889  return NULL;
890 }
891 
892 icalcomponent *icalfilesetiter_to_next(icalset *set, icalsetiter *i)
893 {
894  icalcomponent *c = NULL;
895  struct icaltimetype start, next;
896  icalproperty *dtstart, *rrule, *prop, *due;
897  struct icalrecurrencetype *recur;
898  int g = 0;
899 
900  _unused(set);
901 
902  start = icaltime_from_timet_with_zone(time(0), 0, NULL);
903  next = icaltime_from_timet_with_zone(time(0), 0, NULL);
904 
905  do {
906  c = icalcompiter_next(&(i->iter));
907 
908  if (c == 0) {
909  continue;
910  }
911  if (i->gauge == 0) {
912  return c;
913  }
914 
915  rrule = icalcomponent_get_first_property(c, ICAL_RRULE_PROPERTY);
916  recur = rrule ? icalproperty_get_rrule(rrule) : NULL;
917  g = icalgauge_get_expand(i->gauge);
918 
919  /* a recurring component with expand query */
920  if (recur != 0 && g == 1) {
922  dtstart = icalcomponent_get_first_property(c, ICAL_DTSTART_PROPERTY);
923  if (dtstart) {
924  start = icalproperty_get_dtstart(dtstart);
925  }
926  } else if (icalcomponent_isa(c) == ICAL_VTODO_COMPONENT) {
927  due = icalcomponent_get_first_property(c, ICAL_DUE_PROPERTY);
928  if (due) {
929  start = icalproperty_get_due(due);
930  }
931  }
932 
933  if (i->ritr == NULL) {
934  i->ritr = icalrecur_iterator_new(recur, start);
935  next = icalrecur_iterator_next(i->ritr);
936  i->last_component = c;
937  } else {
938  next = icalrecur_iterator_next(i->ritr);
939  if (icaltime_is_null_time(next)) {
940  /* no more recurrence, returns */
941  i->last_component = NULL;
942  icalrecur_iterator_free(i->ritr);
943  i->ritr = NULL;
944  return NULL;
945  } else {
946  i->last_component = c;
947  }
948  }
949  }
950 
951  /* add recurrence-id to the component
952  * if there is a recurrence-id already, remove it, then add the new one */
953  prop = icalcomponent_get_first_property(c, ICAL_RECURRENCEID_PROPERTY);
954  if (prop) {
956  }
957  icalcomponent_add_property(c, icalproperty_new_recurrenceid(next));
958 
959  if ((i->gauge == 0 || icalgauge_compare(i->gauge, c) == 1)) {
960  return c;
961  }
962  } while (true);
963 
964  return 0;
965 }
bool icalerror_get_errors_are_fatal(void)
Determine if errors are fatal.
Definition: icalerror.c:36
icalcomponent * icalcomponent_get_first_component(icalcomponent *c, icalcomponent_kind kind)
icalerrorenum icalfileset_select(icalset *set, icalgauge *gauge)
Definition: icalfileset.c:475
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)
void icalcomponent_remove_property(icalcomponent *component, icalproperty *property)
icalcomponent * icalcomponent_get_current_component(icalcomponent *component)
Common memory management routines.
Defines the data structure representing iCalendar parameter values.
icalcluster * cluster
Definition: icalfileset.h:114
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)
void icalmemory_free_buffer(void *buf)
Releases a buffer.
Definition: icalmemory.c:355
icalcomponent * icalcomponent_clone(const icalcomponent *old)
void icalparser_set_gen_data(icalparser *parser, void *data)
Sets the data that icalparser_parse will give to the line_gen_func as the parameter 'd'...
Definition: icalparser.c:129
icalcomponent * icalcomponent_get_first_real_component(const icalcomponent *c)
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
void icalerror_clear_errno(void)
Resets icalerrno to ICAL_NO_ERROR.
Definition: icalerror.c:85
icalerrorenum
Represents the different types of errors that can be triggered in libical.
Definition: icalerror.h:41
Options for opening an icalfileset.
Definition: icalfileset.h:110
void icalparser_free(icalparser *parser)
Frees an icalparser object.
Definition: icalparser.c:112
void icalerror_set_errors_are_fatal(bool fatal)
Change if errors are fatal.
Definition: icalerror.c:31
char * icalcomponent_as_ical_string_r(const icalcomponent *component)
icalcomponent * icalcomponent_get_next_component(icalcomponent *c, icalcomponent_kind kind)
void icalfileset_clear(icalset *set)
Clears the gauge.
Definition: icalfileset.c:487
icalerrorenum icalfileset_modify(icalset *set, icalcomponent *old, icalcomponent *new)
Modifies components according to the MODIFY method of CAP.
Definition: icalfileset.c:648
icalcluster * icalcluster_new(const char *key, icalcomponent *data)
Create a cluster with a key/value pair.
Definition: icalcluster.c:39
Manages a database of ical components and offers interfaces for reading, writing and searching for co...
void icalrecur_iterator_free(icalrecur_iterator *impl)
Definition: icalrecur.c:2451
icalcomponent * icalfileset_fetch(icalset *set, icalcomponent_kind kind, const char *uid)
Gets and searches for a component by uid.
Definition: icalfileset.c:497
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 * icalfileset_get_component(icalset *set)
Definition: icalfileset.c:420
icalfileset_options icalfileset_options_default
Definition: icalfileset.c:42
icalparser * icalparser_new(void)
Creates a new icalparser.
Definition: icalparser.c:89
#define icalerrno
Access the current icalerrno value.
Definition: icalerror.h:133
bool icaltime_is_null_time(const struct icaltimetype t)
Definition: icaltime.c:630
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
icalcomponent * icalparser_parse(icalparser *parser, icalparser_line_gen_func line_gen_func)
Message oriented parsing.
Definition: icalparser.c:588
icalcompiter icalcomponent_begin_component(icalcomponent *component, icalcomponent_kind kind)
icalproperty * icalcomponent_get_first_property(icalcomponent *c, icalproperty_kind kind)