X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=webcit%2Fcalendar.c;h=effd9aea1b51c4586d9a452447774b699fcbf846;hb=523c1b0f7a3002c6aaa3eb833b55eb0cf07674ff;hp=7c436ccad6c336c3280216838134343f2bfa6b9c;hpb=34fad51b0ed575eef472584db194f31fcf53ae59;p=citadel.git diff --git a/webcit/calendar.c b/webcit/calendar.c index 7c436ccad..effd9aea1 100644 --- a/webcit/calendar.c +++ b/webcit/calendar.c @@ -5,61 +5,42 @@ * */ -#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 HAVE_ICAL_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," + 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" + "calendaring enabled.

\n") ); } void display_calendar(long msgnum) { - wprintf("" + 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"); + "

\n")); } void display_task(long msgnum) { - wprintf("" + 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"); + "

\n")); } -#else /* HAVE_ICAL_H */ +#else /* WEBCIT_WITH_CALENDAR_SERVICE */ /****** End of handler stubs. Everything below this line is real. ******/ @@ -70,6 +51,7 @@ void display_task(long msgnum) { /* * Process a calendar object * ...at this point it's already been deserialized by cal_process_attachment() + * */ void cal_process_object(icalcomponent *cal, int recursion_level, @@ -82,8 +64,9 @@ void cal_process_object(icalcomponent *cal, 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; /* Leading HTML for the display of this object */ @@ -99,41 +82,54 @@ void cal_process_object(icalcomponent *cal, the_method = icalproperty_get_method(method); switch(the_method) { case ICAL_METHOD_REQUEST: + wprintf("\n" + "" + "  " + ""); + wprintf(_("Meeting invitation")); + wprintf("\n"); + break; + case ICAL_METHOD_REPLY: wprintf("\n" "" + "SRC=\"/static/calarea_48x.gif\">" "  " - "Meeting invitation - \n" - ); + ""); + wprintf(_("Attendee's reply to your invitation")); + wprintf("\n"); break; case ICAL_METHOD_PUBLISH: wprintf("\n" "" + "SRC=\"/static/calarea_48x.gif\">" "  " - "Published event - \n" - ); + ""); + wprintf(_("Published event")); + wprintf("\n"); break; default: - wprintf("" - "I don't know what to do with this." - "\n"); + wprintf(""); + wprintf(_("This is an unknown type of calendar item.")); + wprintf("\n"); break; } } p = icalcomponent_get_first_property(cal, ICAL_SUMMARY_PROPERTY); if (p != NULL) { - wprintf("Summary:"); + wprintf(""); + wprintf(_("Summary:")); + wprintf(""); escputs((char *)icalproperty_get_comment(p)); wprintf("\n"); } p = icalcomponent_get_first_property(cal, ICAL_LOCATION_PROPERTY); if (p != NULL) { - wprintf("Location:"); + wprintf(""); + wprintf(_("Location:")); + wprintf(""); escputs((char *)icalproperty_get_comment(p)); wprintf("\n"); } @@ -150,8 +146,9 @@ void cal_process_object(icalcomponent *cal, t = icalproperty_get_dtstart(p); if (t.is_date) { - wprintf("Date:" - "" + wprintf(""); + wprintf(_("Date:")); + wprintf("" "%s %d, %d", months[t.month - 1], t.day, t.year @@ -159,11 +156,10 @@ void cal_process_object(icalcomponent *cal, } else { tt = icaltime_as_timet(t); - fmt_date(buf, tt); - wprintf("Starting date/time:" - "" - "%s", buf - ); + fmt_date(buf, tt, 0); + wprintf(""); + wprintf(_("Starting date/time:")); + wprintf("%s", buf); } } @@ -171,21 +167,44 @@ void cal_process_object(icalcomponent *cal, if (p != NULL) { t = icalproperty_get_dtend(p); tt = icaltime_as_timet(t); - fmt_date(buf, tt); - wprintf("Ending date/time:" - "%s", buf - ); + fmt_date(buf, tt, 0); + wprintf(""); + wprintf(_("Ending date/time:")); + wprintf("%s", buf); } } p = icalcomponent_get_first_property(cal, ICAL_DESCRIPTION_PROPERTY); if (p != NULL) { - wprintf("Description:"); + wprintf(""); + wprintf(_("Description:")); + wprintf(""); escputs((char *)icalproperty_get_comment(p)); wprintf("\n"); } + /* 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(""); + wprintf(_("Attendee:")); + wprintf(""); + safestrncpy(buf, icalproperty_get_attendee(p), sizeof buf); + if (!strncasecmp(buf, "MAILTO:", 7)) { + + /* screen name or email address */ + strcpy(buf, &buf[7]); + striplt(buf); + escputs(buf); + wprintf(" "); + + /* participant status */ + partstat_as_string(buf, p); + escputs(buf); + } + wprintf("\n"); + } + /* If the component has subcomponents, recurse through them. */ for (c = icalcomponent_get_first_component(cal, ICAL_ANY_COMPONENT); (c != 0); @@ -198,54 +217,74 @@ void cal_process_object(icalcomponent *cal, if (the_method == ICAL_METHOD_REQUEST) { /* Check for conflicts */ + lprintf(9, "Checking server calendar for conflicts...\n"); serv_printf("ICAL conflicts|%ld|%s|", msgnum, cal_partnum); - serv_gets(buf); + serv_getln(buf, sizeof buf); if (buf[0] == '1') { - while (serv_gets(buf), strcmp(buf, "000")) { - extract(conflict_name, buf, 3); + 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("%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); + } + wprintf("%s", (is_update ? - "This is an update of" : - "This event would conflict with" + _("Update:") : + _("CONFLICT:") ) - ); - escputs(conflict_name); - wprintf("" " - "which is already in your calendar." - "\n"); + escputs(conflict_message); + wprintf("\n"); } } + lprintf(9, "...done.\n"); /* Display the Accept/Decline buttons */ - wprintf("" - "
\n" - "\n" - "  " - "\n" - "  " - "\n" - "" - "" - "
" + wprintf("How would you like to respond to this invitation?" + "" + "%s" + " | " + "%s" + " | " + "%s" + "\n", + msgnum, cal_partnum, _("Accept"), + msgnum, cal_partnum, _("Tentative"), + 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("" + "%s" + "" + "%s" + " | " + "%s" + "" "\n", - msgnum, cal_partnum + _("Click Update to accept this reply and update your calendar."), + msgnum, cal_partnum, _("Update"), + msgnum, cal_partnum, _("Ignore") ); } @@ -268,11 +307,12 @@ void cal_process_attachment(char *part_source, long msgnum, char *cal_partnum) { cal = icalcomponent_new_from_string(part_source); if (cal == NULL) { - wprintf("Error parsing calendar object: %s
\n", - icalerror_strerror(icalerrno)); + wprintf(_("There was an error parsing this calendar item.")); + wprintf("
\n"); return; } + ical_dezonify(cal); cal_process_object(cal, 0, msgnum, cal_partnum); /* Free the memory we obtained from libical's constructor */ @@ -288,41 +328,98 @@ void cal_process_attachment(char *part_source, long msgnum, char *cal_partnum) { void respond_to_request(void) { char buf[SIZ]; - output_headers(3); + output_headers(1, 1, 2, 0, 0, 0); - wprintf("
" - "Respond to meeting request" - "

\n" + wprintf("
\n"); + wprintf("
" + ""); + wprintf(_("Respond to meeting request")); + wprintf("" + "
\n" ); + wprintf("
\n
\n"); serv_printf("ICAL respond|%s|%s|%s|", bstr("msgnum"), bstr("cal_partnum"), bstr("sc") ); - serv_gets(buf); + serv_getln(buf, sizeof buf); if (buf[0] == '2') { 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(" "); + wprintf(_("A reply has been sent to the meeting organizer.")); + wprintf("
\n"); + } else { + wprintf("" + "%s\n", &buf[4]); + } + + wprintf("wc_roomname); + wprintf("\">
"); + wprintf(_("Return to messages")); + wprintf("

\n"); + + wDumpContent(1); +} + + + +/* + * Handle an incoming RSVP + */ +void handle_rsvp(void) { + char buf[SIZ]; + + output_headers(1, 1, 2, 0, 0, 0); + + wprintf("
\n"); + wprintf("
" + ""); + wprintf(_("Update your calendar with this RSVP")); + wprintf("" + "
\n" + "
\n
\n" + ); + + serv_printf("ICAL handle_rsvp|%s|%s|%s|", + bstr("msgnum"), + bstr("cal_partnum"), + bstr("sc") + ); + serv_getln(buf, sizeof buf); + + if (buf[0] == '2') { + wprintf("
" + "" + "" + ); + if (!strcasecmp(bstr("sc"), "update")) { + 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("and a reply has been sent to the meeting organizer." - "
\n" + wprintf("\n" ); } else { wprintf("" @@ -331,7 +428,9 @@ void respond_to_request(void) { wprintf("wc_roomname); - wprintf("\">Return to messages
\n"); + wprintf("\">
"); + wprintf(_("Return to messages")); + wprintf("
\n"); wDumpContent(1); } @@ -358,33 +457,17 @@ void display_individual_cal(icalcomponent *cal, long msgnum) { WC->num_cal += 1; WC->disp_cal = realloc(WC->disp_cal, - (sizeof(icalcomponent *) * WC->num_cal) ); - WC->disp_cal[WC->num_cal - 1] = icalcomponent_new_clone(cal); + (sizeof(struct disp_cal) * WC->num_cal) ); + WC->disp_cal[WC->num_cal - 1].cal = icalcomponent_new_clone(cal); - WC->cal_msgnum = realloc(WC->cal_msgnum, - (sizeof(long) * WC->num_cal) ); - WC->cal_msgnum[WC->num_cal - 1] = msgnum; + WC->disp_cal[WC->num_cal - 1].cal_msgnum = msgnum; } -/* - * Display a task in the task list - */ -void display_individual_task(icalcomponent *vtodo, long msgnum) { - icalproperty *p; - - p = icalcomponent_get_first_property(vtodo, ICAL_SUMMARY_PROPERTY); - wprintf("
  • ", msgnum); - if (p != NULL) { - escputs((char *)icalproperty_get_comment(p)); - } - wprintf("\n"); -} - - /* * Display a task by itself (for editing) + * */ void display_edit_individual_task(icalcomponent *supplied_vtodo, long msgnum) { icalcomponent *vtodo; @@ -397,33 +480,62 @@ void display_edit_individual_task(icalcomponent *supplied_vtodo, long msgnum) { if (supplied_vtodo != NULL) { vtodo = supplied_vtodo; + + /* 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 + * tell the next iteration of this function to create a + * new one. + */ + if (icalcomponent_isa(vtodo) == ICAL_VCALENDAR_COMPONENT) { + display_edit_individual_task( + icalcomponent_get_first_component( + vtodo, ICAL_VTODO_COMPONENT + ), msgnum + ); + return; + } } else { vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT); created_new_vtodo = 1; } - output_headers(3); - wprintf("
    " - "Edit task" - "

    \n" + output_headers(1, 1, 2, 0, 0, 0); + wprintf("
    \n" + "" + "" + "
    "); + wprintf(_("Edit task")); + wprintf("" + "
    \n" + "
    \n
    \n" ); + wprintf("
    " + "
    "); + wprintf("
    \n"); wprintf("\n", msgnum); - wprintf("Summary: " + wprintf("\n"); + + wprintf("\n"); - wprintf("Start date: "); + wprintf("\n"); - wprintf("Due date: "); + wprintf("
    "); + wprintf(_("Summary:")); + wprintf("" "
    \n"); + wprintf("\">
    "); + wprintf(_("Start date:")); + wprintf(""); p = icalcomponent_get_first_property(vtodo, ICAL_DTSTART_PROPERTY); if (p != NULL) { t = icalproperty_get_dtstart(p); @@ -432,9 +544,11 @@ void display_edit_individual_task(icalcomponent *supplied_vtodo, long msgnum) { t = icaltime_from_timet(now, 0); } display_icaltimetype_as_webform(&t, "dtstart"); - wprintf("
    \n"); + wprintf("
    "); + wprintf(_("Due date:")); + wprintf(""); p = icalcomponent_get_first_property(vtodo, ICAL_DUE_PROPERTY); if (p != NULL) { t = icalproperty_get_due(p); @@ -443,27 +557,34 @@ void display_edit_individual_task(icalcomponent *supplied_vtodo, long msgnum) { t = icaltime_from_timet(now, 0); } display_icaltimetype_as_webform(&t, "due"); - wprintf("
    \n"); - - wprintf("

    \n"); + wprintf("
    \n"); - wprintf("" + wprintf("
    " + "" "  " - "\n" + "\n" "  " - "\n" - "
    \n" + "\n" + "\n", + _("Save"), + _("Delete"), + _("Cancel") ); wprintf("
    \n"); + wprintf("
    \n"); wDumpContent(1); if (created_new_vtodo) { @@ -473,23 +594,42 @@ void display_edit_individual_task(icalcomponent *supplied_vtodo, long msgnum) { /* * Save an edited task + * */ void save_individual_task(icalcomponent *supplied_vtodo, long msgnum) { char buf[SIZ]; int delete_existing = 0; icalproperty *prop; - icalcomponent *vtodo; + icalcomponent *vtodo, *encaps; 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 + * 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 + * tell the next iteration of this function to create a + * new one. + */ + if (icalcomponent_isa(vtodo) == ICAL_VCALENDAR_COMPONENT) { + save_individual_task( + icalcomponent_get_first_component( + vtodo, ICAL_VTODO_COMPONENT + ), msgnum + ); + return; + } } else { vtodo = icalcomponent_new(ICAL_VTODO_COMPONENT); created_new_vtodo = 1; } - if (!strcasecmp(bstr("sc"), "Save")) { + if (strlen(bstr("save_button")) > 0) { /* Replace values in the component with ones from the form */ @@ -514,10 +654,9 @@ void save_individual_task(icalcomponent *supplied_vtodo, long msgnum) { icalcomponent_remove_property(vtodo, prop); icalproperty_free(prop); } + icaltime_from_webform(&t, "dtstart"); icalcomponent_add_property(vtodo, - icalproperty_new_dtstart( - icaltime_from_webform("dtstart") - ) + icalproperty_new_dtstart(t) ); while (prop = icalcomponent_get_first_property(vtodo, @@ -525,19 +664,54 @@ void save_individual_task(icalcomponent *supplied_vtodo, long msgnum) { icalcomponent_remove_property(vtodo, prop); icalproperty_free(prop); } + icaltime_from_webform(&t, "due"); icalcomponent_add_property(vtodo, - icalproperty_new_due( - icaltime_from_webform("due") - ) + 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) { + generate_uuid(buf); + icalcomponent_add_property(vtodo, + icalproperty_new_uid(buf) + ); + } + + /* Increment the sequence ID */ + lprintf(9, "Increment the sequence ID\n"); + while (prop = icalcomponent_get_first_property(vtodo, + ICAL_SEQUENCE_PROPERTY), (prop != NULL) ) { + i = icalproperty_get_sequence(prop); + lprintf(9, "Sequence was %d\n", i); + if (i > sequence) sequence = i; + icalcomponent_remove_property(vtodo, prop); + icalproperty_free(prop); + } + ++sequence; + lprintf(9, "New sequence is %d. Adding...\n", sequence); + icalcomponent_add_property(vtodo, + 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"); + encaps = ical_encapsulate_subcomponent(icalcomponent_new_clone(vtodo)); + /* Serialize it and save it to the message base */ serv_puts("ENT0 1|||4"); - serv_gets(buf); + serv_getln(buf, sizeof buf); if (buf[0] == '4') { serv_puts("Content-type: text/calendar"); serv_puts(""); - serv_puts(icalcomponent_as_ical_string(vtodo)); + serv_puts(icalcomponent_as_ical_string(encaps)); serv_puts("000"); /* Probably not necessary; the server will see the UID @@ -546,18 +720,19 @@ void save_individual_task(icalcomponent *supplied_vtodo, long msgnum) { */ delete_existing = 1; } + icalcomponent_free(encaps); } /* * If the user clicked 'Delete' then explicitly delete the message. */ - if (!strcasecmp(bstr("sc"), "Delete")) { + if (strlen(bstr("delete_button")) > 0) { delete_existing = 1; } if ( (delete_existing) && (msgnum > 0L) ) { serv_printf("DELE %ld", atol(bstr("msgnum"))); - serv_gets(buf); + serv_getln(buf, sizeof buf); } if (created_new_vtodo) { @@ -575,6 +750,7 @@ void save_individual_task(icalcomponent *supplied_vtodo, long msgnum) { * 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, @@ -593,15 +769,15 @@ void display_using_handler(long msgnum, sprintf(buf, "MSG0 %ld|1", msgnum); /* ask for headers only */ serv_puts(buf); - serv_gets(buf); + serv_getln(buf, sizeof buf); if (buf[0] != '1') return; - while (serv_gets(buf), strcmp(buf, "000")) { + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { if (!strncasecmp(buf, "part=", 5)) { - extract(mime_filename, &buf[5], 1); - extract(mime_partnum, &buf[5], 2); - extract(mime_disposition, &buf[5], 3); - extract(mime_content_type, &buf[5], 4); + extract_token(mime_filename, &buf[5], 1, '|', sizeof mime_filename); + extract_token(mime_partnum, &buf[5], 2, '|', sizeof mime_partnum); + extract_token(mime_disposition, &buf[5], 3, '|', sizeof mime_disposition); + 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")) { @@ -618,6 +794,8 @@ void display_using_handler(long msgnum, 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); @@ -648,12 +826,17 @@ void display_calendar(long msgnum) { void display_task(long msgnum) { display_using_handler(msgnum, "text/calendar", ICAL_VTODO_COMPONENT, - display_individual_task); + display_individual_cal); } void display_edit_task(void) { long msgnum = 0L; + /* Force change the room if we have to */ + if (strlen(bstr("taskrm")) > 0) { + gotoroom(bstr("taskrm")); + } + msgnum = atol(bstr("msgnum")); if (msgnum > 0L) { /* existing task */ @@ -712,4 +895,48 @@ void save_event(void) { } } -#endif /* HAVE_ICAL_H */ + + + + +/* + * freebusy display (for client software) + */ +void do_freebusy(char *req) { + char who[SIZ]; + char buf[SIZ]; + char *fb; + + extract_token(who, req, 1, ' ', sizeof who); + if (!strncasecmp(who, "/freebusy/", 10)) { + strcpy(who, &who[10]); + } + unescape_input(who); + + if ( (!strcasecmp(&who[strlen(who)-4], ".vcf")) + || (!strcasecmp(&who[strlen(who)-4], ".ifb")) + || (!strcasecmp(&who[strlen(who)-4], ".vfb")) ) { + who[strlen(who)-4] = 0; + } + + lprintf(9, "freebusy requested for <%s>\n", who); + serv_printf("ICAL freebusy|%s", who); + 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); + wprintf("Content-Type: text/plain\r\n"); + wprintf("\r\n"); + wprintf("%s\n", &buf[4]); + return; + } + + fb = read_server_text(); + http_transmit_thing(fb, strlen(fb), "text/calendar", 0); + free(fb); +} + + + +#endif /* WEBCIT_WITH_CALENDAR_SERVICE */