1 // Functions which handle calendar objects and their processing/display.
3 // Copyright (c) 1996-2022 by the citadel.org team
5 // This program is open source software. You can redistribute it and/or
6 // modify it under the terms of the GNU General Public License, version 3.
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
14 #include "webserver.h"
17 // Process a calendar object. At this point it's already been deserialized by cal_process_attachment()
19 // cal: the calendar object
20 // recursion_level: Number of times we've recursed into this function
21 // msgnum: Message number on the Citadel server
22 // cal_partnum: MIME part number within that message containing the calendar object
23 void cal_process_object(StrBuf * Target, icalcomponent * cal, int recursion_level, long msgnum, const char *cal_partnum) {
25 icalproperty *method = NULL;
26 icalproperty_method the_method = ICAL_METHOD_NONE;
28 struct icaltimetype t;
31 char conflict_name[256];
32 char conflict_message[256];
35 static int divcount = 0;
38 sprintf(divname, "rsvp%04x", ++divcount);
40 // Convert timezones to something easy to display.
41 // It's safe to do this in memory because we're only changing it on the
42 // display side -- when we tell the server to do something with the object,
43 // the server will be working with its original copy in the database.
44 if ((cal) && (recursion_level == 0)) {
48 // Leading HTML for the display of this object
49 if (recursion_level == 0) {
50 StrBufAppendPrintf(Target, "<div class=\"mimepart\">\n");
54 method = icalcomponent_get_first_property(cal, ICAL_METHOD_PROPERTY);
56 // See what we need to do with this
59 the_method = icalproperty_get_method(method);
61 StrBufAppendPrintf(Target, "<div id=\"%s_title\">", divname);
62 StrBufAppendPrintf(Target, "<img src=\"static/webcit_icons/essen/32x32/calendar.png\">");
63 StrBufAppendPrintf(Target, "<span>");
65 case ICAL_METHOD_REQUEST:
66 title = _("Meeting invitation");
68 case ICAL_METHOD_REPLY:
69 title = _("Attendee's reply to your invitation");
71 case ICAL_METHOD_PUBLISH:
72 title = _("Published event");
75 title = _("This is an unknown type of calendar item.");
78 StrBufAppendPrintf(Target, "</span>");
80 StrBufAppendPrintf(Target, " %s", title);
81 StrBufAppendPrintf(Target, "</div>");
84 StrBufAppendPrintf(Target, "<dl>");
85 p = icalcomponent_get_first_property(cal, ICAL_SUMMARY_PROPERTY);
87 StrBufAppendPrintf(Target, "<dt>");
88 StrBufAppendPrintf(Target, _("Summary:"));
89 StrBufAppendPrintf(Target, "</dt><dd>");
90 StrEscAppend(Target, NULL, (char *) icalproperty_get_comment(p), 0, 0);
91 StrBufAppendPrintf(Target, "</dd>\n");
94 p = icalcomponent_get_first_property(cal, ICAL_LOCATION_PROPERTY);
96 StrBufAppendPrintf(Target, "<dt>");
97 StrBufAppendPrintf(Target, _("Location:"));
98 StrBufAppendPrintf(Target, "</dt><dd>");
99 StrEscAppend(Target, NULL, (char *) icalproperty_get_comment(p), 0, 0);
100 StrBufAppendPrintf(Target, "</dd>\n");
103 // Only show start/end times if we're actually looking at the VEVENT
104 // component. Otherwise it shows bogus dates for things like timezone.
105 if (icalcomponent_isa(cal) == ICAL_VEVENT_COMPONENT) {
107 p = icalcomponent_get_first_property(cal, ICAL_DTSTART_PROPERTY);
109 t = icalproperty_get_dtstart(p);
114 memset(&d_tm, 0, sizeof d_tm);
115 d_tm.tm_year = t.year - 1900;
116 d_tm.tm_mon = t.month - 1;
117 d_tm.tm_mday = t.day;
118 wc_strftime(d_str, sizeof d_str, "%x", &d_tm);
119 StrBufAppendPrintf(Target, "<dt>");
120 StrBufAppendPrintf(Target, _("Date:"));
121 StrBufAppendPrintf(Target, "</dt><dd>%s</dd>", d_str);
124 tt = icaltime_as_timet(t);
125 webcit_fmt_date(buf, 256, tt, DATEFMT_FULL);
126 StrBufAppendPrintf(Target, "<dt>");
127 StrBufAppendPrintf(Target, _("Starting date/time:"));
128 StrBufAppendPrintf(Target, "</dt><dd>%s</dd>", buf);
132 p = icalcomponent_get_first_property(cal, ICAL_DTEND_PROPERTY);
134 t = icalproperty_get_dtend(p);
135 tt = icaltime_as_timet(t);
136 webcit_fmt_date(buf, 256, tt, DATEFMT_FULL);
137 StrBufAppendPrintf(Target, "<dt>");
138 StrBufAppendPrintf(Target, _("Ending date/time:"));
139 StrBufAppendPrintf(Target, "</dt><dd>%s</dd>", buf);
144 p = icalcomponent_get_first_property(cal, ICAL_DESCRIPTION_PROPERTY);
146 StrBufAppendPrintf(Target, "<dt>");
147 StrBufAppendPrintf(Target, _("Description:"));
148 StrBufAppendPrintf(Target, "</dt><dd>");
149 StrEscAppend(Target, NULL, (char *) icalproperty_get_comment(p), 0, 0);
150 StrBufAppendPrintf(Target, "</dd>\n");
153 if (icalcomponent_get_first_property(cal, ICAL_RRULE_PROPERTY)) {
154 // Unusual string syntax used here in order to re-use existing translations
155 StrBufAppendPrintf(Target, "<dt>%s:</dt><dd>%s.</dd>\n", _("Recurrence"), _("This is a recurring event")
159 // If the component has attendees, iterate through them.
160 for (p = icalcomponent_get_first_property(cal, ICAL_ATTENDEE_PROPERTY);
161 (p != NULL); p = icalcomponent_get_next_property(cal, ICAL_ATTENDEE_PROPERTY)) {
162 StrBufAppendPrintf(Target, "<dt>");
163 StrBufAppendPrintf(Target, _("Attendee:"));
164 StrBufAppendPrintf(Target, "</dt><dd>");
165 ch = icalproperty_get_attendee(p);
166 if ((ch != NULL) && !strncasecmp(ch, "MAILTO:", 7)) {
168 // screen name or email address
169 safestrncpy(buf, ch + 7, sizeof(buf));
171 StrEscAppend(Target, NULL, buf, 0, 0);
172 StrBufAppendPrintf(Target, " ");
174 // participant status
175 partstat_as_string(buf, p);
176 StrEscAppend(Target, NULL, buf, 0, 0);
178 StrBufAppendPrintf(Target, "</dd>\n");
181 // If the component has subcomponents, recurse through them.
182 for (c = icalcomponent_get_first_component(cal, ICAL_ANY_COMPONENT);
183 (c != 0); c = icalcomponent_get_next_component(cal, ICAL_ANY_COMPONENT)
185 // Recursively process subcomponent
186 cal_process_object(Target, c, recursion_level + 1, msgnum, cal_partnum);
189 // If this is a REQUEST, display conflicts and buttons
190 if (the_method == ICAL_METHOD_REQUEST) {
192 // Check for conflicts
193 syslog(LOG_DEBUG, "Checking server calendar for conflicts...");
194 serv_printf("ICAL conflicts|%ld|%s|", msgnum, cal_partnum);
195 serv_getln(buf, sizeof buf);
197 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
198 extract_token(conflict_name, buf, 3, '|', sizeof conflict_name);
199 is_update = extract_int(buf, 4);
202 snprintf(conflict_message, sizeof conflict_message,
203 _("This is an update of '%s' which is already in your calendar."), conflict_name);
206 snprintf(conflict_message, sizeof conflict_message,
207 _("This event would conflict with '%s' which is already in your calendar."),
211 StrBufAppendPrintf(Target, "<dt>%s", (is_update ? _("Update:") : _("CONFLICT:")
214 StrBufAppendPrintf(Target, "</dt><dd>");
215 StrEscAppend(Target, NULL, conflict_message, 0, 0);
216 StrBufAppendPrintf(Target, "</dd>\n");
219 syslog(LOG_DEBUG, "...done.\n");
221 StrBufAppendPrintf(Target, "</dl>");
223 // Display the Accept/Decline buttons
224 StrBufAppendPrintf(Target, "<p id=\"%s_question\">"
226 " <span class=\"button_link\"> "
227 "<a href=\"javascript:RespondToInvitation('%s_question','%s_title','%ld','%s','Accept');\">%s</a>"
228 "</span> <span class=\"button_link\">"
229 "<a href=\"javascript:RespondToInvitation('%s_question','%s_title','%ld','%s','Tentative');\">%s</a>"
230 "</span> <span class=\"button_link\">"
231 "<a href=\"javascript:RespondToInvitation('%s_question','%s_title','%ld','%s','Decline');\">%s</a>"
234 _("How would you like to respond to this invitation?"),
235 divname, divname, msgnum, cal_partnum, _("Accept"),
236 divname, divname, msgnum, cal_partnum, _("Tentative"),
237 divname, divname, msgnum, cal_partnum, _("Decline")
242 // If this is a REPLY, display update button
243 if (the_method == ICAL_METHOD_REPLY) {
245 // Display the update buttons
246 StrBufAppendPrintf(Target, "<p id=\"%s_question\" >"
248 " <span class=\"button_link\"> "
249 "<a href=\"javascript:HandleRSVP('%s_question','%s_title','%ld','%s','Update');\">%s</a>"
250 "</span> <span class=\"button_link\">"
251 "<a href=\"javascript:HandleRSVP('%s_question','%s_title','%ld','%s','Ignore');\">%s</a>"
254 _("Click <i>Update</i> to accept this reply and update your calendar."),
255 divname, divname, msgnum, cal_partnum, _("Update"),
256 divname, divname, msgnum, cal_partnum, _("Ignore")
261 // Trailing HTML for the display of this object
262 if (recursion_level == 0) {
263 StrBufAppendPrintf(Target, "<p> </p></div>\n");
268 // Deserialize a calendar object in a message so it can be displayed.
269 void cal_process_attachment(wc_mime_attachment * Mime) {
272 cal = icalcomponent_new_from_string(ChrPtr(Mime->Data));
273 FlushStrBuf(Mime->Data);
275 StrBufAppendPrintf(Mime->Data, _("There was an error parsing this calendar item."));
276 StrBufAppendPrintf(Mime->Data, "<br>\n");
280 cal_process_object(Mime->Data, cal, 0, Mime->msgnum, ChrPtr(Mime->PartNum));
282 // Free the memory we obtained from libical's constructor
283 icalcomponent_free(cal);
287 // Respond to a meeting request - accept/decline meeting
288 void respond_to_request(void) {
291 begin_ajax_response();
293 serv_printf("ICAL respond|%s|%s|%s|", bstr("msgnum"), bstr("cal_partnum"), bstr("sc")
295 serv_getln(buf, sizeof buf);
298 wc_printf("<img src=\"static/webcit_icons/essen/32x32/calendar.png\"><span>");
299 if (!strcasecmp(bstr("sc"), "accept")) {
300 wc_printf(_("You have accepted this meeting invitation. " "It has been entered into your calendar.")
303 else if (!strcasecmp(bstr("sc"), "tentative")) {
304 wc_printf(_("You have tentatively accepted this meeting invitation. "
305 "It has been 'pencilled in' to your calendar.")
308 else if (!strcasecmp(bstr("sc"), "decline")) {
309 wc_printf(_("You have declined this meeting invitation. "
310 "It has <b>not</b> been entered into your calendar.")
314 wc_printf(_("A reply has been sent to the meeting organizer."));
315 wc_printf("</span>");
318 wc_printf("<img align=\"center\" src=\"static/webcit_icons/error.gif\"><span>");
319 wc_printf("%s\n", &buf[4]);
320 wc_printf("</span>");
326 // Handle an incoming RSVP
327 void handle_rsvp(void) {
330 begin_ajax_response();
332 serv_printf("ICAL handle_rsvp|%s|%s|%s|", bstr("msgnum"), bstr("cal_partnum"), bstr("sc")
334 serv_getln(buf, sizeof buf);
337 wc_printf("<img src=\"static/webcit_icons/calendar.png\"><span>");
338 if (!strcasecmp(bstr("sc"), "update")) {
339 // Translators: RSVP aka Répondez s'il-vous-plaît is the term
340 // that the recipient of an ical-invitation should please
341 // answer this request.
342 wc_printf(_("Your calendar has been updated to reflect this RSVP."));
344 else if (!strcasecmp(bstr("sc"), "ignore")) {
345 wc_printf(_("You have chosen to ignore this RSVP. " "Your calendar has <b>not</b> been updated.")
348 wc_printf("</span>");
351 wc_printf("<img src=\"static/webcit_icons/error.gif\"><span> %s\n", &buf[4]);
352 wc_printf("</span>");
359 // free memory allocated using libical
360 void delete_cal(void *vCal) {
361 disp_cal *Cal = (disp_cal *) vCal;
362 icalcomponent_free(Cal->cal);
368 // This is the meat-and-bones of the first part of our two-phase calendar display.
369 // As we encounter calendar items in messages being read from the server, we break out
370 // any iCalendar objects and store them in a hash table. Later on, the second phase will
371 // use this hash table to render the calendar for display.
372 void display_individual_cal(icalcomponent * event, long msgnum, char *from, int unread, calview * calv) {
373 icalproperty *ps = NULL;
374 struct icaltimetype dtstart, dtend;
375 struct icaldurationtype dur;
379 time_t final_recurrence = 0;
380 icalcomponent *cptr = NULL;
383 icalproperty *rrule = NULL;
384 struct icalrecurrencetype recur;
385 icalrecur_iterator *ritr = NULL;
386 struct icaltimetype next;
390 // first and foremost, check for bogosity. bail if we see no DTSTART property
391 if (icalcomponent_get_first_property(icalcomponent_get_first_component(event, ICAL_VEVENT_COMPONENT), ICAL_DTSTART_PROPERTY)
396 // ok, chances are we've got a live one here. let's try to figure out where it goes.
398 dtstart = icaltime_null_time();
399 dtend = icaltime_null_time();
401 if (WCC->disp_cal_items == NULL) {
402 WCC->disp_cal_items = NewHash(0, Flathash);
405 // Note: anything we do here, we also have to do below for the recurrences.
406 Cal = (disp_cal *) malloc(sizeof(disp_cal));
407 memset(Cal, 0, sizeof(disp_cal));
408 Cal->cal = icalcomponent_new_clone(event);
410 // Dezonify and decapsulate at the very last moment
411 ical_dezonify(Cal->cal);
412 if (icalcomponent_isa(Cal->cal) != ICAL_VEVENT_COMPONENT) {
413 cptr = icalcomponent_get_first_component(Cal->cal, ICAL_VEVENT_COMPONENT);
415 cptr = icalcomponent_new_clone(cptr);
416 icalcomponent_free(Cal->cal);
421 Cal->unread = unread;
423 Cal->from = (char *) malloc(len + 1);
424 memcpy(Cal->from, from, len + 1);
425 Cal->cal_msgnum = msgnum;
427 // Precalculate the starting date and time of this event, and store it in our top-level
428 // structure. Later, when we are rendering the calendar, we can just peek at these values
429 // without having to break apart every calendar item.
430 ps = icalcomponent_get_first_property(Cal->cal, ICAL_DTSTART_PROPERTY);
432 dtstart = icalproperty_get_dtstart(ps);
433 Cal->event_start = icaltime_as_timet(dtstart);
436 // Do the same for the ending date and time. It makes the day view much easier to render.
437 ps = icalcomponent_get_first_property(Cal->cal, ICAL_DTEND_PROPERTY);
439 dtend = icalproperty_get_dtend(ps);
440 Cal->event_end = icaltime_as_timet(dtend);
443 // Store it in the hash list.
444 // syslog(LOG_DEBUG, "INITIAL: %s", ctime(&Cal->event_start));
445 Put(WCC->disp_cal_items, (char *) &Cal->event_start, sizeof(Cal->event_start), Cal, delete_cal);
447 //***************************** handle recurring events ******************************
449 if (icaltime_is_null_time(dtstart))
450 return; /* Can't recur without a start time */
452 if (!icaltime_is_null_time(dtend)) { /* Need duration for recurrences */
453 dur = icaltime_subtract(dtend, dtstart);
456 dur = icaltime_subtract(dtstart, dtstart);
460 * Just let libical iterate the recurrence, and keep looping back to the top of this function,
461 * adding new hash entries that all point back to the same msgnum, until either the iteration
462 * stops or some outer bound is reached. The display code will automatically do the Right Thing.
465 if (icalcomponent_isa(cptr) != ICAL_VEVENT_COMPONENT) {
466 cptr = icalcomponent_get_first_component(cptr, ICAL_VEVENT_COMPONENT);
470 ps = icalcomponent_get_first_property(cptr, ICAL_DTSTART_PROPERTY);
473 dtstart = icalproperty_get_dtstart(ps);
474 rrule = icalcomponent_get_first_property(cptr, ICAL_RRULE_PROPERTY);
477 recur = icalproperty_get_rrule(rrule);
478 ritr = icalrecur_iterator_new(recur, dtstart);
482 while (next = icalrecur_iterator_next(ritr), ((!icaltime_is_null_time(next)) && (!stop_rr))) {
484 if (num_recur > 1) { /* Skip the first one. We already did it at the root. */
487 /* Note: anything we do here, we also have to do above for the root event. */
488 Cal = (disp_cal *) malloc(sizeof(disp_cal));
489 memset(Cal, 0, sizeof(disp_cal));
490 Cal->cal = icalcomponent_new_clone(event);
491 Cal->unread = unread;
493 Cal->from = (char *) malloc(len + 1);
494 memcpy(Cal->from, from, len + 1);
495 Cal->cal_msgnum = msgnum;
497 if (icalcomponent_isa(Cal->cal) == ICAL_VEVENT_COMPONENT) {
501 cptr = icalcomponent_get_first_component(Cal->cal, ICAL_VEVENT_COMPONENT);
505 /* Remove any existing DTSTART properties */
506 while (ps = icalcomponent_get_first_property(cptr, ICAL_DTSTART_PROPERTY), ps != NULL) {
507 icalcomponent_remove_property(cptr, ps);
510 /* Add our shiny new DTSTART property from the iteration */
511 ps = icalproperty_new_dtstart(next);
512 icalcomponent_add_property(cptr, ps);
513 Cal->event_start = icaltime_as_timet(next);
514 final_recurrence = Cal->event_start;
516 /* Remove any existing DTEND properties */
517 while (ps = icalcomponent_get_first_property(cptr, ICAL_DTEND_PROPERTY), (ps != NULL)) {
518 icalcomponent_remove_property(cptr, ps);
521 /* Add our shiny new DTEND property from the iteration */
522 ps = icalproperty_new_dtend(icaltime_add(next, dur));
523 icalcomponent_add_property(cptr, ps);
527 /* Dezonify and decapsulate at the very last moment */
528 ical_dezonify(Cal->cal);
529 if (icalcomponent_isa(Cal->cal) != ICAL_VEVENT_COMPONENT) {
530 cptr = icalcomponent_get_first_component(Cal->cal, ICAL_VEVENT_COMPONENT);
532 cptr = icalcomponent_new_clone(cptr);
533 icalcomponent_free(Cal->cal);
538 if ((Cal->event_start > calv->lower_bound)
539 && (Cal->event_start < calv->upper_bound)
541 /* syslog(LOG_DEBUG, "REPEATS: %s", ctime(&Cal->event_start)); */
542 Put(WCC->disp_cal_items, (char *) &Cal->event_start, sizeof(Cal->event_start), Cal, delete_cal);
548 /* If an upper bound is set, stop when we go out of scope */
549 if (final_recurrence > calv->upper_bound)
553 icalrecur_iterator_free(ritr);
554 /* syslog(LOG_DEBUG, "Performed %d recurrences; final one is %s", num_recur, ctime(&final_recurrence)); */
558 void process_ical_object(long msgnum, int unread,
559 char *from, char *FlatIcal, icalcomponent_kind which_kind, IcalCallbackFunc CallBack, calview * calv) {
560 icalcomponent *cal, *c;
562 cal = icalcomponent_new_from_string(FlatIcal);
565 /* A which_kind of (-1) means just load the whole thing */
566 if (which_kind == (-1)) {
567 CallBack(cal, msgnum, from, unread, calv);
570 /* Otherwise recurse and hunt */
573 /* Simple components of desired type */
574 if (icalcomponent_isa(cal) == which_kind) {
575 CallBack(cal, msgnum, from, unread, calv);
578 /* Subcomponents of desired type */
579 for (c = icalcomponent_get_first_component(cal, which_kind);
580 (c != 0); c = icalcomponent_get_next_component(cal, which_kind)) {
581 CallBack(c, msgnum, from, unread, calv);
586 icalcomponent_free(cal);
590 // Code common to all icalendar display handlers. Given a message number and a MIME
591 // type, we load the message and hunt for that MIME type. If found, we load
592 // the relevant part, deserialize it into a libical component, filter it for
593 // the requested object type, and feed it to the specified handler.
594 void load_ical_object(long msgnum, int unread,
595 icalcomponent_kind which_kind, IcalCallbackFunc CallBack, calview * calv, int RenderAsync) {
601 char mime_partnum[256];
602 char mime_filename[256];
603 char mime_content_type[256];
604 char mime_disposition[256];
605 char relevant_partnum[256];
606 char *relevant_source = NULL;
607 int phase = 0; /* 0 = citadel headers, 1 = mime headers, 2 = body */
608 char msg4_content_type[256] = "";
609 char msg4_content_encoding[256] = "";
610 int msg4_content_length = 0;
612 relevant_partnum[0] = '\0';
613 serv_printf("MSG4 %ld", msgnum); /* we need the mime headers */
615 StrBuf_ServGetln(Buf);
616 if (GetServerStatus(Buf, NULL) != 1) {
620 while (!Done && (StrBuf_ServGetln(Buf) >= 0)) {
621 if ((StrLength(Buf) == 3) && !strcmp(ChrPtr(Buf), "000")) {
628 if (!strncasecmp(bptr, "part=", 5)) {
629 extract_token(mime_filename, &bptr[5], 1, '|', sizeof mime_filename);
630 extract_token(mime_partnum, &bptr[5], 2, '|', sizeof mime_partnum);
631 extract_token(mime_disposition, &bptr[5], 3, '|', sizeof mime_disposition);
632 extract_token(mime_content_type, &bptr[5], 4, '|', sizeof mime_content_type);
633 /* do we care? mime_length = */ extract_int(&bptr[5], 5);
635 if ((!strcasecmp(mime_content_type, "text/calendar"))
636 || (!strcasecmp(mime_content_type, "application/ics"))
637 || (!strcasecmp(mime_content_type, "text/vtodo"))
638 || (!strcasecmp(mime_content_type, "text/todo"))
640 strcpy(relevant_partnum, mime_partnum);
643 else if (!strncasecmp(bptr, "from=", 4)) {
644 extract_token(from, bptr, 1, '=', sizeof(from));
646 else if ((phase == 0) && (!strncasecmp(bptr, "text", 4))) {
651 if (!IsEmptyStr(bptr)) {
652 if (!strncasecmp(bptr, "Content-type: ", 14)) {
653 safestrncpy(msg4_content_type, &bptr[14], sizeof msg4_content_type);
654 striplt(msg4_content_type);
656 else if (!strncasecmp(bptr, "Content-transfer-encoding: ", 27)) {
657 safestrncpy(msg4_content_encoding, &bptr[27], sizeof msg4_content_encoding);
658 striplt(msg4_content_type);
660 else if ((!strncasecmp(bptr, "Content-length: ", 16))) {
661 msg4_content_length = atoi(&bptr[16]);
668 if ((msg4_content_length > 0)
669 && (!strcasecmp(msg4_content_encoding, "7bit"))
670 && ((!strcasecmp(mime_content_type, "text/calendar"))
671 || (!strcasecmp(mime_content_type, "application/ics"))
672 || (!strcasecmp(mime_content_type, "text/vtodo"))
673 || (!strcasecmp(mime_content_type, "text/todo"))
680 Data = NewStrBufPlain(NULL, msg4_content_length * 2);
681 if (msg4_content_length > 0) {
682 StrBuf_ServGetBLOBBuffered(Data, msg4_content_length);
686 StrBufAppendBuf(Data, Buf, 0);
687 StrBufAppendBufPlain(Data, "\r\n", 1, 0);
690 StrBufAppendBuf(Data, Buf, 0);
695 // If MSG4 didn't give us the part we wanted, but we know that we can find it
696 // as one of the other MIME parts, attempt to load it now.
697 if ((Data == NULL) && (!IsEmptyStr(relevant_partnum))) {
698 Data = load_mimepart(msgnum, relevant_partnum);
702 relevant_source = (char *) ChrPtr(Data);
703 process_ical_object(msgnum, unread, from, relevant_source, which_kind, CallBack, calv);
707 icalmemory_free_ring();
711 // Display a calendar item
712 int calendar_LoadMsgFromServer(SharedMessageStatus * Stat, void **ViewSpecific, message_summary * Msg, int is_new, int i) {
713 calview *c = (calview *) * ViewSpecific;
714 load_ical_object(Msg->msgnum, is_new, (-1), display_individual_cal, c, 1);
719 // display the editor component for an event
720 void display_edit_event(void) {
723 msgnum = lbstr("msgnum");
726 load_ical_object(msgnum, 0, ICAL_VEVENT_COMPONENT, display_edit_individual_event, NULL, 0);
730 display_edit_individual_event(NULL, 0L, "", 0, NULL);
735 // save an edited event
736 void save_event(void) {
739 msgnum = lbstr("msgnum");
742 load_ical_object(msgnum, 0, (-1), save_individual_event, NULL, 0);
745 save_individual_event(NULL, 0L, "", 0, NULL);
750 // Anonymous request of freebusy data for a user
751 void do_freebusy(void) {
752 const char *req = ChrPtr(WC->Hdr->HR.ReqLine);
758 extract_token(who, req, 0, ' ', sizeof who);
759 if (!strncasecmp(who, "/freebusy/", 10)) {
760 strcpy(who, &who[10]);
765 if ((!strcasecmp(&who[len - 4], ".vcf"))
766 || (!strcasecmp(&who[len - 4], ".ifb"))
767 || (!strcasecmp(&who[len - 4], ".vfb"))) {
771 syslog(LOG_INFO, "freebusy requested for <%s>\n", who);
772 serv_printf("ICAL freebusy|%s", who);
773 serv_getln(buf, sizeof buf);
776 hprintf("HTTP/1.1 404 %s\n", &buf[4]);
777 output_headers(0, 0, 0, 0, 0, 0);
778 hprintf("Content-Type: text/plain\r\n");
779 wc_printf("%s\n", &buf[4]);
784 read_server_text(WC->WBuf, &lines);
785 http_transmit_thing("text/calendar", 0);
789 int calendar_Cleanup(void **ViewSpecific) {
792 c = (calview *) * ViewSpecific;
796 *ViewSpecific = NULL;
802 int __calendar_Cleanup(void **ViewSpecific) {
805 c = (calview *) * ViewSpecific;
808 *ViewSpecific = NULL;
814 void InitModule_CALENDAR(void) {
815 RegisterReadLoopHandlerset(VIEW_CALENDAR,
816 calendar_GetParamsGetServerCall,
818 NULL, NULL, calendar_LoadMsgFromServer, calendar_RenderView_or_Tail, calendar_Cleanup, NULL);
820 RegisterReadLoopHandlerset(VIEW_CALBRIEF,
821 calendar_GetParamsGetServerCall,
823 NULL, NULL, calendar_LoadMsgFromServer, calendar_RenderView_or_Tail, calendar_Cleanup, NULL);
827 RegisterPreference("daystart", _("Calendar day view begins at:"), PRF_INT, NULL);
828 RegisterPreference("dayend", _("Calendar day view ends at:"), PRF_INT, NULL);
829 RegisterPreference("weekstart", _("Week starts on:"), PRF_INT, NULL);
831 WebcitAddUrlHandler(HKEY("freebusy"), "", 0, do_freebusy, COOKIEUNNEEDED | ANONYMOUS | FORCE_SESSIONCLOSE);
832 WebcitAddUrlHandler(HKEY("display_edit_task"), "", 0, display_edit_task, 0);
833 WebcitAddUrlHandler(HKEY("display_edit_event"), "", 0, display_edit_event, 0);
834 WebcitAddUrlHandler(HKEY("save_event"), "", 0, save_event, 0);
835 WebcitAddUrlHandler(HKEY("respond_to_request"), "", 0, respond_to_request, 0);
836 WebcitAddUrlHandler(HKEY("handle_rsvp"), "", 0, handle_rsvp, 0);