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