]> code.citadel.org Git - citadel.git/blob - webcit/event.c
* Display PARTSTAT for attendees
[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 HAVE_ICAL_H
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         struct icaltimetype t_start, t_end;
40         time_t now;
41         int created_new_vevent = 0;
42         icalproperty *organizer = NULL;
43         char organizer_string[SIZ];
44         icalproperty *attendee = NULL;
45         char attendee_string[SIZ];
46         char buf[SIZ];
47         int i;
48         int organizer_is_me = 0;
49         int sequence = 0;
50
51         now = time(NULL) % 60;  /* mod 60 to force :00 seconds */
52         strcpy(organizer_string, "");
53         strcpy(attendee_string, "");
54
55         if (supplied_vevent != NULL) {
56                 vevent = supplied_vevent;
57         }
58         else {
59                 vevent = icalcomponent_new(ICAL_VEVENT_COMPONENT);
60                 created_new_vevent = 1;
61         }
62
63         /* Learn the sequence */
64         p = icalcomponent_get_first_property(vevent, ICAL_SEQUENCE_PROPERTY);
65         if (p != NULL) {
66                 sequence = icalproperty_get_sequence(p);
67         }
68
69         /* Begin output */
70         output_headers(3);
71         wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=007700><TR><TD>"
72                 "<IMG ALIGN=CENTER SRC=\"/static/vcalendar.gif\">"
73                 "<FONT SIZE=+1 COLOR=\"FFFFFF\""
74                 "<B>Edit event</B>"
75                 "</FONT></TD></TR></TABLE><BR>\n"
76         );
77
78         /************************************************************
79          * Uncomment this to see the UID in calendar events for debugging
80         wprintf("UID == ");
81         p = icalcomponent_get_first_property(vevent, ICAL_UID_PROPERTY);
82         if (p != NULL) {
83                 escputs((char *)icalproperty_get_comment(p));
84         }
85         wprintf("<BR>\n");
86         wprintf("SEQUENCE == %d<BR>\n", sequence);
87         *************************************************************/
88
89         wprintf("<FORM NAME=\"EventForm\" METHOD=\"POST\" ACTION=\"/save_event\">\n");
90
91         wprintf("<INPUT TYPE=\"hidden\" NAME=\"msgnum\" VALUE=\"%ld\">\n",
92                 msgnum);
93         wprintf("<INPUT TYPE=\"hidden\" NAME=\"calview\" VALUE=\"%s\">\n",
94                 bstr("calview"));
95         wprintf("<INPUT TYPE=\"hidden\" NAME=\"year\" VALUE=\"%s\">\n",
96                 bstr("year"));
97         wprintf("<INPUT TYPE=\"hidden\" NAME=\"month\" VALUE=\"%s\">\n",
98                 bstr("month"));
99         wprintf("<INPUT TYPE=\"hidden\" NAME=\"day\" VALUE=\"%s\">\n",
100                 bstr("day"));
101
102         /* Put it in a borderless table so it lines up nicely */
103         wprintf("<TABLE border=0 width=100%%>\n");
104
105         wprintf("<TR><TD><B>Summary</B></TD><TD>\n"
106                 "<INPUT TYPE=\"text\" NAME=\"summary\" "
107                 "MAXLENGTH=\"64\" SIZE=\"64\" VALUE=\"");
108         p = icalcomponent_get_first_property(vevent, ICAL_SUMMARY_PROPERTY);
109         if (p != NULL) {
110                 escputs((char *)icalproperty_get_comment(p));
111         }
112         wprintf("\"></TD></TR>\n");
113
114         wprintf("<TR><TD><B>Location</B></TD><TD>\n"
115                 "<INPUT TYPE=\"text\" NAME=\"location\" "
116                 "MAXLENGTH=\"64\" SIZE=\"64\" VALUE=\"");
117         p = icalcomponent_get_first_property(vevent, ICAL_LOCATION_PROPERTY);
118         if (p != NULL) {
119                 escputs((char *)icalproperty_get_comment(p));
120         }
121         wprintf("\"></TD></TR>\n");
122
123         wprintf("<TR><TD><B>Start</B></TD><TD>\n");
124         p = icalcomponent_get_first_property(vevent, ICAL_DTSTART_PROPERTY);
125         if (p != NULL) {
126                 t_start = icalproperty_get_dtstart(p);
127                 if (t_start.is_date) {
128                         t_start.hour = 0;
129                         t_start.minute = 0;
130                         t_start.second = 0;
131                 }
132         }
133         else {
134                 memset(&t_start, 0, sizeof t_start);
135                 t_start.year = atoi(bstr("year"));
136                 t_start.month = atoi(bstr("month"));
137                 t_start.day = atoi(bstr("day"));
138                 if (strlen(bstr("hour")) > 0) {
139                         t_start.hour = atoi(bstr("hour"));
140                         t_start.minute = atoi(bstr("minute"));
141                         t_start.second = 0;
142                 }
143                 else {
144                         t_start.hour = 9;
145                         t_start.minute = 0;
146                         t_start.second = 0;
147                 }
148         }
149         display_icaltimetype_as_webform(&t_start, "dtstart");
150
151         wprintf("<INPUT TYPE=\"checkbox\" NAME=\"alldayevent\" "
152                 "VALUE=\"yes\" onClick=\"
153
154                         if (this.checked) {
155                                 this.form.dtstart_hour.value='0';
156                                 this.form.dtstart_hour.disabled = true;
157                                 this.form.dtstart_minute.value='0';
158                                 this.form.dtstart_minute.disabled = true;
159                                 this.form.dtend_hour.value='0';
160                                 this.form.dtend_hour.disabled = true;
161                                 this.form.dtend_minute.value='0';
162                                 this.form.dtend_minute.disabled = true;
163                                 this.form.dtend_month.disabled = true;
164                                 this.form.dtend_day.disabled = true;
165                                 this.form.dtend_year.disabled = true;
166                         }
167                         else {
168                                 this.form.dtstart_hour.disabled = false;
169                                 this.form.dtstart_minute.disabled = false;
170                                 this.form.dtend_hour.disabled = false;
171                                 this.form.dtend_minute.disabled = false;
172                                 this.form.dtend_month.disabled = false;
173                                 this.form.dtend_day.disabled = false;
174                                 this.form.dtend_year.disabled = false;
175                         }
176
177
178                 \" %s >All day event",
179                 (t_start.is_date ? "CHECKED" : "" )
180         );
181
182         wprintf("</TD></TR>\n");
183
184         /* If this is an all-day-event, set the end time to be identical to
185          * the start time (the hour/minute/second will be set to midnight).
186          * Otherwise extract or create it.
187          */
188         wprintf("<TR><TD><B>End</B></TD><TD>\n");
189         if (t_start.is_date) {
190                 t_end = t_start;
191         }
192         else {
193                 p = icalcomponent_get_first_property(vevent,
194                                                         ICAL_DTEND_PROPERTY);
195                 if (p != NULL) {
196                         t_end = icalproperty_get_dtend(p);
197                 }
198                 else {
199                         /* If this is not an all-day event and there is no
200                          * end time specified, make the default one hour
201                          * from the start time.
202                          */
203                         t_end = t_start;
204                         t_end.hour += 1;
205                         t_end.second = 0;
206                         t_end = icaltime_normalize(t_end);
207                         /* t_end = icaltime_from_timet(now, 0); */
208                 }
209         }
210         display_icaltimetype_as_webform(&t_end, "dtend");
211         wprintf("</TD></TR>\n");
212
213         wprintf("<TR><TD><B>Notes</B></TD><TD>\n"
214                 "<TEXTAREA NAME=\"description\" wrap=soft "
215                 "ROWS=10 COLS=80 WIDTH=80>\n"
216         );
217         p = icalcomponent_get_first_property(vevent, ICAL_DESCRIPTION_PROPERTY);
218         if (p != NULL) {
219                 escputs((char *)icalproperty_get_comment(p));
220         }
221         wprintf("</TEXTAREA></TD></TR>");
222
223         /* For a new event, the user creating the event should be the
224          * organizer.  Set this field accordingly.
225          */
226         if (icalcomponent_get_first_property(vevent, ICAL_ORGANIZER_PROPERTY)
227            == NULL) {
228                 sprintf(organizer_string, "MAILTO:%s", WC->cs_inet_email);
229                 icalcomponent_add_property(vevent,
230                         icalproperty_new_organizer(organizer_string)
231                 );
232         }
233
234         /* Determine who is the organizer of this event.
235          * We need to determine "me" or "not me."
236          */
237         organizer = icalcomponent_get_first_property(vevent,
238                                                 ICAL_ORGANIZER_PROPERTY);
239         if (organizer != NULL) {
240                 strcpy(organizer_string, icalproperty_get_organizer(organizer));
241                 if (!strncasecmp(organizer_string, "MAILTO:", 7)) {
242                         strcpy(organizer_string, &organizer_string[7]);
243                         striplt(organizer_string);
244                         lprintf(9, "ISME %s\n", organizer_string);
245                         serv_printf("ISME %s", organizer_string);
246                         serv_gets(buf);
247                         lprintf(9, "%s\n", buf);
248                         if (buf[0] == '2') {
249                                 organizer_is_me = 1;
250                         }
251                 }
252         }
253         wprintf("<TR><TD><B>Organizer</B></TD><TD>");
254         escputs(organizer_string);
255         if (organizer_is_me) {
256                 wprintf(" <FONT SIZE=-1><I>"
257                         "(you are the organizer)</I></FONT>\n");
258         }
259
260         /*
261          * Transmit the organizer as a hidden field.   We don't want the user
262          * to be able to change it, but we do want it fed back to the server,
263          * especially if this is a new event and there is no organizer already
264          * in the calendar object.
265          */
266         wprintf("<INPUT TYPE=\"hidden\" NAME=\"organizer\" VALUE=\"");
267         escputs(organizer_string);
268         wprintf("\">");
269
270         wprintf("</TD></TR>\n");
271
272         /* Attendees (do more with this later) */
273         wprintf("<TR><TD><B>Attendes</B><BR>"
274                 "<FONT SIZE=-2>(Separate multiple attendees with commas)"
275                 "</FONT></TD><TD>"
276                 "<TEXTAREA %s NAME=\"attendees\" wrap=soft "
277                 "ROWS=3 COLS=80 WIDTH=80>\n",
278                 (organizer_is_me ? "" : "DISABLED ")
279         );
280         i = 0;
281         for (attendee = icalcomponent_get_first_property(vevent, ICAL_ATTENDEE_PROPERTY); attendee != NULL; attendee = icalcomponent_get_next_property(vevent, ICAL_ATTENDEE_PROPERTY)) {
282                 strcpy(attendee_string, icalproperty_get_attendee(attendee));
283                 if (!strncasecmp(attendee_string, "MAILTO:", 7)) {
284
285                         /* screen name or email address */
286                         strcpy(attendee_string, &attendee_string[7]);
287                         striplt(attendee_string);
288                         if (i++) wprintf(", ");
289                         escputs(attendee_string);
290                         wprintf(" ");
291
292                         /* participant status */
293                         partstat_as_string(buf, attendee);
294                         escputs(buf);
295                 }
296         }
297         wprintf("</TEXTAREA></TD></TR>\n");
298
299         /* Done with properties. */
300         wprintf("</TABLE>\n<CENTER>"
301                 "<INPUT TYPE=\"submit\" NAME=\"sc\" VALUE=\"Save\">"
302                 "&nbsp;&nbsp;"
303                 "<INPUT TYPE=\"submit\" NAME=\"sc\" VALUE=\"Delete\">\n"
304                 "&nbsp;&nbsp;"
305                 "<INPUT TYPE=\"submit\" NAME=\"sc\" VALUE=\"Cancel\">\n"
306                 "</CENTER>\n"
307         );
308
309         wprintf("</FORM>\n");
310         
311         wprintf("<SCRIPT language=\"javascript\">
312                 <!--
313
314                         if (document.EventForm.alldayevent.checked) {
315                                 document.EventForm.dtstart_hour.value='0';
316                                 document.EventForm.dtstart_hour.disabled = true;
317                                 document.EventForm.dtstart_minute.value='0';
318                                 document.EventForm.dtstart_minute.disabled = true;
319                                 document.EventForm.dtend_hour.value='0';
320                                 document.EventForm.dtend_hour.disabled = true;
321                                 document.EventForm.dtend_minute.value='0';
322                                 document.EventForm.dtend_minute.disabled = true;
323                                 document.EventForm.dtend_month.disabled = true;
324                                 document.EventForm.dtend_day.disabled = true;
325                                 document.EventForm.dtend_year.disabled = true;
326                         }
327                         else {
328                                 document.EventForm.dtstart_hour.disabled = false;
329                                 document.EventForm.dtstart_minute.disabled = false;
330                                 document.EventForm.dtend_hour.disabled = false;
331                                 document.EventForm.dtend_minute.disabled = false;
332                                 document.EventForm.dtend_month.disabled = false;
333                                 document.EventForm.dtend_day.disabled = false;
334                                 document.EventForm.dtend_year.disabled = false;
335                         }
336                 //-->
337                 </SCRIPT>
338         ");
339
340         wDumpContent(1);
341
342         if (created_new_vevent) {
343                 icalcomponent_free(vevent);
344         }
345 }
346
347 /*
348  * Save an edited event
349  */
350 void save_individual_event(icalcomponent *supplied_vevent, long msgnum) {
351         char buf[SIZ];
352         icalproperty *prop;
353         icalcomponent *vevent;
354         int created_new_vevent = 0;
355         int all_day_event = 0;
356         struct icaltimetype event_start;
357         icalproperty *attendee = NULL;
358         char attendee_string[SIZ];
359         int i;
360         int foundit;
361         char form_attendees[SIZ];
362         char organizer_string[SIZ];
363         int sequence = 0;
364
365         if (supplied_vevent != NULL) {
366                 vevent = supplied_vevent;
367         }
368         else {
369                 vevent = icalcomponent_new(ICAL_VEVENT_COMPONENT);
370                 created_new_vevent = 1;
371         }
372
373         if (!strcasecmp(bstr("sc"), "Save")) {
374
375                 /* Replace values in the component with ones from the form */
376
377                 while (prop = icalcomponent_get_first_property(vevent,
378                       ICAL_SUMMARY_PROPERTY), prop != NULL) {
379                         icalcomponent_remove_property(vevent, prop);
380                         icalproperty_free(prop);
381                 }
382                 icalcomponent_add_property(vevent,
383                         icalproperty_new_summary(bstr("summary")));
384
385                 while (prop = icalcomponent_get_first_property(vevent,
386                       ICAL_LOCATION_PROPERTY), prop != NULL) {
387                         icalcomponent_remove_property(vevent, prop);
388                         icalproperty_free(prop);
389                 }
390                 icalcomponent_add_property(vevent,
391                         icalproperty_new_location(bstr("location")));
392                 
393                 while (prop = icalcomponent_get_first_property(vevent,
394                       ICAL_DESCRIPTION_PROPERTY), prop != NULL) {
395                         icalcomponent_remove_property(vevent, prop);
396                         icalproperty_free(prop);
397                 }
398                 icalcomponent_add_property(vevent,
399                         icalproperty_new_description(bstr("description")));
400         
401                 while (prop = icalcomponent_get_first_property(vevent,
402                       ICAL_DTSTART_PROPERTY), prop != NULL) {
403                         icalcomponent_remove_property(vevent, prop);
404                         icalproperty_free(prop);
405                 }
406
407                 if (!strcmp(bstr("alldayevent"), "yes")) {
408                         all_day_event = 1;
409                 }
410                 else {
411                         all_day_event = 0;
412                 }
413
414                 event_start = icaltime_from_webform("dtstart");
415                 if (all_day_event) {
416                         event_start.is_date = 1;
417                         event_start.hour = 0;
418                         event_start.minute = 0;
419                         event_start.second = 0;
420                 }
421
422
423                 /* The following odd-looking snippet of code looks like it
424                  * takes some unnecessary steps.  It is done this way because
425                  * libical incorrectly turns an "all day event" into a normal
426                  * event starting at midnight (i.e. it serializes as date/time
427                  * instead of just date) unless icalvalue_new_date() is used.
428                  * So we force it, if this is an all day event.
429                  */
430                 prop = icalproperty_new_dtstart(event_start);
431                 if (all_day_event) {
432                         icalproperty_set_value(prop,
433                                 icalvalue_new_date(event_start)
434                         );
435                 }
436
437                 if (prop) icalcomponent_add_property(vevent, prop);
438                 else icalproperty_free(prop);
439
440
441
442                 while (prop = icalcomponent_get_first_property(vevent,
443                       ICAL_DTEND_PROPERTY), prop != NULL) {
444                         icalcomponent_remove_property(vevent, prop);
445                         icalproperty_free(prop);
446                 }
447                 while (prop = icalcomponent_get_first_property(vevent,
448                       ICAL_DURATION_PROPERTY), prop != NULL) {
449                         icalcomponent_remove_property(vevent, prop);
450                         icalproperty_free(prop);
451                 }
452
453                 if (all_day_event == 0) {
454                         icalcomponent_add_property(vevent,
455                                 icalproperty_new_dtend(icaltime_normalize(
456                                         icaltime_from_webform("dtend"))
457                                 )
458                         );
459                 }
460
461                 /* Give this event a UID if it doesn't have one. */
462                 if (icalcomponent_get_first_property(vevent,
463                    ICAL_UID_PROPERTY) == NULL) {
464                         generate_new_uid(buf);
465                         icalcomponent_add_property(vevent,
466                                 icalproperty_new_uid(buf)
467                         );
468                 }
469
470                 /* Increment the sequence ID */
471                 while (prop = icalcomponent_get_first_property(vevent,
472                       ICAL_SEQUENCE_PROPERTY), (prop != NULL) ) {
473                         i = icalproperty_get_sequence(prop);
474                         if (i > sequence) sequence = i;
475                         icalcomponent_remove_property(vevent, prop);
476                         icalproperty_free(prop);
477                 }
478                 ++sequence;
479                 icalcomponent_add_property(vevent,
480                         icalproperty_new_sequence(sequence)
481                 );
482                 
483                 /* Set the organizer, only if one does not already exist *and*
484                  * the form is supplying one
485                  */
486                 strcpy(buf, bstr("organizer"));
487                 if ( (icalcomponent_get_first_property(vevent,
488                    ICAL_ORGANIZER_PROPERTY) == NULL) 
489                    && (strlen(buf) > 0) ) {
490
491                         /* set new organizer */
492                         sprintf(organizer_string, "MAILTO:%s", buf);
493                         icalcomponent_add_property(vevent,
494                                 icalproperty_new_organizer(organizer_string)
495                         );
496
497                 }
498
499                 /*
500                  * Add any new attendees listed in the web form
501                  */
502
503                 /* First, strip out the parenthesized partstats.  */
504                 strcpy(form_attendees, bstr("attendees"));
505                 stripout(form_attendees, '(', ')');
506
507                 /* Now iterate! */
508                 for (i=0; i<num_tokens(form_attendees, ','); ++i) {
509                         extract_token(buf, form_attendees, i, ',');
510                         striplt(buf);
511                         if (strlen(buf) > 0) {
512                                 lprintf(9, "Attendee: <%s>\n", buf);
513                                 sprintf(attendee_string, "MAILTO:%s", buf);
514                                 foundit = 0;
515
516                                 for (attendee = icalcomponent_get_first_property(vevent, ICAL_ATTENDEE_PROPERTY); attendee != NULL; attendee = icalcomponent_get_next_property(vevent, ICAL_ATTENDEE_PROPERTY)) {
517                                         if (!strcasecmp(attendee_string,
518                                            icalproperty_get_attendee(attendee)))
519                                                 ++foundit;
520                                 }
521
522
523                                 if (foundit == 0) {
524                                         icalcomponent_add_property(vevent,
525                                                 icalproperty_new_attendee(attendee_string)
526                                         );
527                                 }
528                         }
529                 }
530
531                 /*
532                  * Remove any attendees *not* listed in the web form
533                  */
534 STARTOVER:
535                 for (attendee = icalcomponent_get_first_property(vevent, ICAL_ATTENDEE_PROPERTY); attendee != NULL; attendee = icalcomponent_get_next_property(vevent, ICAL_ATTENDEE_PROPERTY)) {
536                         strcpy(attendee_string, icalproperty_get_attendee(attendee));
537                         if (!strncasecmp(attendee_string, "MAILTO:", 7)) {
538                                 strcpy(attendee_string, &attendee_string[7]);
539                                 striplt(attendee_string);
540                                 foundit = 0;
541                                 for (i=0; i<num_tokens(form_attendees, ','); ++i) {
542                                         extract_token(buf, form_attendees, i, ',');
543                                         striplt(buf);
544                                         if (!strcasecmp(buf, attendee_string)) ++foundit;
545                                 }
546                                 if (foundit == 0) {
547                                         icalcomponent_remove_property(vevent, attendee);
548                                         icalproperty_free(attendee);
549                                         goto STARTOVER;
550                                 }
551                         }
552                 }
553
554                 /*
555                  * Serialize it and save it to the message base
556                  */
557                 serv_puts("ENT0 1|||4");
558                 serv_gets(buf);
559                 if (buf[0] == '4') {
560                         serv_puts("Content-type: text/calendar");
561                         serv_puts("");
562                         serv_puts(icalcomponent_as_ical_string(vevent));
563                         serv_puts("000");
564                 }
565         }
566
567         /*
568          * If the user clicked 'Delete' then delete it.
569          */
570         if ( (!strcasecmp(bstr("sc"), "Delete")) && (msgnum > 0L) ) {
571                 serv_printf("DELE %ld", atol(bstr("msgnum")));
572                 serv_gets(buf);
573         }
574
575         if (created_new_vevent) {
576                 icalcomponent_free(vevent);
577         }
578
579         /* Go back to the event list */
580         readloop("readfwd");
581 }
582
583
584 #endif /* HAVE_ICAL_H */