summaryrefslogtreecommitdiffabout
path: root/libical/src/libicalss/icaldirset.c
Unidiff
Diffstat (limited to 'libical/src/libicalss/icaldirset.c') (more/less context) (ignore whitespace changes)
-rw-r--r--libical/src/libicalss/icaldirset.c777
1 files changed, 777 insertions, 0 deletions
diff --git a/libical/src/libicalss/icaldirset.c b/libical/src/libicalss/icaldirset.c
new file mode 100644
index 0000000..b6cb673
--- a/dev/null
+++ b/libical/src/libicalss/icaldirset.c
@@ -0,0 +1,777 @@
1/* -*- Mode: C -*-
2 ======================================================================
3 FILE: icaldirset.c
4 CREATOR: eric 28 November 1999
5
6 $Id$
7 $Locker$
8
9 (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org
10
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of either:
13
14 The LGPL as published by the Free Software Foundation, version
15 2.1, available at: http://www.fsf.org/copyleft/lesser.html
16
17 Or:
18
19 The Mozilla Public License Version 1.0. You may obtain a copy of
20 the License at http://www.mozilla.org/MPL/
21
22 The Original Code is eric. The Initial Developer of the Original
23 Code is Eric Busboom
24
25
26 ======================================================================*/
27
28
29/*
30
31 icaldirset manages a database of ical components and offers
32 interfaces for reading, writting and searching for components.
33
34 icaldirset groups components in to clusters based on their DTSTAMP
35 time -- all components that start in the same month are grouped
36 together in a single file. All files in a sotre are kept in a single
37 directory.
38
39 The primary interfaces are icaldirset_first and icaldirset_next. These
40 routine iterate through all of the components in the store, subject
41 to the current gauge. A gauge is an icalcomponent that is tested
42 against other componets for a match. If a gauge has been set with
43 icaldirset_select, icaldirset_first and icaldirset_next will only
44 return componentes that match the gauge.
45
46 The Store generated UIDs for all objects that are stored if they do
47 not already have a UID. The UID is the name of the cluster (month &
48 year as MMYYYY) plus a unique serial number. The serial number is
49 stored as a property of the cluster.
50
51*/
52
53#ifdef HAVE_CONFIG_H
54#include "config.h"
55#endif
56
57
58#include "icalerror.h"
59#include "ical.h"
60#include "icaldirset.h"
61#include "pvl.h"
62#include "icalparser.h"
63#include "icaldirset.h"
64#include "icalfileset.h"
65#include "icalfilesetimpl.h"
66#include "icalgauge.h"
67
68#include <limits.h> /* For PATH_MAX */
69#include <errno.h>
70#include <sys/types.h> /* for opendir() */
71#include <sys/stat.h> /* for stat */
72
73int snprintf(char *str, size_t n, char const *fmt, ...);
74
75// Eugen C. <eug@thekompany.com>
76#include <defines.h>
77#ifndef _QTWIN_
78#include <dirent.h> /* for opendir() */
79#include <unistd.h>
80#include <sys/utsname.h> /* for uname */
81#endif
82// Eugen C. <eug@thekompany.com>
83
84#include <time.h> /* for clock() */
85#include <stdlib.h> /* for rand(), srand() */
86#include <string.h> /* for strdup */
87#include "icaldirsetimpl.h"
88
89
90struct icaldirset_impl* icaldirset_new_impl()
91{
92 struct icaldirset_impl* impl;
93
94 if ( ( impl = (struct icaldirset_impl*)
95 malloc(sizeof(struct icaldirset_impl))) == 0) {
96 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
97 return 0;
98 }
99
100 strcpy(impl->id,ICALDIRSET_ID);
101
102 return impl;
103}
104
105const char* icaldirset_path(icaldirset* cluster)
106{
107 struct icaldirset_impl *impl = icaldirset_new_impl();
108
109 return impl->dir;
110
111}
112
113void icaldirset_mark(icaldirset* store)
114{
115 struct icaldirset_impl *impl = (struct icaldirset_impl*)store;
116
117 icalfileset_mark(impl->cluster);
118}
119
120
121icalerrorenum icaldirset_commit(icaldirset* store)
122{
123 struct icaldirset_impl *impl = (struct icaldirset_impl*)store;
124
125 return icalfileset_commit(impl->cluster);
126
127}
128
129void icaldirset_lock(const char* dir)
130{
131}
132
133
134void icaldirset_unlock(const char* dir)
135{
136}
137
138/* Load the contents of the store directory into the store's internal directory list*/
139icalerrorenum icaldirset_read_directory(struct icaldirset_impl* impl)
140{
141#ifndef _QTWIN_
142 struct dirent *de;
143 DIR* dp;
144 char *str;
145
146 dp = opendir(impl->dir);
147
148 if ( dp == 0) {
149 icalerror_set_errno(ICAL_FILE_ERROR);
150 return ICAL_FILE_ERROR;
151 }
152
153 /* clear contents of directory list */
154 while((str = pvl_pop(impl->directory))){
155 free(str);
156 }
157
158 /* load all of the cluster names in the directory list */
159 for(de = readdir(dp);
160 de != 0;
161 de = readdir(dp)){
162
163 /* Remove known directory names '.' and '..'*/
164 if (strcmp(de->d_name,".") == 0 ||
165 strcmp(de->d_name,"..") == 0 ){
166 continue;
167 }
168
169 pvl_push(impl->directory, (void*)strdup(de->d_name));
170 }
171
172 closedir(dp);
173
174 return ICAL_NO_ERROR;
175#else
176 icalerror_set_errno(ICAL_FILE_ERROR);
177 return ICAL_FILE_ERROR;
178#endif
179}
180
181icaldirset* icaldirset_new(const char* dir)
182{
183 struct icaldirset_impl *impl = icaldirset_new_impl();
184 struct stat sbuf;
185
186 if (impl == 0){
187 return 0;
188 }
189
190 icalerror_check_arg_rz( (dir!=0), "dir");
191
192 if (stat(dir,&sbuf) != 0){
193 icalerror_set_errno(ICAL_FILE_ERROR);
194 return 0;
195 }
196
197#ifndef _QTWIN_
198 /* dir is not the name of a direectory*/
199 if (!S_ISDIR(sbuf.st_mode)){
200 icalerror_set_errno(ICAL_USAGE_ERROR);
201 return 0;
202 }
203#endif
204
205 icaldirset_lock(dir);
206
207 impl = icaldirset_new_impl();
208
209 if (impl ==0){
210 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
211 return 0;
212 }
213
214 impl->directory = pvl_newlist();
215 impl->directory_iterator = 0;
216 impl->dir = (char*)strdup(dir);
217 impl->gauge = 0;
218 impl->first_component = 0;
219 impl->cluster = 0;
220
221 icaldirset_read_directory(impl);
222
223 return (icaldirset*) impl;
224}
225
226void icaldirset_free(icaldirset* s)
227{
228 struct icaldirset_impl *impl = (struct icaldirset_impl*)s;
229 char* str;
230
231 icaldirset_unlock(impl->dir);
232
233 if(impl->dir !=0){
234 free(impl->dir);
235 }
236
237 if(impl->gauge !=0){
238 icalcomponent_free(impl->gauge);
239 }
240
241 if(impl->cluster !=0){
242 icalfileset_free(impl->cluster);
243 }
244
245 while(impl->directory !=0 && (str=pvl_pop(impl->directory)) != 0){
246 free(str);
247 }
248
249 if(impl->directory != 0){
250 pvl_free(impl->directory);
251 }
252
253 impl->directory = 0;
254 impl->directory_iterator = 0;
255 impl->dir = 0;
256 impl->gauge = 0;
257 impl->first_component = 0;
258
259 free(impl);
260
261}
262
263/* icaldirset_next_uid_number updates a serial number in the Store
264 directory in a file called SEQUENCE */
265
266int icaldirset_next_uid_number(icaldirset* store)
267{
268 struct icaldirset_impl *impl = (struct icaldirset_impl*)store;
269 char sequence = 0;
270 char temp[128];
271 char filename[ICAL_PATH_MAX];
272 char *r;
273 FILE *f;
274 struct stat sbuf;
275
276 icalerror_check_arg_rz( (store!=0), "store");
277
278 sprintf(filename,"%s/%s",impl->dir,"SEQUENCE");
279
280 /* Create the file if it does not exist.*/
281#ifndef _QTWIN_
282 if (stat(filename,&sbuf) == -1 || !S_ISREG(sbuf.st_mode)){
283#else
284 if (stat(filename,&sbuf) == -1){
285#endif
286
287 f = fopen(filename,"w");
288 if (f != 0){
289 fprintf(f,"0");
290 fclose(f);
291 } else {
292 icalerror_warn("Can't create SEQUENCE file in icaldirset_next_uid_number");
293 return 0;
294 }
295
296 }
297
298 if ( (f = fopen(filename,"r+")) != 0){
299
300 rewind(f);
301 r = fgets(temp,128,f);
302
303 if (r == 0){
304 sequence = 1;
305 } else {
306 sequence = atoi(temp)+1;
307 }
308
309 rewind(f);
310
311 fprintf(f,"%d",sequence);
312
313 fclose(f);
314
315 return sequence;
316
317 } else {
318 icalerror_warn("Can't create SEQUENCE file in icaldirset_next_uid_number");
319 return 0;
320 }
321
322}
323
324icalerrorenum icaldirset_next_cluster(icaldirset* store)
325{
326 struct icaldirset_impl *impl = (struct icaldirset_impl*)store;
327 char path[ICAL_PATH_MAX];
328
329 if (impl->directory_iterator == 0){
330 icalerror_set_errno(ICAL_INTERNAL_ERROR);
331 return ICAL_INTERNAL_ERROR;
332 }
333 impl->directory_iterator = pvl_next(impl->directory_iterator);
334
335 if (impl->directory_iterator == 0){
336 /* There are no more clusters */
337 if(impl->cluster != 0){
338 icalfileset_free(impl->cluster);
339 impl->cluster = 0;
340 }
341 return ICAL_NO_ERROR;
342 }
343
344 sprintf(path,"%s/%s",impl->dir,(char*)pvl_data(impl->directory_iterator));
345
346 icalfileset_free(impl->cluster);
347
348 impl->cluster = icalfileset_new(path);
349
350 return icalerrno;
351}
352
353void icaldirset_add_uid(icaldirset* store, icaldirset* comp)
354{
355#ifndef _QTWIN_
356
357 char uidstring[ICAL_PATH_MAX];
358 icalproperty *uid;
359 struct utsname unamebuf;
360
361 icalerror_check_arg_rv( (store!=0), "store");
362 icalerror_check_arg_rv( (comp!=0), "comp");
363
364 uid = icalcomponent_get_first_property(comp,ICAL_UID_PROPERTY);
365
366 if (uid == 0) {
367
368 uname(&unamebuf);
369
370 sprintf(uidstring,"%d-%s",(int)getpid(),unamebuf.nodename);
371
372 uid = icalproperty_new_uid(uidstring);
373 icalcomponent_add_property(comp,uid);
374 } else {
375
376 strcpy(uidstring,icalproperty_get_uid(uid));
377 }
378
379#endif
380}
381
382
383/* This assumes that the top level component is a VCALENDAR, and there
384 is an inner component of type VEVENT, VTODO or VJOURNAL. The inner
385 component must have a DTAMP property */
386
387icalerrorenum icaldirset_add_component(icaldirset* store, icaldirset* comp)
388{
389 struct icaldirset_impl *impl;
390 char clustername[ICAL_PATH_MAX];
391 icalproperty *dt;
392 icalvalue *v;
393 struct icaltimetype tm;
394 icalerrorenum error = ICAL_NO_ERROR;
395 icalcomponent *inner;
396
397 impl = (struct icaldirset_impl*)store;
398 icalerror_check_arg_rz( (store!=0), "store");
399 icalerror_check_arg_rz( (comp!=0), "comp");
400
401 errno = 0;
402
403 icaldirset_add_uid(store,comp);
404
405 /* Determine which cluster this object belongs in. This is a HACK */
406
407 for(inner = icalcomponent_get_first_component(comp,ICAL_ANY_COMPONENT);
408 inner != 0;
409 inner = icalcomponent_get_next_component(comp,ICAL_ANY_COMPONENT)){
410
411 dt = icalcomponent_get_first_property(inner,ICAL_DTSTAMP_PROPERTY);
412
413 if (dt != 0){
414 break;
415 }
416 }
417
418 if (dt == 0){
419
420 for(inner = icalcomponent_get_first_component(comp,ICAL_ANY_COMPONENT);
421 inner != 0;
422 inner = icalcomponent_get_next_component(comp,ICAL_ANY_COMPONENT)){
423
424 dt = icalcomponent_get_first_property(inner,ICAL_DTSTART_PROPERTY);
425
426 if (dt != 0){
427 break;
428 }
429 }
430
431 }
432
433 if (dt == 0){
434
435
436 icalerror_warn("The component does not have a DTSTAMP or DTSTART property, so it cannot be added to the store");
437 icalerror_set_errno(ICAL_BADARG_ERROR);
438 return ICAL_BADARG_ERROR;
439 }
440
441 v = icalproperty_get_value(dt);
442
443 tm = icalvalue_get_datetime(v);
444
445 snprintf(clustername,ICAL_PATH_MAX,"%s/%04d%02d",impl->dir,tm.year,tm.month);
446
447 /* Load the cluster and insert the object */
448
449 if(impl->cluster != 0 &&
450 strcmp(clustername,icalfileset_path(impl->cluster)) != 0 ){
451 icalfileset_free(impl->cluster);
452 impl->cluster = 0;
453 }
454
455 if (impl->cluster == 0){
456 impl->cluster = icalfileset_new(clustername);
457
458 if (impl->cluster == 0){
459 error = icalerrno;
460 }
461 }
462
463 if (error != ICAL_NO_ERROR){
464 icalerror_set_errno(error);
465 return error;
466 }
467
468 /* Add the component to the cluster */
469
470 icalfileset_add_component(impl->cluster,comp);
471
472 icalfileset_mark(impl->cluster);
473
474 return ICAL_NO_ERROR;
475}
476
477/* Remove a component in the current cluster. HACK. This routine is a
478 "friend" of icalfileset, and breaks its encapsulation. It was
479 either do it this way, or add several layers of interfaces that had
480 no other use. */
481icalerrorenum icaldirset_remove_component(icaldirset* store, icaldirset* comp)
482{
483 struct icaldirset_impl *impl = (struct icaldirset_impl*)store;
484
485 struct icalfileset_impl *filesetimpl =
486 (struct icalfileset_impl*)impl->cluster;
487
488 icalcomponent *filecomp = filesetimpl->cluster;
489
490 icalcompiter i;
491 int found = 0;
492
493 icalerror_check_arg_re((store!=0),"store",ICAL_BADARG_ERROR);
494 icalerror_check_arg_re((comp!=0),"comp",ICAL_BADARG_ERROR);
495 icalerror_check_arg_re((impl->cluster!=0),"Cluster pointer",ICAL_USAGE_ERROR);
496
497 for(i = icalcomponent_begin_component(filecomp,ICAL_ANY_COMPONENT);
498 icalcompiter_deref(&i)!= 0; icalcompiter_next(&i)){
499
500 icalcomponent *this = icalcompiter_deref(&i);
501
502 if (this == comp){
503 found = 1;
504 break;
505 }
506 }
507
508 if (found != 1){
509 icalerror_warn("icaldirset_remove_component: component is not part of current cluster");
510 icalerror_set_errno(ICAL_USAGE_ERROR);
511 return ICAL_USAGE_ERROR;
512 }
513
514 icalfileset_remove_component(impl->cluster,comp);
515
516 icalfileset_mark(impl->cluster);
517
518 /* If the removal emptied the fileset, get the next fileset */
519 if( icalfileset_count_components(impl->cluster,ICAL_ANY_COMPONENT)==0){
520
521 icalerrorenum error = icaldirset_next_cluster(store);
522
523 if(impl->cluster != 0 && error == ICAL_NO_ERROR){
524 icalfileset_get_first_component(impl->cluster);
525 } else {
526 /* HACK. Not strictly correct for impl->cluster==0 */
527 return error;
528 }
529 } else {
530 /* Do nothing */
531 }
532
533 return ICAL_NO_ERROR;
534}
535
536
537
538int icaldirset_count_components(icaldirset* store,
539 icalcomponent_kind kind)
540{
541 /* HACK, not implemented */
542
543 assert(0);
544
545 return 0;
546}
547
548
549icalcomponent* icaldirset_fetch_match(icaldirset* set, icalcomponent *c)
550{
551 fprintf(stderr," icaldirset_fetch_match is not implemented\n");
552 assert(0);
553}
554
555
556icalcomponent* icaldirset_fetch(icaldirset* store, const char* uid)
557{
558 icalcomponent *gauge;
559 icalcomponent *old_gauge;
560 icalcomponent *c;
561 struct icaldirset_impl *impl = (struct icaldirset_impl*)store;
562
563 icalerror_check_arg_rz( (store!=0), "store");
564 icalerror_check_arg_rz( (uid!=0), "uid");
565
566 gauge =
567 icalcomponent_vanew(
568 ICAL_VCALENDAR_COMPONENT,
569 icalcomponent_vanew(
570 ICAL_VEVENT_COMPONENT,
571 icalproperty_vanew_uid(
572 uid,
573 icalparameter_new_xliccomparetype(
574 ICAL_XLICCOMPARETYPE_EQUAL),
575 0),
576 0),
577 0);
578
579 old_gauge = impl->gauge;
580 impl->gauge = gauge;
581
582 c= icaldirset_get_first_component(store);
583
584 impl->gauge = old_gauge;
585
586 icalcomponent_free(gauge);
587
588 return c;
589}
590
591
592int icaldirset_has_uid(icaldirset* store, const char* uid)
593{
594 icalcomponent *c;
595
596 icalerror_check_arg_rz( (store!=0), "store");
597 icalerror_check_arg_rz( (uid!=0), "uid");
598
599 /* HACK. This is a temporary implementation. _has_uid should use a
600 database, and _fetch should use _has_uid, not the other way
601 around */
602 c = icaldirset_fetch(store,uid);
603
604 return c!=0;
605
606}
607
608
609icalerrorenum icaldirset_select(icaldirset* store, icalcomponent* gauge)
610 {
611 struct icaldirset_impl *impl = (struct icaldirset_impl*)store;
612
613 icalerror_check_arg_re( (store!=0), "store",ICAL_BADARG_ERROR);
614 icalerror_check_arg_re( (gauge!=0), "gauge",ICAL_BADARG_ERROR);
615
616 if (!icalcomponent_is_valid(gauge)){
617 return ICAL_BADARG_ERROR;
618 }
619
620 impl->gauge = gauge;
621
622 return ICAL_NO_ERROR;
623}
624
625
626icalerrorenum icaldirset_modify(icaldirset* store, icalcomponent *old,
627 icalcomponent *new)
628{
629 assert(0);
630 return ICAL_NO_ERROR; /* HACK, not implemented */
631
632}
633
634
635void icaldirset_clear(icaldirset* store)
636{
637
638 assert(0);
639 return;
640 /* HACK, not implemented */
641}
642
643icalcomponent* icaldirset_get_current_component(icaldirset* store)
644{
645 struct icaldirset_impl *impl = (struct icaldirset_impl*)store;
646
647 if(impl->cluster == 0){
648 icaldirset_get_first_component(store);
649 }
650
651 return icalfileset_get_current_component(impl->cluster);
652
653}
654
655
656icalcomponent* icaldirset_get_first_component(icaldirset* store)
657{
658 struct icaldirset_impl *impl = (struct icaldirset_impl*)store;
659 icalerrorenum error;
660 char path[ICAL_PATH_MAX];
661
662 error = icaldirset_read_directory(impl);
663
664 if (error != ICAL_NO_ERROR){
665 icalerror_set_errno(error);
666 return 0;
667 }
668
669 impl->directory_iterator = pvl_head(impl->directory);
670
671 if (impl->directory_iterator == 0){
672 icalerror_set_errno(error);
673 return 0;
674 }
675
676 snprintf(path,ICAL_PATH_MAX,"%s/%s",impl->dir,(char*)pvl_data(impl->directory_iterator));
677
678 /* If the next cluster we need is different than the current cluster,
679 delete the current one and get a new one */
680
681 if(impl->cluster != 0 && strcmp(path,icalfileset_path(impl->cluster)) != 0 ){
682 icalfileset_free(impl->cluster);
683 impl->cluster = 0;
684 }
685
686 if (impl->cluster == 0){
687 impl->cluster = icalfileset_new(path);
688
689 if (impl->cluster == 0){
690 error = icalerrno;
691 }
692 }
693
694 if (error != ICAL_NO_ERROR){
695 icalerror_set_errno(error);
696 return 0;
697 }
698
699 impl->first_component = 1;
700
701 return icaldirset_get_next_component(store);
702}
703
704icalcomponent* icaldirset_get_next_component(icaldirset* store)
705{
706 struct icaldirset_impl *impl;
707 icalcomponent *c;
708 icalerrorenum error;
709
710 icalerror_check_arg_rz( (store!=0), "store");
711
712 impl = (struct icaldirset_impl*)store;
713
714 if(impl->cluster == 0){
715
716 icalerror_warn("icaldirset_get_next_component called with a NULL cluster (Caller must call icaldirset_get_first_component first");
717 icalerror_set_errno(ICAL_USAGE_ERROR);
718 return 0;
719
720 }
721
722 /* Set the component iterator for the following for loop */
723 if (impl->first_component == 1){
724 icalfileset_get_first_component(impl->cluster);
725 impl->first_component = 0;
726 } else {
727 icalfileset_get_next_component(impl->cluster);
728 }
729
730
731 while(1){
732 /* Iterate through all of the objects in the cluster*/
733 for( c = icalfileset_get_current_component(impl->cluster);
734 c != 0;
735 c = icalfileset_get_next_component(impl->cluster)){
736
737 /* If there is a gauge defined and the component does not
738 pass the gauge, skip the rest of the loop */
739
740#if 0 /* HACK */
741 if (impl->gauge != 0 && icalgauge_test(c,impl->gauge) == 0){
742 continue;
743 }
744#else
745 assert(0); /* icalgauge_test needs to be fixed */
746#endif
747 /* Either there is no gauge, or the component passed the
748 gauge, so return it*/
749
750 return c;
751 }
752
753 /* Fell through the loop, so the component we want is not
754 in this cluster. Load a new cluster and try again.*/
755
756 error = icaldirset_next_cluster(store);
757
758 if(impl->cluster == 0 || error != ICAL_NO_ERROR){
759 /* No more clusters */
760 return 0;
761 } else {
762 c = icalfileset_get_first_component(impl->cluster);
763
764 return c;
765 }
766
767 }
768
769 return 0; /* Should never get here */
770}
771
772
773
774
775
776
777