summaryrefslogtreecommitdiff
path: root/src/libicalss/icaldirset.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libicalss/icaldirset.c')
-rw-r--r--src/libicalss/icaldirset.c804
1 files changed, 804 insertions, 0 deletions
diff --git a/src/libicalss/icaldirset.c b/src/libicalss/icaldirset.c
new file mode 100644
index 00000000..852206f1
--- /dev/null
+++ b/src/libicalss/icaldirset.c
@@ -0,0 +1,804 @@
+/* -*- Mode: C -*-
+ ======================================================================
+ FILE: icaldirset.c
+ CREATOR: eric 28 November 1999
+
+ $Id: icaldirset.c,v 1.24 2008-01-02 20:07:40 dothebart Exp $
+ $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
+
+
+ ======================================================================*/
+
+
+/**
+ @file icaldirset.c
+
+ @brief icaldirset manages a database of ical components and offers
+ interfaces for reading, writing and searching for components.
+
+ icaldirset groups components in to clusters based on their DTSTAMP
+ 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.
+
+ The primary interfaces are icaldirset__get_first_component and
+ icaldirset_get_next_component. 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 <libical/ical.h>
+#include "icaldirset.h"
+#include "icaldirset.h"
+#include "icalfileset.h"
+#include "icalfilesetimpl.h"
+#include "icalcluster.h"
+#include "icalgauge.h"
+
+#ifndef WIN32
+#include <dirent.h> /* for opendir() */
+#include <unistd.h> /* for stat, getpid */
+#include <sys/utsname.h> /* for uname */
+#else
+#include <io.h>
+#include <process.h>
+#endif
+
+#include <errno.h>
+#include <sys/types.h> /* for opendir() */
+#include <sys/stat.h> /* for stat */
+#include <limits.h> /* For PATH_MAX */
+#include <time.h> /* for clock() */
+#include <stdlib.h> /* for rand(), srand() */
+#include <string.h> /* for strdup */
+#include "icaldirsetimpl.h"
+
+#if defined(_MSC_VER)
+#define _S_ISTYPE(mode, mask) (((mode) & _S_IFMT) == (mask))
+#define S_ISDIR(mode) _S_ISTYPE((mode), _S_IFDIR)
+#define S_ISREG(mode) _S_ISTYPE((mode), _S_IFREG)
+#define snprintf _snprintf
+#define strcasecmp stricmp
+#endif
+
+/** Default options used when NULL is passed to icalset_new() **/
+icaldirset_options icaldirset_options_default = {O_RDWR|O_CREAT};
+
+
+const char* icaldirset_path(icalset* set)
+{
+ icaldirset *dset = (icaldirset*)set;
+
+ return dset->dir;
+}
+
+
+void icaldirset_mark(icalset* set)
+{
+ icaldirset *dset = (icaldirset*)set;
+
+ icalcluster_mark(dset->cluster);
+}
+
+
+icalerrorenum icaldirset_commit(icalset* set)
+{
+ icaldirset *dset = (icaldirset*)set;
+ icalset *fileset;
+ icalfileset_options options = icalfileset_options_default;
+
+ options.cluster = dset->cluster;
+
+ fileset = icalset_new(ICAL_FILE_SET, icalcluster_key(dset->cluster), &options);
+
+ fileset->commit(fileset);
+ fileset->free(fileset);
+
+ return ICAL_NO_ERROR;
+}
+
+void icaldirset_lock(const char* dir)
+{
+}
+
+
+void icaldirset_unlock(const char* dir)
+{
+}
+
+/* Load the contents of the store directory into the store's internal directory list*/
+icalerrorenum icaldirset_read_directory(icaldirset *dset)
+{
+ char *str;
+#ifndef WIN32
+ struct dirent *de;
+ DIR* dp;
+
+ dp = opendir(dset->dir);
+
+ if (dp == 0) {
+ icalerror_set_errno(ICAL_FILE_ERROR);
+ return ICAL_FILE_ERROR;
+ }
+
+ /* clear contents of directory list */
+ while((str = pvl_pop(dset->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(dset->directory, (void*)strdup(de->d_name));
+ }
+
+ closedir(dp);
+#else
+ struct _finddata_t c_file;
+ intptr_t hFile;
+
+ /* Find first .c file in current directory */
+ if( (hFile = _findfirst( "*", &c_file )) == -1L ) {
+ icalerror_set_errno(ICAL_FILE_ERROR);
+ return ICAL_FILE_ERROR;
+ } else {
+ while((str = pvl_pop(dset->directory))){
+ free(str);
+ }
+
+ /* load all of the cluster names in the directory list */
+ do {
+ /* Remove known directory names '.' and '..'*/
+ if (strcmp(c_file.name,".") == 0 ||
+ strcmp(c_file.name,"..") == 0 ){
+ continue;
+ }
+
+ pvl_push(dset->directory, (void*)strdup(c_file.name));
+ }
+ while ( _findnext( hFile, &c_file ) == 0 );
+
+ _findclose( hFile );
+ }
+
+#endif
+
+ return ICAL_NO_ERROR;
+}
+
+
+icalset* icaldirset_init(icalset* set, const char* dir, void* options_in)
+{
+ icaldirset *dset = (icaldirset*)set;
+ icaldirset_options *options = options_in;
+ struct stat sbuf;
+
+ icalerror_check_arg_rz( (dir!=0), "dir");
+ icalerror_check_arg_rz( (set!=0), "set");
+
+ 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);
+
+ dset->dir = (char*)strdup(dir);
+ dset->options = *options;
+ dset->directory = pvl_newlist();
+ dset->directory_iterator = 0;
+ dset->gauge = 0;
+ dset->first_component = 0;
+ dset->cluster = 0;
+
+ return set;
+}
+
+icalset* icaldirset_new(const char* dir)
+{
+ return icalset_new(ICAL_DIR_SET, dir, &icaldirset_options_default);
+}
+
+
+icalset* icaldirset_new_reader(const char* dir)
+{
+ icaldirset_options reader_options = icaldirset_options_default;
+
+ reader_options.flags = O_RDONLY;
+
+ return icalset_new(ICAL_DIR_SET, dir, &reader_options);
+}
+
+
+icalset* icaldirset_new_writer(const char* dir)
+{
+ icaldirset_options writer_options = icaldirset_options_default;
+
+ writer_options.flags = O_RDWR|O_CREAT;
+
+ return icalset_new(ICAL_DIR_SET, dir, &writer_options);
+}
+
+
+void icaldirset_free(icalset* s)
+{
+ icaldirset *dset = (icaldirset*)s;
+ char* str;
+
+ icaldirset_unlock(dset->dir);
+
+ if(dset->dir !=0){
+ free(dset->dir);
+ dset->dir = 0;
+ }
+
+ if(dset->gauge !=0){
+ icalgauge_free(dset->gauge);
+ dset->gauge = 0;
+ }
+
+ if(dset->cluster !=0){
+ icalcluster_free(dset->cluster);
+ }
+
+ while(dset->directory !=0 && (str=pvl_pop(dset->directory)) != 0){
+ free(str);
+ }
+
+ if(dset->directory != 0){
+ pvl_free(dset->directory);
+ dset->directory = 0;
+ }
+
+ dset->directory_iterator = 0;
+ dset->first_component = 0;
+}
+
+
+/* icaldirset_next_uid_number updates a serial number in the Store
+ directory in a file called SEQUENCE */
+
+int icaldirset_next_uid_number(icaldirset* dset)
+{
+ char sequence = 0;
+ char temp[128];
+ char filename[ICAL_PATH_MAX];
+ char *r;
+ FILE *f;
+ struct stat sbuf;
+
+ icalerror_check_arg_rz( (dset!=0), "dset");
+
+ snprintf(filename,sizeof(filename),"%s/%s",dset->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* dset)
+{
+ char path[ICAL_PATH_MAX];
+
+ if (dset->directory_iterator == 0){
+ icalerror_set_errno(ICAL_INTERNAL_ERROR);
+ return ICAL_INTERNAL_ERROR;
+ }
+ dset->directory_iterator = pvl_next(dset->directory_iterator);
+
+ if (dset->directory_iterator == 0){
+ /* There are no more clusters */
+ if(dset->cluster != 0){
+ icalcluster_free(dset->cluster);
+ dset->cluster = 0;
+ }
+ return ICAL_NO_ERROR;
+ }
+
+ snprintf(path,sizeof(path),"%s/%s", dset->dir,(char*)pvl_data(dset->directory_iterator));
+
+ icalcluster_free(dset->cluster);
+ dset->cluster = icalfileset_produce_icalcluster(path);
+
+ return icalerrno;
+}
+
+static void icaldirset_add_uid(icalcomponent* comp)
+{
+ char uidstring[ICAL_PATH_MAX];
+ icalproperty *uid;
+#ifndef WIN32
+ struct utsname unamebuf;
+#endif
+
+ icalerror_check_arg_rv( (comp!=0), "comp");
+
+ uid = icalcomponent_get_first_property(comp,ICAL_UID_PROPERTY);
+
+ if (uid == 0) {
+
+#ifndef WIN32
+ uname(&unamebuf);
+
+ snprintf(uidstring,sizeof(uidstring),"%d-%s",(int)getpid(),unamebuf.nodename);
+#else
+ snprintf(uidstring,sizeof(uidstring),"%d-%s",(int)getpid(),"WINDOWS"); /* FIX: There must be an easy get the system name */
+#endif
+
+ uid = icalproperty_new_uid(uidstring);
+ icalcomponent_add_property(comp,uid);
+ } else {
+ strncpy(uidstring,icalproperty_get_uid(uid),ICAL_PATH_MAX-1);
+ uidstring[ICAL_PATH_MAX-1]='\0';
+ }
+}
+
+
+/**
+ 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 DSTAMP property
+*/
+
+icalerrorenum icaldirset_add_component(icalset* set, icalcomponent* comp)
+{
+ char clustername[ICAL_PATH_MAX];
+ icalproperty *dt = 0;
+ icalvalue *v;
+ struct icaltimetype tm;
+ icalerrorenum error = ICAL_NO_ERROR;
+ icalcomponent *inner;
+ icaldirset *dset = (icaldirset*) set;
+
+ icalerror_check_arg_rz( (dset!=0), "dset");
+ icalerror_check_arg_rz( (comp!=0), "comp");
+
+ icaldirset_add_uid(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_DTSTAMP_PROPERTY);
+
+ if (dt != 0)
+ break;
+ }
+
+ if (dt == 0) {
+ 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 DTSTAMP or 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,ICAL_PATH_MAX,"%s/%04d%02d",dset->dir, tm.year, tm.month);
+
+ /* Load the cluster and insert the object */
+ if(dset->cluster != 0 &&
+ strcmp(clustername,icalcluster_key(dset->cluster)) != 0 ){
+ icalcluster_free(dset->cluster);
+ dset->cluster = 0;
+ }
+
+ if (dset->cluster == 0){
+ dset->cluster = icalfileset_produce_icalcluster(clustername);
+
+ if (dset->cluster == 0){
+ error = icalerrno;
+ }
+ }
+
+ if (error != ICAL_NO_ERROR){
+ icalerror_set_errno(error);
+ return error;
+ }
+
+ /* Add the component to the cluster */
+ icalcluster_add_component(dset->cluster,comp);
+
+ /* icalcluster_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(icalset* set, icalcomponent* comp)
+{
+ icaldirset *dset = (icaldirset*)set;
+ icalcomponent *filecomp = icalcluster_get_component(dset->cluster);
+
+ icalcompiter i;
+ int found = 0;
+
+ icalerror_check_arg_re((set!=0),"set",ICAL_BADARG_ERROR);
+ icalerror_check_arg_re((comp!=0),"comp",ICAL_BADARG_ERROR);
+ icalerror_check_arg_re((dset->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;
+ }
+
+ icalcluster_remove_component(dset->cluster,comp);
+
+ /* icalcluster_mark(impl->cluster); */
+
+ /* If the removal emptied the fileset, get the next fileset */
+ if( icalcluster_count_components(dset->cluster,ICAL_ANY_COMPONENT)==0){
+ icalerrorenum error = icaldirset_next_cluster(dset);
+
+ if(dset->cluster != 0 && error == ICAL_NO_ERROR){
+ icalcluster_get_first_component(dset->cluster);
+ } else {
+ /* HACK. Not strictly correct for impl->cluster==0 */
+ return error;
+ }
+ } else {
+ /* Do nothing */
+ }
+
+ return ICAL_NO_ERROR;
+}
+
+
+
+int icaldirset_count_components(icalset* store,
+ icalcomponent_kind kind)
+{
+ /* HACK, not implemented */
+ assert(0);
+
+ return 0;
+}
+
+
+icalcomponent* icaldirset_fetch_match(icalset* set, icalcomponent *c)
+{
+ fprintf(stderr," icaldirset_fetch_match is not implemented\n");
+ assert(0);
+ return 0;
+}
+
+
+icalcomponent* icaldirset_fetch(icalset* set, const char* uid)
+{
+ icaldirset *dset = (icaldirset*)set;
+ icalgauge *gauge;
+ icalgauge *old_gauge;
+ icalcomponent *c;
+ char sql[256];
+
+ icalerror_check_arg_rz( (set!=0), "set");
+ icalerror_check_arg_rz( (uid!=0), "uid");
+
+ snprintf(sql, 256, "SELECT * FROM VEVENT WHERE UID = \"%s\"", uid);
+
+ gauge = icalgauge_new_from_sql(sql, 0);
+
+ old_gauge = dset->gauge;
+ dset->gauge = gauge;
+
+ c= icaldirset_get_first_component(set);
+
+ dset->gauge = old_gauge;
+
+ icalgauge_free(gauge);
+
+ return c;
+}
+
+
+int icaldirset_has_uid(icalset* set, const char* uid)
+{
+ icalcomponent *c;
+
+ icalerror_check_arg_rz( (set!=0), "set");
+ 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(set,uid);
+
+ return c!=0;
+
+}
+
+
+icalerrorenum icaldirset_select(icalset* set, icalgauge* gauge)
+{
+ icaldirset *dset = (icaldirset*)set;
+
+ icalerror_check_arg_re( (set!=0), "set",ICAL_BADARG_ERROR);
+ icalerror_check_arg_re( (gauge!=0), "gauge",ICAL_BADARG_ERROR);
+
+ dset->gauge = gauge;
+
+ return ICAL_NO_ERROR;
+}
+
+
+icalerrorenum icaldirset_modify(icalset* set,
+ icalcomponent *old,
+ icalcomponent *new)
+{
+ assert(0);
+ return ICAL_NO_ERROR; /* HACK, not implemented */
+
+}
+
+
+void icaldirset_clear(icalset* set)
+{
+
+ assert(0);
+ return;
+ /* HACK, not implemented */
+}
+
+icalcomponent* icaldirset_get_current_component(icalset* set)
+{
+ icaldirset *dset = (icaldirset*)set;
+
+ if (dset->cluster == 0){
+ icaldirset_get_first_component(set);
+ }
+ if(dset->cluster == 0){
+ return 0;
+ }
+
+ return icalcluster_get_current_component(dset->cluster);
+}
+
+
+icalcomponent* icaldirset_get_first_component(icalset* set)
+{
+ icaldirset *dset = (icaldirset*)set;
+
+ icalerrorenum error;
+ char path[ICAL_PATH_MAX];
+
+ error = icaldirset_read_directory(dset);
+
+ if (error != ICAL_NO_ERROR){
+ icalerror_set_errno(error);
+ return 0;
+ }
+
+ dset->directory_iterator = pvl_head(dset->directory);
+
+ if (dset->directory_iterator == 0){
+ icalerror_set_errno(error);
+ return 0;
+ }
+
+ snprintf(path,ICAL_PATH_MAX,"%s/%s",
+ dset->dir,
+ (char*)pvl_data(dset->directory_iterator));
+
+ /* If the next cluster we need is different than the current cluster,
+ delete the current one and get a new one */
+
+ if(dset->cluster != 0 && strcmp(path,icalcluster_key(dset->cluster)) != 0 ){
+ icalcluster_free(dset->cluster);
+ dset->cluster = 0;
+ }
+
+ if (dset->cluster == 0){
+ dset->cluster = icalfileset_produce_icalcluster(path);
+
+ if (dset->cluster == 0){
+ error = icalerrno;
+ }
+ }
+
+ if (error != ICAL_NO_ERROR){
+ icalerror_set_errno(error);
+ return 0;
+ }
+
+ dset->first_component = 1;
+
+ return icaldirset_get_next_component(set);
+}
+
+
+icalcomponent* icaldirset_get_next_component(icalset* set)
+{
+ icaldirset *dset = (icaldirset*)set;
+ icalcomponent *c;
+ icalerrorenum error;
+
+ icalerror_check_arg_rz( (set!=0), "set");
+
+
+ if(dset->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 (dset->first_component == 1){
+ icalcluster_get_first_component(dset->cluster);
+ dset->first_component = 0;
+ } else {
+ icalcluster_get_next_component(dset->cluster);
+ }
+
+ while(1){
+ /* Iterate through all of the objects in the cluster*/
+ for( c = icalcluster_get_current_component(dset->cluster);
+ c != 0;
+ c = icalcluster_get_next_component(dset->cluster)){
+
+ /* If there is a gauge defined and the component does not
+ pass the gauge, skip the rest of the loop */
+
+ if (dset->gauge != 0 && icalgauge_compare(dset->gauge,c) == 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(dset);
+
+ if(dset->cluster == 0 || error != ICAL_NO_ERROR){
+ /* No more clusters */
+ return 0;
+ } else {
+ c = icalcluster_get_first_component(dset->cluster);
+
+ return c;
+ }
+
+ }
+
+ return 0; /* Should never get here */
+}
+
+icalsetiter icaldirset_begin_component(icalset* set, icalcomponent_kind kind, icalgauge* gauge)
+{
+ icalsetiter itr = icalsetiter_null;
+ icaldirset *fset = (icaldirset*) set;
+
+ icalerror_check_arg_re((fset!=0), "set", icalsetiter_null);
+
+ itr.iter.kind = kind;
+ itr.gauge = gauge;
+
+ /* TO BE IMPLEMENTED */
+ return icalsetiter_null;
+}
+
+icalcomponent* icaldirsetiter_to_next(icalset* set, icalsetiter* i)
+{
+ /* TO BE IMPLEMENTED */
+ return NULL;
+}
+
+icalcomponent* icaldirsetiter_to_prior(icalset* set, icalsetiter* i)
+{
+ /* TO BE IMPLEMENTED */
+ return NULL;
+}