X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fmodules%2Fcalendar%2Fserv_calendar.c;h=b1610c7fecaad4efce96041a7a8888f802ec3e7e;hb=54c53c773c3d40a2e1ebaf4d8d2e7f694a893c74;hp=21d2e3d0451d21c96f7329514946323b4b1ffb67;hpb=a2fda4eafb51bbf58c04471522aa2d0f116c797e;p=citadel.git diff --git a/citadel/modules/calendar/serv_calendar.c b/citadel/modules/calendar/serv_calendar.c index 21d2e3d04..b1610c7fe 100644 --- a/citadel/modules/calendar/serv_calendar.c +++ b/citadel/modules/calendar/serv_calendar.c @@ -19,7 +19,7 @@ #ifdef HAVE_STRINGS_H #include #endif -#include +#include #include #include "citadel.h" #include "server.h" @@ -87,11 +87,6 @@ icalcomponent *ical_encapsulate_subcomponent(icalcomponent *subcomp) { /* Encapsulate the subcomponent inside */ icalcomponent_add_component(encaps, subcomp); - /* Convert all timestamps to UTC so we don't have to deal with - * stupid VTIMEZONE crap. - */ - ical_dezonify(encaps); - /* Return the object we just created. */ return(encaps); } @@ -105,8 +100,6 @@ icalcomponent *ical_encapsulate_subcomponent(icalcomponent *subcomp) { * to the currently selected room. */ void ical_write_to_cal(struct ctdluser *u, icalcomponent *cal) { - char temp[PATH_MAX]; - FILE *fp = NULL; char *ser = NULL; icalcomponent *encaps = NULL; struct CtdlMessage *msg = NULL; @@ -126,29 +119,21 @@ void ical_write_to_cal(struct ctdluser *u, icalcomponent *cal) { return; } - ser = icalcomponent_as_ical_string(cal); + ser = icalcomponent_as_ical_string_r(cal); if (ser == NULL) return; /* If the caller supplied a user, write to that user's default calendar room */ if (u) { - /* Make a temp file out of it */ - CtdlMakeTempFileName(temp, sizeof temp); - fp = fopen(temp, "w"); - if (fp != NULL) { - fwrite(ser, strlen(ser), 1, fp); - fclose(fp); - - /* This handy API function does all the work for us. */ - CtdlWriteObject(USERCALENDARROOM, /* which room */ - "text/calendar", /* MIME type */ - temp, /* temp file */ - u, /* which user */ - 0, /* not binary */ - 0, /* don't delete others of this type */ - 0 /* no flags */ - ); - unlink(temp); - } + /* This handy API function does all the work for us. */ + CtdlWriteObject(USERCALENDARROOM, /* which room */ + "text/calendar", /* MIME type */ + ser, /* data */ + strlen(ser)+1, /* length */ + u, /* which user */ + 0, /* not binary */ + 0, /* don't delete others of this type */ + 0 /* no flags */ + ); } /* If the caller did not supply a user, write to the currently selected room */ @@ -167,12 +152,12 @@ void ical_write_to_cal(struct ctdluser *u, icalcomponent *cal) { strcat(msg->cm_fields['M'], ser); /* Now write the data */ - CtdlSubmitMsg(msg, NULL, ""); + CtdlSubmitMsg(msg, NULL, "", QP_EADDR); CtdlFreeMessage(msg); } /* In either case, now we can free the serialized calendar object */ -// free(ser); + free(ser); } @@ -184,6 +169,15 @@ void ical_write_to_cal(struct ctdluser *u, icalcomponent *cal) { void ical_add(icalcomponent *cal, int recursion_level) { icalcomponent *c; +#if 1 + /* Write the whole thing because it may need to save timezones etc. + * FIXME - if this works, we can probably eliminate this entire function + */ + + ical_write_to_cal(&CC->user, cal); + +#else /* this was the old code to kill everything but the VEVENT component ... probably ng now */ + /* * The VEVENT subcomponent is the one we're interested in saving. */ @@ -200,6 +194,7 @@ void ical_add(icalcomponent *cal, int recursion_level) { /* Recursively process subcomponent */ ical_add(c, recursion_level+1); } +#endif } @@ -325,14 +320,14 @@ void ical_send_a_reply(icalcomponent *request, char *action) { } /* Now generate the reply message and send it out. */ - serialized_reply = strdup(icalcomponent_as_ical_string(the_reply)); + serialized_reply = icalcomponent_as_ical_string_r(the_reply); icalcomponent_free(the_reply); /* don't need this anymore */ if (serialized_reply == NULL) return; reply_message_text = malloc(strlen(serialized_reply) + SIZ); if (reply_message_text != NULL) { sprintf(reply_message_text, - "Content-type: text/calendar charset=\"utf-8\"\r\n\r\n%s\r\n", + "Content-type: text/calendar; charset=\"utf-8\"\r\n\r\n%s\r\n", serialized_reply ); @@ -344,11 +339,12 @@ void ical_send_a_reply(icalcomponent *request, char *action) { "", summary_string, /* Use summary for subject */ NULL, - reply_message_text); + reply_message_text, + NULL); if (msg != NULL) { valid = validate_recipients(organizer_string, NULL, 0); - CtdlSubmitMsg(msg, valid, ""); + CtdlSubmitMsg(msg, valid, "", QP_EADDR); CtdlFreeMessage(msg); free_recipients(valid); } @@ -364,7 +360,7 @@ void ical_send_a_reply(icalcomponent *request, char *action) { */ void ical_locate_part(char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, char *cbcharset, size_t length, char *encoding, - void *cbuserdata) { + char *cbid, void *cbuserdata) { struct ical_respond_data *ird = NULL; @@ -391,9 +387,6 @@ void ical_locate_part(char *name, char *filename, char *partnum, char *disp, } ird->cal = icalcomponent_new_from_string(content); - if (ird->cal != NULL) { - ical_dezonify(ird->cal); - } } @@ -523,7 +516,7 @@ struct original_event_container { */ void ical_locate_original_event(char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, char *cbcharset, size_t length, char *encoding, - void *cbuserdata) { + char *cbid, void *cbuserdata) { struct original_event_container *oec = NULL; @@ -655,7 +648,7 @@ int ical_update_my_calendar_with_reply(icalcomponent *cal) { /* * Look in the EUID index for a message with * the Citadel EUID set to the value we're looking for. Since - * Citadel always sets the message EUID to the vCalendar UID of + * Citadel always sets the message EUID to the iCalendar UID of * the event, this will work. */ msgnum_being_replaced = locate_message_by_euid(uid, &CC->room); @@ -697,7 +690,7 @@ int ical_update_my_calendar_with_reply(icalcomponent *cal) { ical_merge_attendee_reply(original_event, cal); /* Serialize it */ - serialized_event = strdup(icalcomponent_as_ical_string(original_event)); + serialized_event = icalcomponent_as_ical_string_r(original_event); icalcomponent_free(original_event); /* Don't need this anymore. */ if (serialized_event == NULL) return(2); @@ -706,7 +699,7 @@ int ical_update_my_calendar_with_reply(icalcomponent *cal) { message_text = malloc(strlen(serialized_event) + SIZ); if (message_text != NULL) { sprintf(message_text, - "Content-type: text/calendar charset=\"utf-8\"\r\n\r\n%s\r\n", + "Content-type: text/calendar; charset=\"utf-8\"\r\n\r\n%s\r\n", serialized_event ); @@ -719,11 +712,12 @@ int ical_update_my_calendar_with_reply(icalcomponent *cal) { "", "", /* no subject */ NULL, - message_text); + message_text, + NULL); if (msg != NULL) { CIT_ICAL->avoid_sending_invitations = 1; - CtdlSubmitMsg(msg, NULL, roomname); + CtdlSubmitMsg(msg, NULL, roomname, QP_EADDR); CtdlFreeMessage(msg); CIT_ICAL->avoid_sending_invitations = 0; } @@ -898,87 +892,229 @@ int ical_ctdl_is_overlap( +/* + * Phase 6 of "hunt for conflicts" + * called by ical_conflicts_phase5() + * + * Now both the proposed and existing events have been boiled down to start and end times. + * Check for overlap and output any conflicts. + */ +void ical_conflicts_phase6(struct icaltimetype t1start, + struct icaltimetype t1end, + struct icaltimetype t2start, + struct icaltimetype t2end, + long existing_msgnum, + char *conflict_event_uid, + char *conflict_event_summary, + char *compare_uid) +{ + + /* debugging cruft */ + // time_t tt; + // tt = icaltime_as_timet(t1start); + // CtdlLogPrintf(CTDL_DEBUG, "PROPOSED START: %s", ctime(&tt)); + // tt = icaltime_as_timet(t1end); + // CtdlLogPrintf(CTDL_DEBUG, " PROPOSED END: %s", ctime(&tt)); + // tt = icaltime_as_timet(t2start); + // CtdlLogPrintf(CTDL_DEBUG, "EXISTING START: %s", ctime(&tt)); + // tt = icaltime_as_timet(t2end); + // CtdlLogPrintf(CTDL_DEBUG, " EXISTING END: %s", ctime(&tt)); + + /* compare and output */ + + if (ical_ctdl_is_overlap(t1start, t1end, t2start, t2end)) { + cprintf("%ld||%s|%s|%d|\n", + existing_msgnum, + conflict_event_uid, + conflict_event_summary, + ( ((strlen(compare_uid)>0) + &&(!strcasecmp(compare_uid, + conflict_event_uid))) ? 1 : 0 + ) + ); + } + +} + + + /* - * Backend for ical_hunt_for_conflicts() + * Phase 5 of "hunt for conflicts" + * Called by ical_conflicts_phase4() + * + * We have the proposed event boiled down to start and end times. + * Now check it against an existing event. */ -void ical_hunt_for_conflicts_backend(long msgnum, void *data) { - icalcomponent *cal; - struct CtdlMessage *msg = NULL; - struct ical_respond_data ird; - struct icaltimetype t1start, t1end, t2start, t2end; - icalproperty *p; +void ical_conflicts_phase5(struct icaltimetype t1start, + struct icaltimetype t1end, + icalcomponent *existing_event, + long existing_msgnum, + char *compare_uid) +{ char conflict_event_uid[SIZ]; char conflict_event_summary[SIZ]; - char compare_uid[SIZ]; + struct icaltimetype t2start, t2end; + icalproperty *p; - cal = (icalcomponent *)data; - strcpy(compare_uid, ""); + /* recur variables */ + icalproperty *rrule = NULL; + struct icalrecurrencetype recur; + icalrecur_iterator *ritr = NULL; + struct icaldurationtype dur; + int num_recur = 0; + + /* initialization */ strcpy(conflict_event_uid, ""); strcpy(conflict_event_summary, ""); + t2start = icaltime_null_time(); + t2end = icaltime_null_time(); + + /* existing event stuff */ + p = ical_ctdl_get_subprop(existing_event, ICAL_DTSTART_PROPERTY); + if (p == NULL) return; + if (p != NULL) t2start = icalproperty_get_dtstart(p); + + p = ical_ctdl_get_subprop(existing_event, ICAL_DTEND_PROPERTY); + if (p != NULL) { + t2end = icalproperty_get_dtend(p); + dur = icaltime_subtract(t2end, t2start); + } + + rrule = ical_ctdl_get_subprop(existing_event, ICAL_RRULE_PROPERTY); + if (rrule) { + recur = icalproperty_get_rrule(rrule); + ritr = icalrecur_iterator_new(recur, t2start); + CtdlLogPrintf(CTDL_DEBUG, "Recurrence found: %s\n", icalrecurrencetype_as_string(&recur)); + } + + do { + p = ical_ctdl_get_subprop(existing_event, ICAL_UID_PROPERTY); + if (p != NULL) { + strcpy(conflict_event_uid, icalproperty_get_comment(p)); + } + + p = ical_ctdl_get_subprop(existing_event, ICAL_SUMMARY_PROPERTY); + if (p != NULL) { + strcpy(conflict_event_summary, icalproperty_get_comment(p)); + } + + ical_conflicts_phase6(t1start, t1end, t2start, t2end, + existing_msgnum, conflict_event_uid, conflict_event_summary, compare_uid + ); + + if (rrule) { + t2start = icalrecur_iterator_next(ritr); + if (!icaltime_is_null_time(t2end)) { + t2end = icaltime_add(t2start, dur); + } + ++num_recur; + } + + } while ( (rrule) && (!icaltime_is_null_time(t2start)) && (num_recur < MAX_RECUR) ); + icalrecur_iterator_free(ritr); + if (num_recur > 0) CtdlLogPrintf(CTDL_DEBUG, "Iterated over existing event %d times.\n", num_recur); +} + - msg = CtdlFetchMessage(msgnum, 1); - if (msg == NULL) return; - memset(&ird, 0, sizeof ird); - strcpy(ird.desired_partnum, "_HUNT_"); - mime_parser(msg->cm_fields['M'], - NULL, - *ical_locate_part, /* callback function */ - NULL, NULL, - (void *) &ird, /* user data */ - 0 - ); - CtdlFreeMessage(msg); - if (ird.cal == NULL) return; +/* + * Phase 4 of "hunt for conflicts" + * Called by ical_hunt_for_conflicts_backend() + * + * At this point we've got it boiled down to two icalcomponent events in memory. + * If they conflict, output something to the client. + */ +void ical_conflicts_phase4(icalcomponent *proposed_event, + icalcomponent *existing_event, + long existing_msgnum) +{ + struct icaltimetype t1start, t1end; t1start = icaltime_null_time(); t1end = icaltime_null_time(); - t2start = icaltime_null_time(); - t1end = icaltime_null_time(); + icalproperty *p; + char compare_uid[SIZ]; - /* Now compare cal to ird.cal */ - p = ical_ctdl_get_subprop(ird.cal, ICAL_DTSTART_PROPERTY); - if (p == NULL) return; - if (p != NULL) t2start = icalproperty_get_dtstart(p); - - p = ical_ctdl_get_subprop(ird.cal, ICAL_DTEND_PROPERTY); - if (p != NULL) t2end = icalproperty_get_dtend(p); + /* recur variables */ + icalproperty *rrule = NULL; + struct icalrecurrencetype recur; + icalrecur_iterator *ritr = NULL; + struct icaldurationtype dur; + int num_recur = 0; - p = ical_ctdl_get_subprop(cal, ICAL_DTSTART_PROPERTY); + /* initialization */ + strcpy(compare_uid, ""); + + /* proposed event stuff */ + + p = ical_ctdl_get_subprop(proposed_event, ICAL_DTSTART_PROPERTY); if (p == NULL) return; if (p != NULL) t1start = icalproperty_get_dtstart(p); - p = ical_ctdl_get_subprop(cal, ICAL_DTEND_PROPERTY); - if (p != NULL) t1end = icalproperty_get_dtend(p); - - p = ical_ctdl_get_subprop(cal, ICAL_UID_PROPERTY); + p = ical_ctdl_get_subprop(proposed_event, ICAL_DTEND_PROPERTY); if (p != NULL) { - strcpy(compare_uid, icalproperty_get_comment(p)); + t1end = icalproperty_get_dtend(p); + dur = icaltime_subtract(t1end, t1start); } - p = ical_ctdl_get_subprop(ird.cal, ICAL_UID_PROPERTY); - if (p != NULL) { - strcpy(conflict_event_uid, icalproperty_get_comment(p)); + rrule = ical_ctdl_get_subprop(proposed_event, ICAL_RRULE_PROPERTY); + if (rrule) { + recur = icalproperty_get_rrule(rrule); + ritr = icalrecur_iterator_new(recur, t1start); + CtdlLogPrintf(CTDL_DEBUG, "Recurrence found: %s\n", icalrecurrencetype_as_string(&recur)); } - p = ical_ctdl_get_subprop(ird.cal, ICAL_SUMMARY_PROPERTY); + p = ical_ctdl_get_subprop(proposed_event, ICAL_UID_PROPERTY); if (p != NULL) { - strcpy(conflict_event_summary, icalproperty_get_comment(p)); + strcpy(compare_uid, icalproperty_get_comment(p)); } - icalcomponent_free(ird.cal); + do { + ical_conflicts_phase5(t1start, t1end, existing_event, existing_msgnum, compare_uid); - if (ical_ctdl_is_overlap(t1start, t1end, t2start, t2end)) { - cprintf("%ld||%s|%s|%d|\n", - msgnum, - conflict_event_uid, - conflict_event_summary, - ( ((strlen(compare_uid)>0) - &&(!strcasecmp(compare_uid, - conflict_event_uid))) ? 1 : 0 - ) - ); - } + if (rrule) { + t1start = icalrecur_iterator_next(ritr); + if (!icaltime_is_null_time(t1end)) { + t1end = icaltime_add(t1start, dur); + } + ++num_recur; + } + + } while ( (rrule) && (!icaltime_is_null_time(t1start)) && (num_recur < MAX_RECUR) ); + icalrecur_iterator_free(ritr); + if (num_recur > 0) CtdlLogPrintf(CTDL_DEBUG, "Iterated over proposed event %d times.\n", num_recur); +} + + + +/* + * Phase 3 of "hunt for conflicts" + * Called by ical_hunt_for_conflicts() + */ +void ical_hunt_for_conflicts_backend(long msgnum, void *data) { + icalcomponent *proposed_event; + struct CtdlMessage *msg = NULL; + struct ical_respond_data ird; + + proposed_event = (icalcomponent *)data; + + msg = CtdlFetchMessage(msgnum, 1); + if (msg == NULL) return; + memset(&ird, 0, sizeof ird); + strcpy(ird.desired_partnum, "_HUNT_"); + mime_parser(msg->cm_fields['M'], + NULL, + *ical_locate_part, /* callback function */ + NULL, NULL, + (void *) &ird, /* user data */ + 0 + ); + CtdlFreeMessage(msg); + + if (ird.cal == NULL) return; + + ical_conflicts_phase4(proposed_event, ird.cal, msgnum); + icalcomponent_free(ird.cal); } @@ -986,7 +1122,7 @@ void ical_hunt_for_conflicts_backend(long msgnum, void *data) { /* * Phase 2 of "hunt for conflicts" operation. * At this point we have a calendar object which represents the VEVENT that - * we're considering adding to the calendar. Now hunt through the user's + * is proposed for addition to the calendar. Now hunt through the user's * calendar room, and output zero or more existing VEVENTs which conflict * with this one. */ @@ -1080,8 +1216,6 @@ void ical_add_to_freebusy(icalcomponent *fb, icalcomponent *cal) { return; } - ical_dezonify(cal); - /* If this event is not opaque, the user isn't publishing it as * busy time, so don't bother doing anything else. */ @@ -1337,7 +1471,7 @@ void ical_freebusy(char *who) { /* Serialize it */ CtdlLogPrintf(CTDL_DEBUG, "Serializing\n"); - serialized_request = strdup(icalcomponent_as_ical_string(encaps)); + serialized_request = icalcomponent_as_ical_string_r(encaps); icalcomponent_free(encaps); /* Don't need this anymore. */ cprintf("%d Here is the free/busy data:\n", LISTING_FOLLOWS); @@ -1423,7 +1557,7 @@ void ical_getics(void) if ( (CC->room.QRdefaultview != VIEW_CALENDAR) &&(CC->room.QRdefaultview != VIEW_TASKS) ) { cprintf("%d Not a calendar room\n", ERROR+NOT_HERE); - return; /* Not a vCalendar-centric room */ + return; /* Not an iCalendar-centric room */ } encaps = icalcomponent_new_vcalendar(); @@ -1453,7 +1587,7 @@ void ical_getics(void) (void *) encaps ); - ser = strdup(icalcomponent_as_ical_string(encaps)); + ser = icalcomponent_as_ical_string_r(encaps); client_write(ser, strlen(ser)); free(ser); cprintf("\n000\n"); @@ -1475,7 +1609,7 @@ void ical_putics(void) if ( (CC->room.QRdefaultview != VIEW_CALENDAR) &&(CC->room.QRdefaultview != VIEW_TASKS) ) { cprintf("%d Not a calendar room\n", ERROR+NOT_HERE); - return; /* Not a vCalendar-centric room */ + return; /* Not an iCalendar-centric room */ } if (!CtdlDoIHavePermissionToDeleteMessagesFromThisRoom()) { @@ -1491,7 +1625,6 @@ void ical_putics(void) cal = icalcomponent_new_from_string(calstream); free(calstream); - ical_dezonify(cal); /* We got our data stream -- now do something with it. */ @@ -1762,9 +1895,6 @@ void ical_send_out_invitations(icalcomponent *cal) { /* Set the method to REQUEST */ icalcomponent_set_method(encaps, ICAL_METHOD_REQUEST); - /* Now make sure all of the DTSTART and DTEND properties are UTC. */ - ical_dezonify(the_request); - /* Here we go: put the VEVENT into the VCALENDAR. We now no longer * are responsible for "the_request"'s memory -- it will be freed * when we free "encaps". @@ -1772,7 +1902,7 @@ void ical_send_out_invitations(icalcomponent *cal) { icalcomponent_add_component(encaps, the_request); /* Serialize it */ - serialized_request = strdup(icalcomponent_as_ical_string(encaps)); + serialized_request = icalcomponent_as_ical_string_r(encaps); icalcomponent_free(encaps); /* Don't need this anymore. */ if (serialized_request == NULL) return; @@ -1792,11 +1922,12 @@ void ical_send_out_invitations(icalcomponent *cal) { "", summary_string, /* Use summary for subject */ NULL, - request_message_text); + request_message_text, + NULL); if (msg != NULL) { valid = validate_recipients(attendees_string, NULL, 0); - CtdlSubmitMsg(msg, valid, ""); + CtdlSubmitMsg(msg, valid, "", QP_EADDR); CtdlFreeMessage(msg); free_recipients(valid); } @@ -1872,16 +2003,17 @@ void ical_saving_vevent(icalcomponent *cal) { * the summary of the event (becomes message subject), * and the start time (becomes message date/time). */ -void ical_ctdl_set_exclusive_msgid(char *name, char *filename, char *partnum, +void ical_obj_beforesave_backend(char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, char *cbcharset, size_t length, - char *encoding, void *cbuserdata) + char *encoding, char *cbid, void *cbuserdata) { icalcomponent *cal, *nested_event, *nested_todo, *whole_cal; icalproperty *p; - struct icalmessagemod *imm; - char new_uid[SIZ]; + char new_uid[256] = ""; + char buf[1024] = ""; + struct CtdlMessage *msg = (struct CtdlMessage *) cbuserdata; - imm = (struct icalmessagemod *)cbuserdata; + if (!msg) return; /* We're only interested in calendar data. */ if ( (strcasecmp(cbtype, "text/calendar")) @@ -1913,6 +2045,9 @@ void ical_ctdl_set_exclusive_msgid(char *name, char *filename, char *partnum, } if (cal != NULL) { + + /* Set the message EUID to the iCalendar UID */ + p = ical_ctdl_get_subprop(cal, ICAL_UID_PROPERTY); if (p == NULL) { /* If there's no uid we must generate one */ @@ -1921,16 +2056,44 @@ void ical_ctdl_set_exclusive_msgid(char *name, char *filename, char *partnum, p = ical_ctdl_get_subprop(cal, ICAL_UID_PROPERTY); } if (p != NULL) { - strcpy(imm->uid, icalproperty_get_comment(p)); + safestrncpy(buf, icalproperty_get_comment(p), sizeof buf); + if (!IsEmptyStr(buf)) { + if (msg->cm_fields['E'] != NULL) { + free(msg->cm_fields['E']); + } + msg->cm_fields['E'] = strdup(buf); + CtdlLogPrintf(CTDL_DEBUG, "Saving calendar UID <%s>\n", buf); + } } + + /* Set the message subject to the iCalendar summary */ + p = ical_ctdl_get_subprop(cal, ICAL_SUMMARY_PROPERTY); if (p != NULL) { - strcpy(imm->subject, icalproperty_get_comment(p)); + safestrncpy(buf, icalproperty_get_comment(p), sizeof buf); + if (!IsEmptyStr(buf)) { + if (msg->cm_fields['U'] != NULL) { + free(msg->cm_fields['U']); + } + msg->cm_fields['U'] = strdup(buf); + } } + + /* Set the message date/time to the iCalendar start time */ + p = ical_ctdl_get_subprop(cal, ICAL_DTSTART_PROPERTY); if (p != NULL) { - imm->dtstart = icaltime_as_timet(icalproperty_get_dtstart(p)); + time_t idtstart; + idtstart = icaltime_as_timet(icalproperty_get_dtstart(p)); + if (idtstart > 0) { + if (msg->cm_fields['T'] != NULL) { + free(msg->cm_fields['T']); + } + msg->cm_fields['T'] = strdup("000000000000000000"); + sprintf(msg->cm_fields['T'], "%ld", idtstart); + } } + } icalcomponent_free(cal); if (whole_cal != cal) { @@ -1944,24 +2107,18 @@ void ical_ctdl_set_exclusive_msgid(char *name, char *filename, char *partnum, /* * See if we need to prevent the object from being saved (we don't allow - * MIME types other than text/calendar in "calendar" or "tasks" rooms). Also, - * when saving an event to the calendar, set the message's Citadel exclusive - * message ID to the UID of the object. This causes our replication checker to - * automatically delete any existing instances of the same object. (Isn't - * that cool?) + * MIME types other than text/calendar in "calendar" or "tasks" rooms). * - * We also set the message's Subject to the event summary, and the Date/time to - * the event start time. + * If the message is being saved, we also set various message header fields + * using data found in the iCalendar object. */ int ical_obj_beforesave(struct CtdlMessage *msg) { - struct icalmessagemod imm; - /* First determine if this is a calendar or tasks room */ if ( (CC->room.QRdefaultview != VIEW_CALENDAR) && (CC->room.QRdefaultview != VIEW_TASKS) ) { - return(0); /* Not a vCalendar-centric room */ + return(0); /* Not an iCalendar-centric room */ } /* It must be an RFC822 message! */ @@ -1974,38 +2131,15 @@ int ical_obj_beforesave(struct CtdlMessage *msg) return(1); /* You tried to save a null message! */ } - memset(&imm, 0, sizeof(struct icalmessagemod)); - /* Do all of our lovely back-end parsing */ mime_parser(msg->cm_fields['M'], NULL, - *ical_ctdl_set_exclusive_msgid, + *ical_obj_beforesave_backend, NULL, NULL, - (void *)&imm, + (void *)msg, 0 ); - if (!IsEmptyStr(imm.uid)) { - if (msg->cm_fields['E'] != NULL) { - free(msg->cm_fields['E']); - } - msg->cm_fields['E'] = strdup(imm.uid); - CtdlLogPrintf(CTDL_DEBUG, "Saving calendar UID <%s>\n", msg->cm_fields['E']); - } - if (!IsEmptyStr(imm.subject)) { - if (msg->cm_fields['U'] != NULL) { - free(msg->cm_fields['U']); - } - msg->cm_fields['U'] = strdup(imm.subject); - } - if (imm.dtstart > 0) { - if (msg->cm_fields['T'] != NULL) { - free(msg->cm_fields['T']); - } - msg->cm_fields['T'] = strdup("000000000000000000"); - sprintf(msg->cm_fields['T'], "%ld", imm.dtstart); - } - return(0); } @@ -2015,7 +2149,7 @@ int ical_obj_beforesave(struct CtdlMessage *msg) */ void ical_obj_aftersave_backend(char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, char *cbcharset, size_t length, - char *encoding, void *cbuserdata) + char *encoding, char *cbid, void *cbuserdata) { icalcomponent *cal; @@ -2139,7 +2273,7 @@ void ical_fixed_output_backend(icalcomponent *cal, /* - * Function to output vcalendar data as plain text. Nobody uses MSG0 + * Function to output iCalendar data as plain text. Nobody uses MSG0 * anymore, so really this is just so we expose the vCard data to the full * text indexer. */ @@ -2156,7 +2290,6 @@ void ical_fixed_output(char *ptr, int len) { return; } - ical_dezonify(cal); ical_fixed_output_backend(cal, 0); /* Free the memory we obtained from libical's constructor */ @@ -2177,6 +2310,11 @@ CTDL_MODULE_INIT(calendar) { if (!threading) { + + /* Tell libical to return errors instead of aborting if it gets bad data */ + icalerror_errors_are_fatal = 0; + + /* Initialize our hook functions */ CtdlRegisterMessageHook(ical_obj_beforesave, EVT_BEFORESAVE); CtdlRegisterMessageHook(ical_obj_aftersave, EVT_AFTERSAVE); CtdlRegisterSessionHook(ical_create_room, EVT_LOGIN);