X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=webcit%2Fcalendar.c;h=a059acb8ffa78ca274c271dbc735913640448918;hb=808f3be91dd6b6677e380695e2f16e6473141a7e;hp=13c6869c677b241d4186c2f5b6edb1f79a888357;hpb=e0350cf10f7ea7ff0cc9a3f1eb9069db8397d3f4;p=citadel.git diff --git a/webcit/calendar.c b/webcit/calendar.c index 13c6869c6..a059acb8f 100644 --- a/webcit/calendar.c +++ b/webcit/calendar.c @@ -2,94 +2,53 @@ * $Id$ * * Functions which handle calendar objects and their processing/display. - * */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "webcit.h" #include "webserver.h" -#ifndef WEBCIT_WITH_CALENDAR_SERVICE - -/* - * Handler stubs for builds with no calendar library available - */ -void cal_process_attachment(char *part_source, long msgnum, char *cal_partnum) { - - wprintf("This message contains calendaring/scheduling information," - " but support for calendars is not available on this " - "particular system. Please ask your system administrator to " - "install a new version of the Citadel web service with " - "calendaring enabled.
\n" - ); - -} - -void display_calendar(long msgnum) { - wprintf("" - "Cannot display calendar item. You are seeing this error " - "because your WebCit service has not been installed with " - "calendar support. Please contact your system administrator." - "
\n"); -} - -void display_task(long msgnum) { - wprintf("" - "Cannot display to-do item. You are seeing this error " - "because your WebCit service has not been installed with " - "calendar support. Please contact your system administrator." - "
\n"); -} - -#else /* WEBCIT_WITH_CALENDAR_SERVICE */ - - -/****** End of handler stubs. Everything below this line is real. ******/ - - - /* - * Process a calendar object - * ...at this point it's already been deserialized by cal_process_attachment() + * Process a calendar object. At this point it's already been deserialized by cal_process_attachment() * + * cal: the calendar object + * recursion_level: Number of times we've recursed into this function + * msgnum: Message number on the Citadel server + * cal_partnum: MIME part number within that message containing the calendar object */ -void cal_process_object(icalcomponent *cal, +void cal_process_object(StrBuf *Target, + icalcomponent *cal, int recursion_level, long msgnum, - char *cal_partnum -) { + const char *cal_partnum) +{ icalcomponent *c; icalproperty *method = NULL; icalproperty_method the_method = ICAL_METHOD_NONE; icalproperty *p; struct icaltimetype t; time_t tt; - char buf[SIZ]; - char conflict_name[SIZ]; + char buf[256]; + char conflict_name[256]; + char conflict_message[256]; int is_update = 0; + char divname[32]; + static int divcount = 0; + + sprintf(divname, "rsvp%04x", ++divcount); + + /* Convert timezones to something easy to display. + * It's safe to do this in memory because we're only changing it on the + * display side -- when we tell the server to do something with the object, + * the server will be working with its original copy in the database. + */ + if ((cal) && (recursion_level == 0)) { + ical_dezonify(cal); + } /* Leading HTML for the display of this object */ if (recursion_level == 0) { - wprintf("
\n"); + StrBufAppendPrintf(Target, "
\n"); } /* Look for a method */ @@ -97,55 +56,49 @@ void cal_process_object(icalcomponent *cal, /* See what we need to do with this */ if (method != NULL) { + char *title; the_method = icalproperty_get_method(method); + + StrBufAppendPrintf(Target, "
", divname); + StrBufAppendPrintf(Target, ""); + StrBufAppendPrintf(Target, ""); switch(the_method) { - case ICAL_METHOD_REQUEST: - wprintf("
\n" - ); + case ICAL_METHOD_REQUEST: + title = _("Meeting invitation"); break; - case ICAL_METHOD_REPLY: - wprintf("\n" - ); + case ICAL_METHOD_REPLY: + title = _("Attendee's reply to your invitation"); break; - case ICAL_METHOD_PUBLISH: - wprintf("\n" - ); + case ICAL_METHOD_PUBLISH: + title = _("Published event"); break; - default: - wprintf("" - "\n"); + default: + title = _("This is an unknown type of calendar item."); break; } + StrBufAppendPrintf(Target, ""); + + StrBufAppendPrintf(Target, "  %s",title); + StrBufAppendPrintf(Target, ""); } + StrBufAppendPrintf(Target, "
"); p = icalcomponent_get_first_property(cal, ICAL_SUMMARY_PROPERTY); if (p != NULL) { - wprintf("
\n"); + StrBufAppendPrintf(Target, "
"); + StrBufAppendPrintf(Target, _("Summary:")); + StrBufAppendPrintf(Target, "
"); + StrEscAppend(Target, NULL, (char *)icalproperty_get_comment(p), 0, 0); + StrBufAppendPrintf(Target, "
\n"); } p = icalcomponent_get_first_property(cal, ICAL_LOCATION_PROPERTY); if (p != NULL) { - wprintf("\n"); + StrBufAppendPrintf(Target, "
"); + StrBufAppendPrintf(Target, _("Location:")); + StrBufAppendPrintf(Target, "
"); + StrEscAppend(Target, NULL, (char *)icalproperty_get_comment(p), 0, 0); + StrBufAppendPrintf(Target, "
\n"); } /* @@ -154,26 +107,28 @@ void cal_process_object(icalcomponent *cal, */ if (icalcomponent_isa(cal) == ICAL_VEVENT_COMPONENT) { - p = icalcomponent_get_first_property(cal, - ICAL_DTSTART_PROPERTY); + p = icalcomponent_get_first_property(cal, ICAL_DTSTART_PROPERTY); if (p != NULL) { t = icalproperty_get_dtstart(p); if (t.is_date) { - wprintf("", - months[t.month - 1], - t.day, t.year - ); + struct tm d_tm; + char d_str[32]; + memset(&d_tm, 0, sizeof d_tm); + d_tm.tm_year = t.year - 1900; + d_tm.tm_mon = t.month - 1; + d_tm.tm_mday = t.day; + wc_strftime(d_str, sizeof d_str, "%x", &d_tm); + StrBufAppendPrintf(Target, "
"); + StrBufAppendPrintf(Target, _("Date:")); + StrBufAppendPrintf(Target, "
%s
", d_str); } else { tt = icaltime_as_timet(t); - fmt_date(buf, tt, 0); - wprintf("", buf - ); + webcit_fmt_date(buf, tt, 0); + StrBufAppendPrintf(Target, "
"); + StrBufAppendPrintf(Target, _("Starting date/time:")); + StrBufAppendPrintf(Target, "
%s
", buf); } } @@ -181,46 +136,60 @@ void cal_process_object(icalcomponent *cal, if (p != NULL) { t = icalproperty_get_dtend(p); tt = icaltime_as_timet(t); - fmt_date(buf, tt, 0); - wprintf("", buf - ); + webcit_fmt_date(buf, tt, 0); + StrBufAppendPrintf(Target, "
"); + StrBufAppendPrintf(Target, _("Ending date/time:")); + StrBufAppendPrintf(Target, "
%s
", buf); } } p = icalcomponent_get_first_property(cal, ICAL_DESCRIPTION_PROPERTY); if (p != NULL) { - wprintf("\n"); + StrBufAppendPrintf(Target, "
"); + StrBufAppendPrintf(Target, _("Description:")); + StrBufAppendPrintf(Target, "
"); + StrEscAppend(Target, NULL, (char *)icalproperty_get_comment(p), 0, 0); + StrBufAppendPrintf(Target, "
\n"); + } + + if (icalcomponent_get_first_property(cal, ICAL_RRULE_PROPERTY)) { + /* Unusual string syntax used here in order to re-use existing translations */ + StrBufAppendPrintf(Target, "
%s:
%s.
\n", + _("Recurrence"), + _("This is a recurring event") + ); } /* If the component has attendees, iterate through them. */ - for (p = icalcomponent_get_first_property(cal, ICAL_ATTENDEE_PROPERTY); (p != NULL); p = icalcomponent_get_next_property(cal, ICAL_ATTENDEE_PROPERTY)) { - wprintf("\n"); + StrBufAppendPrintf(Target, "\n"); } /* If the component has subcomponents, recurse through them. */ for (c = icalcomponent_get_first_component(cal, ICAL_ANY_COMPONENT); - (c != 0); - c = icalcomponent_get_next_component(cal, ICAL_ANY_COMPONENT)) { + (c != 0); + c = icalcomponent_get_next_component(cal, ICAL_ANY_COMPONENT)) { /* Recursively process subcomponent */ - cal_process_object(c, recursion_level+1, msgnum, cal_partnum); + cal_process_object(Target, c, recursion_level+1, msgnum, cal_partnum); } /* If this is a REQUEST, display conflicts and buttons */ @@ -234,96 +203,93 @@ void cal_process_object(icalcomponent *cal, while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { extract_token(conflict_name, buf, 3, '|', sizeof conflict_name); is_update = extract_int(buf, 4); - wprintf("" - "\n"); + _("Update:") : + _("CONFLICT:") + ) + ); + StrBufAppendPrintf(Target, "
"); + StrEscAppend(Target, NULL, conflict_message, 0, 0); + StrBufAppendPrintf(Target, "
\n"); } } lprintf(9, "...done.\n"); + StrBufAppendPrintf(Target, ""); + /* Display the Accept/Decline buttons */ - wprintf("" - "\n", - msgnum, cal_partnum, - msgnum, cal_partnum, - msgnum, cal_partnum - ); + StrBufAppendPrintf(Target, "

