X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fmodules%2Fcalendar%2Fserv_calendar.c;h=d8f02831cc673768a252325ba15a5323433a93c7;hb=038aeea0faf257e005d81ad4196aa40489ad6705;hp=cd55a1339789224d74c61397c130d15f474d5e67;hpb=1e656d277fe91b7c4f5d73eab4a0dd0b7a173145;p=citadel.git diff --git a/citadel/modules/calendar/serv_calendar.c b/citadel/modules/calendar/serv_calendar.c index cd55a1339..d8f02831c 100644 --- a/citadel/modules/calendar/serv_calendar.c +++ b/citadel/modules/calendar/serv_calendar.c @@ -19,6 +19,7 @@ #ifdef HAVE_STRINGS_H #include #endif +#include #include #include "citadel.h" #include "server.h" @@ -32,10 +33,6 @@ #include "serv_calendar.h" #include "euidindex.h" #include "ctdl_module.h" - -#ifdef CITADEL_WITH_CALENDAR_SERVICE - -#include #include "ical_dezonify.h" @@ -55,7 +52,7 @@ icalcomponent *icalcomponent_new_citadel_vcalendar(void) { encaps = icalcomponent_new_vcalendar(); if (encaps == NULL) { - lprintf(CTDL_CRIT, "Error at %s:%d - could not allocate component!\n", + CtdlLogPrintf(CTDL_CRIT, "Error at %s:%d - could not allocate component!\n", __FILE__, __LINE__); return NULL; } @@ -108,8 +105,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; @@ -134,24 +129,16 @@ void ical_write_to_cal(struct ctdluser *u, icalcomponent *cal) { /* 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 */ @@ -170,7 +157,7 @@ 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); } @@ -236,13 +223,13 @@ void ical_send_a_reply(icalcomponent *request, char *action) { strcpy(summary_string, "Calendar item"); if (request == NULL) { - lprintf(CTDL_ERR, "ERROR: trying to reply to NULL event?\n"); + CtdlLogPrintf(CTDL_ERR, "ERROR: trying to reply to NULL event?\n"); return; } the_reply = icalcomponent_new_clone(request); if (the_reply == NULL) { - lprintf(CTDL_ERR, "ERROR: cannot clone request\n"); + CtdlLogPrintf(CTDL_ERR, "ERROR: cannot clone request\n"); return; } @@ -264,7 +251,7 @@ void ical_send_a_reply(icalcomponent *request, char *action) { if (!strncasecmp(attendee_string, "MAILTO:", 7)) { strcpy(attendee_string, &attendee_string[7]); striplt(attendee_string); - recp = validate_recipients(attendee_string); + recp = validate_recipients(attendee_string, NULL, 0); if (recp != NULL) { if (!strcasecmp(recp->recp_local, CC->user.fullname)) { if (me_attend) icalproperty_free(me_attend); @@ -335,7 +322,7 @@ void ical_send_a_reply(icalcomponent *request, char *action) { 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 ); @@ -347,11 +334,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); - CtdlSubmitMsg(msg, valid, ""); + valid = validate_recipients(organizer_string, NULL, 0); + CtdlSubmitMsg(msg, valid, "", QP_EADDR); CtdlFreeMessage(msg); free_recipients(valid); } @@ -383,7 +371,8 @@ void ical_locate_part(char *name, char *filename, char *partnum, char *disp, } } - if (strcasecmp(cbtype, "text/calendar")) { + if ( (strcasecmp(cbtype, "text/calendar")) + && (strcasecmp(cbtype, "application/ics")) ) { return; } @@ -529,7 +518,8 @@ void ical_locate_original_event(char *name, char *filename, char *partnum, char struct original_event_container *oec = NULL; - if (strcasecmp(cbtype, "text/calendar")) { + if ( (strcasecmp(cbtype, "text/calendar")) + && (strcasecmp(cbtype, "application/ics")) ) { return; } oec = (struct original_event_container *) cbuserdata; @@ -643,27 +633,27 @@ int ical_update_my_calendar_with_reply(icalcomponent *cal) { /* Figure out just what event it is we're dealing with */ strcpy(uid, "--==<< InVaLiD uId >>==--"); ical_learn_uid_of_reply(uid, cal); - lprintf(CTDL_DEBUG, "UID of event being replied to is <%s>\n", uid); + CtdlLogPrintf(CTDL_DEBUG, "UID of event being replied to is <%s>\n", uid); strcpy(hold_rm, CC->room.QRname); /* save current room */ if (getroom(&CC->room, USERCALENDARROOM) != 0) { getroom(&CC->room, hold_rm); - lprintf(CTDL_CRIT, "cannot get user calendar room\n"); + CtdlLogPrintf(CTDL_CRIT, "cannot get user calendar room\n"); return(2); } /* * 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); getroom(&CC->room, hold_rm); /* return to saved room */ - lprintf(CTDL_DEBUG, "msgnum_being_replaced == %ld\n", msgnum_being_replaced); + CtdlLogPrintf(CTDL_DEBUG, "msgnum_being_replaced == %ld\n", msgnum_being_replaced); if (msgnum_being_replaced == 0) { return(1); /* no calendar event found */ } @@ -690,7 +680,7 @@ int ical_update_my_calendar_with_reply(icalcomponent *cal) { original_event = oec.c; if (original_event == NULL) { - lprintf(CTDL_ERR, "ERROR: Original_component is NULL.\n"); + CtdlLogPrintf(CTDL_ERR, "ERROR: Original_component is NULL.\n"); return(2); } @@ -707,7 +697,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 ); @@ -720,11 +710,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; } @@ -899,87 +890,181 @@ 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 + ) + ); + CtdlLogPrintf(CTDL_DEBUG, " --- CONFLICT --- \n"); + } + else { + CtdlLogPrintf(CTDL_DEBUG, " --- no conflict --- \n"); + } + +} + + + /* - * 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; + + /* initialization */ - cal = (icalcomponent *)data; - strcpy(compare_uid, ""); strcpy(conflict_event_uid, ""); strcpy(conflict_event_summary, ""); + t2start = icaltime_null_time(); + t2end = icaltime_null_time(); - 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; - t1start = icaltime_null_time(); - t1end = icaltime_null_time(); - t2start = icaltime_null_time(); - t1end = icaltime_null_time(); + /* existing event stuff */ - /* Now compare cal to ird.cal */ - p = ical_ctdl_get_subprop(ird.cal, ICAL_DTSTART_PROPERTY); + 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(ird.cal, ICAL_DTEND_PROPERTY); + p = ical_ctdl_get_subprop(existing_event, ICAL_DTEND_PROPERTY); if (p != NULL) t2end = icalproperty_get_dtend(p); - p = ical_ctdl_get_subprop(cal, ICAL_DTSTART_PROPERTY); + 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 + ); + +} + + + + +/* + * 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(); + icalproperty *p; + char compare_uid[SIZ]; + + + /* 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); + p = ical_ctdl_get_subprop(proposed_event, 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_UID_PROPERTY); if (p != NULL) { strcpy(compare_uid, icalproperty_get_comment(p)); } - p = ical_ctdl_get_subprop(ird.cal, ICAL_UID_PROPERTY); - if (p != NULL) { - strcpy(conflict_event_uid, icalproperty_get_comment(p)); - } + ical_conflicts_phase5(t1start, t1end, existing_event, existing_msgnum, compare_uid); +} - p = ical_ctdl_get_subprop(ird.cal, ICAL_SUMMARY_PROPERTY); - if (p != NULL) { - strcpy(conflict_event_summary, icalproperty_get_comment(p)); - } - icalcomponent_free(ird.cal); - 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 - ) - ); - } +/* + * 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); } @@ -987,7 +1072,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. */ @@ -1214,8 +1299,8 @@ void ical_freebusy(char *who) { /* If not found, try it as an unqualified email address. */ if (found_user != 0) { strcpy(buf, who); - recp = validate_recipients(buf); - lprintf(CTDL_DEBUG, "Trying <%s>\n", buf); + recp = validate_recipients(buf, NULL, 0); + CtdlLogPrintf(CTDL_DEBUG, "Trying <%s>\n", buf); if (recp != NULL) { if (recp->num_local == 1) { found_user = getuser(&usbuf, recp->recp_local); @@ -1229,8 +1314,8 @@ void ical_freebusy(char *who) { */ if (found_user != 0) { snprintf(buf, sizeof buf, "%s@%s", who, config.c_fqdn); - lprintf(CTDL_DEBUG, "Trying <%s>\n", buf); - recp = validate_recipients(buf); + CtdlLogPrintf(CTDL_DEBUG, "Trying <%s>\n", buf); + recp = validate_recipients(buf, NULL, 0); if (recp != NULL) { if (recp->num_local == 1) { found_user = getuser(&usbuf, recp->recp_local); @@ -1252,8 +1337,8 @@ void ical_freebusy(char *who) { if ( (!strcasecmp(type, "localhost")) || (!strcasecmp(type, "directory")) ) { snprintf(buf, sizeof buf, "%s@%s", who, host); - lprintf(CTDL_DEBUG, "Trying <%s>\n", buf); - recp = validate_recipients(buf); + CtdlLogPrintf(CTDL_DEBUG, "Trying <%s>\n", buf); + recp = validate_recipients(buf, NULL, 0); if (recp != NULL) { if (recp->num_local == 1) { found_user = getuser(&usbuf, recp->recp_local); @@ -1281,7 +1366,7 @@ void ical_freebusy(char *who) { } /* Create a VFREEBUSY subcomponent */ - lprintf(CTDL_DEBUG, "Creating VFREEBUSY component\n"); + CtdlLogPrintf(CTDL_DEBUG, "Creating VFREEBUSY component\n"); fb = icalcomponent_new_vfreebusy(); if (fb == NULL) { cprintf("%d Internal error: cannot allocate memory.\n", @@ -1308,7 +1393,7 @@ void ical_freebusy(char *who) { icalcomponent_add_property(fb, icalproperty_new_organizer(buf)); /* Add busy time from events */ - lprintf(CTDL_DEBUG, "Adding busy time from events\n"); + CtdlLogPrintf(CTDL_DEBUG, "Adding busy time from events\n"); CtdlForEachMessage(MSGS_ALL, 0, NULL, NULL, NULL, ical_freebusy_backend, (void *)fb ); /* If values for DTSTART and DTEND are still not present, set them @@ -1322,7 +1407,7 @@ void ical_freebusy(char *who) { } /* Put the freebusy component into the calendar component */ - lprintf(CTDL_DEBUG, "Encapsulating\n"); + CtdlLogPrintf(CTDL_DEBUG, "Encapsulating\n"); encaps = ical_encapsulate_subcomponent(fb); if (encaps == NULL) { icalcomponent_free(fb); @@ -1333,11 +1418,11 @@ void ical_freebusy(char *who) { } /* Set the method to PUBLISH */ - lprintf(CTDL_DEBUG, "Setting method\n"); + CtdlLogPrintf(CTDL_DEBUG, "Setting method\n"); icalcomponent_set_method(encaps, ICAL_METHOD_PUBLISH); /* Serialize it */ - lprintf(CTDL_DEBUG, "Serializing\n"); + CtdlLogPrintf(CTDL_DEBUG, "Serializing\n"); serialized_request = strdup(icalcomponent_as_ical_string(encaps)); icalcomponent_free(encaps); /* Don't need this anymore. */ @@ -1424,12 +1509,12 @@ 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(); if (encaps == NULL) { - lprintf(CTDL_DEBUG, "Error at %s:%d - could not allocate component!\n", + CtdlLogPrintf(CTDL_DEBUG, "Error at %s:%d - could not allocate component!\n", __FILE__, __LINE__); cprintf("%d Could not allocate memory\n", ERROR+INTERNAL_ERROR); return; @@ -1476,7 +1561,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()) { @@ -1611,7 +1696,7 @@ void ical_create_room(void) /* Set expiration policy to manual; otherwise objects will be lost! */ if (lgetroom(&qr, USERCALENDARROOM)) { - lprintf(CTDL_CRIT, "Couldn't get the user calendar room!\n"); + CtdlLogPrintf(CTDL_CRIT, "Couldn't get the user calendar room!\n"); return; } qr.QRep.expire_mode = EXPIRE_MANUAL; @@ -1628,7 +1713,7 @@ void ical_create_room(void) /* Set expiration policy to manual; otherwise objects will be lost! */ if (lgetroom(&qr, USERTASKSROOM)) { - lprintf(CTDL_CRIT, "Couldn't get the user calendar room!\n"); + CtdlLogPrintf(CTDL_CRIT, "Couldn't get the user calendar room!\n"); return; } qr.QRep.expire_mode = EXPIRE_MANUAL; @@ -1645,7 +1730,7 @@ void ical_create_room(void) /* Set expiration policy to manual; otherwise objects will be lost! */ if (lgetroom(&qr, USERNOTESROOM)) { - lprintf(CTDL_CRIT, "Couldn't get the user calendar room!\n"); + CtdlLogPrintf(CTDL_CRIT, "Couldn't get the user calendar room!\n"); return; } qr.QRep.expire_mode = EXPIRE_MANUAL; @@ -1678,9 +1763,10 @@ void ical_send_out_invitations(icalcomponent *cal) { icalproperty *attendee = NULL; char summary_string[SIZ]; icalproperty *summary = NULL; + size_t reqsize; if (cal == NULL) { - lprintf(CTDL_ERR, "ERROR: trying to reply to NULL event?\n"); + CtdlLogPrintf(CTDL_ERR, "ERROR: trying to reply to NULL event?\n"); return; } @@ -1698,7 +1784,7 @@ void ical_send_out_invitations(icalcomponent *cal) { /* Clone the event */ the_request = icalcomponent_new_clone(cal); if (the_request == NULL) { - lprintf(CTDL_ERR, "ERROR: cannot clone calendar object\n"); + CtdlLogPrintf(CTDL_ERR, "ERROR: cannot clone calendar object\n"); return; } @@ -1734,7 +1820,7 @@ void ical_send_out_invitations(icalcomponent *cal) { } } - lprintf(CTDL_DEBUG, "<%d> attendees: <%s>\n", num_attendees, attendees_string); + CtdlLogPrintf(CTDL_DEBUG, "<%d> attendees: <%s>\n", num_attendees, attendees_string); /* If there are no attendees, there are no invitations to send, so... * don't bother putting one together! Punch out, Maverick! @@ -1747,7 +1833,7 @@ void ical_send_out_invitations(icalcomponent *cal) { /* Encapsulate the VEVENT component into a complete VCALENDAR */ encaps = icalcomponent_new_vcalendar(); if (encaps == NULL) { - lprintf(CTDL_DEBUG, "Error at %s:%d - could not allocate component!\n", + CtdlLogPrintf(CTDL_DEBUG, "Error at %s:%d - could not allocate component!\n", __FILE__, __LINE__); icalcomponent_free(the_request); return; @@ -1776,9 +1862,10 @@ void ical_send_out_invitations(icalcomponent *cal) { icalcomponent_free(encaps); /* Don't need this anymore. */ if (serialized_request == NULL) return; - request_message_text = malloc(strlen(serialized_request) + SIZ); + reqsize = strlen(serialized_request) + SIZ; + request_message_text = malloc(reqsize); if (request_message_text != NULL) { - sprintf(request_message_text, + snprintf(request_message_text, reqsize, "Content-type: text/calendar\r\n\r\n%s\r\n", serialized_request ); @@ -1791,11 +1878,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); - CtdlSubmitMsg(msg, valid, ""); + valid = validate_recipients(attendees_string, NULL, 0); + CtdlSubmitMsg(msg, valid, "", QP_EADDR); CtdlFreeMessage(msg); free_recipients(valid); } @@ -1815,7 +1903,7 @@ void ical_saving_vevent(icalcomponent *cal) { icalproperty *organizer = NULL; char organizer_string[SIZ]; - lprintf(CTDL_DEBUG, "ical_saving_vevent() has been called!\n"); + CtdlLogPrintf(CTDL_DEBUG, "ical_saving_vevent() has been called!\n"); /* Don't send out invitations unless the client wants us to. */ if (CIT_ICAL->server_generated_invitations == 0) { @@ -1871,19 +1959,21 @@ 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) { 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")) { + if ( (strcasecmp(cbtype, "text/calendar")) + && (strcasecmp(cbtype, "application/ics")) ) { return; } @@ -1911,6 +2001,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 */ @@ -1919,16 +2012,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) { @@ -1942,29 +2063,23 @@ 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! */ if (msg->cm_format_type != 4) { - lprintf(CTDL_DEBUG, "Rejecting non-RFC822 message\n"); + CtdlLogPrintf(CTDL_DEBUG, "Rejecting non-RFC822 message\n"); return(1); /* You tried to save a non-RFC822 message! */ } @@ -1972,38 +2087,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); - lprintf(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); } @@ -2018,7 +2110,8 @@ void ical_obj_aftersave_backend(char *name, char *filename, char *partnum, icalcomponent *cal; /* We're only interested in calendar items here. */ - if (strcasecmp(cbtype, "text/calendar")) { + if ( (strcasecmp(cbtype, "text/calendar")) + && (strcasecmp(cbtype, "application/ics")) ) { return; } @@ -2027,7 +2120,8 @@ void ical_obj_aftersave_backend(char *name, char *filename, char *partnum, * ical_obj_beforesave() sees it there, it'll set the Exclusive msgid * to that string. */ - if (!strcasecmp(cbtype, "text/calendar")) { + if ( (!strcasecmp(cbtype, "text/calendar")) + || (!strcasecmp(cbtype, "application/ics")) ) { cal = icalcomponent_new_from_string(content); if (cal != NULL) { ical_saving_vevent(cal); @@ -2135,7 +2229,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. */ @@ -2160,32 +2254,31 @@ void ical_fixed_output(char *ptr, int len) { } -#endif /* CITADEL_WITH_CALENDAR_SERVICE */ + +void serv_calendar_destroy(void) +{ + icaltimezone_free_builtin_timezones(); +} /* * Register this module with the Citadel server. */ CTDL_MODULE_INIT(calendar) { -#ifdef CITADEL_WITH_CALENDAR_SERVICE - CtdlRegisterMessageHook(ical_obj_beforesave, EVT_BEFORESAVE); - CtdlRegisterMessageHook(ical_obj_aftersave, EVT_AFTERSAVE); - CtdlRegisterSessionHook(ical_create_room, EVT_LOGIN); - CtdlRegisterProtoHook(cmd_ical, "ICAL", "Citadel iCal commands"); - CtdlRegisterSessionHook(ical_session_startup, EVT_START); - CtdlRegisterSessionHook(ical_session_shutdown, EVT_STOP); - CtdlRegisterFixedOutputHook("text/calendar", ical_fixed_output); -#endif - + if (!threading) + { + CtdlRegisterMessageHook(ical_obj_beforesave, EVT_BEFORESAVE); + CtdlRegisterMessageHook(ical_obj_aftersave, EVT_AFTERSAVE); + CtdlRegisterSessionHook(ical_create_room, EVT_LOGIN); + CtdlRegisterProtoHook(cmd_ical, "ICAL", "Citadel iCal commands"); + CtdlRegisterSessionHook(ical_session_startup, EVT_START); + CtdlRegisterSessionHook(ical_session_shutdown, EVT_STOP); + CtdlRegisterFixedOutputHook("text/calendar", ical_fixed_output); + CtdlRegisterFixedOutputHook("application/ics", ical_fixed_output); + CtdlRegisterCleanupHook(serv_calendar_destroy); + } + /* return our Subversion id for the Log */ return "$Id$"; } - - -void serv_calendar_destroy(void) -{ -#ifdef CITADEL_WITH_CALENDAR_SERVICE - icaltimezone_free_builtin_timezones(); -#endif -}