diff options
Diffstat (limited to 'libical/src/libicalss/icaldirset.c')
-rw-r--r-- | libical/src/libicalss/icaldirset.c | 718 |
1 files changed, 718 insertions, 0 deletions
diff --git a/libical/src/libicalss/icaldirset.c b/libical/src/libicalss/icaldirset.c new file mode 100644 index 0000000000..ff5357126c --- /dev/null +++ b/libical/src/libicalss/icaldirset.c @@ -0,0 +1,718 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icaldirset.c + CREATOR: eric 28 November 1999 + + $Id$ + $Locker$ + + (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The Original Code is eric. The Initial Developer of the Original + Code is Eric Busboom + + + ======================================================================*/ + + +/* + + icaldirset manages a database of ical components and offers + interfaces for reading, writting and searching for components. + + icaldirset groups components in to clusters based on their DTSTART + time -- all components that start in the same month are grouped + together in a single file. All files in a sotre are kept in a single + directory. ( If a component does not have DTSTART, the store uses + DTSTAMP or CREATE ) + + The primary interfaces are icaldirset_first and icaldirset_next. These + routine iterate through all of the components in the store, subject + to the current gauge. A gauge is an icalcomponent that is tested + against other componets for a match. If a gauge has been set with + icaldirset_select, icaldirset_first and icaldirset_next will only + return componentes that match the gauge. + + The Store generated UIDs for all objects that are stored if they do + not already have a UID. The UID is the name of the cluster (month & + year as MMYYYY) plus a unique serial number. The serial number is + stored as a property of the cluster. + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +#include "ical.h" +#include "icaldirset.h" +#include "pvl.h" +#include "icalerror.h" +#include "icalparser.h" +#include "icaldirset.h" +#include "icalfileset.h" +#include "icalfilesetimpl.h" +#include "icalgauge.h" + +#include <limits.h> +#include <dirent.h> /* for opendir() */ +#include <errno.h> +#include <sys/types.h> /* for opendir() */ +#include <sys/stat.h> /* for stat */ +#include <unistd.h> /* for stat, getpid */ +#include <time.h> /* for clock() */ +#include <stdlib.h> /* for rand(), srand() */ +#include <sys/utsname.h> /* for uname */ +#include <string.h> /* for strdup */ + + +struct icaldirset_impl +{ + char* dir; + icalcomponent* gauge; + icaldirset* cluster; + int first_component; + pvl_list directory; + pvl_elem directory_iterator; +}; + +struct icaldirset_impl* icaldirset_new_impl() +{ + struct icaldirset_impl* comp; + + if ( ( comp = (struct icaldirset_impl*) + malloc(sizeof(struct icaldirset_impl))) == 0) { + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return 0; + } + + return comp; +} + +char* icaldirset_path(icaldirset* cluster) +{ + struct icaldirset_impl *impl = icaldirset_new_impl(); + + return impl->dir; + +} + +void icaldirset_mark(icaldirset* store) +{ + struct icaldirset_impl *impl = (struct icaldirset_impl*)store; + + icalfileset_mark(impl->cluster); +} + + +icalerrorenum icaldirset_commit(icaldirset* store) +{ + struct icaldirset_impl *impl = (struct icaldirset_impl*)store; + + return icalfileset_commit(impl->cluster); + +} + +void icaldirset_lock(char* dir) +{ +} + + +void icaldirset_unlock(char* dir) +{ +} + +/* Load the contents of the store directory into the store's internal directory list*/ +icalerrorenum icaldirset_read_directory(struct icaldirset_impl* impl) +{ + struct dirent *de; + DIR* dp; + char *str; + + dp = opendir(impl->dir); + + if ( dp == 0) { + icalerror_set_errno(ICAL_FILE_ERROR); + return ICAL_FILE_ERROR; + } + + /* clear contents of directory list */ + while((str = pvl_pop(impl->directory))){ + free(str); + } + + /* load all of the cluster names in the directory list */ + for(de = readdir(dp); + de != 0; + de = readdir(dp)){ + + /* Remove known directory names '.' and '..'*/ + if (strcmp(de->d_name,".") == 0 || + strcmp(de->d_name,"..") == 0 ){ + continue; + } + + pvl_push(impl->directory, (void*)strdup(de->d_name)); + } + + closedir(dp); + + return ICAL_NO_ERROR; +} + +icaldirset* icaldirset_new(char* dir) +{ + struct icaldirset_impl *impl = icaldirset_new_impl(); + struct stat sbuf; + + if (impl == 0){ + return 0; + } + + icalerror_check_arg_rz( (dir!=0), "dir"); + + if (stat(dir,&sbuf) != 0){ + icalerror_set_errno(ICAL_FILE_ERROR); + return 0; + } + + /* dir is not the name of a direectory*/ + if (!S_ISDIR(sbuf.st_mode)){ + icalerror_set_errno(ICAL_USAGE_ERROR); + return 0; + } + + icaldirset_lock(dir); + + impl = icaldirset_new_impl(); + + if (impl ==0){ + icalerror_set_errno(ICAL_ALLOCATION_ERROR); + return 0; + } + + impl->directory = pvl_newlist(); + impl->directory_iterator = 0; + impl->dir = (char*)strdup(dir); + impl->gauge = 0; + impl->first_component = 0; + impl->cluster = 0; + + icaldirset_read_directory(impl); + + return (icaldirset*) impl; +} + +void icaldirset_free(icaldirset* s) +{ + struct icaldirset_impl *impl = (struct icaldirset_impl*)s; + char* str; + + icaldirset_unlock(impl->dir); + + if(impl->dir !=0){ + free(impl->dir); + } + + if(impl->gauge !=0){ + icalcomponent_free(impl->gauge); + } + + if(impl->cluster !=0){ + icalfileset_free(impl->cluster); + } + + while(impl->directory !=0 && (str=pvl_pop(impl->directory)) != 0){ + free(str); + } + + if(impl->directory != 0){ + pvl_free(impl->directory); + } + + impl->directory = 0; + impl->directory_iterator = 0; + impl->dir = 0; + impl->gauge = 0; + impl->first_component = 0; + + free(impl); + +} + +/* icaldirset_next_uid_number updates a serial number in the Store + directory in a file called SEQUENCE */ + +int icaldirset_next_uid_number(icaldirset* store) +{ + struct icaldirset_impl *impl = (struct icaldirset_impl*)store; + char sequence = 0; + char temp[128]; + char filename[PATH_MAX]; + char *r; + FILE *f; + struct stat sbuf; + + icalerror_check_arg_rz( (store!=0), "store"); + + sprintf(filename,"%s/%s",impl->dir,"SEQUENCE"); + + /* Create the file if it does not exist.*/ + if (stat(filename,&sbuf) == -1 || !S_ISREG(sbuf.st_mode)){ + + f = fopen(filename,"w"); + if (f != 0){ + fprintf(f,"0"); + fclose(f); + } else { + icalerror_warn("Can't create SEQUENCE file in icaldirset_next_uid_number"); + return 0; + } + + } + + if ( (f = fopen(filename,"r+")) != 0){ + + rewind(f); + r = fgets(temp,128,f); + + if (r == 0){ + sequence = 1; + } else { + sequence = atoi(temp)+1; + } + + rewind(f); + + fprintf(f,"%d",sequence); + + fclose(f); + + return sequence; + + } else { + icalerror_warn("Can't create SEQUENCE file in icaldirset_next_uid_number"); + return 0; + } + +} + +icalerrorenum icaldirset_next_cluster(icaldirset* store) +{ + struct icaldirset_impl *impl = (struct icaldirset_impl*)store; + char path[PATH_MAX]; + + if (impl->directory_iterator == 0){ + icalerror_set_errno(ICAL_INTERNAL_ERROR); + return ICAL_INTERNAL_ERROR; + } + impl->directory_iterator = pvl_next(impl->directory_iterator); + + if (impl->directory_iterator == 0){ + /* There are no more clusters */ + if(impl->cluster != 0){ + icalfileset_free(impl->cluster); + impl->cluster = 0; + } + return ICAL_NO_ERROR; + } + + sprintf(path,"%s/%s",impl->dir,(char*)pvl_data(impl->directory_iterator)); + + icalfileset_free(impl->cluster); + + impl->cluster = icalfileset_new(path); + + return icalerrno; +} + +void icaldirset_add_uid(icaldirset* store, icaldirset* comp) +{ + char uidstring[PATH_MAX]; + icalproperty *uid; + struct utsname unamebuf; + + icalerror_check_arg_rv( (store!=0), "store"); + icalerror_check_arg_rv( (comp!=0), "comp"); + + uid = icalcomponent_get_first_property(comp,ICAL_UID_PROPERTY); + + if (uid == 0) { + + uname(&unamebuf); + + sprintf(uidstring,"%d-%s",(int)getpid(),unamebuf.nodename); + + uid = icalproperty_new_uid(uidstring); + icalcomponent_add_property(comp,uid); + } else { + + strcpy(uidstring,icalproperty_get_uid(uid)); + } +} + + +/* This assumes that the top level component is a VCALENDAR, and there + is an inner component of type VEVENT, VTODO or VJOURNAL. The inner + component must have a DTSTART property */ + +icalerrorenum icaldirset_add_component(icaldirset* store, icaldirset* comp) +{ + struct icaldirset_impl *impl; + char clustername[PATH_MAX]; + icalproperty *dt; + icalvalue *v; + struct icaltimetype tm; + icalerrorenum error = ICAL_NO_ERROR; + icalcomponent *inner; + + impl = (struct icaldirset_impl*)store; + icalerror_check_arg_rz( (store!=0), "store"); + icalerror_check_arg_rz( (comp!=0), "comp"); + + errno = 0; + + icaldirset_add_uid(store,comp); + + /* Determine which cluster this object belongs in. This is a HACK */ + + for(inner = icalcomponent_get_first_component(comp,ICAL_ANY_COMPONENT); + inner != 0; + inner = icalcomponent_get_next_component(comp,ICAL_ANY_COMPONENT)){ + + dt = icalcomponent_get_first_property(inner,ICAL_DTSTART_PROPERTY); + + if (dt != 0){ + break; + } + } + + if (dt == 0){ + icalerror_warn("The component does not have a DTSTART property, so it cannot be added to the store"); + icalerror_set_errno(ICAL_BADARG_ERROR); + return ICAL_BADARG_ERROR; + } + + v = icalproperty_get_value(dt); + + tm = icalvalue_get_datetime(v); + + snprintf(clustername,PATH_MAX,"%s/%04d%02d",impl->dir,tm.year,tm.month); + + /* Load the cluster and insert the object */ + + if(impl->cluster != 0 && + strcmp(clustername,icalfileset_path(impl->cluster)) != 0 ){ + icalfileset_free(impl->cluster); + impl->cluster = 0; + } + + if (impl->cluster == 0){ + impl->cluster = icalfileset_new(clustername); + + if (impl->cluster == 0){ + error = icalerrno; + } + } + + if (error != ICAL_NO_ERROR){ + icalerror_set_errno(error); + return error; + } + + /* Add the component to the cluster */ + + icalfileset_add_component(impl->cluster,comp); + + icalfileset_mark(impl->cluster); + + return ICAL_NO_ERROR; +} + +/* Remove a component in the current cluster. HACK. This routine is a + "friend" of icalfileset, and breaks its encapsulation. It was + either do it this way, or add several layers of interfaces that had + no other use. */ +icalerrorenum icaldirset_remove_component(icaldirset* store, icaldirset* comp) +{ + struct icaldirset_impl *impl = (struct icaldirset_impl*)store; + + struct icalfileset_impl *filesetimpl = + (struct icalfileset_impl*)impl->cluster; + + icalcomponent *filecomp = filesetimpl->cluster; + + icalcompiter i; + int found = 0; + + icalerror_check_arg_re((store!=0),"store",ICAL_BADARG_ERROR); + icalerror_check_arg_re((comp!=0),"comp",ICAL_BADARG_ERROR); + icalerror_check_arg_re((impl->cluster!=0),"Cluster pointer",ICAL_USAGE_ERROR); + + for(i = icalcomponent_begin_component(filecomp,ICAL_ANY_COMPONENT); + icalcompiter_deref(&i)!= 0; icalcompiter_next(&i)){ + + icalcomponent *this = icalcompiter_deref(&i); + + if (this == comp){ + found = 1; + break; + } + } + + if (found != 1){ + icalerror_warn("icaldirset_remove_component: component is not part of current cluster"); + icalerror_set_errno(ICAL_USAGE_ERROR); + return ICAL_USAGE_ERROR; + } + + icalfileset_remove_component(impl->cluster,comp); + + icalfileset_mark(impl->cluster); + + /* If the removal emptied the fileset, get the next fileset */ + if( icalfileset_count_components(impl->cluster,ICAL_ANY_COMPONENT)==0){ + + icalerrorenum error = icaldirset_next_cluster(store); + + if(impl->cluster != 0 && error == ICAL_NO_ERROR){ + icalfileset_get_first_component(impl->cluster,ICAL_ANY_COMPONENT); + } else { + /* HACK. Not strictly correct for impl->cluster==0 */ + return error; + } + } else { + /* Do nothing */ + } + + return ICAL_NO_ERROR; +} + + + +int icaldirset_count_components(icaldirset* store, + icalcomponent_kind kind); + + +icalcomponent* icaldirset_fetch(icaldirset* store, char* uid) +{ + icalcomponent *gauge; + icalcomponent *old_gauge; + icalcomponent *c; + struct icaldirset_impl *impl = (struct icaldirset_impl*)store; + + icalerror_check_arg_rz( (store!=0), "store"); + icalerror_check_arg_rz( (uid!=0), "uid"); + + gauge = + icalcomponent_vanew( + ICAL_VCALENDAR_COMPONENT, + icalcomponent_vanew( + ICAL_VEVENT_COMPONENT, + icalproperty_vanew_uid( + uid, + icalparameter_new_xliccomparetype( + ICAL_XLICCOMPARETYPE_EQUAL), + 0), + 0), + 0); + + old_gauge = impl->gauge; + impl->gauge = gauge; + + c= icaldirset_get_first_component(store,ICAL_ANY_COMPONENT); + + impl->gauge = old_gauge; + + icalcomponent_free(gauge); + + return c; +} + + +int icaldirset_has_uid(icaldirset* store, char* uid) +{ + icalcomponent *c; + + icalerror_check_arg_rz( (store!=0), "store"); + icalerror_check_arg_rz( (uid!=0), "uid"); + + /* HACK. This is a temporary implementation. _has_uid should use a + database, and _fetch should use _has_uid, not the other way + around */ + c = icaldirset_fetch(store,uid); + + return c!=0; + +} + + +icalerrorenum icaldirset_select(icaldirset* store, icalcomponent* gauge) +{ + struct icaldirset_impl *impl = (struct icaldirset_impl*)store; + + icalerror_check_arg_re( (store!=0), "store",ICAL_BADARG_ERROR); + icalerror_check_arg_re( (gauge!=0), "gauge",ICAL_BADARG_ERROR); + + if (!icalcomponent_is_valid(gauge)){ + return ICAL_BADARG_ERROR; + } + + impl->gauge = gauge; + + return ICAL_NO_ERROR; +} + +void icaldirset_clear(icaldirset* store); +icalcomponent* icaldirset_fetch(icaldirset* store, char* uid); +int icaldirset_has_uid(icaldirset* store, char* uid); + +icalcomponent* icaldirset_get_current_component(icaldirset* store) +{ + struct icaldirset_impl *impl = (struct icaldirset_impl*)store; + + if(impl->cluster == 0){ + icaldirset_get_first_component(store,ICAL_ANY_COMPONENT); + } + + return icalfileset_get_current_component(impl->cluster); + +} + + +icalcomponent* icaldirset_get_first_component(icaldirset* store, + icalcomponent_kind kind) +{ + struct icaldirset_impl *impl = (struct icaldirset_impl*)store; + icalerrorenum error; + char path[PATH_MAX]; + + error = icaldirset_read_directory(impl); + + if (error != ICAL_NO_ERROR){ + icalerror_set_errno(error); + return 0; + } + + impl->directory_iterator = pvl_head(impl->directory); + + if (impl->directory_iterator == 0){ + icalerror_set_errno(error); + return 0; + } + + snprintf(path,PATH_MAX,"%s/%s",impl->dir,(char*)pvl_data(impl->directory_iterator)); + + /* If the next cluster we need is different than the current cluster, + delete the current one and get a new one */ + + if(impl->cluster != 0 && strcmp(path,icalfileset_path(impl->cluster)) != 0 ){ + icalfileset_free(impl->cluster); + impl->cluster = 0; + } + + if (impl->cluster == 0){ + impl->cluster = icalfileset_new(path); + + if (impl->cluster == 0){ + error = icalerrno; + } + } + + if (error != ICAL_NO_ERROR){ + icalerror_set_errno(error); + return 0; + } + + impl->first_component = 1; + + return icaldirset_get_next_component(store, kind); +} + +icalcomponent* icaldirset_get_next_component(icaldirset* store, + icalcomponent_kind kind) +{ + struct icaldirset_impl *impl; + icalcomponent *c; + icalerrorenum error; + + icalerror_check_arg_rz( (store!=0), "store"); + + impl = (struct icaldirset_impl*)store; + + if(impl->cluster == 0){ + + icalerror_warn("icaldirset_get_next_component called with a NULL cluster (Caller must call icaldirset_get_first_component first"); + icalerror_set_errno(ICAL_USAGE_ERROR); + return 0; + + } + + /* Set the component iterator for the following for loop */ + if (impl->first_component == 1){ + icalfileset_get_first_component(impl->cluster,kind); + impl->first_component = 0; + } else { + icalfileset_get_next_component(impl->cluster,kind); + } + + + while(1){ + /* Iterate through all of the objects in the cluster*/ + for( c = icalfileset_get_current_component(impl->cluster); + c != 0; + c = icalfileset_get_next_component( + impl->cluster, + kind)){ + + /* If there is a gauge defined and the component does not + pass the gauge, skip the rest of the loop */ + if (impl->gauge != 0 && icalgauge_test(c,impl->gauge) == 0){ + continue; + } + + /* Either there is no gauge, or the component passed the + gauge, so return it*/ + + return c; + } + + /* Fell through the loop, so the component we want is not + in this cluster. Load a new cluster and try again.*/ + + error = icaldirset_next_cluster(store); + + if(impl->cluster == 0 || error != ICAL_NO_ERROR){ + /* No more clusters */ + return 0; + } else { + c = icalfileset_get_first_component( + impl->cluster, + kind); + + return c; + } + + } + + return 0; /* Should never get here */ +} + + + + + + + |