]> code.citadel.org Git - citadel.git/blob - webcit/calendar.c
* Add "Tentative" accept button
[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 = ICAL_METHOD_NONE;
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                     case ICAL_METHOD_PUBLISH:
109                         wprintf("<TR><TD COLSPAN=2>\n"
110                                 "<IMG ALIGN=CENTER "
111                                 "SRC=\"/static/vcalendar.gif\">"
112                                 "&nbsp;&nbsp;"  
113                                 "<B>Published event</B>
114                                 </TD></TR>\n"
115                         );
116                         break;
117                     default:
118                         wprintf("<TR><TD COLSPAN=2>"
119                                 "I don't know what to do with this.</TD></TR>"
120                                 "\n");
121                         break;
122                 }
123         }
124
125         p = icalcomponent_get_first_property(cal, ICAL_SUMMARY_PROPERTY);
126         if (p != NULL) {
127                 wprintf("<TR><TD><B>Summary:</B></TD><TD>");
128                 escputs((char *)icalproperty_get_comment(p));
129                 wprintf("</TD></TR>\n");
130         }
131
132         p = icalcomponent_get_first_property(cal, ICAL_LOCATION_PROPERTY);
133         if (p != NULL) {
134                 wprintf("<TR><TD><B>Location:</B></TD><TD>");
135                 escputs((char *)icalproperty_get_comment(p));
136                 wprintf("</TD></TR>\n");
137         }
138
139         /*
140          * Only show start/end times if we're actually looking at the VEVENT
141          * component.  Otherwise it shows bogus dates for things like timezone.
142          */
143         if (icalcomponent_isa(cal) == ICAL_VEVENT_COMPONENT) {
144
145                 p = icalcomponent_get_first_property(cal,
146                                                 ICAL_DTSTART_PROPERTY);
147                 if (p != NULL) {
148                         t = icalproperty_get_dtstart(p);
149                         tt = icaltime_as_timet(t);
150                         fmt_date(buf, tt);
151                         wprintf("<TR><TD><B>Starting date/time:</B></TD><TD>"
152                                 "%s</TD></TR>", buf
153                         );
154                 }
155         
156                 p = icalcomponent_get_first_property(cal, ICAL_DTEND_PROPERTY);
157                 if (p != NULL) {
158                         t = icalproperty_get_dtend(p);
159                         tt = icaltime_as_timet(t);
160                         fmt_date(buf, tt);
161                         wprintf("<TR><TD><B>Ending date/time:</B></TD><TD>"
162                                 "%s</TD></TR>", buf
163                         );
164                 }
165
166         }
167
168         p = icalcomponent_get_first_property(cal, ICAL_DESCRIPTION_PROPERTY);
169         if (p != NULL) {
170                 wprintf("<TR><TD><B>Description:</B></TD><TD>");
171                 escputs((char *)icalproperty_get_comment(p));
172                 wprintf("</TD></TR>\n");
173         }
174
175         /* If the component has subcomponents, recurse through them. */
176         for (c = icalcomponent_get_first_component(cal, ICAL_ANY_COMPONENT);
177             (c != 0);
178             c = icalcomponent_get_next_component(cal, ICAL_ANY_COMPONENT)) {
179                 /* Recursively process subcomponent */
180                 cal_process_object(c, recursion_level+1, msgnum, cal_partnum);
181         }
182
183         /* If this is a REQUEST, display the Accept/Decline buttons */
184         if (the_method == ICAL_METHOD_REQUEST) {
185                 wprintf("<TR><TD COLSPAN=2>"
186                         "<FORM METHOD=\"GET\" "
187                         "ACTION=\"/respond_to_request\">\n"
188                         "<INPUT TYPE=\"submit\" NAME=\"sc\" "
189                                 "VALUE=\"Accept\">\n"
190                         "&nbsp;&nbsp;"
191                         "<INPUT TYPE=\"submit\" NAME=\"sc\" "
192                                 "VALUE=\"Tentative\">\n"
193                         "&nbsp;&nbsp;"
194                         "<INPUT TYPE=\"submit\" NAME=\"sc\" "
195                                 "VALUE=\"Decline\">\n"
196                         "<INPUT TYPE=\"hidden\" NAME=\"msgnum\" "
197                                 "VALUE=\"%ld\">"
198                         "<INPUT TYPE=\"hidden\" NAME=\"cal_partnum\" "
199                                 "VALUE=\"%s\">"
200                         "</FORM>"
201                         "</TD></TR>\n",
202                         msgnum, cal_partnum
203                 );
204         }
205
206         /* Trailing HTML for the display of this object */
207         if (recursion_level == 0) {
208                 wprintf("</TD></TR></TABLE></CENTER>\n");
209         }
210 }
211
212
213 /*
214  * Back end for cal_add() -- this writes it to the message base
215  */
216 void pencil_it_in(icalcomponent *cal) {
217         char hold_rm[SIZ];
218         char buf[SIZ];
219         char *serialized_event;
220
221         /* Save the name of the room we're in */
222         strcpy(hold_rm, WC->wc_roomname);
223
224         /* Go find the user's calendar */
225         serv_printf("GOTO %s", CALENDAR_ROOM_NAME);
226         serv_gets(buf);
227         if (buf[0] != '2') return;
228
229         /* Enter the message */
230         serialized_event = icalcomponent_as_ical_string(cal);
231         if (serialized_event != NULL) {
232                 sprintf(buf, "ENT0 1|||4||");
233                 serv_puts(buf);
234                 serv_gets(buf);
235                 if (buf[0] == '4') {
236                         serv_puts("Content-type: text/calendar");
237                         serv_puts("");
238                         serv_write(serialized_event, strlen(serialized_event));
239                         serv_puts("");
240                         serv_puts("000");
241                 }
242         }
243
244         /* Return to the room we were in */
245         serv_printf("GOTO %s", hold_rm);
246         serv_gets(buf);
247 }
248
249
250 /*
251  * Add a calendar object to the user's calendar
252  */
253 void cal_add(icalcomponent *cal, int recursion_level, int tentative) {
254         icalcomponent *c;
255
256         /*
257          * The VEVENT subcomponent is the one we're interested in saving.
258          */
259         if (icalcomponent_isa(cal) == ICAL_VEVENT_COMPONENT) {
260                 /* Save to the message base */
261                 pencil_it_in(cal);
262
263         }
264
265         /* If the component has subcomponents, recurse through them. */
266         for (c = icalcomponent_get_first_component(cal, ICAL_ANY_COMPONENT);
267             (c != 0);
268             c = icalcomponent_get_next_component(cal, ICAL_ANY_COMPONENT)) {
269                 /* Recursively process subcomponent */
270                 cal_add(c, recursion_level+1, tentative);
271         }
272
273 }
274
275
276 /*
277  * Deserialize a calendar object in a message so it can be processed.
278  * (This is the main entry point for these things)
279  */
280 void cal_process_attachment(char *part_source, long msgnum, char *cal_partnum) {
281         icalcomponent *cal;
282
283         cal = icalcomponent_new_from_string(part_source);
284
285         if (cal == NULL) {
286                 wprintf("Error parsing calendar object: %s<BR>\n",
287                         icalerror_strerror(icalerrno));
288                 return;
289         }
290
291         cal_process_object(cal, 0, msgnum, cal_partnum);
292
293         /* Free the memory we obtained from libical's constructor */
294         icalcomponent_free(cal);
295 }
296
297
298
299
300 /*
301  * Respond to a meeting request
302  */
303 void respond_to_request(void) {
304         char buf[SIZ];
305         size_t total_len;
306         char *serialized_cal;
307         icalcomponent *cal;
308
309         output_headers(3);
310
311         wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=007700><TR><TD>"
312                 "<FONT SIZE=+1 COLOR=\"FFFFFF\""
313                 "<B>Respond to meeting request</B>"
314                 "</FONT></TD></TR></TABLE><BR>\n"
315         );
316
317         sprintf(buf, "OPNA %s|%s", bstr("msgnum"), bstr("cal_partnum"));
318         serv_puts(buf);
319         serv_gets(buf);
320         if (buf[0] != '2') {
321                 wprintf("Error: %s<BR>\n", &buf[4]);
322                 wDumpContent(1);
323                 return;
324         }
325
326         total_len = atoi(&buf[4]);
327         serialized_cal = malloc(total_len + 1);
328
329         read_server_binary(serialized_cal, total_len);
330
331         serv_puts("CLOS");
332         serv_gets(buf);
333         serialized_cal[total_len + 1] = 0;
334
335         /* Deserialize it */
336         cal = icalcomponent_new_from_string(serialized_cal);
337         free(serialized_cal);
338
339         if (cal == NULL) {
340                 wprintf("Error parsing calendar object: %s<BR>\n",
341                         icalerror_strerror(icalerrno));
342                 wDumpContent(1);
343                 return;
344         }
345
346         /* Save this in the user's calendar if necessary */
347         if (!strcasecmp(bstr("sc"), "Accept")) {
348                 cal_add(cal, 0, 0);
349         }
350         if (!strcasecmp(bstr("sc"), "Tentative")) {
351                 cal_add(cal, 0, 1);
352         }
353
354         /* Send a reply if necessary */
355         /* FIXME ... do this */
356
357         /* Free the memory we obtained from libical's constructor */
358         icalcomponent_free(cal);
359
360         /* Delete the message from the inbox */
361         /* FIXME ... do this */
362
363
364         wprintf("Done!<BR>\n");
365
366         /* ...and now we're done. */
367         wDumpContent(1);
368 }
369
370
371
372
373 /*****************************************************************************/
374
375
376
377 /*
378  * Display handlers for message reading
379  */
380
381
382
383 /*
384  * If we're reading calendar items, just store them for now.  We have to
385  * sort and re-output them later when we draw the calendar.
386  */
387 void display_individual_cal(icalcomponent *cal, long msgnum) {
388
389         WC->num_cal += 1;
390
391         WC->disp_cal = realloc(WC->disp_cal,
392                         (sizeof(icalcomponent *) * WC->num_cal) );
393         WC->disp_cal[WC->num_cal - 1] = icalcomponent_new_clone(cal);
394
395         WC->cal_msgnum = realloc(WC->cal_msgnum,
396                         (sizeof(long) * WC->num_cal) );
397         WC->cal_msgnum[WC->num_cal - 1] = msgnum;
398 }
399
400
401
402 /*
403  * Display a task in the task list
404  */
405 void display_individual_task(icalcomponent *vtodo, long msgnum) {
406         icalproperty *p;
407
408         p = icalcomponent_get_first_property(vtodo, ICAL_SUMMARY_PROPERTY);
409         wprintf("<LI><A HREF=\"/display_edit_task?msgnum=%ld\">", msgnum);
410         if (p != NULL) {
411                 escputs((char *)icalproperty_get_comment(p));
412         }
413         wprintf("</A>\n");
414 }
415
416
417 /*
418  * Display a task by itself (for editing)
419  */
420 void display_edit_individual_task(icalcomponent *supplied_vtodo, long msgnum) {
421         icalcomponent *vtodo;
422         icalproperty *p;
423         struct icaltimetype t;
424         time_t now;
425         int created_new_vtodo = 0;
426
427         now = time(NULL);
428
429         if (supplied_vtodo != NULL) {
430                 vtodo = supplied_vtodo;
431         }
432         else {
433                 vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT);
434                 created_new_vtodo = 1;
435         }
436
437         output_headers(3);
438         wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=007700><TR><TD>"
439                 "<FONT SIZE=+1 COLOR=\"FFFFFF\""
440                 "<B>Edit task</B>"
441                 "</FONT></TD></TR></TABLE><BR>\n"
442         );
443
444         wprintf("<FORM METHOD=\"POST\" ACTION=\"/save_task\">\n");
445         wprintf("<INPUT TYPE=\"hidden\" NAME=\"msgnum\" VALUE=\"%ld\">\n",
446                 msgnum);
447
448         wprintf("Summary: "
449                 "<INPUT TYPE=\"text\" NAME=\"summary\" "
450                 "MAXLENGTH=\"64\" SIZE=\"64\" VALUE=\"");
451         p = icalcomponent_get_first_property(vtodo, ICAL_SUMMARY_PROPERTY);
452         if (p != NULL) {
453                 escputs((char *)icalproperty_get_comment(p));
454         }
455         wprintf("\"><BR>\n");
456
457         wprintf("Start date: ");
458         p = icalcomponent_get_first_property(vtodo, ICAL_DTSTART_PROPERTY);
459         if (p != NULL) {
460                 t = icalproperty_get_dtstart(p);
461         }
462         else {
463                 t = icaltime_from_timet(now, 0);
464         }
465         display_icaltimetype_as_webform(&t, "dtstart");
466         wprintf("<BR>\n");
467
468         wprintf("Due date: ");
469         p = icalcomponent_get_first_property(vtodo, ICAL_DUE_PROPERTY);
470         if (p != NULL) {
471                 t = icalproperty_get_due(p);
472         }
473         else {
474                 t = icaltime_from_timet(now, 0);
475         }
476         display_icaltimetype_as_webform(&t, "due");
477         wprintf("<BR>\n");
478
479         wprintf("<CENTER><TEXTAREA NAME=\"description\" wrap=soft "
480                 "ROWS=10 COLS=80 WIDTH=80>\n"
481         );
482         p = icalcomponent_get_first_property(vtodo, ICAL_DESCRIPTION_PROPERTY);
483         if (p != NULL) {
484                 escputs((char *)icalproperty_get_comment(p));
485         }
486         wprintf("</TEXTAREA><BR>\n");
487
488         wprintf("<INPUT TYPE=\"submit\" NAME=\"sc\" VALUE=\"Save\">"
489                 "&nbsp;&nbsp;"
490                 "<INPUT TYPE=\"submit\" NAME=\"sc\" VALUE=\"Delete\">\n"
491                 "&nbsp;&nbsp;"
492                 "<INPUT TYPE=\"submit\" NAME=\"sc\" VALUE=\"Cancel\">\n"
493                 "</CENTER>\n"
494         );
495
496         wprintf("</FORM>\n");
497
498         wDumpContent(1);
499
500         if (created_new_vtodo) {
501                 icalcomponent_free(vtodo);
502         }
503 }
504
505 /*
506  * Save an edited task
507  */
508 void save_individual_task(icalcomponent *supplied_vtodo, long msgnum) {
509         char buf[SIZ];
510         int delete_existing = 0;
511         icalproperty *prop;
512         icalcomponent *vtodo;
513         int created_new_vtodo = 0;
514
515         if (supplied_vtodo != NULL) {
516                 vtodo = supplied_vtodo;
517         }
518         else {
519                 vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT);
520                 created_new_vtodo = 1;
521         }
522
523         if (!strcasecmp(bstr("sc"), "Save")) {
524
525                 /* Replace values in the component with ones from the form */
526
527                 while (prop = icalcomponent_get_first_property(vtodo,
528                       ICAL_SUMMARY_PROPERTY), prop != NULL) {
529                         icalcomponent_remove_property(vtodo, prop);
530                 }
531                 icalcomponent_add_property(vtodo,
532                         icalproperty_new_summary(bstr("summary")));
533                 
534                 while (prop = icalcomponent_get_first_property(vtodo,
535                       ICAL_DESCRIPTION_PROPERTY), prop != NULL) {
536                         icalcomponent_remove_property(vtodo, prop);
537                 }
538                 icalcomponent_add_property(vtodo,
539                         icalproperty_new_description(bstr("description")));
540         
541                 while (prop = icalcomponent_get_first_property(vtodo,
542                       ICAL_DTSTART_PROPERTY), prop != NULL) {
543                         icalcomponent_remove_property(vtodo, prop);
544                 }
545                 icalcomponent_add_property(vtodo,
546                         icalproperty_new_dtstart(
547                                 icaltime_from_webform("dtstart")
548                         )
549                 );
550         
551                 while (prop = icalcomponent_get_first_property(vtodo,
552                       ICAL_DUE_PROPERTY), prop != NULL) {
553                         icalcomponent_remove_property(vtodo, prop);
554                 }
555                 icalcomponent_add_property(vtodo,
556                         icalproperty_new_due(
557                                 icaltime_from_webform("due")
558                         )
559                 );
560         
561                 /* Serialize it and save it to the message base */
562                 serv_puts("ENT0 1|||4");
563                 serv_gets(buf);
564                 if (buf[0] == '4') {
565                         serv_puts("Content-type: text/calendar");
566                         serv_puts("");
567                         serv_puts(icalcomponent_as_ical_string(vtodo));
568                         serv_puts("000");
569                         delete_existing = 1;
570                 }
571         }
572
573         /*
574          * If the user clicked 'Delete' then delete it, period.
575          */
576         if (!strcasecmp(bstr("sc"), "Delete")) {
577                 delete_existing = 1;
578         }
579
580         if ( (delete_existing) && (msgnum > 0L) ) {
581                 serv_printf("DELE %ld", atol(bstr("msgnum")));
582                 serv_gets(buf);
583         }
584
585         if (created_new_vtodo) {
586                 icalcomponent_free(vtodo);
587         }
588
589         /* Go back to the task list */
590         readloop("readfwd");
591 }
592
593
594
595 /*
596  * Code common to all display handlers.  Given a message number and a MIME
597  * type, we load the message and hunt for that MIME type.  If found, we load
598  * the relevant part, deserialize it into a libical component, filter it for
599  * the requested object type, and feed it to the specified handler.
600  */
601 void display_using_handler(long msgnum,
602                         char *mimetype,
603                         icalcomponent_kind which_kind,
604                         void (*callback)(icalcomponent *, long)
605         ) {
606         char buf[SIZ];
607         char mime_partnum[SIZ];
608         char mime_filename[SIZ];
609         char mime_content_type[SIZ];
610         char mime_disposition[SIZ];
611         int mime_length;
612         char relevant_partnum[SIZ];
613         char *relevant_source = NULL;
614         icalcomponent *cal, *c;
615
616         sprintf(buf, "MSG0 %ld|1", msgnum);     /* ask for headers only */
617         serv_puts(buf);
618         serv_gets(buf);
619         if (buf[0] != '1') return;
620
621         while (serv_gets(buf), strcmp(buf, "000")) {
622                 if (!strncasecmp(buf, "part=", 5)) {
623                         extract(mime_filename, &buf[5], 1);
624                         extract(mime_partnum, &buf[5], 2);
625                         extract(mime_disposition, &buf[5], 3);
626                         extract(mime_content_type, &buf[5], 4);
627                         mime_length = extract_int(&buf[5], 5);
628
629                         if (!strcasecmp(mime_content_type, "text/calendar")) {
630                                 strcpy(relevant_partnum, mime_partnum);
631                         }
632
633                 }
634         }
635
636         if (strlen(relevant_partnum) > 0) {
637                 relevant_source = load_mimepart(msgnum, relevant_partnum);
638                 if (relevant_source != NULL) {
639
640                         cal = icalcomponent_new_from_string(relevant_source);
641                         if (cal != NULL) {
642
643                                 /* Simple components of desired type */
644                                 if (icalcomponent_isa(cal) == which_kind) {
645                                         callback(cal, msgnum);
646                                 }
647
648                                 /* Subcomponents of desired type */
649                                 for (c = icalcomponent_get_first_component(cal,
650                                     which_kind);
651                                     (c != 0);
652                                     c = icalcomponent_get_next_component(cal,
653                                     which_kind)) {
654                                         callback(c, msgnum);
655                                 }
656                                 icalcomponent_free(cal);
657                         }
658                         free(relevant_source);
659                 }
660         }
661
662 }
663
664 void display_calendar(long msgnum) {
665         display_using_handler(msgnum, "text/calendar",
666                                 ICAL_VEVENT_COMPONENT,
667                                 display_individual_cal);
668 }
669
670 void display_task(long msgnum) {
671         display_using_handler(msgnum, "text/calendar",
672                                 ICAL_VTODO_COMPONENT,
673                                 display_individual_task);
674 }
675
676 void display_edit_task(void) {
677         long msgnum = 0L;
678
679         msgnum = atol(bstr("msgnum"));
680         if (msgnum > 0L) {
681                 /* existing task */
682                 display_using_handler(msgnum, "text/calendar",
683                                 ICAL_VTODO_COMPONENT,
684                                 display_edit_individual_task);
685         }
686         else {
687                 /* new task */
688                 display_edit_individual_task(NULL, 0L);
689         }
690 }
691
692 void save_task(void) {
693         long msgnum = 0L;
694
695         msgnum = atol(bstr("msgnum"));
696         if (msgnum > 0L) {
697                 display_using_handler(msgnum, "text/calendar",
698                                 ICAL_VTODO_COMPONENT,
699                                 save_individual_task);
700         }
701         else {
702                 save_individual_task(NULL, 0L);
703         }
704 }
705
706 void display_edit_event(void) {
707         long msgnum = 0L;
708
709         msgnum = atol(bstr("msgnum"));
710         if (msgnum > 0L) {
711                 /* existing event */
712                 display_using_handler(msgnum, "text/calendar",
713                                 ICAL_VEVENT_COMPONENT,
714                                 display_edit_individual_event);
715         }
716         else {
717                 /* new event */
718                 display_edit_individual_event(NULL, 0L);
719         }
720 }
721
722 void save_event(void) {
723         long msgnum = 0L;
724
725         msgnum = atol(bstr("msgnum"));
726
727         if (msgnum > 0L) {
728                 display_using_handler(msgnum, "text/calendar",
729                                 ICAL_VEVENT_COMPONENT,
730                                 save_individual_event);
731         }
732         else {
733                 save_individual_event(NULL, 0L);
734         }
735 }
736
737 #endif /* HAVE_ICAL_H */