4 * Functions which handle calendar objects and their processing/display.
11 #ifndef WEBCIT_WITH_CALENDAR_SERVICE
14 * Handler stubs for builds with no calendar library available
16 void cal_process_attachment(char *part_source, long msgnum, char *cal_partnum) {
18 wprintf(_("<I>This message contains calendaring/scheduling information,"
19 " but support for calendars is not available on this "
20 "particular system. Please ask your system administrator to "
21 "install a new version of the Citadel web service with "
22 "calendaring enabled.</I><br />\n")
27 void display_calendar(long msgnum) {
29 "Cannot display calendar item. You are seeing this error "
30 "because your WebCit service has not been installed with "
31 "calendar support. Please contact your system administrator."
35 void display_task(long msgnum) {
37 "Cannot display to-do item. You are seeing this error "
38 "because your WebCit service has not been installed with "
39 "calendar support. Please contact your system administrator."
43 #else /* WEBCIT_WITH_CALENDAR_SERVICE */
46 /****** End of handler stubs. Everything below this line is real. ******/
52 * Process a calendar object
53 * ...at this point it's already been deserialized by cal_process_attachment()
56 void cal_process_object(icalcomponent *cal,
62 icalproperty *method = NULL;
63 icalproperty_method the_method = ICAL_METHOD_NONE;
65 struct icaltimetype t;
68 char conflict_name[256];
69 char conflict_message[256];
72 /* Leading HTML for the display of this object */
73 if (recursion_level == 0) {
74 wprintf("<CENTER><TABLE border=0>\n");
77 /* Look for a method */
78 method = icalcomponent_get_first_property(cal, ICAL_METHOD_PROPERTY);
80 /* See what we need to do with this */
82 the_method = icalproperty_get_method(method);
84 case ICAL_METHOD_REQUEST:
85 wprintf("<tr><td colspan=\"2\">\n"
86 "<img align=\"center\" "
87 "src=\"/static/calarea_48x.gif\">"
90 wprintf(_("Meeting invitation"));
91 wprintf("</B></TD></TR>\n");
93 case ICAL_METHOD_REPLY:
94 wprintf("<TR><TD COLSPAN=2>\n"
96 "src=\"/static/calarea_48x.gif\">"
99 wprintf(_("Attendee's reply to your invitation"));
100 wprintf("</B></TD></TR>\n");
102 case ICAL_METHOD_PUBLISH:
103 wprintf("<TR><TD COLSPAN=2>\n"
105 "src=\"/static/calarea_48x.gif\">"
108 wprintf(_("Published event"));
109 wprintf("</B></TD></TR>\n");
112 wprintf("<TR><TD COLSPAN=2>");
113 wprintf(_("This is an unknown type of calendar item."));
114 wprintf("</TD></TR>\n");
119 p = icalcomponent_get_first_property(cal, ICAL_SUMMARY_PROPERTY);
121 wprintf("<TR><TD><B>");
122 wprintf(_("Summary:"));
123 wprintf("</B></TD><TD>");
124 escputs((char *)icalproperty_get_comment(p));
125 wprintf("</TD></TR>\n");
128 p = icalcomponent_get_first_property(cal, ICAL_LOCATION_PROPERTY);
130 wprintf("<TR><TD><B>");
131 wprintf(_("Location:"));
132 wprintf("</B></TD><TD>");
133 escputs((char *)icalproperty_get_comment(p));
134 wprintf("</TD></TR>\n");
138 * Only show start/end times if we're actually looking at the VEVENT
139 * component. Otherwise it shows bogus dates for things like timezone.
141 if (icalcomponent_isa(cal) == ICAL_VEVENT_COMPONENT) {
143 p = icalcomponent_get_first_property(cal,
144 ICAL_DTSTART_PROPERTY);
146 t = icalproperty_get_dtstart(p);
149 wprintf("<TR><TD><B>");
151 wprintf("</B></TD><TD>"
152 "%s %d, %d</TD></TR>",
158 tt = icaltime_as_timet(t);
159 fmt_date(buf, tt, 0);
160 wprintf("<TR><TD><B>");
161 wprintf(_("Starting date/time:"));
162 wprintf("</B></TD><TD>%s</TD></TR>", buf);
166 p = icalcomponent_get_first_property(cal, ICAL_DTEND_PROPERTY);
168 t = icalproperty_get_dtend(p);
169 tt = icaltime_as_timet(t);
170 fmt_date(buf, tt, 0);
171 wprintf("<TR><TD><B>");
172 wprintf(_("Ending date/time:"));
173 wprintf("</B></TD><TD>%s</TD></TR>", buf);
178 p = icalcomponent_get_first_property(cal, ICAL_DESCRIPTION_PROPERTY);
180 wprintf("<TR><TD><B>");
181 wprintf(_("Description:"));
182 wprintf("</B></TD><TD>");
183 escputs((char *)icalproperty_get_comment(p));
184 wprintf("</TD></TR>\n");
187 /* If the component has attendees, iterate through them. */
188 for (p = icalcomponent_get_first_property(cal, ICAL_ATTENDEE_PROPERTY); (p != NULL); p = icalcomponent_get_next_property(cal, ICAL_ATTENDEE_PROPERTY)) {
189 wprintf("<TR><TD><B>");
190 wprintf(_("Attendee:"));
191 wprintf("</B></TD><TD>");
192 safestrncpy(buf, icalproperty_get_attendee(p), sizeof buf);
193 if (!strncasecmp(buf, "MAILTO:", 7)) {
195 /* screen name or email address */
196 strcpy(buf, &buf[7]);
201 /* participant status */
202 partstat_as_string(buf, p);
205 wprintf("</TD></TR>\n");
208 /* If the component has subcomponents, recurse through them. */
209 for (c = icalcomponent_get_first_component(cal, ICAL_ANY_COMPONENT);
211 c = icalcomponent_get_next_component(cal, ICAL_ANY_COMPONENT)) {
212 /* Recursively process subcomponent */
213 cal_process_object(c, recursion_level+1, msgnum, cal_partnum);
216 /* If this is a REQUEST, display conflicts and buttons */
217 if (the_method == ICAL_METHOD_REQUEST) {
219 /* Check for conflicts */
220 lprintf(9, "Checking server calendar for conflicts...\n");
221 serv_printf("ICAL conflicts|%ld|%s|", msgnum, cal_partnum);
222 serv_getln(buf, sizeof buf);
224 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
225 extract_token(conflict_name, buf, 3, '|', sizeof conflict_name);
226 is_update = extract_int(buf, 4);
229 snprintf(conflict_message, sizeof conflict_message,
230 _("This is an update of '%s' which is already in your calendar."), conflict_name);
233 snprintf(conflict_message, sizeof conflict_message,
234 _("This event would conflict with '%s' which is already in your calendar."), conflict_name);
237 wprintf("<TR><TD><B><I>%s</I></B></TD><td>",
243 escputs(conflict_message);
244 wprintf("</TD></TR>\n");
247 lprintf(9, "...done.\n");
249 /* Display the Accept/Decline buttons */
250 wprintf("<TR><TD>How would you like to respond to this invitation?</td>"
252 "<a href=\"/respond_to_request?msgnum=%ld&cal_partnum=%s&sc=Accept\">%s</a>"
254 "<a href=\"/respond_to_request?msgnum=%ld&cal_partnum=%s&sc=Tentative\">%s</a>"
256 "<a href=\"/respond_to_request?msgnum=%ld&cal_partnum=%s&sc=Decline\">%s</a>"
257 "</FONT></TD></TR>\n",
258 msgnum, cal_partnum, _("Accept"),
259 msgnum, cal_partnum, _("Tentative"),
260 msgnum, cal_partnum, _("Decline")
265 /* If this is a REPLY, display update button */
266 if (the_method == ICAL_METHOD_REPLY) {
269 * In the future, if we want to validate this object before
270 * continuing, we can do it this way:
271 serv_printf("ICAL whatever|%ld|%s|", msgnum, cal_partnum);
272 serv_getln(buf, sizeof buf);
276 /* Display the update buttons */
279 "</td><td><font size=+1>"
280 "<a href=\"/handle_rsvp?msgnum=%ld&cal_partnum=%s&sc=Update\">%s</a>"
282 "<a href=\"/handle_rsvp?msgnum=%ld&cal_partnum=%s&sc=Ignore\">%s</a>"
285 _("Click <i>Update</i> to accept this reply and update your calendar."),
286 msgnum, cal_partnum, _("Update"),
287 msgnum, cal_partnum, _("Ignore")
292 /* Trailing HTML for the display of this object */
293 if (recursion_level == 0) {
295 wprintf("</TR></TABLE></CENTER>\n");
301 * Deserialize a calendar object in a message so it can be processed.
302 * (This is the main entry point for these things)
304 void cal_process_attachment(char *part_source, long msgnum, char *cal_partnum) {
307 cal = icalcomponent_new_from_string(part_source);
310 wprintf(_("There was an error parsing this calendar item."));
316 cal_process_object(cal, 0, msgnum, cal_partnum);
318 /* Free the memory we obtained from libical's constructor */
319 icalcomponent_free(cal);
326 * Respond to a meeting request
328 void respond_to_request(void) {
331 output_headers(1, 1, 2, 0, 0, 0);
333 wprintf("<div id=\"banner\">\n");
334 wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
335 "<SPAN CLASS=\"titlebar\">");
336 wprintf(_("Respond to meeting request"));
338 "</TD></TR></TABLE>\n"
340 wprintf("</div>\n<div id=\"content\">\n");
342 serv_printf("ICAL respond|%s|%s|%s|",
347 serv_getln(buf, sizeof buf);
350 wprintf("<TABLE BORDER=0><TR><TD>"
351 "<img src=\"static/calarea_48x.gif\" ALIGN=CENTER>"
354 if (!strcasecmp(bstr("sc"), "accept")) {
355 wprintf(_("You have accepted this meeting invitation. "
356 "It has been entered into your calendar.")
358 } else if (!strcasecmp(bstr("sc"), "tentative")) {
359 wprintf(_("You have tentatively accepted this meeting invitation. "
360 "It has been 'pencilled in' to your calendar.")
362 } else if (!strcasecmp(bstr("sc"), "decline")) {
363 wprintf(_("You have declined this meeting invitation. "
364 "It has <b>not</b> been entered into your calendar.")
368 wprintf(_("A reply has been sent to the meeting organizer."));
369 wprintf("</TD></TR></TABLE>\n");
371 wprintf("<img src=\"static/error.gif\" ALIGN=CENTER>"
375 wprintf("<a href=\"/dotskip?room=");
376 urlescputs(WC->wc_roomname);
377 wprintf("\"><br />");
378 wprintf(_("Return to messages"));
379 wprintf("</A><br />\n");
387 * Handle an incoming RSVP
389 void handle_rsvp(void) {
392 output_headers(1, 1, 2, 0, 0, 0);
394 wprintf("<div id=\"banner\">\n");
395 wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
396 "<SPAN CLASS=\"titlebar\">");
397 wprintf(_("Update your calendar with this RSVP"));
399 "</TD></TR></TABLE>\n"
400 "</div>\n<div id=\"content\">\n"
403 serv_printf("ICAL handle_rsvp|%s|%s|%s|",
408 serv_getln(buf, sizeof buf);
411 wprintf("<TABLE BORDER=0><TR><TD>"
412 "<img src=\"static/calarea_48x.gif\" ALIGN=CENTER>"
415 if (!strcasecmp(bstr("sc"), "update")) {
416 wprintf(_("Your calendar has been updated to reflect this RSVP."));
417 } else if (!strcasecmp(bstr("sc"), "ignore")) {
418 wprintf(_("You have chosen to ignore this RSVP. "
419 "Your calendar has <b>not</b> been updated.")
422 wprintf("</TD></TR></TABLE>\n"
425 wprintf("<img src=\"static/error.gif\" ALIGN=CENTER>"
429 wprintf("<a href=\"/dotskip?room=");
430 urlescputs(WC->wc_roomname);
431 wprintf("\"><br />");
432 wprintf(_("Return to messages"));
433 wprintf("</A><br />\n");
441 /*****************************************************************************/
446 * Display handlers for message reading
452 * If we're reading calendar items, just store them for now. We have to
453 * sort and re-output them later when we draw the calendar.
455 void display_individual_cal(icalcomponent *cal, long msgnum) {
459 WC->disp_cal = realloc(WC->disp_cal,
460 (sizeof(struct disp_cal) * WC->num_cal) );
461 WC->disp_cal[WC->num_cal - 1].cal = icalcomponent_new_clone(cal);
463 WC->disp_cal[WC->num_cal - 1].cal_msgnum = msgnum;
469 * Display a task by itself (for editing)
472 void display_edit_individual_task(icalcomponent *supplied_vtodo, long msgnum) {
473 icalcomponent *vtodo;
475 struct icaltimetype t;
477 int created_new_vtodo = 0;
481 if (supplied_vtodo != NULL) {
482 vtodo = supplied_vtodo;
484 /* If we're looking at a fully encapsulated VCALENDAR
485 * rather than a VTODO component, attempt to use the first
486 * relevant VTODO subcomponent. If there is none, the
487 * NULL returned by icalcomponent_get_first_component() will
488 * tell the next iteration of this function to create a
491 if (icalcomponent_isa(vtodo) == ICAL_VCALENDAR_COMPONENT) {
492 display_edit_individual_task(
493 icalcomponent_get_first_component(
494 vtodo, ICAL_VTODO_COMPONENT
501 vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT);
502 created_new_vtodo = 1;
505 output_headers(1, 1, 2, 0, 0, 0);
506 wprintf("<div id=\"banner\">\n"
507 "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR>"
508 "<TD><img src=\"/static/taskmanag_48x.gif\"></TD>"
509 "<td><SPAN CLASS=\"titlebar\">");
510 wprintf(_("Edit task"));
512 "</TD></TR></TABLE>\n"
513 "</div>\n<div id=\"content\">\n"
516 wprintf("<div id=\"fix_scrollbar_bug\">"
517 "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>");
519 wprintf("<FORM METHOD=\"POST\" action=\"/save_task\">\n");
520 wprintf("<INPUT TYPE=\"hidden\" NAME=\"msgnum\" VALUE=\"%ld\">\n",
523 wprintf("<TABLE border=0>\n");
526 wprintf(_("Summary:"));
528 "<INPUT TYPE=\"text\" NAME=\"summary\" "
529 "MAXLENGTH=\"64\" SIZE=\"64\" VALUE=\"");
530 p = icalcomponent_get_first_property(vtodo, ICAL_SUMMARY_PROPERTY);
532 escputs((char *)icalproperty_get_comment(p));
534 wprintf("\"></TD></TR>\n");
537 wprintf(_("Start date:"));
538 wprintf("</TD><TD>");
539 p = icalcomponent_get_first_property(vtodo, ICAL_DTSTART_PROPERTY);
541 t = icalproperty_get_dtstart(p);
544 t = icaltime_from_timet(now, 0);
546 display_icaltimetype_as_webform(&t, "dtstart");
547 wprintf("</TD></TR>\n");
550 wprintf(_("Due date:"));
551 wprintf("</TD><TD>");
552 p = icalcomponent_get_first_property(vtodo, ICAL_DUE_PROPERTY);
554 t = icalproperty_get_due(p);
557 t = icaltime_from_timet(now, 0);
559 display_icaltimetype_as_webform(&t, "due");
560 wprintf("</TD></TR>\n");
562 wprintf(_("Description:"));
563 wprintf("</TD><TD>");
564 wprintf("<TEXTAREA NAME=\"description\" wrap=soft "
565 "ROWS=10 COLS=80 WIDTH=80>\n"
567 p = icalcomponent_get_first_property(vtodo, ICAL_DESCRIPTION_PROPERTY);
569 escputs((char *)icalproperty_get_comment(p));
571 wprintf("</TEXTAREA></TD></TR></TABLE>\n");
574 "<INPUT TYPE=\"submit\" NAME=\"save_button\" VALUE=\"%s\">"
576 "<INPUT TYPE=\"submit\" NAME=\"delete_button\" VALUE=\"%s\">\n"
578 "<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">\n"
585 wprintf("</FORM>\n");
587 wprintf("</td></tr></table></div>\n");
590 if (created_new_vtodo) {
591 icalcomponent_free(vtodo);
596 * Save an edited task
599 void save_individual_task(icalcomponent *supplied_vtodo, long msgnum) {
601 int delete_existing = 0;
603 icalcomponent *vtodo, *encaps;
604 int created_new_vtodo = 0;
607 struct icaltimetype t;
609 if (supplied_vtodo != NULL) {
610 vtodo = supplied_vtodo;
611 /* If we're looking at a fully encapsulated VCALENDAR
612 * rather than a VTODO component, attempt to use the first
613 * relevant VTODO subcomponent. If there is none, the
614 * NULL returned by icalcomponent_get_first_component() will
615 * tell the next iteration of this function to create a
618 if (icalcomponent_isa(vtodo) == ICAL_VCALENDAR_COMPONENT) {
619 save_individual_task(
620 icalcomponent_get_first_component(
621 vtodo, ICAL_VTODO_COMPONENT
628 vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT);
629 created_new_vtodo = 1;
632 if (strlen(bstr("save_button")) > 0) {
634 /* Replace values in the component with ones from the form */
636 while (prop = icalcomponent_get_first_property(vtodo,
637 ICAL_SUMMARY_PROPERTY), prop != NULL) {
638 icalcomponent_remove_property(vtodo, prop);
639 icalproperty_free(prop);
641 icalcomponent_add_property(vtodo,
642 icalproperty_new_summary(bstr("summary")));
644 while (prop = icalcomponent_get_first_property(vtodo,
645 ICAL_DESCRIPTION_PROPERTY), prop != NULL) {
646 icalcomponent_remove_property(vtodo, prop);
647 icalproperty_free(prop);
649 icalcomponent_add_property(vtodo,
650 icalproperty_new_description(bstr("description")));
652 while (prop = icalcomponent_get_first_property(vtodo,
653 ICAL_DTSTART_PROPERTY), prop != NULL) {
654 icalcomponent_remove_property(vtodo, prop);
655 icalproperty_free(prop);
657 icaltime_from_webform(&t, "dtstart");
658 icalcomponent_add_property(vtodo,
659 icalproperty_new_dtstart(t)
662 while (prop = icalcomponent_get_first_property(vtodo,
663 ICAL_DUE_PROPERTY), prop != NULL) {
664 icalcomponent_remove_property(vtodo, prop);
665 icalproperty_free(prop);
667 icaltime_from_webform(&t, "due");
668 icalcomponent_add_property(vtodo,
669 icalproperty_new_due(t)
672 /* Give this task a UID if it doesn't have one. */
673 lprintf(9, "Give this task a UID if it doesn't have one.\n");
674 if (icalcomponent_get_first_property(vtodo,
675 ICAL_UID_PROPERTY) == NULL) {
677 icalcomponent_add_property(vtodo,
678 icalproperty_new_uid(buf)
682 /* Increment the sequence ID */
683 lprintf(9, "Increment the sequence ID\n");
684 while (prop = icalcomponent_get_first_property(vtodo,
685 ICAL_SEQUENCE_PROPERTY), (prop != NULL) ) {
686 i = icalproperty_get_sequence(prop);
687 lprintf(9, "Sequence was %d\n", i);
688 if (i > sequence) sequence = i;
689 icalcomponent_remove_property(vtodo, prop);
690 icalproperty_free(prop);
693 lprintf(9, "New sequence is %d. Adding...\n", sequence);
694 icalcomponent_add_property(vtodo,
695 icalproperty_new_sequence(sequence)
699 * Encapsulate event into full VCALENDAR component. Clone it first,
700 * for two reasons: one, it's easier to just free the whole thing
701 * when we're done instead of unbundling, but more importantly, we
702 * can't encapsulate something that may already be encapsulated
705 lprintf(9, "Encapsulating into full VCALENDAR component\n");
706 encaps = ical_encapsulate_subcomponent(icalcomponent_new_clone(vtodo));
708 /* Serialize it and save it to the message base */
709 serv_puts("ENT0 1|||4");
710 serv_getln(buf, sizeof buf);
712 serv_puts("Content-type: text/calendar");
714 serv_puts(icalcomponent_as_ical_string(encaps));
717 /* Probably not necessary; the server will see the UID
718 * of the object and delete the old one anyway, but
723 icalcomponent_free(encaps);
727 * If the user clicked 'Delete' then explicitly delete the message.
729 if (strlen(bstr("delete_button")) > 0) {
733 if ( (delete_existing) && (msgnum > 0L) ) {
734 serv_printf("DELE %ld", atol(bstr("msgnum")));
735 serv_getln(buf, sizeof buf);
738 if (created_new_vtodo) {
739 icalcomponent_free(vtodo);
742 /* Go back to the task list */
749 * Code common to all display handlers. Given a message number and a MIME
750 * type, we load the message and hunt for that MIME type. If found, we load
751 * the relevant part, deserialize it into a libical component, filter it for
752 * the requested object type, and feed it to the specified handler.
755 void display_using_handler(long msgnum,
757 icalcomponent_kind which_kind,
758 void (*callback)(icalcomponent *, long)
761 char mime_partnum[SIZ];
762 char mime_filename[SIZ];
763 char mime_content_type[SIZ];
764 char mime_disposition[SIZ];
766 char relevant_partnum[SIZ];
767 char *relevant_source = NULL;
768 icalcomponent *cal, *c;
770 sprintf(buf, "MSG0 %ld|1", msgnum); /* ask for headers only */
772 serv_getln(buf, sizeof buf);
773 if (buf[0] != '1') return;
775 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
776 if (!strncasecmp(buf, "part=", 5)) {
777 extract_token(mime_filename, &buf[5], 1, '|', sizeof mime_filename);
778 extract_token(mime_partnum, &buf[5], 2, '|', sizeof mime_partnum);
779 extract_token(mime_disposition, &buf[5], 3, '|', sizeof mime_disposition);
780 extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type);
781 mime_length = extract_int(&buf[5], 5);
783 if (!strcasecmp(mime_content_type, "text/calendar")) {
784 strcpy(relevant_partnum, mime_partnum);
790 if (strlen(relevant_partnum) > 0) {
791 relevant_source = load_mimepart(msgnum, relevant_partnum);
792 if (relevant_source != NULL) {
794 cal = icalcomponent_new_from_string(relevant_source);
799 /* Simple components of desired type */
800 if (icalcomponent_isa(cal) == which_kind) {
801 callback(cal, msgnum);
804 /* Subcomponents of desired type */
805 for (c = icalcomponent_get_first_component(cal,
808 c = icalcomponent_get_next_component(cal,
812 icalcomponent_free(cal);
814 free(relevant_source);
820 void display_calendar(long msgnum) {
821 display_using_handler(msgnum, "text/calendar",
822 ICAL_VEVENT_COMPONENT,
823 display_individual_cal);
826 void display_task(long msgnum) {
827 display_using_handler(msgnum, "text/calendar",
828 ICAL_VTODO_COMPONENT,
829 display_individual_cal);
832 void display_edit_task(void) {
835 /* Force change the room if we have to */
836 if (strlen(bstr("taskrm")) > 0) {
837 gotoroom(bstr("taskrm"));
840 msgnum = atol(bstr("msgnum"));
843 display_using_handler(msgnum, "text/calendar",
844 ICAL_VTODO_COMPONENT,
845 display_edit_individual_task);
849 display_edit_individual_task(NULL, 0L);
853 void save_task(void) {
856 msgnum = atol(bstr("msgnum"));
858 display_using_handler(msgnum, "text/calendar",
859 ICAL_VTODO_COMPONENT,
860 save_individual_task);
863 save_individual_task(NULL, 0L);
867 void display_edit_event(void) {
870 msgnum = atol(bstr("msgnum"));
873 display_using_handler(msgnum, "text/calendar",
874 ICAL_VEVENT_COMPONENT,
875 display_edit_individual_event);
879 display_edit_individual_event(NULL, 0L);
883 void save_event(void) {
886 msgnum = atol(bstr("msgnum"));
889 display_using_handler(msgnum, "text/calendar",
890 ICAL_VEVENT_COMPONENT,
891 save_individual_event);
894 save_individual_event(NULL, 0L);
903 * freebusy display (for client software)
905 void do_freebusy(char *req) {
910 extract_token(who, req, 1, ' ', sizeof who);
911 if (!strncasecmp(who, "/freebusy/", 10)) {
912 strcpy(who, &who[10]);
916 if ( (!strcasecmp(&who[strlen(who)-4], ".vcf"))
917 || (!strcasecmp(&who[strlen(who)-4], ".ifb"))
918 || (!strcasecmp(&who[strlen(who)-4], ".vfb")) ) {
919 who[strlen(who)-4] = 0;
922 lprintf(9, "freebusy requested for <%s>\n", who);
923 serv_printf("ICAL freebusy|%s", who);
924 serv_getln(buf, sizeof buf);
927 wprintf("HTTP/1.1 404 %s\n", &buf[4]);
928 output_headers(0, 0, 0, 0, 0, 0);
929 wprintf("Content-Type: text/plain\r\n");
931 wprintf("%s\n", &buf[4]);
935 fb = read_server_text();
936 http_transmit_thing(fb, strlen(fb), "text/calendar", 0);
942 #endif /* WEBCIT_WITH_CALENDAR_SERVICE */