9bd6c062bf54a02cc6be1500747e552dacd3e5cf
[citadel.git] / webcit / calendar.c
1 /*
2  * $Id$
3  *
4  * Functions which handle calendar objects and their processing/display.
5  *
6  */
7
8 #include <ctype.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <stdio.h>
12 #include <fcntl.h>
13 #include <signal.h>
14 #include <sys/types.h>
15 #include <sys/wait.h>
16 #include <sys/socket.h>
17 #include <limits.h>
18 #include <netinet/in.h>
19 #include <netdb.h>
20 #include <string.h>
21 #include <pwd.h>
22 #include <errno.h>
23 #include <stdarg.h>
24 #include <pthread.h>
25 #include <signal.h>
26 #include "webcit.h"
27 #include "webserver.h"
28
29 #ifdef HAVE_ICAL_H
30 #include <ical.h>
31 #endif
32
33
34 #ifndef HAVE_ICAL_H
35
36 /*
37  * Handler stubs for builds with no calendar library available
38  */
39 void cal_process_attachment(char *part_source) {
40
41         wprintf("<I>This message contains calendaring/scheduling information,"
42                 " but support for calendars is not available on this "
43                 "particular system.  Please ask your system administrator to "
44                 "install a new version of the Citadel web service with "
45                 "calendaring enabled.</I><BR>\n"
46         );
47
48 }
49
50 void display_calendar(long msgnum) {
51         wprintf("<i>Cannot display calendar item</i><br>\n");
52 }
53
54 void display_task(long msgnum) {
55         wprintf("<i>Cannot display item from task list</i><br>\n");
56 }
57
58 #else /* HAVE_ICAL_H */
59
60
61 /******   End of handler stubs.  Everything below this line is real.   ******/
62
63
64 /*
65  * Process a single calendar component.
66  * It won't be a compound component at this point because those have
67  * already been broken down by cal_process_object().
68  */
69 void cal_process_subcomponent(icalcomponent *cal) {
70         wprintf("cal_process_subcomponent() called<BR>\n");
71         wprintf("cal_process_subcomponent() exiting<BR>\n");
72 }
73
74
75
76
77
78 /*
79  * Process a calendar object
80  * ...at this point it's already been deserialized by cal_process_attachment()
81  */
82 void cal_process_object(icalcomponent *cal) {
83         icalcomponent *c;
84         int num_subcomponents = 0;
85
86         wprintf("cal_process_object() called<BR>\n");
87
88         /* Iterate through all subcomponents */
89         wprintf("Iterating through all sub-components<BR>\n");
90         for (c = icalcomponent_get_first_component(cal, ICAL_ANY_COMPONENT);
91             (c != 0);
92             c = icalcomponent_get_next_component(cal, ICAL_ANY_COMPONENT)) {
93                 cal_process_subcomponent(c);
94                 ++num_subcomponents;
95         }
96
97         /* Iterate through all subcomponents */
98         wprintf("Iterating through VEVENTs<BR>\n");
99         for (c = icalcomponent_get_first_component(cal, ICAL_VEVENT_COMPONENT);
100             (c != 0);
101             c = icalcomponent_get_next_component(cal, ICAL_VEVENT_COMPONENT)) {
102                 cal_process_subcomponent(c);
103                 --num_subcomponents;
104         }
105
106         /* Iterate through all subcomponents */
107         wprintf("Iterating through VTODOs<BR>\n");
108         for (c = icalcomponent_get_first_component(cal, ICAL_VTODO_COMPONENT);
109             (c != 0);
110             c = icalcomponent_get_next_component(cal, ICAL_VTODO_COMPONENT)) {
111                 cal_process_subcomponent(c);
112                 --num_subcomponents;
113         }
114
115         /* Iterate through all subcomponents */
116         wprintf("Iterating through VJOURNALs<BR>\n");
117         for (c = icalcomponent_get_first_component(cal, ICAL_VJOURNAL_COMPONENT);
118             (c != 0);
119             c = icalcomponent_get_next_component(cal, ICAL_VJOURNAL_COMPONENT)) {
120                 cal_process_subcomponent(c);
121                 --num_subcomponents;
122         }
123
124         /* Iterate through all subcomponents */
125         wprintf("Iterating through VCALENDARs<BR>\n");
126         for (c = icalcomponent_get_first_component(cal, ICAL_VCALENDAR_COMPONENT);
127             (c != 0);
128             c = icalcomponent_get_next_component(cal, ICAL_VCALENDAR_COMPONENT)) {
129                 cal_process_subcomponent(c);
130                 --num_subcomponents;
131         }
132
133         /* Iterate through all subcomponents */
134         wprintf("Iterating through VFREEBUSYs<BR>\n");
135         for (c = icalcomponent_get_first_component(cal, ICAL_VFREEBUSY_COMPONENT);
136             (c != 0);
137             c = icalcomponent_get_next_component(cal, ICAL_VFREEBUSY_COMPONENT)) {
138                 cal_process_subcomponent(c);
139                 --num_subcomponents;
140         }
141
142         /* Iterate through all subcomponents */
143         wprintf("Iterating through VALARMs<BR>\n");
144         for (c = icalcomponent_get_first_component(cal, ICAL_VALARM_COMPONENT);
145             (c != 0);
146             c = icalcomponent_get_next_component(cal, ICAL_VALARM_COMPONENT)) {
147                 cal_process_subcomponent(c);
148                 --num_subcomponents;
149         }
150
151         /* Iterate through all subcomponents */
152         wprintf("Iterating through VTIMEZONEs<BR>\n");
153         for (c = icalcomponent_get_first_component(cal, ICAL_VTIMEZONE_COMPONENT);
154             (c != 0);
155             c = icalcomponent_get_next_component(cal, ICAL_VTIMEZONE_COMPONENT)) {
156                 cal_process_subcomponent(c);
157                 --num_subcomponents;
158         }
159
160         /* Iterate through all subcomponents */
161         wprintf("Iterating through VSCHEDULEs<BR>\n");
162         for (c = icalcomponent_get_first_component(cal, ICAL_VSCHEDULE_COMPONENT);
163             (c != 0);
164             c = icalcomponent_get_next_component(cal, ICAL_VSCHEDULE_COMPONENT)) {
165                 cal_process_subcomponent(c);
166                 --num_subcomponents;
167         }
168
169         /* Iterate through all subcomponents */
170         wprintf("Iterating through VQUERYs<BR>\n");
171         for (c = icalcomponent_get_first_component(cal, ICAL_VQUERY_COMPONENT);
172             (c != 0);
173             c = icalcomponent_get_next_component(cal, ICAL_VQUERY_COMPONENT)) {
174                 cal_process_subcomponent(c);
175                 --num_subcomponents;
176         }
177
178         /* Iterate through all subcomponents */
179         wprintf("Iterating through VCARs<BR>\n");
180         for (c = icalcomponent_get_first_component(cal, ICAL_VCAR_COMPONENT);
181             (c != 0);
182             c = icalcomponent_get_next_component(cal, ICAL_VCAR_COMPONENT)) {
183                 cal_process_subcomponent(c);
184                 --num_subcomponents;
185         }
186
187         /* Iterate through all subcomponents */
188         wprintf("Iterating through VCOMMANDs<BR>\n");
189         for (c = icalcomponent_get_first_component(cal, ICAL_VCOMMAND_COMPONENT);
190             (c != 0);
191             c = icalcomponent_get_next_component(cal, ICAL_VCOMMAND_COMPONENT)) {
192                 cal_process_subcomponent(c);
193                 --num_subcomponents;
194         }
195
196         if (num_subcomponents != 0) {
197                 wprintf("Warning: %d subcomponents unhandled<BR>\n",
198                         num_subcomponents);
199         }
200
201         wprintf("cal_process_object() exiting<BR>\n");
202 }
203
204
205 /*
206  * Deserialize a calendar object in a message so it can be processed.
207  * (This is the main entry point for these things)
208  */
209 void cal_process_attachment(char *part_source) {
210         icalcomponent *cal;
211
212         wprintf("Processing calendar attachment<BR>\n");
213         cal = icalcomponent_new_from_string(part_source);
214
215         if (cal == NULL) {
216                 wprintf("Error parsing calendar object: %s<BR>\n",
217                         icalerror_strerror(icalerrno));
218                 return;
219         }
220
221         cal_process_object(cal);
222
223         /* Free the memory we obtained from libical's constructor */
224         icalcomponent_free(cal);
225 }
226
227 /*****************************************************************************/
228
229
230
231
232
233 /*
234  * Display handlers for message reading
235  */
236 void display_individual_cal(icalcomponent *cal, long msgnum) {
237         wprintf("display_individual_cal() called<BR>\n");
238 }
239
240
241
242 /*
243  * Display a task in the task list
244  */
245 void display_individual_task(icalcomponent *vtodo, long msgnum) {
246         icalproperty *p;
247
248         p = icalcomponent_get_first_property(vtodo, ICAL_SUMMARY_PROPERTY);
249         wprintf("<LI><A HREF=\"/display_edit_task?msgnum=%ld\">", msgnum);
250         if (p != NULL) escputs((char *)icalproperty_get_comment(p));
251         wprintf("</A>\n");
252         icalproperty_free(p);
253 }
254
255
256 /*
257  * Display a task by itself (for editing)
258  */
259 void display_edit_individual_task(icalcomponent *vtodo, long msgnum) {
260         icalproperty *p;
261
262         output_headers(3);
263         wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=007700><TR><TD>"
264                 "<FONT SIZE=+1 COLOR=\"FFFFFF\""
265                 "<B>Edit task</B>"
266                 "</FONT></TD></TR></TABLE><BR>\n"
267         );
268
269         wprintf("<FORM METHOD=\"POST\" ACTION=\"/save_task\">\n");
270         wprintf("<INPUT TYPE=\"hidden\" NAME=\"msgnum\" VALUE=\"%ld\">\n",
271                 msgnum);
272
273         wprintf("Summary: "
274                 "<INPUT TYPE=\"text\" NAME=\"summary\" "
275                 "MAXLENGTH=\"64\" SIZE=\"64\" VALUE=\"");
276         p = icalcomponent_get_first_property(vtodo, ICAL_SUMMARY_PROPERTY);
277         if (p != NULL) escputs((char *)icalproperty_get_comment(p));
278         icalproperty_free(p);
279         wprintf("\"><BR>\n");
280
281         wprintf("Start date: FIXME <BR>\n");
282
283         wprintf("Due date: FIXME <BR>\n");
284
285         wprintf("<CENTER><TEXTAREA NAME=\"msgtext\" wrap=soft "
286                 "ROWS=10 COLS=80 WIDTH=80>\n"
287         );
288         p = icalcomponent_get_first_property(vtodo, ICAL_DESCRIPTION_PROPERTY);
289         if (p != NULL) escputs((char *)icalproperty_get_comment(p));
290         icalproperty_free(p);
291         wprintf("</TEXTAREA><BR>\n");
292
293         wprintf("<INPUT TYPE=\"submit\" NAME=\"sc\" VALUE=\"Save\">"
294                 "&nbsp;&nbsp;"
295                 "<INPUT TYPE=\"submit\" NAME=\"sc\" VALUE=\"Delete\">\n"
296                 "&nbsp;&nbsp;"
297                 "<INPUT TYPE=\"submit\" NAME=\"sc\" VALUE=\"Cancel\">\n"
298                 "</CENTER>\n"
299         );
300
301         wprintf("</FORM>\n");
302
303         wDumpContent(1);
304 }
305
306 /*
307  * Save an edited task
308  */
309 void save_individual_task(icalcomponent *vtodo, long msgnum) {
310         char buf[SIZ];
311         int delete_existing = 0;
312
313         if (!strcasecmp(bstr("sc"), "Save")) {
314
315                 /* Replace values in the component with ones from the form */
316                 /* FIXME ... do this */
317
318                 /* Serialize it and save it to the message base */
319                 serv_puts("ENT0 1|||4");
320                 serv_gets(buf);
321                 if (buf[0] == '4') {
322                         serv_puts("Content-type: text/calendar");
323                         serv_puts("");
324                         serv_puts(icalcomponent_as_ical_string(vtodo));
325                         serv_puts("000");
326                         /* delete_existing = 1; */
327                 }
328         }
329
330         /*
331          * If the user clicked 'Delete' then delete it, period.
332          */
333         if (!strcasecmp(bstr("sc"), "Delete")) {
334                 delete_existing = 1;
335         }
336
337         if (delete_existing) {
338                 serv_printf("DELE %ld", atol(bstr("msgnum")));
339                 serv_gets(buf);
340         }
341
342         /* Go back to the task list */
343         readloop("readfwd");
344 }
345
346
347
348 /*
349  * Code common to all display handlers.  Given a message number and a MIME
350  * type, we load the message and hunt for that MIME type.  If found, we load
351  * the relevant part, deserialize it into a libical component, filter it for
352  * the requested object type, and feed it to the specified handler.
353  */
354 void display_using_handler(long msgnum,
355                         char *mimetype,
356                         icalcomponent_kind which_kind,
357                         void (*callback)(icalcomponent *, long)
358         ) {
359         char buf[SIZ];
360         char mime_partnum[SIZ];
361         char mime_filename[SIZ];
362         char mime_content_type[SIZ];
363         char mime_disposition[SIZ];
364         int mime_length;
365         char relevant_partnum[SIZ];
366         char *relevant_source = NULL;
367         icalcomponent *cal, *c;
368
369         sprintf(buf, "MSG0 %ld|1", msgnum);     /* ask for headers only */
370         serv_puts(buf);
371         serv_gets(buf);
372         if (buf[0] != '1') return;
373
374         while (serv_gets(buf), strcmp(buf, "000")) {
375                 if (!strncasecmp(buf, "part=", 5)) {
376                         extract(mime_filename, &buf[5], 1);
377                         extract(mime_partnum, &buf[5], 2);
378                         extract(mime_disposition, &buf[5], 3);
379                         extract(mime_content_type, &buf[5], 4);
380                         mime_length = extract_int(&buf[5], 5);
381
382                         if (!strcasecmp(mime_content_type, "text/calendar")) {
383                                 strcpy(relevant_partnum, mime_partnum);
384                         }
385
386                 }
387         }
388
389         if (strlen(relevant_partnum) > 0) {
390                 relevant_source = load_mimepart(msgnum, relevant_partnum);
391                 if (relevant_source != NULL) {
392
393                         /* Display the task */
394                         cal = icalcomponent_new_from_string(relevant_source);
395                         if (cal != NULL) {
396                                 for (c = icalcomponent_get_first_component(cal,
397                                     which_kind);
398                                     (c != 0);
399                                     c = icalcomponent_get_next_component(cal,
400                                     which_kind)) {
401                                         callback(c, msgnum);
402                                 }
403                                 icalcomponent_free(cal);
404                         }
405                         free(relevant_source);
406                 }
407         }
408
409 }
410
411 void display_calendar(long msgnum) {
412         display_using_handler(msgnum, "text/calendar",
413                                 ICAL_ANY_COMPONENT,
414                                 display_individual_cal);
415 }
416
417 void display_task(long msgnum) {
418         display_using_handler(msgnum, "text/calendar",
419                                 ICAL_VTODO_COMPONENT,
420                                 display_individual_task);
421 }
422
423 void display_edit_task(void) {
424         long msgnum = 0L;
425
426         msgnum = atol(bstr("msgnum"));
427         display_using_handler(msgnum, "text/calendar",
428                                 ICAL_VTODO_COMPONENT,
429                                 display_edit_individual_task);
430 }
431
432 void save_task(void) {
433         long msgnum = 0L;
434
435         msgnum = atol(bstr("msgnum"));
436         display_using_handler(msgnum, "text/calendar",
437                                 ICAL_VTODO_COMPONENT,
438                                 save_individual_task);
439 }
440
441 #endif /* HAVE_ICAL_H */