1 /*
2  * icalfilter -- filter an iCalendar file
3  *
4  * Author: Bert Bos <bert@w3.org>
5  * Created: 30 Sep 2002
6  */
7 
8 #include "config.h"
9 #include <unistd.h>
10 #include <stdio.h>
11 #include <errno.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <stdlib.h>
15 #include <stdarg.h>
16 #include <getopt.h>
17 #include <ctype.h>
18 /*
19 #include <icaltime.h>
20 #include <icalcomponent.h>
21 #include <icalerror.h>
22 #include <icaltimezone.h>
23 */
24 #include <libical/ical.h>
25 #include <libical/icalss.h>
26 /*
27 #include <icalset.h>
28 #include <icalfileset.h>
29 */
30 
31 #define PRODID "-//W3C//NONSGML icalfilter 0.1//EN"
32 
33 #define ERR_OUT_OF_MEM 1	/* Program exit codes */
34 #define ERR_USAGE 2
35 #define ERR_DATE 3
36 #define ERR_PARSE 4
37 #define ERR_FILEIO 5
38 #define ERR_ICAL_ERR 6		/* Other error */
39 
40 #define USAGE "Usage: ical2html [options] input output\n\
41   -p, --class=CLASS            only (PUBLIC, CONFIDENTIAL, PRIVATE, NONE)\n\
42   -P, --not-class=CLASS        exclude (PUBLIC, CONFIDENTIAL, PRIVATE, NONE)\n\
43   -c, --category=CATEGORY      only events of this category\n\
44   -C, --not-category=CATEGORY  exclude events of this category\n\
45   input and output are iCalendar files\n"
46 
47 /* Long command line options */
48 static struct option options[] = {
49   {"class", 1, 0, 'p'},
50   {"not-class", 1, 0, 'P'},
51   {"category", 1, 0, 'c'},
52   {"not-category", 1, 0, 'C'},
53   {0, 0, 0, 0}
54 };
55 
56 #define OPTIONS "p:P:c:C:"
57 
58 /* Structure for storing applicable events */
59 typedef struct _event_item {
60   struct icaltimetype start;
61   struct icaltimetype end;
62   icalcomponent *event;
63 } event_item;
64 
65 
66 
67 /* fatal -- print error message and exit with errcode */
fatal(int errcode,const char * message,...)68 static void fatal(int errcode, const char *message,...)
69 {
70   va_list args;
71   va_start(args, message);
72   vfprintf(stderr, message, args);
73   va_end(args);
74   exit(errcode);
75 }
76 
77 
78 /* read_stream -- read size bytes into s from stream d */
read_stream(char * s,size_t size,void * d)79 static char* read_stream(char *s, size_t size, void *d)
80 {
81   return fgets(s, size, (FILE*)d);
82 }
83 
84 
85 /* main */
main(int argc,char * argv[])86 int main(int argc, char *argv[])
87 {
88   FILE* stream;
89   icalcomponent *comp;
90   icalparser *parser;
91   char *classmask = NULL, *categorymask = NULL;
92   char *notclassmask = NULL, *notcategorymask = NULL;
93   char c;
94   const char *class;
95   icalset *out;
96   icalcomponent *h, *next, *newset;
97   icalproperty *p;
98 
99   /* We handle errors ourselves */
100   icalerrno = 0;
101 
102   /* Read commandline */
103   while ((c = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
104     switch (c) {
105     case 'p': classmask = strdup(optarg); break;
106     case 'P': notclassmask = strdup(optarg); break;
107     case 'c': categorymask = strdup(optarg); break;
108     case 'C': notcategorymask = strdup(optarg); break;
109     default: fatal(ERR_USAGE, USAGE);
110     }
111   }
112   /* Get input file name */
113   if (optind == argc) fatal(ERR_USAGE, USAGE);
114   stream = optind == argc ? stdin : fopen(argv[optind], "r");
115   if (!stream) fatal(ERR_FILEIO, "%s: %s\n", argv[optind], strerror(errno));
116   optind++;
117 
118   /* Get output file name */
119   if (optind == argc) fatal(ERR_USAGE, USAGE);
120   (void) unlink(argv[optind]);	/* Remove output file if it already exists */
121   out = icalfileset_new(argv[optind]);
122   if (!out) fatal(ERR_FILEIO, "%s: %s\n", argv[optind], strerror(errno));
123 
124   /* Should have no more arguments */
125   if (optind + 1 != argc) fatal(ERR_USAGE, USAGE);
126 
127   /* Create a new parser object */
128   parser = icalparser_new();
129 
130   /* Tell the parser what input stream it should use */
131   icalparser_set_gen_data(parser, stream);
132 
133   /* Let the parser read the file and return all components */
134   if (! (comp = icalparser_parse(parser, read_stream)))
135     fatal(ERR_PARSE, "Parse error: %s\n", icalerror_strerror(icalerrno));
136   icalparser_free(parser);
137 
138   /* Initialize a new VCALENDAR */
139   newset = icalcomponent_vanew(ICAL_VCALENDAR_COMPONENT,
140 			       icalproperty_new_version("2.0"),
141 			       icalproperty_new_prodid(PRODID),
142 			       0);
143 
144   /* Iterate over VEVENTs */
145   for (h = icalcomponent_get_first_component(comp, ICAL_VEVENT_COMPONENT);
146        h; h = next) {
147 
148     next = icalcomponent_get_next_component(comp, ICAL_VEVENT_COMPONENT);
149 
150     /* Check if the event is of the right class (unless we accept all) */
151     if (classmask || notclassmask) {
152       p = icalcomponent_get_first_property(h, ICAL_CLASS_PROPERTY);
153       class = p ? icalvalue_as_ical_string(icalproperty_get_value(p))
154 	: "NONE";
155       if (classmask && strcasecmp(classmask, class) != 0) continue;
156       if (notclassmask && strcasecmp(notclassmask, class) == 0) continue;
157     }
158 
159     /* Check if the event is of the right category (unless we accept all) */
160     if (categorymask) {
161       p = icalcomponent_get_first_property(h, ICAL_CATEGORIES_PROPERTY);
162       while (p && strcasecmp(categorymask, icalproperty_get_categories(p)))
163 	p = icalcomponent_get_next_property(h, ICAL_CATEGORIES_PROPERTY);
164       if (!p) continue;		/* No category was equal to categorymask */
165     }
166     if (notcategorymask) {
167       p = icalcomponent_get_first_property(h, ICAL_CATEGORIES_PROPERTY);
168       while (p && strcasecmp(notcategorymask, icalproperty_get_categories(p)))
169 	p = icalcomponent_get_next_property(h, ICAL_CATEGORIES_PROPERTY);
170       if (p) continue;		/* Some category equal to notcategorymask */
171     }
172 
173     /* The event passed our filters, so add it to the output set */
174     icalcomponent_remove_component(comp, h); /* Move from comp... */
175     icalcomponent_add_component(newset, h); /* ... to out */
176   }
177 
178   /* Put new vcalendar in output file, flush to disk and close file */
179   icalfileset_add_component(out, newset);
180   icalfileset_free(out);
181 
182   /* Clean up */
183   icalcomponent_free(comp);
184 
185   return 0;
186 }
187