diff options
Diffstat (limited to 'src/test/stow.c')
-rw-r--r-- | src/test/stow.c | 895 |
1 files changed, 895 insertions, 0 deletions
diff --git a/src/test/stow.c b/src/test/stow.c new file mode 100644 index 00000000..2836604a --- /dev/null +++ b/src/test/stow.c @@ -0,0 +1,895 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: stow.c + CREATOR: eric 29 April 2000 + + $Id: stow.c,v 1.10 2008-01-02 20:07:46 dothebart Exp $ + $Locker: $ + + (C) COPYRIGHT 2000 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 Initial Developer of the Original Code is Eric Busboom + + ======================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <limits.h> /* for PATH_MAX */ +#include <assert.h> +#include <stdlib.h> +#include <sys/utsname.h> /* for uname */ +#include <sys/stat.h> /* for stat */ +#if defined(HAVE_UNISTD_H) +#include <unistd.h> /* for stat, getpid, getopt */ +#endif +#include <pwd.h> /* For getpwent */ +#include <sys/types.h> /* For getpwent */ +#include <ctype.h> /* for tolower */ + +#include <libical/ical.h> +#include <libicalss/icalss.h> + +char* program_name; +#define TMPSIZE 2048 +#define SENDMAIL "/usr/lib/sendmail -t" + +void usage(char *message); + +#ifndef PATH_MAX +#define PATH_MAX 256 /* HACK */ +#endif + + +enum options { + STORE_IN_FILE, + STORE_IN_DB, + INPUT_IS_MIME, + INPUT_IS_ICAL, + INPUT_FROM_STDIN, + INPUT_FROM_FILE, + ERRORS_TO_STDOUT, + ERRORS_TO_ORGANIZER +}; + +struct options_struct +{ + enum options storage; + enum options input_type; + enum options input_source; + enum options errors; + char* input_file; + char* calid; + char* output_file; +}; + + +enum file_type +{ + ERROR, + NO_FILE, + DIRECTORY, + REGULAR, + OTHER +}; + +enum file_type test_file(char *path) +{ + struct stat sbuf; + enum file_type type; + + errno = 0; + + /* Check if the path already exists and if it is a directory*/ + if (stat(path,&sbuf) != 0){ + + /* A file by the given name does not exist, or there was + another error */ + if(errno == ENOENT) + { + type = NO_FILE; + } else { + type = ERROR; + } + + } else { + /* A file by the given name exists, but is it a directory? */ + + if (S_ISDIR(sbuf.st_mode)){ + type = DIRECTORY; + } else if(S_ISREG(sbuf.st_mode)){ + type = REGULAR; + } else { + type = OTHER; + } + } + + return type; +} + +char* lowercase(const char* str) +{ + char* p = 0; + char* new = strdup(str); + + if(str ==0){ + return 0; + } + + for(p = new; *p!=0; p++){ + *p = tolower(*p); + } + + return new; +} + +#if 0 +char* get_local_attendee(struct options_struct *opt) +{ + char attendee[PATH_MAX]; + + if(opt->calid){ + + strncpy(attendee,opt->calid,PATH_MAX); + + } else { + + char* user = getenv("USER"); + struct utsname uts; + uname(&utget_option); + /* HACK nodename may not be a fully qualified domain name */ + snprintf(attendee,PATH_MAX,"%s@%s",user,uts.nodename); + + } + + return lowercase(attendee); +} +#endif + + +icalcomponent* get_first_real_component(icalcomponent *comp) +{ + icalcomponent *c; + + for(c = icalcomponent_get_first_component(comp,ICAL_ANY_COMPONENT); + c != 0; + c = icalcomponent_get_next_component(comp,ICAL_ANY_COMPONENT)){ + if (icalcomponent_isa(c) == ICAL_VEVENT_COMPONENT || + icalcomponent_isa(c) == ICAL_VTODO_COMPONENT || + icalcomponent_isa(c) == ICAL_VJOURNAL_COMPONENT ) + { + return c; + } + } + + return 0; +} + + + +char* make_mime(const char* to, const char* from, const char* subject, + const char* text_message, const char* method, + const char* ical_message) +{ + size_t size = strlen(to)+strlen(from)+strlen(subject)+ + strlen(text_message)+ strlen(ical_message)+TMPSIZE; + + char mime_part_1[TMPSIZE]; + char mime_part_2[TMPSIZE]; + char content_id[TMPSIZE]; + char boundary[TMPSIZE]; + struct utsname uts; + char* m; + + + if ((m = malloc(sizeof(char)*size)) == 0){ + fprintf(stderr,"%s: Can't allocate memory: %s\n",program_name,strerror(errno)); + exit(1); + } + + uname(&uts); + + srand(time(0)<<getpid()); + sprintf(content_id,"%d-%d@%s",(int)time(0),rand(),uts.nodename); + sprintf(boundary,"%d-%d-%s",(int)time(0),rand(),uts.nodename); + + sprintf(mime_part_1,"Content-ID: %s\n\ +Content-type: text/plain\n\ +Content-Description: Text description of error message\n\n\ +%s\n\n--%s", + content_id,text_message,boundary); + + if(ical_message != 0 && method != 0){ + sprintf(mime_part_2,"Content-ID: %s\n\ +Content-type: text/calendar; method=%s\n\ +Content-Description: iCal component reply\n\n\ +%s\n\n--%s--", + content_id,method,ical_message,boundary); + } + + sprintf(m,"To: %s\n\ +From: %s\n\ +Subject: %s\n\ +MIME-Version: 1.0\n\ +Content-ID: %s\n\ +Content-Type: multipart/mixed; boundary=\"%s\"\n\ +\n\ + This is a multimedia message in MIME format\n\ +\n\ +--%s\n\ +%s\n\ +", + to,from,subject,content_id,boundary,boundary, + mime_part_1); + + if(ical_message != 0 && method != 0){ + strcat(m, mime_part_2); + } else { + strcat(m,"--\n"); + } + + return m; +} + +/* The incoming component had fatal errors */ +void return_failure(icalcomponent* comp, char* message, + struct options_struct *opt) +{ + char* local_attendee = opt->calid; + FILE* p; + const char *org_addr; + + icalcomponent *inner = get_first_real_component(comp); + + icalproperty *organizer_prop = icalcomponent_get_first_property(inner,ICAL_ORGANIZER_PROPERTY); + const char *organizer = icalproperty_get_organizer(organizer_prop); + + org_addr = strchr(organizer,':'); + + if(org_addr != 0){ + org_addr++; /* Skip the ';' */ + } else { + org_addr = organizer; + } + + if (opt->errors == ERRORS_TO_ORGANIZER){ + p = popen(SENDMAIL,"w"); + } else { + p = stdout; + } + + if(p == 0){ + fprintf(stderr, + "%s: fatal. Could not open pipe to sendmail (\"%s\") \n", + program_name,SENDMAIL); + exit(1); + } + + fputs(make_mime(org_addr, local_attendee, "iMIP error", + message, "reply", + icalcomponent_as_ical_string(comp)),p); + + if (opt->errors == ERRORS_TO_ORGANIZER){ + pclose(p); + } +} + +/* The program had a fatal error and could not process the incoming component*/ +void return_error(icalcomponent* comp, char* message, struct options_struct *opt) +{ + + + fputs(make_mime("Dest", "Source", "iMIP system failure", + message, 0,0),stdout); + +} + +icalcomponent* make_reply(icalcomponent *comp, icalproperty *return_status, + struct options_struct *opt) + +{ + icalcomponent *reply, *rinner; + icalcomponent *inner = get_first_real_component(comp); + icalproperty *p=0; + char* local_attendee = opt->calid; + char attendee[TMPSIZE]; + + char prodid[TMPSIZE]; + + snprintf(attendee,TMPSIZE,"mailto:%s",local_attendee); + + snprintf(prodid,TMPSIZE,"-//Softwarestudio.org//%s version %s//EN",ICAL_PACKAGE,ICAL_VERSION); + + /* Create the base component */ + reply = icalcomponent_vanew( + ICAL_VCALENDAR_COMPONENT, + icalproperty_new_version(strdup("2.0")), + icalproperty_new_prodid(strdup(prodid)), + icalproperty_new_method(ICAL_METHOD_REPLY), + icalcomponent_vanew( + ICAL_VEVENT_COMPONENT, + icalproperty_new_clone( + icalcomponent_get_first_property(inner,ICAL_DTSTAMP_PROPERTY)), + icalproperty_new_clone( + icalcomponent_get_first_property(inner,ICAL_ORGANIZER_PROPERTY)), + icalproperty_new_clone( + icalcomponent_get_first_property(inner,ICAL_UID_PROPERTY)), + icalproperty_new_attendee(attendee), + 0), + 0); + + + /* Convert errors into request-status properties and transfers + them to the reply component */ + + icalcomponent_convert_errors(comp); + + rinner = get_first_real_component(reply); + + for(p = icalcomponent_get_first_property(inner, + ICAL_REQUESTSTATUS_PROPERTY); + p != 0; + p = icalcomponent_get_next_property(inner, + ICAL_REQUESTSTATUS_PROPERTY)){ + + icalcomponent_add_property(rinner,icalproperty_new_clone(p)); + } + + if(return_status != 0){ + icalcomponent_add_property(rinner, return_status); + } + + return reply; + +} + +int check_attendee(icalproperty *p, struct options_struct *opt){ + const char* s = icalproperty_get_attendee(p); + char* lower_attendee = lowercase(s); + char* local_attendee = opt->calid; + + /* Check that attendee begins with "mailto:" */ + if (strncmp(lower_attendee,"mailto:",7) == 0){ + /* skip over the mailto: part */ + lower_attendee += 7; + + if(strcmp(lower_attendee,local_attendee) == 0){ + return 1; + } + + lower_attendee -= 7; + + free(lower_attendee); + } + + return 0; +} + +char static_component_error_str[PATH_MAX]; +char* check_component(icalcomponent* comp, icalproperty **return_status, + struct options_struct *opt) +{ + char* component_error_str=0; + icalcomponent* inner; + int errors = 0; + icalproperty *p; + int found_attendee = 0; + struct icalreqstattype rs; + + rs.code = ICAL_UNKNOWN_STATUS; + rs.desc = 0; + rs.debug = 0; + + /*{ + icalrequeststatus code; + const char* desc; + const char* debug; + };*/ + + *return_status = 0; + + /* This do/while loop only executes once because it is being used + to fake exceptions */ + + do { + + /* Check that we actually got a component */ + if(comp == 0){ + strcpy(static_component_error_str, + "Did not find a component"); + component_error_str = static_component_error_str; + break; + } + + /* Check that the root component is a VCALENDAR */ + if(icalcomponent_isa(comp) != ICAL_VCALENDAR_COMPONENT){ + strcpy(static_component_error_str, + "Root component is not a VCALENDAR"); + component_error_str = static_component_error_str; + rs.code = ICAL_3_11_MISSREQCOMP_STATUS; + + break; + } + + + /* Check that the component has a METHOD */ + + if (icalcomponent_get_first_property(comp,ICAL_METHOD_PROPERTY) == 0) + { + strcpy(static_component_error_str, + "The component you sent did not have a METHOD property"); + component_error_str = static_component_error_str; + rs.code = ICAL_3_11_MISSREQCOMP_STATUS; + break; + } + + inner = get_first_real_component(comp); + + + /* Check that the compopnent has an organizer */ + if(icalcomponent_get_first_property(inner,ICAL_ORGANIZER_PROPERTY) == 0){ + fprintf(stderr,"%s: fatal. Component does not have an ORGANIZER property\n",program_name); + rs.code = ICAL_3_11_MISSREQCOMP_STATUS; + break; + } + + + /* Check for this user as an attendee or organizer */ + + for(p = icalcomponent_get_first_property(inner,ICAL_ATTENDEE_PROPERTY); + p != 0; + p = icalcomponent_get_next_property(inner,ICAL_ATTENDEE_PROPERTY)){ + + found_attendee += check_attendee(p,opt); + } + + for(p = icalcomponent_get_first_property(inner,ICAL_ORGANIZER_PROPERTY); + p != 0; + p = icalcomponent_get_next_property(inner,ICAL_ORGANIZER_PROPERTY)){ + + found_attendee += check_attendee(p,opt); + } + + if (found_attendee == 0){ + struct icalreqstattype rs; + memset(static_component_error_str,0,PATH_MAX); + + snprintf(static_component_error_str,PATH_MAX, + "This target user (%s) is not listed as an attendee or organizer", + opt->calid ); + component_error_str = static_component_error_str; + + rs.code = ICAL_3_7_INVCU_STATUS; + + break; + } + + + + /* Check that the component passes iTIP restrictions */ + + errors = icalcomponent_count_errors(comp); + icalrestriction_check(comp); + + if(errors != icalcomponent_count_errors(comp)){ + snprintf(static_component_error_str,PATH_MAX, + "The component does not conform to iTIP restrictions.\n Here is the original component; look at the X-LIC-ERROR properties\nfor details\n\n%s",icalcomponent_as_ical_string(comp)); + component_error_str = static_component_error_str; + break; + } + + + + } while(0); + + if(rs.code != ICAL_UNKNOWN_STATUS){ + *return_status = icalproperty_new_requeststatus(rs); + } + + return component_error_str; +} + + +void usage(char *message) +{ + fprintf(stderr,"Usage: %s [-emdcn] [-i inputfile] [-o outputfile] [-u calid]\n",program_name); + fprintf(stderr,"-e\tInput data is encapsulated in a MIME Message \n\ +-m\tInput is raw iCal \n\ +-i\tSpecify input file. Otherwise, input comes from stdin\n\ +-o\tSpecify file to save incoming message to\n\ +-d\tSpecify database to send data to\n\ +-u\tSet the calid to store the data to\n\ +-n\tSend errors to stdout instead of organizer\n\ +"); + +} + + +void get_options(int argc, char* argv[], struct options_struct *opt) +{ + int c; +#if !defined(HAVE_UNISTD_H) + extern char *optarg; + extern int optind, optopt; +#endif + int errflg=0; + + opt->storage = STORE_IN_FILE; + opt->input_source = INPUT_FROM_STDIN; + opt->input_type = INPUT_IS_ICAL; + opt->input_file = 0; + opt->errors = ERRORS_TO_ORGANIZER; + opt->calid = 0; + opt->output_file = 0; + + + while ((c = getopt(argc, argv, "nemu:o:d:b:c:i:")) != -1) { + switch (c) { + case 'e': { /* Input data is MIME encapsulated */ + opt->input_type = INPUT_IS_MIME; + break; + } + case 'm': { /* Input is iCal. Default*/ + opt->input_type = INPUT_IS_ICAL; + break; + } + case 'i': { /* Input comes from named file */ + opt->input_source = INPUT_FROM_FILE; + opt->input_file = strdup(optarg); + break; + } + case 'o': { /* Output goes to named file. Default*/ + opt->output_file = strdup(optarg); + opt->storage = STORE_IN_FILE; + break; + } + case 'd': { /* Output goes to database */ + fprintf(stderr,"%s: option -d is unimplmented\n",program_name); + opt->storage = STORE_IN_DB; + errflg++; + break; + } + case 'c': { + + break; + } + case 'u': { /* Set the calid for the output database or + file. Default is user name of user running + program */ + opt->calid = strdup(optarg); + break; + } + + case 'n': { /* Dump error to stdout. Default is to + send error to the organizer specified + in the iCal data */ + opt->errors = ERRORS_TO_STDOUT; + break; + } + + case ':': {/* Option given without an operand */ + fprintf(stderr, + "%s: Option -%c requires an operand\n", + program_name,optopt); + errflg++; + break; + } + case '?': { + errflg++; + } + + } + + if (errflg >0){ + usage(""); + exit(1); + } + } + + if(opt->calid == 0){ + /* If no calid specified, use username */ + char attendee[PATH_MAX]; + char* user = getenv("USER"); + struct utsname uts; + uname(&uts); + /* HACK nodename may not be a fully qualified domain name */ + snprintf(attendee,PATH_MAX,"%s@%s",user,uts.nodename); + + opt->calid = lowercase(attendee); + } + + if(opt->storage == STORE_IN_FILE && + opt->output_file ==0){ + char file[PATH_MAX]; + char* user = getenv("USER"); + struct passwd *pw; + + if(!user){ + fprintf(stderr,"%s: Can't get username. Try explicitly specifing the output file with -o", program_name); + exit(1); + } + + /* Find password entry for user */ + while( (pw = getpwent())!=0){ + if(strcmp(user,pw->pw_name)==0){ + break; + } + } + + if(pw==0){ + fprintf(stderr,"%s: Can't get get password entry for user \"%s\" Try explicitly specifing the output file with -o", + program_name,user); + exit(1); + } + + if(pw->pw_dir==0){ + fprintf(stderr,"%s: User \"%s\" has no home directory. Try explicitly specifing the output file with -o", + program_name, user); + exit(1); + } + + snprintf(file,PATH_MAX,"%s/.facs/%s",pw->pw_dir,opt->calid); + + opt->output_file = strdup(file); + } + + + /* Now try to create the calendar directory if it does + not exist */ + + if(opt->storage == STORE_IN_FILE ) { + char * p; + char* facspath = strdup(opt->output_file); + enum file_type type; + + /* Cut off the last slash to make it just a directoy */ + + p = strrchr(facspath,'/'); + + if (p != 0){ + /* Use some other directory */ + *p='\0'; + + type = test_file(facspath); + + errno = 0; + if (type == NO_FILE){ + + if(mkdir(facspath,0775) != 0){ + fprintf(stderr, + "%s: Failed to create calendar directory %s: %s\n", + program_name,facspath, strerror(errno)); + exit(1); + } else { + fprintf(stderr,"%s: Creating calendar directory %s\n", + program_name,facspath); + } + + } else if(type==REGULAR || type == ERROR){ + fprintf(stderr,"%s: Cannot create calendar directory %s\n", + program_name,facspath); + exit(1); + } + } + } +} + +char* check_options(struct options_struct *opt) +{ + return 0; +} + +void store_component(icalcomponent *comp, struct options_struct *opt) +{ + icalerrorenum error; + + + if(opt->storage == STORE_IN_FILE){ + icalset *fs = icalfileset_new(opt->output_file); + + if (fs == 0){ + fprintf(stderr, + "%s: Failed to get incoming component directory: %s\n", + program_name, icalerror_strerror(icalerrno)); + exit(1); + } + + + error = icalfileset_add_component(fs,comp); + + if (error != ICAL_NO_ERROR){ + fprintf(stderr,"%s: Failed to write incoming component: %s\n", + program_name, icalerror_strerror(icalerrno)); + exit(1); + } + + error = icalfileset_commit(fs); + + if (error != ICAL_NO_ERROR){ + fprintf(stderr,"%s: Failed to commit incoming cluster: %s\n", + program_name, icalerror_strerror(icalerrno)); + exit(1); + } + + icalset_free(fs); + + return; + } else { + assert(0); + } +} + +char* read_stream(char *s, size_t size, void *d) +{ + char *c = fgets(s,size, (FILE*)d); + + return c; +} + +icalcomponent* read_nonmime_component(struct options_struct *opt) +{ + FILE *stream; + icalcomponent *comp; + icalparser* parser = icalparser_new(); + icalerrorstate es = icalerror_get_error_state(ICAL_MALFORMEDDATA_ERROR); + char* line; + + if(opt->input_source == INPUT_FROM_FILE){ + stream = fopen(opt->input_file,"r"); + + if (stream == 0){ + perror("Can't open input file"); + exit(1); + } + + } else { + stream = stdin; + } + + assert(stream != 0); + icalparser_set_gen_data(parser,stream); + + do { + line = icalparser_get_line(parser,read_stream); + + icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,ICAL_ERROR_NONFATAL); + comp = icalparser_add_line(parser,line); + icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,es); + + if (comp != 0){ + return comp; + } + + } while ( line != 0); + + if(opt->input_source == INPUT_FROM_FILE){ + fclose(stream); + } + + + return comp; + } + +icalcomponent* find_vcalendar(icalcomponent* comp) +{ + icalcomponent *c,*rtrn; + + for(c = icalcomponent_get_first_component(comp,ICAL_ANY_COMPONENT); + c != 0; + c = icalcomponent_get_next_component(comp,ICAL_ANY_COMPONENT)){ + + if(icalcomponent_isa(c) == ICAL_VCALENDAR_COMPONENT){ + icalcomponent_remove_component(comp,c); + return c; + } + + if((rtrn=find_vcalendar(c)) != 0){ + return rtrn; + } + } + + return 0; +} + +icalcomponent* read_mime_component(struct options_struct *opt) +{ + icalcomponent *comp,*mimecomp; + FILE* stream; + + if(opt->input_source == INPUT_FROM_FILE){ + stream = fopen(opt->input_file,"r"); + + if (stream == 0){ + perror("Can't open input file"); + exit(1); + } + + } else { + stream = stdin; + } + + assert(stream != 0); + + mimecomp = icalmime_parse(read_stream,(void*)stream); + + /* now find the iCal component embedded within the mime component */ + comp = find_vcalendar(mimecomp); + + + if(comp == 0){ + return 0; + } + + return comp; +} + +icalcomponent* read_component(struct options_struct *opt) +{ + if(opt->input_type == INPUT_IS_MIME){ + return read_mime_component(opt); + } else if (opt->input_type == INPUT_IS_ICAL){ + return read_nonmime_component(opt); + } else { + fprintf(stderr,"%s: Internal Error; unknown option for input_type\n", + program_name); + exit(1); + } +} + +int main(int argc, char* argv[] ) +{ + char* options_error_str; + char* component_error_str; + icalcomponent* comp, *reply; + struct options_struct opt; + icalproperty *return_status; + + program_name = strrchr(argv[0],'/'); + + get_options(argc, argv, &opt); + + if ( (options_error_str = check_options(&opt)) != 0 ){ + usage(options_error_str); + exit(1); + } + + comp = read_component(&opt); + + /* If the component had any fatal errors, return an error message + to the organizer */ + if ( (component_error_str = + check_component(comp,&return_status,&opt)) != 0){ + + reply = make_reply(comp,return_status,&opt); + + return_failure(reply, component_error_str, &opt); + icalcomponent_free(reply); + exit(0); + + } + + store_component(comp,&opt); + + + /* Don't free the component comp, since it is now part of the + store, and will be freed there */ + + exit(0); +} + |