* Moved to the new string tokenizer API
[citadel.git] / webcit / event.c
1 /*
2  * $Id$
3  *
4  * Editing calendar events.
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
31 #ifdef WEBCIT_WITH_CALENDAR_SERVICE
32
33 /*
34  * Display an event by itself (for editing)
35  */
36 void display_edit_individual_event(icalcomponent *supplied_vevent, long msgnum) {
37         icalcomponent *vevent;
38         icalproperty *p;
39         icalvalue *v;
40         struct icaltimetype t_start, t_end;
41         time_t now;
42         struct tm tm_now;
43         int created_new_vevent = 0;
44         icalproperty *organizer = NULL;
45         char organizer_string[SIZ];
46         icalproperty *attendee = NULL;
47         char attendee_string[SIZ];
48         char buf[SIZ];
49         int organizer_is_me = 0;
50         int i;
51         int sequence = 0;
52
53         now = time(NULL);
54         strcpy(organizer_string, "");
55         strcpy(attendee_string, "");
56
57         if (supplied_vevent != NULL) {
58                 vevent = supplied_vevent;
59                 /* If we're looking at a fully encapsulated VCALENDAR
60                  * rather than a VEVENT component, attempt to use the first
61                  * relevant VEVENT subcomponent.  If there is none, the
62                  * NULL returned by icalcomponent_get_first_component() will
63                  * tell the next iteration of this function to create a
64                  * new one.
65                  */
66                 if (icalcomponent_isa(vevent) == ICAL_VCALENDAR_COMPONENT) {
67                         display_edit_individual_event(
68                                 icalcomponent_get_first_component(
69                                         vevent, ICAL_VEVENT_COMPONENT
70                                 ), msgnum
71                         );
72                         return;
73                 }
74         }
75         else {
76                 vevent = icalcomponent_new(ICAL_VEVENT_COMPONENT);
77                 created_new_vevent = 1;
78         }
79
80         /* Learn the sequence */
81         p = icalcomponent_get_first_property(vevent, ICAL_SEQUENCE_PROPERTY);
82         if (p != NULL) {
83                 sequence = icalproperty_get_sequence(p);
84         }
85
86         /* Begin output */
87         output_headers(1, 1, 2, 0, 0, 0, 0);
88         wprintf("<div id=\"banner\">\n"
89                 "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
90                 "<SPAN CLASS=\"titlebar\">Add or edit an event</SPAN>"
91                 "</TD></TR></TABLE>\n"
92                 "</div>\n<div id=\"content\">\n"
93         );
94
95         wprintf("<div id=\"fix_scrollbar_bug\">"
96                 "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
97
98         /************************************************************
99          * Uncomment this to see the UID in calendar events for debugging
100         wprintf("UID == ");
101         p = icalcomponent_get_first_property(vevent, ICAL_UID_PROPERTY);
102         if (p != NULL) {
103                 escputs((char *)icalproperty_get_comment(p));
104         }
105         wprintf("<br />\n");
106         wprintf("SEQUENCE == %d<br />\n", sequence);
107         *************************************************************/
108
109         wprintf("<FORM NAME=\"EventForm\" METHOD=\"POST\" ACTION=\"/save_event\">\n");
110
111         wprintf("<INPUT TYPE=\"hidden\" NAME=\"msgnum\" VALUE=\"%ld\">\n",
112                 msgnum);
113         wprintf("<INPUT TYPE=\"hidden\" NAME=\"calview\" VALUE=\"%s\">\n",
114                 bstr("calview"));
115         wprintf("<INPUT TYPE=\"hidden\" NAME=\"year\" VALUE=\"%s\">\n",
116                 bstr("year"));
117         wprintf("<INPUT TYPE=\"hidden\" NAME=\"month\" VALUE=\"%s\">\n",
118                 bstr("month"));
119         wprintf("<INPUT TYPE=\"hidden\" NAME=\"day\" VALUE=\"%s\">\n",
120                 bstr("day"));
121
122         /* Put it in a borderless table so it lines up nicely */
123         wprintf("<TABLE border=0 width=100%%>\n");
124
125         wprintf("<TR><TD><B>Summary</B></TD><TD>\n"
126                 "<INPUT TYPE=\"text\" NAME=\"summary\" "
127                 "MAXLENGTH=\"64\" SIZE=\"64\" VALUE=\"");
128         p = icalcomponent_get_first_property(vevent, ICAL_SUMMARY_PROPERTY);
129         if (p != NULL) {
130                 escputs((char *)icalproperty_get_comment(p));
131         }
132         wprintf("\"></TD></TR>\n");
133
134         wprintf("<TR><TD><B>Location</B></TD><TD>\n"
135                 "<INPUT TYPE=\"text\" NAME=\"location\" "
136                 "MAXLENGTH=\"64\" SIZE=\"64\" VALUE=\"");
137         p = icalcomponent_get_first_property(vevent, ICAL_LOCATION_PROPERTY);
138         if (p != NULL) {
139                 escputs((char *)icalproperty_get_comment(p));
140         }
141         wprintf("\"></TD></TR>\n");
142
143         wprintf("<TR><TD><B>Start</B></TD><TD>\n");
144         p = icalcomponent_get_first_property(vevent, ICAL_DTSTART_PROPERTY);
145         if (p != NULL) {
146                 t_start = icalproperty_get_dtstart(p);
147                 if (t_start.is_date) {
148                         t_start.hour = 0;
149                         t_start.minute = 0;
150                         t_start.second = 0;
151                 }
152         }
153         else {
154                 localtime_r(&now, &tm_now);
155                 if (strlen(bstr("year")) > 0) {
156                         tm_now.tm_year = atoi(bstr("year")) - 1900;
157                         tm_now.tm_mon = atoi(bstr("month")) - 1;
158                         tm_now.tm_mday = atoi(bstr("day"));
159                 }
160                 if (strlen(bstr("hour")) > 0) {
161                         tm_now.tm_hour = atoi(bstr("hour"));
162                         tm_now.tm_min = atoi(bstr("minute"));
163                         tm_now.tm_sec = 0;
164                 }
165                 else {
166                         tm_now.tm_hour = 9;
167                         tm_now.tm_min = 0;
168                         tm_now.tm_sec = 0;
169                 }
170
171                 t_start = icaltime_from_timet_with_zone(
172                         mktime(&tm_now),
173                         ((!strcasecmp(bstr("alldayevent"), "yes")) ? 1 : 0),
174                         icaltimezone_get_utc_timezone()
175                 );
176                 t_start.is_utc = 1;
177
178         }
179         display_icaltimetype_as_webform(&t_start, "dtstart");
180
181         wprintf("<INPUT TYPE=\"checkbox\" NAME=\"alldayevent\" "
182                 "VALUE=\"yes\" onClick=\""
183 ""
184 "                       if (this.checked) { "
185 "                               this.form.dtstart_hour.value='0'; "
186 "                               this.form.dtstart_hour.disabled = true; "
187 "                               this.form.dtstart_minute.value='0'; "
188 "                               this.form.dtstart_minute.disabled = true; "
189 "                               this.form.dtend_hour.value='0'; "
190 "                               this.form.dtend_hour.disabled = true; "
191 "                               this.form.dtend_minute.value='0'; "
192 "                               this.form.dtend_minute.disabled = true; "
193 "                               this.form.dtend_month.disabled = true; "
194 "                               this.form.dtend_day.disabled = true; "
195 "                               this.form.dtend_year.disabled = true; "
196 "                       } "
197 "                       else { "
198 "                               this.form.dtstart_hour.disabled = false; "
199 "                               this.form.dtstart_minute.disabled = false; "
200 "                               this.form.dtend_hour.disabled = false; "
201 "                               this.form.dtend_minute.disabled = false; "
202 "                               this.form.dtend_month.disabled = false; "
203 "                               this.form.dtend_day.disabled = false; "
204 "                               this.form.dtend_year.disabled = false; "
205 "                       } "
206 " "
207 " "
208 "               \" %s >All day event",
209                 (t_start.is_date ? "CHECKED" : "" )
210         );
211
212         wprintf("</TD></TR>\n");
213
214         /* If this is an all-day-event, set the end time to be identical to
215          * the start time (the hour/minute/second will be set to midnight).
216          * Otherwise extract or create it.
217          */
218         wprintf("<TR><TD><B>End</B></TD><TD>\n");
219         if (t_start.is_date) {
220                 t_end = t_start;
221         }
222         else {
223                 p = icalcomponent_get_first_property(vevent,
224                                                         ICAL_DTEND_PROPERTY);
225                 if (p != NULL) {
226                         t_end = icalproperty_get_dtend(p);
227                 }
228                 else {
229                         /* If this is not an all-day event and there is no
230                          * end time specified, make the default one hour
231                          * from the start time.
232                          */
233                         t_end = t_start;
234                         t_end.hour += 1;
235                         t_end.second = 0;
236                         t_end = icaltime_normalize(t_end);
237                         /* t_end = icaltime_from_timet(now, 0); */
238                 }
239         }
240         display_icaltimetype_as_webform(&t_end, "dtend");
241         wprintf("</TD></TR>\n");
242
243         wprintf("<TR><TD><B>Notes</B></TD><TD>\n"
244                 "<TEXTAREA NAME=\"description\" wrap=soft "
245                 "ROWS=5 COLS=80 WIDTH=80>\n"
246         );
247         p = icalcomponent_get_first_property(vevent, ICAL_DESCRIPTION_PROPERTY);
248         if (p != NULL) {
249                 escputs((char *)icalproperty_get_comment(p));
250         }
251         wprintf("</TEXTAREA></TD></TR>");
252
253         /* For a new event, the user creating the event should be the
254          * organizer.  Set this field accordingly.
255          */
256         if (icalcomponent_get_first_property(vevent, ICAL_ORGANIZER_PROPERTY)
257            == NULL) {
258                 sprintf(organizer_string, "MAILTO:%s", WC->cs_inet_email);
259                 icalcomponent_add_property(vevent,
260                         icalproperty_new_organizer(organizer_string)
261                 );
262         }
263
264         /* Determine who is the organizer of this event.
265          * We need to determine "me" or "not me."
266          */
267         organizer = icalcomponent_get_first_property(vevent,
268                                                 ICAL_ORGANIZER_PROPERTY);
269         if (organizer != NULL) {
270                 strcpy(organizer_string, icalproperty_get_organizer(organizer));
271                 if (!strncasecmp(organizer_string, "MAILTO:", 7)) {
272                         strcpy(organizer_string, &organizer_string[7]);
273                         striplt(organizer_string);
274                         lprintf(9, "ISME %s\n", organizer_string);
275                         serv_printf("ISME %s", organizer_string);
276                         serv_gets(buf);
277                         lprintf(9, "%s\n", buf);
278                         if (buf[0] == '2') {
279                                 organizer_is_me = 1;
280                         }
281                 }
282         }
283
284         wprintf("<TR><TD><B>Organizer</B></TD><TD>");
285         escputs(organizer_string);
286         if (organizer_is_me) {
287                 wprintf(" <FONT SIZE=-1><I>"
288                         "(you are the organizer)</I></FONT>\n");
289         }
290
291         /*
292          * Transmit the organizer as a hidden field.   We don't want the user
293          * to be able to change it, but we do want it fed back to the server,
294          * especially if this is a new event and there is no organizer already
295          * in the calendar object.
296          */
297         wprintf("<INPUT TYPE=\"hidden\" NAME=\"organizer\" VALUE=\"");
298         escputs(organizer_string);
299         wprintf("\">");
300
301         wprintf("</TD></TR>\n");
302
303         /* Transparency */
304         wprintf("<TR><TD><B>Show time as:</B></TD><TD>");
305
306         p = icalcomponent_get_first_property(vevent, ICAL_TRANSP_PROPERTY);
307         if (p == NULL) {
308                 /* No transparency found.  Default to opaque (busy). */
309                 p = icalproperty_new_transp(ICAL_TRANSP_OPAQUE);
310                 if (p != NULL) {
311                         icalcomponent_add_property(vevent, p);
312                 }
313         }
314         if (p != NULL) {
315                 v = icalproperty_get_value(p);
316         }
317         else {
318                 v = NULL;
319         }
320
321         wprintf("<INPUT TYPE=\"radio\" NAME=\"transp\" VALUE=\"transparent\"");
322         if (v != NULL) if (icalvalue_get_transp(v) == ICAL_TRANSP_TRANSPARENT)
323                 wprintf(" CHECKED");
324         wprintf(">Free&nbsp;&nbsp;");
325
326         wprintf("<INPUT TYPE=\"radio\" NAME=\"transp\" VALUE=\"opaque\"");
327         if (v != NULL) if (icalvalue_get_transp(v) == ICAL_TRANSP_OPAQUE)
328                 wprintf(" CHECKED");
329         wprintf(">Busy");
330
331         wprintf("</TD></TR>\n");
332
333         /* Attendees */
334         wprintf("<TR><TD><B>Attendees</B><br />"
335                 "<FONT SIZE=-2>(One per line)"
336                 "</FONT></TD><TD>"
337                 "<TEXTAREA %s NAME=\"attendees\" wrap=soft "
338                 "ROWS=3 COLS=80 WIDTH=80>\n",
339                 (organizer_is_me ? "" : "DISABLED ")
340         );
341         i = 0;
342         for (attendee = icalcomponent_get_first_property(vevent, ICAL_ATTENDEE_PROPERTY);
343             attendee != NULL;
344             attendee = icalcomponent_get_next_property(vevent, ICAL_ATTENDEE_PROPERTY)) {
345                 strcpy(attendee_string, icalproperty_get_attendee(attendee));
346                 if (!strncasecmp(attendee_string, "MAILTO:", 7)) {
347
348                         /* screen name or email address */
349                         strcpy(attendee_string, &attendee_string[7]);
350                         striplt(attendee_string);
351                         if (i++) wprintf("\n");
352                         escputs(attendee_string);
353                         wprintf(" ");
354
355                         /* participant status */
356                         partstat_as_string(buf, attendee);
357                         escputs(buf);
358                 }
359         }
360         wprintf("</TEXTAREA></TD></TR>\n");
361
362         /* Done with properties. */
363         wprintf("</TABLE>\n<CENTER>"
364                 "<INPUT TYPE=\"submit\" NAME=\"sc\" VALUE=\"Save\">"
365                 "&nbsp;&nbsp;"
366                 "<INPUT TYPE=\"submit\" NAME=\"sc\" VALUE=\"Delete\">\n"
367                 "&nbsp;&nbsp;"
368                 "<INPUT TYPE=\"submit\" NAME=\"sc\" "
369                                 "VALUE=\"Check attendee availability\">\n"
370                 "&nbsp;&nbsp;"
371                 "<INPUT TYPE=\"submit\" NAME=\"sc\" VALUE=\"Cancel\">\n"
372                 "</CENTER>\n"
373         );
374
375         wprintf("</FORM>\n");
376         
377         wprintf("<script type=\"text/javascript\">"
378                 "<!--"
379                         "if (document.EventForm.alldayevent.checked) {"
380                                 "document.EventForm.dtstart_hour.value='0';"
381                                 "document.EventForm.dtstart_hour.disabled = true;"
382                                 "document.EventForm.dtstart_minute.value='0';"
383                                 "document.EventForm.dtstart_minute.disabled = true;"
384                                 "document.EventForm.dtend_hour.value='0';"
385                                 "document.EventForm.dtend_hour.disabled = true;"
386                                 "document.EventForm.dtend_minute.value='0';"
387                                 "document.EventForm.dtend_minute.disabled = true;"
388                                 "document.EventForm.dtend_month.disabled = true;"
389                                 "document.EventForm.dtend_day.disabled = true;"
390                                 "document.EventForm.dtend_year.disabled = true;"
391                         "}"
392                         "else {"
393                                 "document.EventForm.dtstart_hour.disabled = false;"
394                                 "document.EventForm.dtstart_minute.disabled = false;"
395                                 "document.EventForm.dtend_hour.disabled = false;"
396                                 "document.EventForm.dtend_minute.disabled = false;"
397                                 "document.EventForm.dtend_month.disabled = false;"
398                                 "document.EventForm.dtend_day.disabled = false;"
399                                 "document.EventForm.dtend_year.disabled = false;"
400                         "}"
401                 "//-->"
402                 "</script>\n"
403         );
404
405         wprintf("</td></tr></table></div>\n");
406         wDumpContent(1);
407
408         if (created_new_vevent) {
409                 icalcomponent_free(vevent);
410         }
411 }
412
413 /*
414  * Save an edited event
415  * 
416  */
417 void save_individual_event(icalcomponent *supplied_vevent, long msgnum) {
418         char buf[SIZ];
419         icalproperty *prop;
420         icalcomponent *vevent, *encaps;
421         int created_new_vevent = 0;
422         int all_day_event = 0;
423         struct icaltimetype event_start;
424         icalproperty *attendee = NULL;
425         char attendee_string[SIZ];
426         int i;
427         int foundit;
428         char form_attendees[SIZ];
429         char organizer_string[SIZ];
430         int sequence = 0;
431         enum icalproperty_transp formtransp = ICAL_TRANSP_NONE;
432
433         if (supplied_vevent != NULL) {
434                 vevent = supplied_vevent;
435                 /* If we're looking at a fully encapsulated VCALENDAR
436                  * rather than a VEVENT component, attempt to use the first
437                  * relevant VEVENT subcomponent.  If there is none, the
438                  * NULL returned by icalcomponent_get_first_component() will
439                  * tell the next iteration of this function to create a
440                  * new one.
441                  */
442                 if (icalcomponent_isa(vevent) == ICAL_VCALENDAR_COMPONENT) {
443                         save_individual_event(
444                                 icalcomponent_get_first_component(
445                                         vevent, ICAL_VEVENT_COMPONENT
446                                 ), msgnum
447                         );
448                         return;
449                 }
450         }
451         else {
452                 vevent = icalcomponent_new(ICAL_VEVENT_COMPONENT);
453                 created_new_vevent = 1;
454         }
455
456         if ( (!strcasecmp(bstr("sc"), "Save"))
457            || (!strcasecmp(bstr("sc"), "Check attendee availability")) ) {
458
459                 /* Replace values in the component with ones from the form */
460
461                 while (prop = icalcomponent_get_first_property(vevent,
462                       ICAL_SUMMARY_PROPERTY), prop != NULL) {
463                         icalcomponent_remove_property(vevent, prop);
464                         icalproperty_free(prop);
465                 }
466                 icalcomponent_add_property(vevent,
467                         icalproperty_new_summary(bstr("summary")));
468
469                 while (prop = icalcomponent_get_first_property(vevent,
470                       ICAL_LOCATION_PROPERTY), prop != NULL) {
471                         icalcomponent_remove_property(vevent, prop);
472                         icalproperty_free(prop);
473                 }
474                 icalcomponent_add_property(vevent,
475                         icalproperty_new_location(bstr("location")));
476                 
477                 while (prop = icalcomponent_get_first_property(vevent,
478                       ICAL_DESCRIPTION_PROPERTY), prop != NULL) {
479                         icalcomponent_remove_property(vevent, prop);
480                         icalproperty_free(prop);
481                 }
482                 icalcomponent_add_property(vevent,
483                         icalproperty_new_description(bstr("description")));
484         
485                 while (prop = icalcomponent_get_first_property(vevent,
486                       ICAL_DTSTART_PROPERTY), prop != NULL) {
487                         icalcomponent_remove_property(vevent, prop);
488                         icalproperty_free(prop);
489                 }
490
491                 if (!strcmp(bstr("alldayevent"), "yes")) {
492                         all_day_event = 1;
493                 }
494                 else {
495                         all_day_event = 0;
496                 }
497
498                 event_start = icaltime_from_webform("dtstart");
499                 if (all_day_event) {
500                         event_start.is_date = 1;
501                         event_start.hour = 0;
502                         event_start.minute = 0;
503                         event_start.second = 0;
504                 }
505
506
507                 /* The following odd-looking snippet of code looks like it
508                  * takes some unnecessary steps.  It is done this way because
509                  * libical incorrectly turns an "all day event" into a normal
510                  * event starting at midnight (i.e. it serializes as date/time
511                  * instead of just date) unless icalvalue_new_date() is used.
512                  * So we force it, if this is an all day event.
513                  */
514                 prop = icalproperty_new_dtstart(event_start);
515                 if (all_day_event) {
516                         icalproperty_set_value(prop,
517                                 icalvalue_new_date(event_start)
518                         );
519                 }
520
521                 if (prop) icalcomponent_add_property(vevent, prop);
522                 else icalproperty_free(prop);
523
524                 while (prop = icalcomponent_get_first_property(vevent,
525                       ICAL_DTEND_PROPERTY), prop != NULL) {
526                         icalcomponent_remove_property(vevent, prop);
527                         icalproperty_free(prop);
528                 }
529                 while (prop = icalcomponent_get_first_property(vevent,
530                       ICAL_DURATION_PROPERTY), prop != NULL) {
531                         icalcomponent_remove_property(vevent, prop);
532                         icalproperty_free(prop);
533                 }
534
535                 if (all_day_event == 0) {
536                         icalcomponent_add_property(vevent,
537                                 icalproperty_new_dtend(icaltime_normalize(
538                                         icaltime_from_webform("dtend"))
539                                 )
540                         );
541                 }
542
543                 /* See if transparency is indicated */
544                 if (strlen(bstr("transp")) > 0) {
545                         if (!strcasecmp(bstr("transp"), "opaque")) {
546                                 formtransp = ICAL_TRANSP_OPAQUE;
547                         }
548                         else if (!strcasecmp(bstr("transp"), "transparent")) {
549                                 formtransp = ICAL_TRANSP_TRANSPARENT;
550                         }
551
552                         while (prop = icalcomponent_get_first_property(vevent, ICAL_TRANSP_PROPERTY),
553                               (prop != NULL)) {
554                                 icalcomponent_remove_property(vevent, prop);
555                                 icalproperty_free(prop);
556                         }
557
558                         lprintf(9, "adding new property...\n");
559                         icalcomponent_add_property(vevent, icalproperty_new_transp(formtransp));
560                         lprintf(9, "...added it.\n");
561                 }
562
563                 /* Give this event a UID if it doesn't have one. */
564                 lprintf(9, "Give this event a UID if it doesn't have one.\n");
565                 if (icalcomponent_get_first_property(vevent,
566                    ICAL_UID_PROPERTY) == NULL) {
567                         generate_uuid(buf);
568                         icalcomponent_add_property(vevent,
569                                 icalproperty_new_uid(buf)
570                         );
571                 }
572
573                 /* Increment the sequence ID */
574                 lprintf(9, "Increment the sequence ID\n");
575                 while (prop = icalcomponent_get_first_property(vevent,
576                       ICAL_SEQUENCE_PROPERTY), (prop != NULL) ) {
577                         i = icalproperty_get_sequence(prop);
578                         lprintf(9, "Sequence was %d\n", i);
579                         if (i > sequence) sequence = i;
580                         icalcomponent_remove_property(vevent, prop);
581                         icalproperty_free(prop);
582                 }
583                 ++sequence;
584                 lprintf(9, "New sequence is %d.  Adding...\n", sequence);
585                 icalcomponent_add_property(vevent,
586                         icalproperty_new_sequence(sequence)
587                 );
588                 
589                 /* Set the organizer, only if one does not already exist *and*
590                  * the form is supplying one
591                  */
592                 lprintf(9, "Setting the organizer...\n");
593                 strcpy(buf, bstr("organizer"));
594                 if ( (icalcomponent_get_first_property(vevent,
595                    ICAL_ORGANIZER_PROPERTY) == NULL) 
596                    && (strlen(buf) > 0) ) {
597
598                         /* set new organizer */
599                         sprintf(organizer_string, "MAILTO:%s", buf);
600                         icalcomponent_add_property(vevent,
601                                 icalproperty_new_organizer(organizer_string)
602                         );
603
604                 }
605
606                 /*
607                  * Add any new attendees listed in the web form
608                  */
609                 lprintf(9, "Add any new attendees\n");
610
611                 /* First, strip out the parenthesized partstats.  */
612                 strcpy(form_attendees, bstr("attendees"));
613                 stripout(form_attendees, '(', ')');
614
615                 /* Now iterate! */
616                 for (i=0; i<num_tokens(form_attendees, '\n'); ++i) {
617                         extract_token(buf, form_attendees, i, '\n', sizeof buf);
618                         striplt(buf);
619                         if (strlen(buf) > 0) {
620                                 lprintf(9, "Attendee: <%s>\n", buf);
621                                 sprintf(attendee_string, "MAILTO:%s", buf);
622                                 foundit = 0;
623
624                                 for (attendee = icalcomponent_get_first_property(vevent, ICAL_ATTENDEE_PROPERTY); attendee != NULL; attendee = icalcomponent_get_next_property(vevent, ICAL_ATTENDEE_PROPERTY)) {
625                                         if (!strcasecmp(attendee_string,
626                                            icalproperty_get_attendee(attendee)))
627                                                 ++foundit;
628                                 }
629
630
631                                 if (foundit == 0) {
632                                         icalcomponent_add_property(vevent,
633                                                 icalproperty_new_attendee(attendee_string)
634                                         );
635                                 }
636                         }
637                 }
638
639                 /*
640                  * Remove any attendees *not* listed in the web form
641                  */
642 STARTOVER:      lprintf(9, "Remove unlisted attendees\n");
643                 for (attendee = icalcomponent_get_first_property(vevent, ICAL_ATTENDEE_PROPERTY); attendee != NULL; attendee = icalcomponent_get_next_property(vevent, ICAL_ATTENDEE_PROPERTY)) {
644                         strcpy(attendee_string, icalproperty_get_attendee(attendee));
645                         if (!strncasecmp(attendee_string, "MAILTO:", 7)) {
646                                 strcpy(attendee_string, &attendee_string[7]);
647                                 striplt(attendee_string);
648                                 foundit = 0;
649                                 for (i=0; i<num_tokens(form_attendees, '\n'); ++i) {
650                                         extract_token(buf, form_attendees, i, '\n', sizeof buf);
651                                         striplt(buf);
652                                         if (!strcasecmp(buf, attendee_string)) ++foundit;
653                                 }
654                                 if (foundit == 0) {
655                                         icalcomponent_remove_property(vevent, attendee);
656                                         icalproperty_free(attendee);
657                                         goto STARTOVER;
658                                 }
659                         }
660                 }
661
662                 /*
663                  * Encapsulate event into full VCALENDAR component.  Clone it first,
664                  * for two reasons: one, it's easier to just free the whole thing
665                  * when we're done instead of unbundling, but more importantly, we
666                  * can't encapsulate something that may already be encapsulated
667                  * somewhere else.
668                  */
669                 lprintf(9, "Encapsulating into full VCALENDAR component\n");
670                 encaps = ical_encapsulate_subcomponent(icalcomponent_new_clone(vevent));
671
672                 /* If the user clicked 'Save' then save it to the server. */
673                 lprintf(9, "Serializing it for saving\n");
674                 if ( (encaps != NULL) && (!strcasecmp(bstr("sc"), "Save")) ) {
675                         serv_puts("ENT0 1|||4|||1|");
676                         serv_gets(buf);
677                         if (buf[0] == '8') {
678                                 serv_puts("Content-type: text/calendar");
679                                 serv_puts("");
680                                 serv_puts(icalcomponent_as_ical_string(encaps));
681                                 serv_puts("000");
682                         }
683                         while (serv_gets(buf), strcmp(buf, "000")) {
684                                 lprintf(9, "ENT0 REPLY: %s\n", buf);
685                         }
686                         icalcomponent_free(encaps);
687                 }
688
689                 /* Or, check attendee availability if the user asked for that. */
690                 if ( (encaps != NULL) && (!strcasecmp(bstr("sc"), "Check attendee availability")) ) {
691
692                         /* Call this function, which does the real work */
693                         check_attendee_availability(encaps);
694
695                         /* This displays the form again, with our annotations */
696                         display_edit_individual_event(encaps, msgnum);
697
698                         icalcomponent_free(encaps);
699                 }
700
701         }
702
703         /*
704          * If the user clicked 'Delete' then delete it.
705          */
706         lprintf(9, "Checking to see if we have to delete an old event\n");
707         if ( (!strcasecmp(bstr("sc"), "Delete")) && (msgnum > 0L) ) {
708                 serv_printf("DELE %ld", atol(bstr("msgnum")));
709                 serv_gets(buf);
710         }
711
712         if (created_new_vevent) {
713                 icalcomponent_free(vevent);
714         }
715
716         /* If this was a save or deelete, go back to the calendar view. */
717         if (strcasecmp(bstr("sc"), "Check attendee availability")) {
718                 readloop("readfwd");
719         }
720 }
721
722
723 #endif /* WEBCIT_WITH_CALENDAR_SERVICE */