$darkmode
Libical API Documentation 4.0 STABLE VERSION Visit the v3.0 documentation
icaldirset.c
Go to the documentation of this file.
1 /*
2 ======================================================================
3  FILE: icaldirset.c
4  CREATOR: eric 28 November 1999
5 
6  SPDX-FileCopyrightText: 2000, Eric Busboom <eric@civicknowledge.com>
7  SPDX-License-Identifier: LGPL-2.1-only OR MPL-2.0
8 
9  The Original Code is eric. The Initial Developer of the Original
10  Code is Eric Busboom
11  ======================================================================*/
12 
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22 
23 #include "icaldirset.h"
24 #include "icaldirsetimpl.h"
25 #include "icalerror_p.h"
26 #include "icalfileset.h"
27 #include "icalpvl_p.h"
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 
32 #if defined(HAVE_DIRENT_H)
33 #include <dirent.h>
34 #endif
35 
36 #if defined(HAVE_SYS_UTSNAME_H)
37 #include <sys/utsname.h>
38 #endif
39 
41 static icaldirset_options icaldirset_options_default = {O_RDWR | O_CREAT};
42 
43 const char *icaldirset_path(icalset *set)
44 {
45  const icaldirset *dset = (icaldirset *)set;
46 
47  return dset->dir;
48 }
49 
50 void icaldirset_mark(icalset *set)
51 {
52  icaldirset *dset = (icaldirset *)set;
53 
54  icalcluster_mark(dset->cluster);
55 }
56 
57 icalerrorenum icaldirset_commit(icalset *set)
58 {
59  icaldirset *dset = (icaldirset *)set;
60  icalset *fileset;
62 
63  options.cluster = dset->cluster;
64 
65  fileset = icalset_new(ICAL_FILE_SET, icalcluster_key(dset->cluster), &options);
66 
67  (void)fileset->commit(fileset);
68  fileset->free(fileset);
69 
70  return ICAL_NO_ERROR;
71 }
72 
73 static void icaldirset_lock(const char *dir)
74 {
75  _unused(dir);
76 }
77 
78 static void icaldirset_unlock(const char *dir)
79 {
80  _unused(dir);
81 }
82 
83 /* Load the contents of the store directory into the store's internal directory list*/
84 static icalerrorenum icaldirset_read_directory(icaldirset *dset)
85 {
86  char *str;
87 
88 #if defined(HAVE_DIRENT_H)
89  const struct dirent *de;
90  DIR *dp;
91 
92  dp = opendir(dset->dir);
93 
94  if (dp == 0) {
96  return ICAL_FILE_ERROR;
97  }
98 
99  /* clear contents of directory list */
100  while ((str = icalpvl_pop(dset->directory))) {
101  free(str);
102  }
103 
104  /* load all of the cluster names in the directory list */
105  for (de = readdir(dp); de != 0; de = readdir(dp)) {
106  /* Remove known directory names '.' and '..' */
107  if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) {
108  continue;
109  }
110 
111  icalpvl_push(dset->directory, (void *)strdup(de->d_name));
112  }
113 
114  closedir(dp);
115 #else
116  struct _finddata_t c_file;
117  intptr_t hFile;
118 
119  /* Find first .c file in current directory */
120  if ((hFile = _findfirst("*", &c_file)) == -1L) {
122  return ICAL_FILE_ERROR;
123  } else {
124  while ((str = icalpvl_pop(dset->directory))) {
125  free(str);
126  }
127 
128  /* load all of the cluster names in the directory list */
129  do {
130  /* Remove known directory names '.' and '..' */
131  if (strcmp(c_file.name, ".") == 0 || strcmp(c_file.name, "..") == 0) {
132  continue;
133  }
134 
135  icalpvl_push(dset->directory, (void *)strdup(c_file.name));
136  } while (_findnext(hFile, &c_file) == 0);
137 
138  _findclose(hFile);
139  }
140 
141 #endif
142 
143  return ICAL_NO_ERROR;
144 }
145 
146 /* cppcheck-suppress constParameterPointer */
147 icalset *icaldirset_init(icalset *set, const char *dir, void *options_in)
148 {
149  icaldirset *dset;
150  const icaldirset_options *options = options_in;
151  struct stat sbuf;
152 
153  icalerror_check_arg_rz((dir != 0), "dir");
154  icalerror_check_arg_rz((set != 0), "set");
155  dset = (icaldirset *)set;
156 
157  if (stat(dir, &sbuf) != 0) {
159  return 0;
160  }
161 
162  /* dir is not the name of a direectory */
163  if (!S_ISDIR(sbuf.st_mode)) {
165  return 0;
166  }
167 
168  icaldirset_lock(dir);
169 
170  dset->dir = (char *)strdup(dir);
171  dset->options = *options;
172  dset->directory = icalpvl_newlist();
173  dset->directory_iterator = 0;
174  dset->gauge = 0;
175  dset->first_component = 0;
176  dset->cluster = 0;
177 
178  return set;
179 }
180 
181 icalset *icaldirset_new(const char *dir)
182 {
183  return icalset_new(ICAL_DIR_SET, dir, &icaldirset_options_default);
184 }
185 
186 icalset *icaldirset_new_reader(const char *dir)
187 {
188  icaldirset_options reader_options = icaldirset_options_default;
189 
190  reader_options.flags = O_RDONLY;
191 
192  return icalset_new(ICAL_DIR_SET, dir, &reader_options);
193 }
194 
195 icalset *icaldirset_new_writer(const char *dir)
196 {
197  icaldirset_options writer_options = icaldirset_options_default;
198 
199  writer_options.flags = O_RDWR | O_CREAT;
200 
201  return icalset_new(ICAL_DIR_SET, dir, &writer_options);
202 }
203 
204 void icaldirset_free(icalset *s)
205 {
206  icaldirset *dset = (icaldirset *)s;
207  char *str;
208 
209  icaldirset_unlock(dset->dir);
210 
211  if (dset->dir != 0) {
212  free(dset->dir);
213  dset->dir = 0;
214  }
215 
216  if (dset->gauge != 0) {
217  icalgauge_free(dset->gauge);
218  dset->gauge = 0;
219  }
220 
221  if (dset->cluster != 0) {
222  icalcluster_free(dset->cluster);
223  }
224 
225  while (dset->directory != 0 && (str = icalpvl_pop(dset->directory)) != 0) {
226  free(str);
227  }
228 
229  if (dset->directory != 0) {
230  icalpvl_free(dset->directory);
231  dset->directory = 0;
232  }
233 
234  dset->directory_iterator = 0;
235  dset->first_component = 0;
236 }
237 
238 static icalerrorenum icaldirset_next_cluster(icaldirset *dset)
239 {
240  char path[MAXPATHLEN];
241 
242  if (dset->directory_iterator == 0) {
244  return ICAL_INTERNAL_ERROR;
245  }
246  dset->directory_iterator = icalpvl_next(dset->directory_iterator);
247 
248  if (dset->directory_iterator == 0) {
249  /* There are no more clusters */
250  if (dset->cluster != 0) {
251  icalcluster_free(dset->cluster);
252  dset->cluster = 0;
253  }
254  return ICAL_NO_ERROR;
255  }
256 
257  snprintf(path, sizeof(path), "%s/%s", dset->dir, (char *)icalpvl_data(dset->directory_iterator));
258 
259  icalcluster_free(dset->cluster);
260  dset->cluster = icalfileset_produce_icalcluster(path);
261 
262  return icalerrno;
263 }
264 
265 static void icaldirset_add_uid(icalcomponent *comp)
266 {
267  char uidstring[MAXPATHLEN] = {0};
268  icalproperty *uid;
269 
270 #if defined(HAVE_SYS_UTSNAME_H)
271  struct utsname unamebuf;
272 #endif
273 
274  icalerror_check_arg_rv((comp != 0), "comp");
275 
276  uid = icalcomponent_get_first_property(comp, ICAL_UID_PROPERTY);
277 
278  if (uid == 0) {
279 #if defined(HAVE_SYS_UTSNAME_H)
280  uname(&unamebuf);
281  snprintf(uidstring, sizeof(uidstring), "%d-%s", (int)getpid(), unamebuf.nodename);
282 #else
283  /* FIXME: There must be an easy get the system name */
284  snprintf(uidstring, sizeof(uidstring), "%d-%s", (int)getpid(), "WINDOWS");
285 #endif
286  uid = icalproperty_new_uid(uidstring);
287  icalcomponent_add_property(comp, uid);
288  } else {
289  strncpy(uidstring, icalproperty_get_uid(uid), MAXPATHLEN - 1);
290  }
291 }
292 
293 icalerrorenum icaldirset_add_component(icalset *set, icalcomponent *comp)
294 {
295  char clustername[MAXPATHLEN] = {0};
296  icalproperty *dt = 0;
297  icalvalue *v;
298  struct icaltimetype tm;
300  icalcomponent *inner;
301  icaldirset *dset;
302 
303  icalerror_check_arg_rz((set != 0), "set");
304  icalerror_check_arg_rz((comp != 0), "comp");
305 
306  dset = (icaldirset *)set;
307 
308  icaldirset_add_uid(comp);
309 
310  /* Determine which cluster this object belongs in. This is a HACK */
311 
313  inner != 0; inner = icalcomponent_get_next_component(comp, ICAL_ANY_COMPONENT)) {
314  dt = icalcomponent_get_first_property(inner, ICAL_DTSTAMP_PROPERTY);
315 
316  if (dt != 0) {
317  break;
318  }
319  }
320 
321  if (dt == 0) {
323  inner != 0; inner = icalcomponent_get_next_component(comp, ICAL_ANY_COMPONENT)) {
324  dt = icalcomponent_get_first_property(inner, ICAL_DTSTART_PROPERTY);
325 
326  if (dt != 0) {
327  break;
328  }
329  }
330  }
331 
332  if (dt == 0) {
333  icalerror_warn("The component does not have a DTSTAMP or DTSTART property, "
334  "so it cannot be added to the store");
336  return ICAL_BADARG_ERROR;
337  }
338 
339  v = icalproperty_get_value(dt);
340  tm = icalvalue_get_datetime(v);
341 
342  snprintf(clustername, MAXPATHLEN, "%s/%04d%02d", dset->dir, tm.year, tm.month);
343 
344  /* Load the cluster and insert the object */
345  if (dset->cluster != 0 && strcmp(clustername, icalcluster_key(dset->cluster)) != 0) {
346  icalcluster_free(dset->cluster);
347  dset->cluster = 0;
348  }
349 
350  if (dset->cluster == 0) {
351  dset->cluster = icalfileset_produce_icalcluster(clustername);
352 
353  if (dset->cluster == 0) {
354  error = icalerrno;
355  }
356  }
357 
358  if (error != ICAL_NO_ERROR) {
359  icalerror_set_errno(error);
360  return error;
361  }
362 
363  /* Add the component to the cluster */
364  (void)icalcluster_add_component(dset->cluster, comp);
365 
366  /* icalcluster_mark(cluster->cluster); */
367 
368  return ICAL_NO_ERROR;
369 }
370 
371 icalerrorenum icaldirset_remove_component(icalset *set, icalcomponent *comp)
372 {
373  icaldirset *dset;
374  icalcomponent *filecomp;
375  icalcompiter i;
376  int found = 0;
377 
378  icalerror_check_arg_re((set != 0), "set", ICAL_BADARG_ERROR);
379  icalerror_check_arg_re((comp != 0), "comp", ICAL_BADARG_ERROR);
380 
381  dset = (icaldirset *)set;
382  icalerror_check_arg_re((dset->cluster != 0), "Cluster pointer", ICAL_USAGE_ERROR);
383 
384  filecomp = icalcluster_get_component(dset->cluster);
385 
387  icalcompiter_deref(&i) != 0; icalcompiter_next(&i)) {
388  const icalcomponent *this = icalcompiter_deref(&i);
389  if (this == comp) {
390  found = 1;
391  break;
392  }
393  }
394 
395  if (found != 1) {
396  icalerror_warn("icaldirset_remove_component: component is not part of current cluster");
398  return ICAL_USAGE_ERROR;
399  }
400 
401  (void)icalcluster_remove_component(dset->cluster, comp);
402 
403  /* icalcluster_mark(cluster->cluster); */
404 
405  /* If the removal emptied the fileset, get the next fileset */
406  if (icalcluster_count_components(dset->cluster, ICAL_ANY_COMPONENT) == 0) {
407  icalerrorenum error = icaldirset_next_cluster(dset);
408 
409  if (dset->cluster != 0 && error == ICAL_NO_ERROR) {
410  (void)icalcluster_get_first_component(dset->cluster);
411  } else {
412  /* HACK. Not strictly correct for cluster->cluster==0 */
413  return error;
414  }
415  } else {
416  /* Do nothing */
417  }
418 
419  return ICAL_NO_ERROR;
420 }
421 
422 int icaldirset_count_components(icalset *store, icalcomponent_kind kind)
423 {
424  _unused(store);
425  _unused(kind);
426 
427  /* NOT IMPLEMENTED */
428  assert(0);
429 
430  return 0;
431 }
432 
433 icalcomponent *icaldirset_fetch_match(icalset *set, const icalcomponent *c)
434 {
435  _unused(set);
436  _unused(c);
437 
438  fprintf(stderr, " icaldirset_fetch_match is not implemented\n");
439  assert(0);
440  return 0;
441 }
442 
443 icalcomponent *icaldirset_fetch(icalset *set, icalcomponent_kind kind, const char *uid)
444 {
445  icaldirset *dset;
446  icalgauge *gauge;
447  icalgauge *old_gauge;
448  icalcomponent *c;
449  char sql[256];
450 
451  _unused(kind);
452 
453  icalerror_check_arg_rz((set != 0), "set");
454  icalerror_check_arg_rz((uid != 0), "uid");
455 
456  snprintf(sql, 256, "SELECT * FROM VEVENT WHERE UID = \"%s\"", uid);
457 
458  gauge = icalgauge_new_from_sql(sql, 0);
459  dset = (icaldirset *)set;
460  old_gauge = dset->gauge;
461  dset->gauge = gauge;
462 
463  c = icaldirset_get_first_component(set);
464 
465  dset->gauge = old_gauge;
466 
467  if (gauge) {
468  icalgauge_free(gauge);
469  }
470 
471  return c;
472 }
473 
474 int icaldirset_has_uid(icalset *set, const char *uid)
475 {
476  const icalcomponent *c;
477 
478  icalerror_check_arg_rz((set != 0), "set");
479  icalerror_check_arg_rz((uid != 0), "uid");
480 
481  /* HACK. This is a temporary implementation. _has_uid should use a
482  database, and _fetch should use _has_uid, not the other way
483  around */
484  c = icaldirset_fetch(set, 0, uid);
485 
486  return c != 0;
487 }
488 
489 icalerrorenum icaldirset_select(icalset *set, icalgauge *gauge)
490 {
491  icaldirset *dset;
492 
493  icalerror_check_arg_re((set != 0), "set", ICAL_BADARG_ERROR);
494  icalerror_check_arg_re((gauge != 0), "gauge", ICAL_BADARG_ERROR);
495 
496  dset = (icaldirset *)set;
497  dset->gauge = gauge;
498 
499  return ICAL_NO_ERROR;
500 }
501 
502 icalerrorenum icaldirset_modify(icalset *set, icalcomponent *oldcomp, icalcomponent *newcomp)
503 {
504  _unused(set);
505  _unused(oldcomp);
506  _unused(newcomp);
507 
508  /* NOT IMPLEMENTED */
509  assert(0);
510  return ICAL_NO_ERROR;
511 }
512 
513 void icaldirset_clear(icalset *set)
514 {
515  _unused(set);
516 
517  /* NOT IMPLEMENTED */
518  assert(0);
519 }
520 
521 icalcomponent *icaldirset_get_current_component(icalset *set)
522 {
523  icaldirset *dset = (icaldirset *)set;
524 
525  icalerror_check_arg_rz((dset != 0), "dset");
526 
527  if (dset->cluster == 0) {
528  return icaldirset_get_first_component(set);
529  } else {
530  return icalcluster_get_current_component(dset->cluster);
531  }
532 }
533 
534 icalcomponent *icaldirset_get_first_component(icalset *set)
535 {
536  icaldirset *dset = (icaldirset *)set;
537 
538  icalerrorenum error;
539  char path[MAXPATHLEN];
540 
541  error = icaldirset_read_directory(dset);
542 
543  if (error != ICAL_NO_ERROR) {
544  icalerror_set_errno(error);
545  return 0;
546  }
547 
548  dset->directory_iterator = icalpvl_head(dset->directory);
549 
550  if (dset->directory_iterator == 0) {
551  icalerror_set_errno(error);
552  return 0;
553  }
554 
555  snprintf(path, MAXPATHLEN, "%s/%s", dset->dir, (char *)icalpvl_data(dset->directory_iterator));
556 
557  /* If the next cluster we need is different than the current cluster,
558  delete the current one and get a new one */
559 
560  if (dset->cluster != 0 && strcmp(path, icalcluster_key(dset->cluster)) != 0) {
561  icalcluster_free(dset->cluster);
562  dset->cluster = 0;
563  }
564 
565  if (dset->cluster == 0) {
566  dset->cluster = icalfileset_produce_icalcluster(path);
567 
568  if (dset->cluster == 0) {
569  error = icalerrno;
570  }
571  }
572 
573  if (error != ICAL_NO_ERROR) {
574  icalerror_set_errno(error);
575  return 0;
576  }
577 
578  dset->first_component = 1;
579 
580  return icaldirset_get_next_component(set);
581 }
582 
583 icalcomponent *icaldirset_get_next_component(icalset *set)
584 {
585  icaldirset *dset;
586  icalcomponent *c;
587  icalerrorenum error;
588 
589  icalerror_check_arg_rz((set != 0), "set");
590  dset = (icaldirset *)set;
591 
592  if (dset->cluster == 0) {
593  icalerror_warn("icaldirset_get_next_component called with a NULL cluster "
594  "(Caller must call icaldirset_get_first_component first)");
596  return 0;
597  }
598 
599  /* Set the component iterator for the following for loop */
600  if (dset->first_component == 1) {
601  (void)icalcluster_get_first_component(dset->cluster);
602  dset->first_component = 0;
603  } else {
604  (void)icalcluster_get_next_component(dset->cluster);
605  }
606 
607  while (1) {
608  /* Iterate through all of the objects in the cluster */
609  for (c = icalcluster_get_current_component(dset->cluster);
610  c != 0; c = icalcluster_get_next_component(dset->cluster)) {
611  /* If there is a gauge defined and the component does not
612  pass the gauge, skip the rest of the loop */
613 
614  if (dset->gauge != 0 && icalgauge_compare(dset->gauge, c) == 0) {
615  continue;
616  }
617 
618  /* Either there is no gauge, or the component passed the
619  gauge, so return it */
620 
621  return c;
622  }
623 
624  /* Fell through the loop, so the component we want is not
625  in this cluster. Load a new cluster and try again. */
626 
627  error = icaldirset_next_cluster(dset);
628 
629  if (dset->cluster == 0 || error != ICAL_NO_ERROR) {
630  /* No more clusters */
631  return 0;
632  } else {
633  c = icalcluster_get_first_component(dset->cluster);
634 
635  return c;
636  }
637  }
638 
639  return 0; /* Should never get here */
640 }
641 
642 icalsetiter icaldirset_begin_component(icalset *set, icalcomponent_kind kind, icalgauge *gauge,
643  const char *tzid)
644 {
645  _unused(set);
646  _unused(kind);
647  _unused(gauge);
648  _unused(tzid);
649  /*
650  icalsetiter itr = icalsetiter_null;
651  icaldirset *fset = (icaldirset*) set;
652 
653  icalerror_check_arg_re((fset!=0), "set", icalsetiter_null);
654 
655  itr.iter.kind = kind;
656  itr.gauge = gauge;
657  */
658  /* TO BE IMPLEMENTED */
659  return icalsetiter_null;
660 }
661 
662 icalcomponent *icaldirsetiter_to_next(icalset *set, icalsetiter *i)
663 {
664  _unused(set);
665  _unused(i);
666  /* TO BE IMPLEMENTED */
667  return NULL;
668 }
669 
670 icalcomponent *icaldirsetiter_to_prior(icalset *set, icalsetiter *i)
671 {
672  _unused(set);
673  _unused(i);
674  /* TO BE IMPLEMENTED */
675  return NULL;
676 }
icalcomponent * icalcomponent_get_first_component(icalcomponent *c, icalcomponent_kind kind)
bool icalgauge_compare(icalgauge *gauge, icalcomponent *comp)
Definition: icalgauge.c:216
icalvalue * icalproperty_get_value(const icalproperty *prop)
Definition: icalproperty.c:817
icalcomponent * icalcompiter_next(icalcompiter *i)
icalerrorenum icaldirset_add_component(icalset *set, icalcomponent *comp)
Definition: icaldirset.c:293
icalcluster * cluster
Definition: icalfileset.h:114
Manages a database of ical components and offers interfaces for reading, writing and searching for co...
icalsetiter icalsetiter_null
Definition: icalset.c:413
icalgauge * icalgauge_new_from_sql(const char *sql, int expand)
Definition: icalgauge.c:38
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
Options for opening an icalfileset.
Definition: icalfileset.h:110
icalcomponent * icalcomponent_get_next_component(icalcomponent *c, icalcomponent_kind kind)
Manages a database of ical components and offers interfaces for reading, writing and searching for co...
void icalgauge_free(icalgauge *gauge)
Definition: icalgauge.c:72
icalcomponent_kind
Definition: icalenums.h:28
icalcomponent * icalcompiter_deref(icalcompiter *i)
icalfileset_options icalfileset_options_default
Definition: icalfileset.c:42
#define icalerrno
Access the current icalerrno value.
Definition: icalerror.h:133
void icalcomponent_add_property(icalcomponent *component, icalproperty *property)
icalset * icalset_new(icalset_kind kind, const char *dsn, void *options)
Definition: icalset.c:193
icalcomponent * icalcluster_get_current_component(icalcluster *cluster)
Iterate through components.
Definition: icalcluster.c:179
icalcompiter icalcomponent_begin_component(icalcomponent *component, icalcomponent_kind kind)
icalerrorenum icaldirset_remove_component(icalset *set, icalcomponent *comp)
Definition: icaldirset.c:371
icalproperty * icalcomponent_get_first_property(icalcomponent *c, icalproperty_kind kind)