]> code.citadel.org Git - citadel.git/blob - webcit/calendar.c
* When displaying meeting invitations, only show date/time for VEVENT
[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 <time.h>
27 #include "webcit.h"
28 #include "webserver.h"
29
30 #ifndef HAVE_ICAL_H
31
32 /*
33  * Handler stubs for builds with no calendar library available
34  */
35 void cal_process_attachment(char *part_source) {
36
37         wprintf("<I>This message contains calendaring/scheduling information,"
38                 " but support for calendars is not available on this "
39                 "particular system.  Please ask your system administrator to "
40                 "install a new version of the Citadel web service with "
41                 "calendaring enabled.</I><BR>\n"
42         );
43
44 }
45
46 void display_calendar(long msgnum) {
47         wprintf("<i>"
48                 "Cannot display calendar item.  You are seeing this error "
49                 "because your WebCit service has not been installed with "
50                 "calendar support.  Please contact your system administrator."
51                 "</i><br>\n");
52 }
53
54 void display_task(long msgnum) {
55         wprintf("<i>"
56                 "Cannot display to-do item.  You are seeing this error "
57                 "because your WebCit service has not been installed with "
58                 "calendar support.  Please contact your system administrator."
59                 "</i><br>\n");
60 }
61
62 #else /* HAVE_ICAL_H */
63
64
65 /******   End of handler stubs.  Everything below this line is real.   ******/
66
67
68
69
70 /*
71  * Process a calendar object
72  * ...at this point it's already been deserialized by cal_process_attachment()
73  */
74 void cal_process_object(icalcomponent *cal,
75                         int recursion_level
76 ) {
77         icalcomponent *c;
78         icalproperty *method = NULL;
79         icalproperty_method the_method;
80         icalproperty *p;
81         struct icaltimetype t;
82         time_t tt;
83         char buf[SIZ];
84
85         /* Leading HTML for the display of this object */
86         if (recursion_level == 0) {
87                 wprintf("<CENTER><TABLE border=0 cellpadding=5>\n");
88         }
89
90         /* Look for a method */
91         method = icalcomponent_get_first_property(cal, ICAL_METHOD_PROPERTY);
92
93         /* See what we need to do with this */
94         if (method != NULL) {
95                 the_method = icalproperty_get_method(method);
96                 switch(the_method) {
97                     case ICAL_METHOD_REQUEST:
98                         wprintf("<TR><TD COLSPAN=2>\n"
99                                 "<IMG ALIGN=CENTER "
100                                 "SRC=\"/static/vcalendar.gif\">"
101                                 "&nbsp;&nbsp;"  
102                                 "<B>Meeting invitation</B>
103                                 </TD></TR>\n"
104                         );
105                         break;
106                     default:
107                         wprintf("<TR><TD COLSPAN=2>"
108                                 "I don't know what to do with this.</TD></TR>"
109                                 "\n");
110                         break;
111                 }
112         }
113
114         p = icalcomponent_get_first_property(cal, ICAL_SUMMARY_PROPERTY);
115         if (p != NULL) {
116                 wprintf("<TR><TD><B>Summary:</B></TD><TD>");
117                 escputs((char *)icalproperty_get_comment(p));
118                 wprintf("</TD></TR>\n");
119         }
120
121         p = icalcomponent_get_first_property(cal, ICAL_LOCATION_PROPERTY);
122         if (p != NULL) {
123                 wprintf("<TR><TD><B>Location:</B></TD><TD>");
124                 escputs((char *)icalproperty_get_comment(p));
125                 wprintf("</TD></TR>\n");
126         }
127
128         /*
129          * Only show start/end times if we're actually looking at the VEVENT
130          * component.  Otherwise it shows bogus dates for things like timezone.
131          */
132         if (icalcomponent_isa(cal) == ICAL_VEVENT_COMPONENT) {
133
134                 p = icalcomponent_get_first_property(cal,
135                                                 ICAL_DTSTART_PROPERTY);
136                 if (p != NULL) {
137                         t = icalproperty_get_dtstart(p);
138                         tt = icaltime_as_timet(t);
139                         fmt_date(buf, tt);
140                         wprintf("<TR><TD><B>Starting date/time:</B></TD><TD>"
141                                 "%s</TD></TR>", buf
142                         );
143                 }
144         
145                 p = icalcomponent_get_first_property(cal, ICAL_DTEND_PROPERTY);
146                 if (p != NULL) {
147                         t = icalproperty_get_dtend(p);
148                         tt = icaltime_as_timet(t);
149                         fmt_date(buf, tt);
150                         wprintf("<TR><TD><B>Ending date/time:</B></TD><TD>"
151                                 "%s</TD></TR>", buf
152                         );
153                 }
154
155         }
156
157         p = icalcomponent_get_first_property(cal, ICAL_DESCRIPTION_PROPERTY);
158         if (p != NULL) {
159                 wprintf("<TR><TD><B>Description:</B></TD><TD>");
160                 escputs((char *)icalproperty_get_comment(p));
161                 wprintf("</TD></TR>\n");
162         }
163
164         /* If the component has subcomponents, recurse through them. */
165         for (c = icalcomponent_get_first_component(cal, ICAL_ANY_COMPONENT);
166             (c != 0);
167             c = icalcomponent_get_next_component(cal, ICAL_ANY_COMPONENT)) {
168                 /* Recursively process subcomponent */
169                 cal_process_object(c, recursion_level+1);
170         }
171
172         /* Trailing HTML for the display of this object */
173         if (recursion_level == 0) {
174                 wprintf("<TR><TD COLSPAN=2>"
175                         "<FORM METHOD=\"GET\" "
176                         "ACTION=\"/respond_to_request\">\n"
177                         "<INPUT TYPE=\"submit\" NAME=\"sc\" "
178                                 "VALUE=\"Accept\">"
179                         "&nbsp;&nbsp;"
180                         "<INPUT TYPE=\"submit\" NAME=\"sc\" "
181                                 "VALUE=\"Decline\">"
182                         "</FORM>"
183                         "</TD></TR></TABLE></CENTER>\n"
184                 );
185         }
186 }
187
188
189 /*
190  * Deserialize a calendar object in a message so it can be processed.
191  * (This is the main entry point for these things)
192  */
193 void cal_process_attachment(char *part_source) {
194         icalcomponent *cal;
195
196         cal = icalcomponent_new_from_string(part_source);
197
198         if (cal == NULL) {
199                 wprintf("Error parsing calendar object: %s<BR>\n",
200                         icalerror_strerror(icalerrno));
201                 return;
202         }
203
204         cal_process_object(cal, 0);
205
206         /* Free the memory we obtained from libical's constructor */
207         icalcomponent_free(cal);
208 }
209
210 /*****************************************************************************/
211
212
213
214 /*
215  * Display handlers for message reading
216  */
217
218
219
220 /*
221  * If we're reading calendar items, just store them for now.  We have to
222  * sort and re-output them later when we draw the calendar.
223  */
224 void display_individual_cal(icalcomponent *cal, long msgnum) {
225
226         WC->num_cal += 1;
227
228         WC->disp_cal = realloc(WC->disp_cal,
229                         (sizeof(icalcomponent *) * WC->num_cal) );
230         WC->disp_cal[WC->num_cal - 1] = icalcomponent_new_clone(cal);
231
232         WC->cal_msgnum = realloc(WC->cal_msgnum,
233                         (sizeof(long) * WC->num_cal) );
234         WC->cal_msgnum[WC->num_cal - 1] = msgnum;
235 }
236
237
238
239 /*
240  * Display a task in the task list
241  */
242 void display_individual_task(icalcomponent *vtodo, long msgnum) {
243         icalproperty *p;
244
245         p = icalcomponent_get_first_property(vtodo, ICAL_SUMMARY_PROPERTY);
246         wprintf("<LI><A HREF=\"/display_edit_task?msgnum=%ld\">", msgnum);
247         if (p != NULL) {
248                 escputs((char *)icalproperty_get_comment(p));
249         }
250         wprintf("</A>\n");
251 }
252
253
254 /*
255  * Display a task by itself (for editing)
256  */
257 void display_edit_individual_task(icalcomponent *supplied_vtodo, long msgnum) {
258         icalcomponent *vtodo;
259         icalproperty *p;
260         struct icaltimetype t;
261         time_t now;
262         int created_new_vtodo = 0;
263
264         now = time(NULL);
265
266         if (supplied_vtodo != NULL) {
267                 vtodo = supplied_vtodo;
268         }
269         else {
270                 vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT);
271                 created_new_vtodo = 1;
272         }
273
274         output_headers(3);
275         wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=007700><TR><TD>"
276                 "<FONT SIZE=+1 COLOR=\"FFFFFF\""
277                 "<B>Edit task</B>"
278                 "</FONT></TD></TR></TABLE><BR>\n"
279         );
280
281         wprintf("<FORM METHOD=\"POST\" ACTION=\"/save_task\">\n");
282         wprintf("<INPUT TYPE=\"hidden\" NAME=\"msgnum\" VALUE=\"%ld\">\n",
283                 msgnum);
284
285         wprintf("Summary: "
286                 "<INPUT TYPE=\"text\" NAME=\"summary\" "
287                 "MAXLENGTH=\"64\" SIZE=\"64\" VALUE=\"");
288         p = icalcomponent_get_first_property(vtodo, ICAL_SUMMARY_PROPERTY);
289         if (p != NULL) {
290                 escputs((char *)icalproperty_get_comment(p));
291         }
292         wprintf("\"><BR>\n");
293
294         wprintf("Start date: ");
295         p = icalcomponent_get_first_property(vtodo, ICAL_DTSTART_PROPERTY);
296         if (p != NULL) {
297                 t = icalproperty_get_dtstart(p);
298         }
299         else {
300                 t = icaltime_from_timet(now, 0);
301         }
302         display_icaltimetype_as_webform(&t, "dtstart");
303         wprintf("<BR>\n");
304
305         wprintf("Due date: ");
306         p = icalcomponent_get_first_property(vtodo, ICAL_DUE_PROPERTY);
307         if (p != NULL) {
308                 t = icalproperty_get_due(p);
309         }
310         else {
311                 t = icaltime_from_timet(now, 0);
312         }
313         display_icaltimetype_as_webform(&t, "due");
314         wprintf("<BR>\n");
315
316         wprintf("<CENTER><TEXTAREA NAME=\"description\" wrap=soft "
317                 "ROWS=10 COLS=80 WIDTH=80>\n"
318         );
319         p = icalcomponent_get_first_property(vtodo, ICAL_DESCRIPTION_PROPERTY);
320         if (p != NULL) {
321                 escputs((char *)icalproperty_get_comment(p));
322         }
323         wprintf("</TEXTAREA><BR>\n");
324
325         wprintf("<INPUT TYPE=\"submit\" NAME=\"sc\" VALUE=\"Save\">"
326                 "&nbsp;&nbsp;"
327                 "<INPUT TYPE=\"submit\" NAME=\"sc\" VALUE=\"Delete\">\n"
328                 "&nbsp;&nbsp;"
329                 "<INPUT TYPE=\"submit\" NAME=\"sc\" VALUE=\"Cancel\">\n"
330                 "</CENTER>\n"
331         );
332
333         wprintf("</FORM>\n");
334
335         wDumpContent(1);
336
337         if (created_new_vtodo) {
338                 icalcomponent_free(vtodo);
339         }
340 }
341
342 /*
343  * Save an edited task
344  */
345 void save_individual_task(icalcomponent *supplied_vtodo, long msgnum) {
346         char buf[SIZ];
347         int delete_existing = 0;
348         icalproperty *prop;
349         icalcomponent *vtodo;
350         int created_new_vtodo = 0;
351
352         if (supplied_vtodo != NULL) {
353                 vtodo = supplied_vtodo;
354         }
355         else {
356                 vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT);
357                 created_new_vtodo = 1;
358         }
359
360         if (!strcasecmp(bstr("sc"), "Save")) {
361
362                 /* Replace values in the component with ones from the form */
363
364                 while (prop = icalcomponent_get_first_property(vtodo,
365                       ICAL_SUMMARY_PROPERTY), prop != NULL) {
366                         icalcomponent_remove_property(vtodo, prop);
367                 }
368                 icalcomponent_add_property(vtodo,
369                         icalproperty_new_summary(bstr("summary")));
370                 
371                 while (prop = icalcomponent_get_first_property(vtodo,
372                       ICAL_DESCRIPTION_PROPERTY), prop != NULL) {
373                         icalcomponent_remove_property(vtodo, prop);
374                 }
375                 icalcomponent_add_property(vtodo,
376                         icalproperty_new_description(bstr("description")));
377         
378                 while (prop = icalcomponent_get_first_property(vtodo,
379                       ICAL_DTSTART_PROPERTY), prop != NULL) {
380                         icalcomponent_remove_property(vtodo, prop);
381                 }
382                 icalcomponent_add_property(vtodo,
383                         icalproperty_new_dtstart(
384                                 icaltime_from_webform("dtstart")
385                         )
386                 );
387         
388                 while (prop = icalcomponent_get_first_property(vtodo,
389                       ICAL_DUE_PROPERTY), prop != NULL) {
390                         icalcomponent_remove_property(vtodo, prop);
391                 }
392                 icalcomponent_add_property(vtodo,
393                         icalproperty_new_due(
394                                 icaltime_from_webform("due")
395                         )
396                 );
397         
398                 /* Serialize it and save it to the message base */
399                 serv_puts("ENT0 1|||4");
400                 serv_gets(buf);
401                 if (buf[0] == '4') {
402                         serv_puts("Content-type: text/calendar");
403                         serv_puts("");
404                         serv_puts(icalcomponent_as_ical_string(vtodo));
405                         serv_puts("000");
406                         delete_existing = 1;
407                 }
408         }
409
410         /*
411          * If the user clicked 'Delete' then delete it, period.
412          */
413         if (!strcasecmp(bstr("sc"), "Delete")) {
414                 delete_existing = 1;
415         }
416
417         if ( (delete_existing) && (msgnum > 0L) ) {
418                 serv_printf("DELE %ld", atol(bstr("msgnum")));
419                 serv_gets(buf);
420         }
421
422         if (created_new_vtodo) {
423                 icalcomponent_free(vtodo);
424         }
425
426         /* Go back to the task list */
427         readloop("readfwd");
428 }
429
430
431
432 /*
433  * Code common to all display handlers.  Given a message number and a MIME
434  * type, we load the message and hunt for that MIME type.  If found, we load
435  * the relevant part, deserialize it into a libical component, filter it for
436  * the requested object type, and feed it to the specified handler.
437  */
438 void display_using_handler(long msgnum,
439                         char *mimetype,
440                         icalcomponent_kind which_kind,
441                         void (*callback)(icalcomponent *, long)
442         ) {
443         char buf[SIZ];
444         char mime_partnum[SIZ];
445         char mime_filename[SIZ];
446         char mime_content_type[SIZ];
447         char mime_disposition[SIZ];
448         int mime_length;
449         char relevant_partnum[SIZ];
450         char *relevant_source = NULL;
451         icalcomponent *cal, *c;
452
453         sprintf(buf, "MSG0 %ld|1", msgnum);     /* ask for headers only */
454         serv_puts(buf);
455         serv_gets(buf);
456         if (buf[0] != '1') return;
457
458         while (serv_gets(buf), strcmp(buf, "000")) {
459                 if (!strncasecmp(buf, "part=", 5)) {
460                         extract(mime_filename, &buf[5], 1);
461                         extract(mime_partnum, &buf[5], 2);
462                         extract(mime_disposition, &buf[5], 3);
463                         extract(mime_content_type, &buf[5], 4);
464                         mime_length = extract_int(&buf[5], 5);
465
466                         if (!strcasecmp(mime_content_type, "text/calendar")) {
467                                 strcpy(relevant_partnum, mime_partnum);
468                         }
469
470                 }
471         }
472
473         if (strlen(relevant_partnum) > 0) {
474                 relevant_source = load_mimepart(msgnum, relevant_partnum);
475                 if (relevant_source != NULL) {
476
477                         cal = icalcomponent_new_from_string(relevant_source);
478                         if (cal != NULL) {
479
480                                 /* Simple components of desired type */
481                                 if (icalcomponent_isa(cal) == which_kind) {
482                                         callback(cal, msgnum);
483                                 }
484
485                                 /* Subcomponents of desired type */
486                                 for (c = icalcomponent_get_first_component(cal,
487                                     which_kind);
488                                     (c != 0);
489                                     c = icalcomponent_get_next_component(cal,
490                                     which_kind)) {
491                                         callback(c, msgnum);
492                                 }
493                                 icalcomponent_free(cal);
494                         }
495                         free(relevant_source);
496                 }
497         }
498
499 }
500
501 void display_calendar(long msgnum) {
502         display_using_handler(msgnum, "text/calendar",
503                                 ICAL_VEVENT_COMPONENT,
504                                 display_individual_cal);
505 }
506
507 void display_task(long msgnum) {
508         display_using_handler(msgnum, "text/calendar",
509                                 ICAL_VTODO_COMPONENT,
510                                 display_individual_task);
511 }
512
513 void display_edit_task(void) {
514         long msgnum = 0L;
515
516         msgnum = atol(bstr("msgnum"));
517         if (msgnum > 0L) {
518                 /* existing task */
519                 display_using_handler(msgnum, "text/calendar",
520                                 ICAL_VTODO_COMPONENT,
521                                 display_edit_individual_task);
522         }
523         else {
524                 /* new task */
525                 display_edit_individual_task(NULL, 0L);
526         }
527 }
528
529 void save_task(void) {
530         long msgnum = 0L;
531
532         msgnum = atol(bstr("msgnum"));
533         if (msgnum > 0L) {
534                 display_using_handler(msgnum, "text/calendar",
535                                 ICAL_VTODO_COMPONENT,
536                                 save_individual_task);
537         }
538         else {
539                 save_individual_task(NULL, 0L);
540         }
541 }
542
543 void display_edit_event(void) {
544         long msgnum = 0L;
545
546         msgnum = atol(bstr("msgnum"));
547         if (msgnum > 0L) {
548                 /* existing event */
549                 display_using_handler(msgnum, "text/calendar",
550                                 ICAL_VEVENT_COMPONENT,
551                                 display_edit_individual_event);
552         }
553         else {
554                 /* new event */
555                 display_edit_individual_event(NULL, 0L);
556         }
557 }
558
559 void save_event(void) {
560         long msgnum = 0L;
561
562         msgnum = atol(bstr("msgnum"));
563
564         if (msgnum > 0L) {
565                 display_using_handler(msgnum, "text/calendar",
566                                 ICAL_VEVENT_COMPONENT,
567                                 save_individual_event);
568         }
569         else {
570                 save_individual_event(NULL, 0L);
571         }
572 }
573
574 #endif /* HAVE_ICAL_H */