" + "%s " + "    " + "%s" + "   " + "%s" + "   " + "%s" + "

\n", + divname, + _("How would you like to respond to this invitation?"), + divname, divname, msgnum, cal_partnum, _("Accept"), + divname, divname, msgnum, cal_partnum, _("Tentative"), + divname, divname, msgnum, cal_partnum, _("Decline") + ); } /* If this is a REPLY, display update button */ if (the_method == ICAL_METHOD_REPLY) { - /*********** - * In the future, if we want to validate this object before - * continuing, we can do it this way: - serv_printf("ICAL whatever|%ld|%s|", msgnum, cal_partnum); - serv_getln(buf, sizeof buf); - } - ***********/ - /* Display the update buttons */ - wprintf("\n", - msgnum, cal_partnum - ); - + StrBufAppendPrintf(Target, "

" + "%s " + "    " + "%s" + "   " + "%s" + "

\n", + divname, + _("Click Update to accept this reply and update your calendar."), + divname, divname, msgnum, cal_partnum, _("Update"), + divname, divname, msgnum, cal_partnum, _("Ignore") + ); + } - + /* Trailing HTML for the display of this object */ if (recursion_level == 0) { - - wprintf("
\n" - "" - "  " - "Meeting invitation" - "
\n" - "" - "  " - "Attendee's reply to your invitation" - "
\n" - "" - "  " - "Published event" - "
" - "I don't know what to do with this.
Summary:"); - escputs((char *)icalproperty_get_comment(p)); - wprintf("
Location:"); - escputs((char *)icalproperty_get_comment(p)); - wprintf("
Date:" - "" - "%s %d, %d
Starting date/time:" - "" - "%s
Ending date/time:" - "%s
Description:"); - escputs((char *)icalproperty_get_comment(p)); - wprintf("
Attendee:"); - strcpy(buf, icalproperty_get_attendee(p)); + for (p = icalcomponent_get_first_property(cal, ICAL_ATTENDEE_PROPERTY); + (p != NULL); + p = icalcomponent_get_next_property(cal, ICAL_ATTENDEE_PROPERTY)) { + StrBufAppendPrintf(Target, "
"); + StrBufAppendPrintf(Target, _("Attendee:")); + StrBufAppendPrintf(Target, "
"); + safestrncpy(buf, icalproperty_get_attendee(p), sizeof buf); if (!strncasecmp(buf, "MAILTO:", 7)) { - /* screen name or email address */ + /** screen name or email address */ strcpy(buf, &buf[7]); striplt(buf); - escputs(buf); - wprintf(" "); + StrEscAppend(Target, NULL, buf, 0, 0); + StrBufAppendPrintf(Target, " "); - /* participant status */ + /** participant status */ partstat_as_string(buf, p); - escputs(buf); + StrEscAppend(Target, NULL, buf, 0, 0); } - wprintf("
%s" - "%s " - """, - (is_update ? - "Update:" : - "CONFLICT:" - ), + if (is_update) { + snprintf(conflict_message, sizeof conflict_message, + _("This is an update of '%s' which is already in your calendar."), conflict_name); + } + else { + snprintf(conflict_message, sizeof conflict_message, + _("This event would conflict with '%s' which is already in your calendar."), conflict_name); + } + StrBufAppendPrintf(Target, "
%s", (is_update ? - "This is an update of" : - "This event would conflict with" - ) - - ); - escputs(conflict_name); - wprintf("" " - "which is already in your calendar." - "
How would you like to respond to this invitation?" - "Accept" - " | " - "Tentative" - " | " - "Decline" - "
" - "Click Update to accept this reply and " - "update your calendar." - "" - "Update" - " | " - "Ignore" - "" - "
\n"); + StrBufAppendPrintf(Target, "

 

\n"); } } /* - * Deserialize a calendar object in a message so it can be processed. - * (This is the main entry point for these things) + * Deserialize a calendar object in a message so it can be displayed. + * */ -void cal_process_attachment(char *part_source, long msgnum, char *cal_partnum) { +void cal_process_attachment(wc_mime_attachment *Mime) +{ icalcomponent *cal; - - cal = icalcomponent_new_from_string(part_source); - + + cal = icalcomponent_new_from_string(ChrPtr(Mime->Data)); + FlushStrBuf(Mime->Data); if (cal == NULL) { - wprintf("Error parsing calendar object
\n"); + StrBufAppendPrintf(Mime->Data, _("There was an error parsing this calendar item.")); + StrBufAppendPrintf(Mime->Data, "
\n"); return; } - ical_dezonify(cal); - cal_process_object(cal, 0, msgnum, cal_partnum); + cal_process_object(Mime->Data, cal, 0, Mime->msgnum, ChrPtr(Mime->PartNum)); /* Free the memory we obtained from libical's constructor */ icalcomponent_free(cal); @@ -332,20 +298,15 @@ void cal_process_attachment(char *part_source, long msgnum, char *cal_partnum) { -/* +/** + * \brief accept/decline meeting * Respond to a meeting request */ -void respond_to_request(void) { - char buf[SIZ]; - - output_headers(1, 1, 2, 0, 0, 0, 0); +void respond_to_request(void) +{ + char buf[1024]; - wprintf("
\n"); - wprintf("
" - "Respond to meeting request" - "
\n" - ); - wprintf("
\n
\n"); + begin_ajax_response(); serv_printf("ICAL respond|%s|%s|%s|", bstr("msgnum"), @@ -355,55 +316,42 @@ void respond_to_request(void) { serv_getln(buf, sizeof buf); if (buf[0] == '2') { - wprintf("
" - "" - "" - ); + wprintf(""); if (!strcasecmp(bstr("sc"), "accept")) { - wprintf("You have accepted this meeting invitation. " - "It has been entered into your calendar, " + wprintf(_("You have accepted this meeting invitation. " + "It has been entered into your calendar.") ); } else if (!strcasecmp(bstr("sc"), "tentative")) { - wprintf("You have tentatively accepted this meeting invitation. " - "It has been 'pencilled in' to your calendar, " + wprintf(_("You have tentatively accepted this meeting invitation. " + "It has been 'pencilled in' to your calendar.") ); } else if (!strcasecmp(bstr("sc"), "decline")) { - wprintf("You have declined this meeting invitation. " - "It has not been entered into your calendar, " - ); + wprintf(_("You have declined this meeting invitation. " + "It has not been entered into your calendar.") + ); } - wprintf("and a reply has been sent to the meeting organizer." - "
\n" - ); + wprintf(" "); + wprintf(_("A reply has been sent to the meeting organizer.")); + wprintf(""); } else { - wprintf("" - "%s\n", &buf[4]); + wprintf(""); + wprintf("%s\n", &buf[4]); + wprintf(""); } - wprintf("wc_roomname); - wprintf("\">
Return to messages

\n"); - - wDumpContent(1); + end_ajax_response(); } -/* - * Handle an incoming RSVP +/** + * \brief Handle an incoming RSVP */ -void handle_rsvp(void) { - char buf[SIZ]; +void handle_rsvp(void) +{ + char buf[1024]; - output_headers(1, 1, 2, 0, 0, 0, 0); - - wprintf("
\n"); - wprintf("
" - "" - "Update your calendar with this RSVP" - "
\n" - "
\n
\n" - ); + begin_ajax_response(); serv_printf("ICAL handle_rsvp|%s|%s|%s|", bstr("msgnum"), @@ -413,80 +361,246 @@ void handle_rsvp(void) { serv_getln(buf, sizeof buf); if (buf[0] == '2') { - wprintf("
" - "" - "" - ); + wprintf(""); if (!strcasecmp(bstr("sc"), "update")) { - wprintf("Your calendar has been updated " - "to reflect this RSVP." - ); + wprintf(_("Your calendar has been updated to reflect this RSVP.")); } else if (!strcasecmp(bstr("sc"), "ignore")) { - wprintf("You have chosen to ignore this RSVP. " - "Your calendar has not been updated." - ); + wprintf(_("You have chosen to ignore this RSVP. " + "Your calendar has not been updated.") + ); } - wprintf("
\n" - ); + wprintf(""); } else { - wprintf("" - "%s\n", &buf[4]); + wprintf(" %s\n", &buf[4]); + wprintf(""); } - wprintf("wc_roomname); - wprintf("\">
Return to messages

\n"); + end_ajax_response(); +} - wDumpContent(1); + + + +/* + * free memory allocated using libical + */ +void delete_cal(void *vCal) +{ + disp_cal *Cal = (disp_cal*) vCal; + icalcomponent_free(Cal->cal); + free(Cal->from); + free(Cal); } +/* + * This is the meat-and-bones of the first part of our two-phase calendar display. + * As we encounter calendar items in messages being read from the server, we break out + * any iCalendar objects and store them in a hash table. Later on, the second phase will + * use this hash table to render the calendar for display. + */ +void display_individual_cal(icalcomponent *cal, long msgnum, char *from, int unread, struct calview *calv) +{ + icalproperty *ps = NULL; + struct icaltimetype dtstart, dtend; + struct icaldurationtype dur; + wcsession *WCC = WC; + disp_cal *Cal; + size_t len; + time_t final_recurrence = 0; + icalcomponent *cptr = NULL; + + /* recur variables */ + icalproperty *rrule = NULL; + struct icalrecurrencetype recur; + icalrecur_iterator *ritr = NULL; + struct icaltimetype next; + int num_recur = 0; + int stop_rr = 0; + + dtstart = icaltime_null_time(); + dtend = icaltime_null_time(); + + if (WCC->disp_cal_items == NULL) + WCC->disp_cal_items = NewHash(0, Flathash); + + /* Note: anything we do here, we also have to do below for the recurrences. */ + Cal = (disp_cal*) malloc(sizeof(disp_cal)); + memset(Cal, 0, sizeof(disp_cal)); + Cal->cal = icalcomponent_new_clone(cal); + + /* Dezonify and decapsulate at the very last moment */ + /* lprintf(9, "INITIAL: %s\n", icaltime_as_ical_string(icalproperty_get_dtstart( + icalcomponent_get_first_property(icalcomponent_get_first_component( + Cal->cal, ICAL_VEVENT_COMPONENT), ICAL_DTSTART_PROPERTY))) + ); */ + ical_dezonify(Cal->cal); + if (icalcomponent_isa(Cal->cal) != ICAL_VEVENT_COMPONENT) { + cptr = icalcomponent_get_first_component(Cal->cal, ICAL_VEVENT_COMPONENT); + if (cptr) { + cptr = icalcomponent_new_clone(cptr); + icalcomponent_free(Cal->cal); + Cal->cal = cptr; + } + } + Cal->unread = unread; + len = strlen(from); + Cal->from = (char*)malloc(len+ 1); + memcpy(Cal->from, from, len + 1); + Cal->cal_msgnum = msgnum; + /* Precalculate the starting date and time of this event, and store it in our top-level + * structure. Later, when we are rendering the calendar, we can just peek at these values + * without having to break apart every calendar item. + */ + ps = icalcomponent_get_first_property(Cal->cal, ICAL_DTSTART_PROPERTY); + if (ps != NULL) { + dtstart = icalproperty_get_dtstart(ps); + Cal->event_start = icaltime_as_timet(dtstart); + } -/*****************************************************************************/ + /* Do the same for the ending date and time. It makes the day view much easier to render. */ + ps = icalcomponent_get_first_property(Cal->cal, ICAL_DTEND_PROPERTY); + if (ps != NULL) { + dtend = icalproperty_get_dtend(ps); + Cal->event_end = icaltime_as_timet(dtend); + } + /* Store it in the hash list. */ + Put(WCC->disp_cal_items, + (char*) &Cal->event_start, + sizeof(Cal->event_start), + Cal, + delete_cal); + /****************************** handle recurring events ******************************/ -/* - * Display handlers for message reading - */ + if (icaltime_is_null_time(dtstart)) return; /* Can't recur without a start time */ + if (!icaltime_is_null_time(dtend)) { /* Need duration for recurrences */ + dur = icaltime_subtract(dtend, dtstart); + } + /* + * Just let libical iterate the recurrence, and keep looping back to the top of this function, + * adding new hash entries that all point back to the same msgnum, until either the iteration + * stops or some outer bound is reached. The display code will automatically do the Right Thing. + */ + cptr = cal; + if (icalcomponent_isa(cptr) != ICAL_VEVENT_COMPONENT) { + cptr = icalcomponent_get_first_component(cptr, ICAL_VEVENT_COMPONENT); + } + if (!cptr) return; + ps = icalcomponent_get_first_property(cptr, ICAL_DTSTART_PROPERTY); + if (ps == NULL) return; + dtstart = icalproperty_get_dtstart(ps); + rrule = icalcomponent_get_first_property(cptr, ICAL_RRULE_PROPERTY); + if (!rrule) return; + recur = icalproperty_get_rrule(rrule); + ritr = icalrecur_iterator_new(recur, dtstart); + if (!ritr) return; + + while (next = icalrecur_iterator_next(ritr), ((!icaltime_is_null_time(next))&&(!stop_rr)) ) { + ++num_recur; + if (num_recur > 1) { /* Skip the first one. We already did it at the root. */ + icalcomponent *cptr; + /* lprintf(9, "REPEATS: %s\n", icaltime_as_ical_string(next)); */ + + /* Note: anything we do here, we also have to do above for the root event. */ + Cal = (disp_cal*) malloc(sizeof(disp_cal)); + memset(Cal, 0, sizeof(disp_cal)); + Cal->cal = icalcomponent_new_clone(cal); + Cal->unread = unread; + len = strlen(from); + Cal->from = (char*)malloc(len+ 1); + memcpy(Cal->from, from, len + 1); + Cal->cal_msgnum = msgnum; + + if (icalcomponent_isa(Cal->cal) == ICAL_VEVENT_COMPONENT) { + cptr = Cal->cal; + } + else { + cptr = icalcomponent_get_first_component(Cal->cal, ICAL_VEVENT_COMPONENT); + } + if (cptr) { + ps = icalcomponent_get_first_property(cptr, ICAL_DTSTART_PROPERTY); + if (ps != NULL) { + icalcomponent_remove_property(cptr, ps); + ps = icalproperty_new_dtstart(next); + icalcomponent_add_property(cptr, ps); + + Cal->event_start = icaltime_as_timet(next); + final_recurrence = Cal->event_start; + } -/* - * If we're reading calendar items, just store them for now. We have to - * sort and re-output them later when we draw the calendar. - */ -void display_individual_cal(icalcomponent *cal, long msgnum) { + ps = icalcomponent_get_first_property(cptr, ICAL_DTEND_PROPERTY); + if (ps != NULL) { + icalcomponent_remove_property(cptr, ps); + + /* Make a new dtend */ + ps = icalproperty_new_dtend(icaltime_add(next, dur)); + + /* and stick it somewhere */ + icalcomponent_add_property(cptr, ps); + } - WC->num_cal += 1; + } - WC->disp_cal = realloc(WC->disp_cal, - (sizeof(struct disp_cal) * WC->num_cal) ); - WC->disp_cal[WC->num_cal - 1].cal = icalcomponent_new_clone(cal); + /* Dezonify and decapsulate at the very last moment */ + ical_dezonify(Cal->cal); + if (icalcomponent_isa(Cal->cal) != ICAL_VEVENT_COMPONENT) { + cptr = icalcomponent_get_first_component(Cal->cal, ICAL_VEVENT_COMPONENT); + if (cptr) { + cptr = icalcomponent_new_clone(cptr); + icalcomponent_free(Cal->cal); + Cal->cal = cptr; + } + } + + if ( (Cal->event_start > calv->lower_bound) + && (Cal->event_start < calv->upper_bound) ) { + Put(WCC->disp_cal_items, + (char*) &Cal->event_start, + sizeof(Cal->event_start), + Cal, + delete_cal + ); + } + else { + delete_cal(Cal); + } + + /* If an upper bound is set, stop when we go out of scope */ + if (final_recurrence > calv->upper_bound) stop_rr = 1; + } + } + icalrecur_iterator_free(ritr); + /* lprintf(9, "Performed %d recurrences; final one is %s", num_recur, ctime(&final_recurrence)); */ - WC->disp_cal[WC->num_cal - 1].cal_msgnum = msgnum; } /* * Display a task by itself (for editing) - * */ -void display_edit_individual_task(icalcomponent *supplied_vtodo, long msgnum) { +void display_edit_individual_task(icalcomponent *supplied_vtodo, long msgnum, char *from, + int unread, struct calview *calv) +{ icalcomponent *vtodo; icalproperty *p; - struct icaltimetype t; + struct icaltimetype IcalTime; time_t now; int created_new_vtodo = 0; + icalproperty_status todoStatus; now = time(NULL); if (supplied_vtodo != NULL) { vtodo = supplied_vtodo; - /* If we're looking at a fully encapsulated VCALENDAR + /** + * If we're looking at a fully encapsulated VCALENDAR * rather than a VTODO component, attempt to use the first * relevant VTODO subcomponent. If there is none, the * NULL returned by icalcomponent_get_first_component() will @@ -497,8 +611,9 @@ void display_edit_individual_task(icalcomponent *supplied_vtodo, long msgnum) { display_edit_individual_task( icalcomponent_get_first_component( vtodo, ICAL_VTODO_COMPONENT - ), msgnum - ); + ), + msgnum, from, unread, calv + ); return; } } @@ -506,25 +621,35 @@ void display_edit_individual_task(icalcomponent *supplied_vtodo, long msgnum) { vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT); created_new_vtodo = 1; } - - output_headers(1, 1, 2, 0, 0, 0, 0); - wprintf("
\n" - "
" - "Edit task" - "
\n" - "
\n
\n" - ); - - wprintf("
" - "
"); - wprintf("
\n"); - wprintf("\n", - msgnum); - - wprintf("\n"); - - wprintf("
Summary:" + /*/ TODO: Can we take all this and move it into a template? */ + output_headers(1, 1, 1, 0, 0, 0); + wprintf(""); + p = icalcomponent_get_first_property(vtodo, ICAL_SUMMARY_PROPERTY); + /* Get summary early for title */ + wprintf("
\n"); + wprintf("
"); + wprintf(_("Edit task")); + wprintf("- "); + if (p != NULL) { + escputs((char *)icalproperty_get_comment(p)); + } + wprintf("
"); + + wprintf("
\n"); + wprintf("\n"); + wprintf("
\n "); + wprintf("\n", WC->nonce); + wprintf("\n", msgnum); + wprintf("\n", + ibstr("return_to_summary")); + wprintf("
"); + wprintf("
"); + wprintf("\n"); + + wprintf("\n"); - wprintf("\n"); - wprintf("\n"); - wprintf("
"); + wprintf(_("Summary:")); + wprintf("" "
Start date:"); + wprintf("
"); + wprintf(_("Start date:")); + wprintf(""); p = icalcomponent_get_first_property(vtodo, ICAL_DTSTART_PROPERTY); - if (p != NULL) { - t = icalproperty_get_dtstart(p); + wprintf(""); + wprintf(_("No date")); + + wprintf(" "); + wprintf(_("or")); + wprintf(" "); + if (p != NULL) { + IcalTime = icalproperty_get_dtstart(p); } - display_icaltimetype_as_webform(&t, "dtstart"); + else + IcalTime = icaltime_current_time_with_zone(get_default_icaltimezone()); + display_icaltimetype_as_webform(&IcalTime, "dtstart", 0); wprintf("
Due date:"); + wprintf("
"); + wprintf(_("Due date:")); + wprintf(""); p = icalcomponent_get_first_property(vtodo, ICAL_DUE_PROPERTY); - if (p != NULL) { - t = icalproperty_get_due(p); + wprintf(""); + wprintf(_("No date")); + wprintf(" "); + wprintf(_("or")); + wprintf(" "); + if (p != NULL) { + IcalTime = icalproperty_get_due(p); } - display_icaltimetype_as_webform(&t, "due"); + else + IcalTime = icaltime_current_time_with_zone(get_default_icaltimezone()); + display_icaltimetype_as_webform(&IcalTime, "due", 0); + wprintf("
Description:"); - wprintf("
\n"); - wprintf("
" - "" + wprintf("" + "" "  " - "\n" + "\n" "  " - "\n" - "
\n" - ); - + "\n" + "\n", + _("Save"), + _("Delete"), + _("Cancel") + ); + wprintf("
"); wprintf("\n"); - - wprintf("
\n"); + wprintf("\n"); + wprintf(""); wDumpContent(1); if (created_new_vtodo) { @@ -584,10 +758,13 @@ void display_edit_individual_task(icalcomponent *supplied_vtodo, long msgnum) { } /* - * Save an edited task - * + * \brief Save an edited task + * \param supplied_vtodo the task to save + * \param msgnum number of the mesage in our db */ -void save_individual_task(icalcomponent *supplied_vtodo, long msgnum) { +void save_individual_task(icalcomponent *supplied_vtodo, long msgnum, char* from, int unread, + struct calview *calv) +{ char buf[SIZ]; int delete_existing = 0; icalproperty *prop; @@ -595,10 +772,12 @@ void save_individual_task(icalcomponent *supplied_vtodo, long msgnum) { int created_new_vtodo = 0; int i; int sequence = 0; + struct icaltimetype t; if (supplied_vtodo != NULL) { vtodo = supplied_vtodo; - /* If we're looking at a fully encapsulated VCALENDAR + /** + * If we're looking at a fully encapsulated VCALENDAR * rather than a VTODO component, attempt to use the first * relevant VTODO subcomponent. If there is none, the * NULL returned by icalcomponent_get_first_component() will @@ -608,9 +787,9 @@ void save_individual_task(icalcomponent *supplied_vtodo, long msgnum) { if (icalcomponent_isa(vtodo) == ICAL_VCALENDAR_COMPONENT) { save_individual_task( icalcomponent_get_first_component( - vtodo, ICAL_VTODO_COMPONENT - ), msgnum - ); + vtodo, ICAL_VTODO_COMPONENT), + msgnum, from, unread, calv + ); return; } } @@ -619,62 +798,89 @@ void save_individual_task(icalcomponent *supplied_vtodo, long msgnum) { created_new_vtodo = 1; } - if (!strcasecmp(bstr("sc"), "Save")) { + if (havebstr("save_button")) { - /* Replace values in the component with ones from the form */ + /** Replace values in the component with ones from the form */ while (prop = icalcomponent_get_first_property(vtodo, - ICAL_SUMMARY_PROPERTY), prop != NULL) { + ICAL_SUMMARY_PROPERTY), prop != NULL) { icalcomponent_remove_property(vtodo, prop); icalproperty_free(prop); } - icalcomponent_add_property(vtodo, - icalproperty_new_summary(bstr("summary"))); - + if (havebstr("summary")) { + + icalcomponent_add_property(vtodo, + icalproperty_new_summary(bstr("summary"))); + } else { + icalcomponent_add_property(vtodo, + icalproperty_new_summary("Untitled Task")); + } + while (prop = icalcomponent_get_first_property(vtodo, - ICAL_DESCRIPTION_PROPERTY), prop != NULL) { + ICAL_DESCRIPTION_PROPERTY), prop != NULL) { icalcomponent_remove_property(vtodo, prop); icalproperty_free(prop); } - icalcomponent_add_property(vtodo, - icalproperty_new_description(bstr("description"))); + if (havebstr("description")) { + icalcomponent_add_property(vtodo, + icalproperty_new_description(bstr("description"))); + } while (prop = icalcomponent_get_first_property(vtodo, - ICAL_DTSTART_PROPERTY), prop != NULL) { + ICAL_DTSTART_PROPERTY), prop != NULL) { icalcomponent_remove_property(vtodo, prop); icalproperty_free(prop); } - icalcomponent_add_property(vtodo, - icalproperty_new_dtstart( - icaltime_from_webform("dtstart") - ) - ); - + if (IsEmptyStr(bstr("nodtstart"))) { + icaltime_from_webform(&t, "dtstart"); + icalcomponent_add_property(vtodo, + icalproperty_new_dtstart(t) + ); + } + while(prop = icalcomponent_get_first_property(vtodo, + ICAL_STATUS_PROPERTY), prop != NULL) { + icalcomponent_remove_property(vtodo,prop); + icalproperty_free(prop); + } + if (havebstr("status")) { + icalproperty_status taskStatus = icalproperty_string_to_status( + bstr("status")); + icalcomponent_set_status(vtodo, taskStatus); + } while (prop = icalcomponent_get_first_property(vtodo, - ICAL_DUE_PROPERTY), prop != NULL) { + ICAL_CATEGORIES_PROPERTY), prop != NULL) { + icalcomponent_remove_property(vtodo,prop); + icalproperty_free(prop); + } + if (!IsEmptyStr(bstr("category"))) { + prop = icalproperty_new_categories(bstr("category")); + icalcomponent_add_property(vtodo,prop); + } + while (prop = icalcomponent_get_first_property(vtodo, + ICAL_DUE_PROPERTY), prop != NULL) { icalcomponent_remove_property(vtodo, prop); icalproperty_free(prop); } - icalcomponent_add_property(vtodo, - icalproperty_new_due( - icaltime_from_webform("due") - ) - ); - - /* Give this task a UID if it doesn't have one. */ + if (IsEmptyStr(bstr("nodue"))) { + icaltime_from_webform(&t, "due"); + icalcomponent_add_property(vtodo, + icalproperty_new_due(t) + ); + } + /** Give this task a UID if it doesn't have one. */ lprintf(9, "Give this task a UID if it doesn't have one.\n"); if (icalcomponent_get_first_property(vtodo, - ICAL_UID_PROPERTY) == NULL) { + ICAL_UID_PROPERTY) == NULL) { generate_uuid(buf); icalcomponent_add_property(vtodo, - icalproperty_new_uid(buf) - ); + icalproperty_new_uid(buf) + ); } - /* Increment the sequence ID */ + /** Increment the sequence ID */ lprintf(9, "Increment the sequence ID\n"); while (prop = icalcomponent_get_first_property(vtodo, - ICAL_SEQUENCE_PROPERTY), (prop != NULL) ) { + ICAL_SEQUENCE_PROPERTY), (prop != NULL) ) { i = icalproperty_get_sequence(prop); lprintf(9, "Sequence was %d\n", i); if (i > sequence) sequence = i; @@ -684,17 +890,17 @@ void save_individual_task(icalcomponent *supplied_vtodo, long msgnum) { ++sequence; lprintf(9, "New sequence is %d. Adding...\n", sequence); icalcomponent_add_property(vtodo, - icalproperty_new_sequence(sequence) - ); + icalproperty_new_sequence(sequence) + ); - /* + /** * Encapsulate event into full VCALENDAR component. Clone it first, * for two reasons: one, it's easier to just free the whole thing * when we're done instead of unbundling, but more importantly, we * can't encapsulate something that may already be encapsulated * somewhere else. */ - lprintf(9, "Encapsulating into full VCALENDAR component\n"); + lprintf(9, "Encapsulating into a full VCALENDAR component\n"); encaps = ical_encapsulate_subcomponent(icalcomponent_new_clone(vtodo)); /* Serialize it and save it to the message base */ @@ -706,7 +912,8 @@ void save_individual_task(icalcomponent *supplied_vtodo, long msgnum) { serv_puts(icalcomponent_as_ical_string(encaps)); serv_puts("000"); - /* Probably not necessary; the server will see the UID + /** + * Probably not necessary; the server will see the UID * of the object and delete the old one anyway, but * just in case... */ @@ -715,15 +922,15 @@ void save_individual_task(icalcomponent *supplied_vtodo, long msgnum) { icalcomponent_free(encaps); } - /* + /** * If the user clicked 'Delete' then explicitly delete the message. */ - if (!strcasecmp(bstr("sc"), "Delete")) { + if (havebstr("delete_button")) { delete_existing = 1; } if ( (delete_existing) && (msgnum > 0L) ) { - serv_printf("DELE %ld", atol(bstr("msgnum"))); + serv_printf("DELE %ld", lbstr("msgnum")); serv_getln(buf, sizeof buf); } @@ -731,35 +938,42 @@ void save_individual_task(icalcomponent *supplied_vtodo, long msgnum) { icalcomponent_free(vtodo); } - /* Go back to the task list */ - readloop("readfwd"); + /* Go back to wherever we came from */ + if (ibstr("return_to_summary") == 1) { + summary(); + } + else { + readloop(readfwd); + } } /* - * Code common to all display handlers. Given a message number and a MIME + * Code common to all icalendar display handlers. Given a message number and a MIME * type, we load the message and hunt for that MIME type. If found, we load * the relevant part, deserialize it into a libical component, filter it for * the requested object type, and feed it to the specified handler. - * */ -void display_using_handler(long msgnum, - char *mimetype, - icalcomponent_kind which_kind, - void (*callback)(icalcomponent *, long) - ) { - char buf[SIZ]; - char mime_partnum[SIZ]; - char mime_filename[SIZ]; - char mime_content_type[SIZ]; - char mime_disposition[SIZ]; +void load_ical_object(long msgnum, int unread, + icalcomponent_kind which_kind, + void (*callback)(icalcomponent *, long, char*, int, struct calview *), + struct calview *calv + ) +{ + char buf[1024]; + char from[128] = ""; + char mime_partnum[256]; + char mime_filename[256]; + char mime_content_type[256]; + char mime_disposition[256]; int mime_length; - char relevant_partnum[SIZ]; + char relevant_partnum[256]; char *relevant_source = NULL; icalcomponent *cal, *c; - sprintf(buf, "MSG0 %ld|1", msgnum); /* ask for headers only */ + relevant_partnum[0] = '\0'; + sprintf(buf, "MSG4 %ld", msgnum); /* we need the mime headers */ serv_puts(buf); serv_getln(buf, sizeof buf); if (buf[0] != '1') return; @@ -772,118 +986,141 @@ void display_using_handler(long msgnum, extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type); mime_length = extract_int(&buf[5], 5); - if (!strcasecmp(mime_content_type, "text/calendar")) { + if ( (!strcasecmp(mime_content_type, "text/calendar")) + || (!strcasecmp(mime_content_type, "application/ics")) + || (!strcasecmp(mime_content_type, "text/vtodo")) + ) { strcpy(relevant_partnum, mime_partnum); } - + } + else if (!strncasecmp(buf, "from=", 4)) { + extract_token(from, buf, 1, '=', sizeof(from)); } } - if (strlen(relevant_partnum) > 0) { + if (!IsEmptyStr(relevant_partnum)) { relevant_source = load_mimepart(msgnum, relevant_partnum); if (relevant_source != NULL) { cal = icalcomponent_new_from_string(relevant_source); if (cal != NULL) { - ical_dezonify(cal); - - /* Simple components of desired type */ - if (icalcomponent_isa(cal) == which_kind) { - callback(cal, msgnum); + /* A which_kind of (-1) means just load the whole thing */ + if (which_kind == (-1)) { + callback(cal, msgnum, from, unread, calv); } - /* Subcomponents of desired type */ - for (c = icalcomponent_get_first_component(cal, - which_kind); - (c != 0); - c = icalcomponent_get_next_component(cal, - which_kind)) { - callback(c, msgnum); + /* Otherwise recurse and hunt */ + else { + + /* Simple components of desired type */ + if (icalcomponent_isa(cal) == which_kind) { + callback(cal, msgnum, from, unread, calv); + } + + /* Subcomponents of desired type */ + for (c = icalcomponent_get_first_component(cal, which_kind); + (c != 0); + c = icalcomponent_get_next_component(cal, which_kind)) { + callback(c, msgnum, from, unread, calv); + } + } + icalcomponent_free(cal); } free(relevant_source); } } - + icalmemory_free_ring(); } -void display_calendar(long msgnum) { - display_using_handler(msgnum, "text/calendar", - ICAL_VEVENT_COMPONENT, - display_individual_cal); +/* + * Display a calendar item + */ +void load_calendar_item(message_summary *Msg, int unread, struct calview *c) { + /*load_ical_object(Msg->msgnum, unread, ICAL_VEVENT_COMPONENT, display_individual_cal, c);*/ + load_ical_object(Msg->msgnum, unread, (-1), display_individual_cal, c); } -void display_task(long msgnum) { - display_using_handler(msgnum, "text/calendar", - ICAL_VTODO_COMPONENT, - display_individual_cal); +/* + * Display task view + */ +void display_task(message_summary *Msg, int unread) { + load_ical_object(Msg->msgnum, unread, ICAL_VTODO_COMPONENT, display_individual_cal, NULL); } +/* + * Display the editor component for a task + */ void display_edit_task(void) { long msgnum = 0L; - + /* Force change the room if we have to */ - if (strlen(bstr("taskrm")) > 0) { - gotoroom(bstr("taskrm")); + if (havebstr("taskrm")) { + gotoroom(sbstr("taskrm")); } - msgnum = atol(bstr("msgnum")); + msgnum = lbstr("msgnum"); if (msgnum > 0L) { /* existing task */ - display_using_handler(msgnum, "text/calendar", - ICAL_VTODO_COMPONENT, - display_edit_individual_task); + load_ical_object(msgnum, 0, + ICAL_VTODO_COMPONENT, + display_edit_individual_task, + NULL + ); } else { /* new task */ - display_edit_individual_task(NULL, 0L); + display_edit_individual_task(NULL, 0L, "", 0, NULL); } } +/* + * save an edited task + */ void save_task(void) { long msgnum = 0L; - - msgnum = atol(bstr("msgnum")); + msgnum = lbstr("msgnum"); if (msgnum > 0L) { - display_using_handler(msgnum, "text/calendar", - ICAL_VTODO_COMPONENT, - save_individual_task); + load_ical_object(msgnum, 0, ICAL_VTODO_COMPONENT, save_individual_task, NULL); } else { - save_individual_task(NULL, 0L); + save_individual_task(NULL, 0L, "", 0, NULL); } } +/* + * display the editor component for an event + */ void display_edit_event(void) { long msgnum = 0L; - msgnum = atol(bstr("msgnum")); + msgnum = lbstr("msgnum"); if (msgnum > 0L) { /* existing event */ - display_using_handler(msgnum, "text/calendar", - ICAL_VEVENT_COMPONENT, - display_edit_individual_event); + load_ical_object(msgnum, 0, ICAL_VEVENT_COMPONENT, display_edit_individual_event, NULL); } else { /* new event */ - display_edit_individual_event(NULL, 0L); + display_edit_individual_event(NULL, 0L, "", 0, NULL); } } +/* + * save an edited event + */ void save_event(void) { long msgnum = 0L; - msgnum = atol(bstr("msgnum")); + msgnum = lbstr("msgnum"); if (msgnum > 0L) { - display_using_handler(msgnum, "text/calendar", - ICAL_VEVENT_COMPONENT, - save_individual_event); + /* load_ical_object(msgnum, 0, ICAL_VEVENT_COMPONENT, save_individual_event, NULL); */ + load_ical_object(msgnum, 0, (-1), save_individual_event, NULL); } else { - save_individual_event(NULL, 0L); + save_individual_event(NULL, 0L, "", 0, NULL); } } @@ -892,12 +1129,13 @@ void save_event(void) { /* - * freebusy display (for client software) + * Anonymous request of freebusy data for a user */ -void do_freebusy(char *req) { +void do_freebusy(const char *req) { char who[SIZ]; char buf[SIZ]; - char *fb; + int len; + long lines; extract_token(who, req, 1, ' ', sizeof who); if (!strncasecmp(who, "/freebusy/", 10)) { @@ -905,9 +1143,11 @@ void do_freebusy(char *req) { } unescape_input(who); - if ( (!strcasecmp(&who[strlen(who)-4], ".vcf")) - || (!strcasecmp(&who[strlen(who)-4], ".vfb")) ) { - who[strlen(who)-4] = 0; + len = strlen(who); + if ( (!strcasecmp(&who[len-4], ".vcf")) + || (!strcasecmp(&who[len-4], ".ifb")) + || (!strcasecmp(&who[len-4], ".vfb")) ) { + who[len-4] = 0; } lprintf(9, "freebusy requested for <%s>\n", who); @@ -915,19 +1155,30 @@ void do_freebusy(char *req) { serv_getln(buf, sizeof buf); if (buf[0] != '1') { - wprintf("HTTP/1.0 404 %s\n", &buf[4]); - output_headers(0, 0, 0, 0, 0, 0, 0); - wprintf("Content-Type: text/plain\r\n"); - wprintf("\r\n"); + hprintf("HTTP/1.1 404 %s\n", &buf[4]); + output_headers(0, 0, 0, 0, 0, 0); + hprintf("Content-Type: text/plain\r\n"); wprintf("%s\n", &buf[4]); + end_burst(); return; } - fb = read_server_text(); - http_transmit_thing(fb, strlen(fb), "text/calendar", 0); - free(fb); + read_server_text(WC->WBuf, &lines); + http_transmit_thing("text/calendar", 0); } -#endif /* WEBCIT_WITH_CALENDAR_SERVICE */ + + +void +InitModule_CALENDAR +(void) +{ + WebcitAddUrlHandler(HKEY("display_edit_task"), display_edit_task, 0); + WebcitAddUrlHandler(HKEY("save_task"), save_task, 0); + WebcitAddUrlHandler(HKEY("display_edit_event"), display_edit_event, 0); + WebcitAddUrlHandler(HKEY("save_event"), save_event, 0); + WebcitAddUrlHandler(HKEY("respond_to_request"), respond_to_request, 0); + WebcitAddUrlHandler(HKEY("handle_rsvp"), handle_rsvp, 0); +}