From 788bcf6373185aa29f8e2f83ad48cb10bf6e8a9b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 17 Feb 2000 18:02:37 +0000 Subject: Initial revision svn path=/trunk/; revision=1823 --- libical/src/libicalss/icalstore.c | 815 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 815 insertions(+) create mode 100644 libical/src/libicalss/icalstore.c (limited to 'libical/src/libicalss/icalstore.c') diff --git a/libical/src/libicalss/icalstore.c b/libical/src/libicalss/icalstore.c new file mode 100644 index 0000000000..014c9ed19b --- /dev/null +++ b/libical/src/libicalss/icalstore.c @@ -0,0 +1,815 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icalstore.c + CREATOR: eric 28 November 1999 + + $Id$ + $Locker$ + + (C) COPYRIGHT 1999 Eric Busboom + http://www.softwarestudio.org + + The contents of this file are subject to the Mozilla Public License + Version 1.0 (the "License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and + limitations under the License. + + The Original Code is eric. The Initial Developer of the Original + Code is Eric Busboom + + + ======================================================================*/ + + +/* + + icalstore manages a database of ical components and offers + interfaces for reading, writting and searching for components. + + icalstore 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 icalstore_first and icalstore_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 + icalstore_select, icalstore_first and icalstore_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. + +*/ + +#include "ical.h" +#include "icalstore.h" +#include "pvl.h" +#include "icalerror.h" +#include "icalparser.h" +#include "icalcluster.h" + +#include "filelock.h" + +#include +#include /* for opendir() */ +#include +#include /* for opendir() */ +#include /* for stat */ +#include /* for stat, getpid */ +#include /* for clock() */ +#include /* for rand(), srand() */ +#include /* for uname */ +#include /* for strdup */ + + +struct icalstore_impl +{ + char* dir; + icalcomponent* gauge; + icalcluster* cluster; + int first_component; + pvl_list directory; + pvl_elem directory_iterator; +}; + +struct icalstore_impl* icalstore_new_impl() +{ + struct icalstore_impl* comp; + + if ( ( comp = (struct icalstore_impl*) + malloc(sizeof(struct icalstore_impl))) == 0) { + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return 0; + } + + return comp; +} + + + +void icalstore_lock_dir(char* dir) +{ +} + + +void icalstore_unlock_dir(char* dir) +{ +} + +/* Load the contents of the store directory into the store's internal directory list*/ +icalerrorenum icalstore_read_directory(struct icalstore_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; +} + +icalstore* icalstore_new(char* dir) +{ + struct icalstore_impl *impl = icalstore_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; + } + + icalstore_lock_dir(dir); + + impl = icalstore_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; + + icalstore_read_directory(impl); + + return (icalstore*) impl; +} + +void icalstore_free(icalstore* s) +{ + struct icalstore_impl *impl = (struct icalstore_impl*)s; + char* str; + + icalstore_unlock_dir(impl->dir); + + if(impl->dir !=0){ + free(impl->dir); + } + + if(impl->gauge !=0){ + icalcomponent_free(impl->gauge); + } + + if(impl->cluster !=0){ + icalcluster_free(impl->cluster); + } + + while( (str=pvl_pop(impl->directory)) != 0){ + free(str); + } + + pvl_free(impl->directory); + + impl->directory = 0; + impl->directory_iterator = 0; + impl->dir = 0; + impl->gauge = 0; + impl->first_component = 0; + + free(impl); + +} + +/* icalstore_next_uid_number updates a serial number in the Store + directory in a file called SEQUENCE */ + +int icalstore_next_uid_number(icalstore* store) +{ + struct icalstore_impl *impl = (struct icalstore_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 icalstore_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 icalstore_next_uid_number"); + return 0; + } + +} + +icalerrorenum icalstore_next_cluster(icalstore* store) +{ + struct icalstore_impl *impl = (struct icalstore_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 */ + impl->cluster = 0; + return ICAL_NO_ERROR; + } + + + sprintf(path,"%s/%s",impl->dir,(char*)pvl_data(impl->directory_iterator)); + + return icalcluster_load(impl->cluster,path); +} + +void icalstore_add_uid(icalstore* store, icalstore* comp) +{ + char uidstring[PATH_MAX]; + icalproperty *uid; + struct utsname unamebuf; + + icalerror_check_arg_rz( (store!=0), "store"); + icalerror_check_arg_rz( (comp!=0), "comp"); + + uid = icalcomponent_get_first_property(comp,ICAL_UID_PROPERTY); + + if (uid == 0) { + + uname(&unamebuf); + + sprintf(uidstring,"%d-%s",getpid(),unamebuf.nodename); + + uid = icalproperty_new_uid(uidstring); + icalcomponent_add_property(comp,uid); + } else { + + strcpy(uidstring,icalproperty_get_uid(uid)); + } +} + +icalerrorenum icalstore_add_component(icalstore* store, icalstore* comp) +{ + struct icalstore_impl *impl; + char clustername[PATH_MAX]; + icalproperty *dt, *count, *lm; + icalvalue *v; + struct icaltimetype tm; + icalerrorenum error = ICAL_NO_ERROR; + + impl = (struct icalstore_impl*)store; + icalerror_check_arg_rz( (store!=0), "store"); + icalerror_check_arg_rz( (comp!=0), "comp"); + + errno = 0; + + icalstore_add_uid(store,comp); + + /* Determine which cluster this object belongs in */ + + dt = icalcomponent_get_first_property(comp,ICAL_DTSTART_PROPERTY); + + if (dt == 0){ + dt = icalcomponent_get_first_property(comp,ICAL_DTSTAMP_PROPERTY); + } + + if (dt == 0){ + dt = icalcomponent_get_first_property(comp,ICAL_CREATED_PROPERTY); + } + + if (dt == 0){ + icalerror_warn("The component does not have a DTSTART, DTSTAMP or a CREATED 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); + + sprintf(clustername,"%s/%04d%02d",impl->dir,tm.year,tm.month); + + /* Load the cluster and insert the object */ + + if (impl->cluster == 0){ + impl->cluster = icalcluster_new(clustername); + + if (impl->cluster == 0){ + error = icalerrno; + } + } else { + error = icalcluster_load(impl->cluster, + clustername); + + } + + + if (error != ICAL_NO_ERROR){ + icalerror_set_errno(error); + return error; + } + + /* Update or add the LAST-MODIFIED property */ + + lm = icalcomponent_get_first_property(comp, + ICAL_LASTMODIFIED_PROPERTY); + + if (lm == 0){ + lm = icalproperty_new_lastmodified(icaltimetype_from_timet( time(0),1)); + icalcomponent_add_property(comp,lm); + } else { + icalproperty_set_lastmodified(comp,icaltimetype_from_timet( time(0),1)); + } + + + /* Add the component to the cluster */ + + icalcluster_add_component(impl->cluster,comp); + + + /* Increment the clusters count value */ + count = icalcomponent_get_first_property( + icalcluster_get_component(impl->cluster), + ICAL_XLICCLUSTERCOUNT_PROPERTY); + + if (count == 0){ + icalerror_set_errno(ICAL_INTERNAL_ERROR); + return ICAL_INTERNAL_ERROR; + } + + icalproperty_set_xlicclustercount(count, + icalproperty_get_xlicclustercount(count)+1); + + + icalcluster_mark(impl->cluster); + + return ICAL_NO_ERROR; +} + +/* Remove a component in the current cluster */ +icalerrorenum icalstore_remove_component(icalstore* store, icalstore* comp) +{ + struct icalstore_impl *impl = (struct icalstore_impl*)store; + icalproperty *count; + + 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); + +/* HACK The following code should be used to ensure that the component +the caller is trying to remove is actually in the cluster, but it +resets the internal iterators, which immediately ends any loops over +the cluster the caller may have in progress + + for(c = icalcluster_get_first_component( + impl->cluster, + ICAL_ANY_COMPONENT); + c != 0; + c = icalcluster_get_next_component( + impl->cluster, + ICAL_ANY_COMPONENT)){ + + if (c == comp){ + found = 1; + } + + } + + if (found != 1){ + icalerror_warn("icalstore_remove_component: component is not part of current cluster"); + icalerror_set_errno(ICAL_USAGE_ERROR); + return ICAL_USAGE_ERROR; + } + +*/ + + icalcluster_remove_component(impl->cluster, + comp); + + icalcluster_mark(impl->cluster); + + /* Decrement the clusters count value */ + count = icalcomponent_get_first_property( + icalcluster_get_component(impl->cluster), + ICAL_XLICCLUSTERCOUNT_PROPERTY); + + if (count == 0){ + icalerror_set_errno(ICAL_INTERNAL_ERROR); + return ICAL_INTERNAL_ERROR; + } + + icalproperty_set_xlicclustercount(count, + icalproperty_get_xlicclustercount(count)-1); + + return ICAL_NO_ERROR; +} + +/* Convert a VQUERY component into a gauge */ +icalcomponent* icalstore_make_gauge(icalcomponent* query); + +/* icalstore_test compares a component against a gauge, and returns + true if the component passes the test + + The gauge is a VCALENDAR component that specifies how to test the + target components. The guage holds a collection of VEVENT, VTODO or + VJOURNAL sub-components. Each of the sub-components has a + collection of properties that are compared to corresponding + properties in the target component, according to the + X-LIC-COMPARETYPE parameters to the gauge's properties. + + When a gauge has several sub-components, the results of testing the + target against each of them is ORed together - the target + component will pass if it matches any of the sub-components in the + gauge. However, the results of matching the proeprties in a + sub-component are ANDed -- the target must match every property in + a gauge sub-component to match the sub-component. + + Here is an example: + + BEGIN:VCOMPONENT + BEGIN:VEVENT + DTSTART;X-LIC-COMPARETYPE=LESS:19981025T020000 + ORGANIZER;X-LIC-COMPARETYPE=EQUAL:mrbig@host.com + END:VEVENT + BEGIN:VEVENT + LOCATION;X-LIC-COMPARETYPE=EQUAL:McNary's Pub + END:VEVENT + END:VCALENDAR + + This gauge has two sub-components; one which will match a VEVENT + based on start time, and organizer, and another that matches based + on LOCATION. A target component will pass the test if it matched + either of the gauge. + + */ + +int icalstore_test(icalcomponent* comp, icalcomponent* gauge) +{ + int pass = 0,localpass = 0; + icalcomponent *c; + icalproperty *p; + icalcomponent *child; + + icalerror_check_arg_rz( (comp!=0), "comp"); + icalerror_check_arg_rz( (gauge!=0), "gauge"); + + for(c = icalcomponent_get_first_component(gauge,ICAL_ANY_COMPONENT); + c != 0; + c = icalcomponent_get_next_component(gauge,ICAL_ANY_COMPONENT)){ + + + /* Test properties. For each property in the gauge, search through + the component for a similar property. If one is found, compare + the two properties value with the comparison specified in the + gauge with the X-LIC-COMPARETYPE parameter */ + + for(p = icalcomponent_get_first_property(c,ICAL_ANY_PROPERTY); + p != 0; + p = icalcomponent_get_next_property(c,ICAL_ANY_PROPERTY)){ + + icalproperty* targetprop; + icalparameter* compareparam; + icalparameter_xliccomparetype compare; + int rel; /* The realtionship between the gauge and target values.*/ + + /* Extract the comparison type from the gauge. If there is no + comparison type, assume that it is "EQUAL" */ + + compareparam = icalproperty_get_first_parameter( + p, + ICAL_XLICCOMPARETYPE_PARAMETER); + + if (compareparam!=0){ + compare = icalparameter_get_xliccomparetype(compareparam); + } else { + compare = ICAL_XLICCOMPARETYPE_EQUAL; + } + + /* Find a property in the component that has the same type as + the gauge property */ + + targetprop = icalcomponent_get_first_property(comp, + icalproperty_isa(p)); + + + if(targetprop == 0){ + continue; + } + + /* Compare the values of the gauge property and the target + property */ + + rel = icalvalue_compare(icalproperty_get_value(p), + icalproperty_get_value(targetprop)); + + /* Now see if the comparison is equavalent to the comparison + specified in the gauge */ + + if (rel == compare){ + localpass++; + } else if (compare == ICAL_XLICCOMPARETYPE_LESSEQUAL && + ( rel == ICAL_XLICCOMPARETYPE_LESS || + rel == ICAL_XLICCOMPARETYPE_EQUAL)) { + localpass++; + } else if (compare == ICAL_XLICCOMPARETYPE_GREATEREQUAL && + ( rel == ICAL_XLICCOMPARETYPE_GREATER || + rel == ICAL_XLICCOMPARETYPE_EQUAL)) { + localpass++; + } else if (compare == ICAL_XLICCOMPARETYPE_NOTEQUAL && + ( rel == ICAL_XLICCOMPARETYPE_GREATER || + rel == ICAL_XLICCOMPARETYPE_LESS)) { + pass++; + } else { + localpass = 0; + } + + pass += localpass; + } + + + /* test subcomponents. Look for a child component that has a + counterpart in the gauge. If one is found, recursively call + icalstore_test */ + + for(child = icalcomponent_get_first_component(comp,ICAL_ANY_COMPONENT); + child != 0; + child = icalcomponent_get_next_component(comp,ICAL_ANY_COMPONENT)){ + + pass += icalstore_test(child,gauge); + + } + } + + return pass>0; + +} + +icalcomponent* icalstore_query(icalstore* store, icalstore* query); + + +icalcomponent* icalstore_fetch(icalstore* store, char* uid) +{ + icalcomponent *gauge; + icalcomponent *old_gauge; + icalcomponent *c; + struct icalstore_impl *impl = (struct icalstore_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= icalstore_get_first_component(store); + + impl->gauge = old_gauge; + + icalcomponent_free(gauge); + + return c; +} + + +int icalstore_has_uid(icalstore* 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 = icalstore_fetch(store,uid); + + return c!=0; + +} + + +icalerrorenum icalstore_select(icalstore* store, icalcomponent* gauge) +{ + struct icalstore_impl *impl = (struct icalstore_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; +} + + + +icalcomponent* icalstore_get_first_component(icalstore* store) +{ + struct icalstore_impl *impl = (struct icalstore_impl*)store; + icalerrorenum error; + char path[PATH_MAX]; + + error = icalstore_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; + } + + sprintf(path,"%s/%s",impl->dir,(char*)pvl_data(impl->directory_iterator)); + + if (impl->cluster == 0){ + impl->cluster = icalcluster_new(path); + + if (impl->cluster == 0){ + error = icalerrno; + } + } else { + error = icalcluster_load(impl->cluster,path); + + } + + if (error != ICAL_NO_ERROR){ + icalerror_set_errno(error); + return 0; + } + + impl->first_component = 1; + + return icalstore_get_next_component(store); +} + +icalcomponent* icalstore_get_next_component(icalstore* store) +{ + struct icalstore_impl *impl; + icalcomponent *c; + icalerrorenum error; + + icalerror_check_arg_rz( (store!=0), "store"); + + impl = (struct icalstore_impl*)store; + + if(impl->cluster == 0){ + + icalerror_warn("icalstore_get_next_component called with a NULL cluster (Caller must call icalstore_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){ + icalcluster_get_first_component( + impl->cluster, + ICAL_ANY_COMPONENT); + impl->first_component = 0; + } else { + icalcluster_get_next_component( + impl->cluster, + ICAL_ANY_COMPONENT); + } + + + while(1){ + /* Iterate through all of the objects in the cluster*/ + for( c = icalcluster_get_current_component( + impl->cluster); + c != 0; + c = icalcluster_get_next_component( + impl->cluster, + ICAL_ANY_COMPONENT)){ + + /* If there is a gauge defined and the component does not + pass the gauge, skip the rest of the loop */ + if (impl->gauge != 0 && icalstore_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 = icalstore_next_cluster(store); + + if(impl->cluster == 0 || error != ICAL_NO_ERROR){ + /* No more clusters */ + return 0; + } else { + c = icalcluster_get_first_component( + impl->cluster, + ICAL_ANY_COMPONENT); + } + } +} + + + + + + -- cgit v1.2.3