static void e_cal_util_remove_instances_ex (icalcomponent *icalcomp, struct icaltimetype rid, ECalObjModType mod, gboolean keep_rid, gboolean can_add_exrule) { icalproperty *prop; struct icaltimetype itt, recur; struct icalrecurrencetype rule; icalrecur_iterator *iter; GSList *remove_props = NULL, *rrules = NULL, *link; g_return_if_fail (icalcomp != NULL); g_return_if_fail (mod != E_CAL_OBJ_MOD_ALL); /* First remove RDATEs and EXDATEs in the indicated range. */ for (prop = icalcomponent_get_first_property (icalcomp, ICAL_RDATE_PROPERTY); prop; prop = icalcomponent_get_next_property (icalcomp, ICAL_RDATE_PROPERTY)) { struct icaldatetimeperiodtype period; period = icalproperty_get_rdate (prop); if (time_matches_rid (period.time, rid, mod) && (!keep_rid || icaltime_compare (period.time, rid) != 0)) remove_props = g_slist_prepend (remove_props, prop); } for (prop = icalcomponent_get_first_property (icalcomp, ICAL_EXDATE_PROPERTY); prop; prop = icalcomponent_get_next_property (icalcomp, ICAL_EXDATE_PROPERTY)) { itt = icalproperty_get_exdate (prop); if (time_matches_rid (itt, rid, mod) && (!keep_rid || icaltime_compare (itt, rid) != 0)) remove_props = g_slist_prepend (remove_props, prop); } for (link = remove_props; link; link = g_slist_next (link)) { prop = link->data; icalcomponent_remove_property (icalcomp, prop); } g_slist_free (remove_props); remove_props = NULL; /* If we're only removing one instance, just add an EXDATE. */ if (mod == E_CAL_OBJ_MOD_THIS) { prop = icalproperty_new_exdate (rid); icalcomponent_add_property (icalcomp, prop); return; } /* Otherwise, iterate through RRULEs */ /* FIXME: this may generate duplicate EXRULEs */ for (prop = icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY); prop; prop = icalcomponent_get_next_property (icalcomp, ICAL_RRULE_PROPERTY)) { rrules = g_slist_prepend (rrules, prop); } for (link = rrules; link; link = g_slist_next (link)) { prop = link->data; rule = icalproperty_get_rrule (prop); iter = icalrecur_iterator_new (rule, rid); recur = icalrecur_iterator_next (iter); if (mod & E_CAL_OBJ_MOD_THIS_AND_FUTURE) { /* Truncate the rule at rid. */ if (!icaltime_is_null_time (recur)) { /* Use count if it was used */ if (rule.count > 0) { gint occurrences_count = 0; icalrecur_iterator *count_iter; struct icaltimetype count_recur; count_iter = icalrecur_iterator_new (rule, icalcomponent_get_dtstart (icalcomp)); while (count_recur = icalrecur_iterator_next (count_iter), !icaltime_is_null_time (count_recur) && occurrences_count < rule.count) { if (icaltime_compare (count_recur, rid) >= 0) break; occurrences_count++; } icalrecur_iterator_free (count_iter); if (keep_rid && icaltime_compare (count_recur, rid) == 0) occurrences_count++; /* The caller should make sure that the remove will keep at least one instance */ g_warn_if_fail (occurrences_count > 0); rule.count = occurrences_count; } else { if (keep_rid && icaltime_compare (recur, rid) == 0) rule.until = icaltime_add (rid, icalcomponent_get_duration (icalcomp)); else rule.until = rid; icaltime_adjust (&rule.until, 0, 0, 0, -1); } icalproperty_set_rrule (prop, rule); icalproperty_remove_parameter_by_name (prop, "X-EVOLUTION-ENDDATE"); } } else { /* (If recur == rid, skip to the next occurrence) */ if (!keep_rid && icaltime_compare (recur, rid) == 0) recur = icalrecur_iterator_next (iter); /* If there is a recurrence after rid, add * an EXRULE to block instances up to rid. * Otherwise, just remove the RRULE. */ if (!icaltime_is_null_time (recur)) { if (can_add_exrule) { rule.count = 0; /* iCalendar says we should just use rid * here, but Outlook/Exchange handle * UNTIL incorrectly. */ if (keep_rid && icaltime_compare (recur, rid) == 0) { struct icaldurationtype duration = icalcomponent_get_duration (icalcomp); duration.is_neg = !duration.is_neg; rule.until = icaltime_add (rid, duration); } else rule.until = icaltime_add (rid, icalcomponent_get_duration (icalcomp)); prop = icalproperty_new_exrule (rule); icalcomponent_add_property (icalcomp, prop); } } else { remove_props = g_slist_prepend (remove_props, prop); } } icalrecur_iterator_free (iter); } for (link = remove_props; link; link = g_slist_next (link)) { prop = link->data; icalcomponent_remove_property (icalcomp, prop); } g_slist_free (remove_props); g_slist_free (rrules); }
/** * e_cal_util_split_at_instance: * @icalcomp: A (recurring) #icalcomponent * @rid: The base RECURRENCE-ID to remove * @master_dtstart: The DTSTART of the master object * * Splits a recurring @icalcomp into two at time @rid. The returned icalcomponent * is modified @icalcomp which contains recurrences beginning at @rid, inclusive. * The instance identified by @rid should exist. The @master_dtstart can be * a null time, then it is read from the @icalcomp. * * Use e_cal_util_remove_instances() with E_CAL_OBJ_MOD_THIS_AND_FUTURE mode * on the @icalcomp to remove the overlapping interval from it, if needed. * * Returns: the split icalcomponent, or %NULL. * * Since: 3.16 **/ icalcomponent * e_cal_util_split_at_instance (icalcomponent *icalcomp, struct icaltimetype rid, struct icaltimetype master_dtstart) { icalproperty *prop; struct instance_data instance; struct icaltimetype start, end; struct icaldurationtype duration; GSList *remove_props = NULL, *link; g_return_val_if_fail (icalcomp != NULL, NULL); g_return_val_if_fail (!icaltime_is_null_time (rid), NULL); /* Make sure this is really recurring */ if (!icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY) && !icalcomponent_get_first_property (icalcomp, ICAL_RDATE_PROPERTY)) return NULL; /* Make sure the specified instance really exists */ start = icaltime_convert_to_zone (rid, icaltimezone_get_utc_timezone ()); end = start; icaltime_adjust (&end, 0, 0, 0, 1); instance.start = icaltime_as_timet (start); instance.found = FALSE; icalcomponent_foreach_recurrence (icalcomp, start, end, check_instance, &instance); /* Make the copy */ icalcomp = icalcomponent_new_clone (icalcomp); e_cal_util_remove_instances_ex (icalcomp, rid, E_CAL_OBJ_MOD_THIS_AND_PRIOR, TRUE, FALSE); start = rid; if (icaltime_is_null_time (master_dtstart)) master_dtstart = icalcomponent_get_dtstart (icalcomp); duration = icalcomponent_get_duration (icalcomp); /* Expect that DTSTART and DTEND are already set when the instance could not be found */ if (instance.found) { icalcomponent_set_dtstart (icalcomp, start); /* Update either DURATION or DTEND */ if (icaltime_is_null_time (icalcomponent_get_dtend (icalcomp))) { icalcomponent_set_duration (icalcomp, duration); } else { end = start; if (duration.is_neg) icaltime_adjust (&end, -duration.days - 7 * duration.weeks, -duration.hours, -duration.minutes, -duration.seconds); else icaltime_adjust (&end, duration.days + 7 * duration.weeks, duration.hours, duration.minutes, duration.seconds); icalcomponent_set_dtend (icalcomp, end); } } /* any RRULE with 'count' should be shortened */ for (prop = icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY); prop; prop = icalcomponent_get_next_property (icalcomp, ICAL_RRULE_PROPERTY)) { struct icaltimetype recur; struct icalrecurrencetype rule; rule = icalproperty_get_rrule (prop); if (rule.count != 0) { gint occurrences_count = 0; icalrecur_iterator *iter; iter = icalrecur_iterator_new (rule, master_dtstart); while (recur = icalrecur_iterator_next (iter), !icaltime_is_null_time (recur) && occurrences_count < rule.count) { if (icaltime_compare (recur, rid) >= 0) break; occurrences_count++; } icalrecur_iterator_free (iter); if (icaltime_is_null_time (recur)) { remove_props = g_slist_prepend (remove_props, prop); } else { rule.count -= occurrences_count; icalproperty_set_rrule (prop, rule); icalproperty_remove_parameter_by_name (prop, "X-EVOLUTION-ENDDATE"); } } } for (link = remove_props; link; link = g_slist_next (link)) { prop = link->data; icalcomponent_remove_property (icalcomp, prop); } g_slist_free (remove_props); return icalcomp; }
int main(int argc, char *argv[]) { icalcomponent *c, *next_c = NULL; int i = 0; int dont_remove; icalfileset_options options = { O_RDONLY, 0644, 0, NULL }; icalset *f = icalset_new(ICAL_FILE_SET, TEST_DATADIR "/process-incoming.ics", &options); icalset *trash = icalset_new_file("trash.ics"); icalset *cal = icalset_new(ICAL_FILE_SET, TEST_DATADIR "/process-calendar.ics", &options); icalset *out = icalset_new_file("outgoing.ics"); const char *this_user = "******"; _unused(argc); _unused(argv); assert(f != 0); assert(cal != 0); assert(trash != 0); assert(out != 0); /* Foreach incoming message */ for (c = icalset_get_first_component(f); c != 0; c = next_c) { icalproperty_xlicclass class; icalcomponent *match; icalcomponent *inner; icalcomponent *reply = 0; assert(c != 0); inner = icalcomponent_get_first_real_component(c); i++; reply = 0; dont_remove = 0; if (inner == 0) { printf("Bad component, no inner\n %s\n", icalcomponent_as_ical_string(c)); continue; } /* Find a booked component that is matched to the incoming message, based on the incoming component's UID, SEQUENCE and RECURRENCE-ID */ match = icalset_fetch_match(cal, c); class = icalclassify(c, match, this_user); /* Print out the notes associated with the incoming component and the matched component in the */ { const char *inc_note = 0; const char *match_note = 0; icalproperty *p; for (p = icalcomponent_get_first_property(c, ICAL_X_PROPERTY); p != 0; p = icalcomponent_get_next_property(c, ICAL_X_PROPERTY)) { if (strcmp(icalproperty_get_x_name(p), "X-LIC-NOTE") == 0) { inc_note = icalproperty_get_x(p); } } if (match != 0) { for (p = icalcomponent_get_first_property(match, ICAL_X_PROPERTY); p != 0; p = icalcomponent_get_next_property(match, ICAL_X_PROPERTY)) { if (strcmp(icalproperty_get_x_name(p), "X-LIC-NOTE") == 0) { match_note = icalproperty_get_x(p); } } } if (inc_note != 0) { printf("Incoming: %s\n", inc_note); } if (match_note != 0) { printf("Match : %s\n", match_note); } } /* Main processing structure */ switch (class) { case ICAL_XLICCLASS_NONE:{ char temp[1024]; /* Huh? Return an error to sender */ icalrestriction_check(c); icalcomponent_convert_errors(c); snprintf(temp, 1024, "I can't understand the component you sent.\n" "Here is the component you sent, possibly with error messages:\n" "%s", icalcomponent_as_ical_string(c)); reply = icalmessage_new_error_reply(c, this_user, temp, "", ICAL_UNKNOWN_STATUS); break; } case ICAL_XLICCLASS_PUBLISHNEW:{ /* Don't accept published events from anyone but self. If self, fall through to ICAL_XLICCLASS_REQUESTNEW */ } case ICAL_XLICCLASS_REQUESTNEW:{ /* Book the new component if it does not overlap anything. If the time is busy and the start time is an even modulo 4, delegate to [email protected]. If the time is busy and is 1 modulo 4, counterpropose for the first available free time. Otherwise, deline the meeting */ icalcomponent *overlaps = icalclassify_find_overlaps(cal, c); if (overlaps == 0) { /* No overlaps, book the meeting */ /* icalset_add_component(cal,icalcomponent_new_clone(c));*/ /* Return a reply */ reply = icalmessage_new_accept_reply( c, this_user, "I can make it to this meeting"); (void)icalset_add_component(out, reply); } else { /* There was a conflict, so delegate, counterpropose or decline it */ struct icaltimetype dtstart = icalcomponent_get_dtstart(c); if (dtstart.hour % 4 == 0) { /* Delegate the meeting */ reply = icalmessage_new_delegate_reply( c, this_user, "*****@*****.**", "Unfortunately, I have another commitment that conflicts " "with this meeting. I am delegating my attendance to Bob."); (void)icalset_add_component(out, reply); } else if (dtstart.hour % 4 == 1) { /* Counter propose to next available time */ icalcomponent *newc; struct icalperiodtype next_time; icalspanlist *spanl = icalspanlist_new(cal, dtstart, icaltime_null_time()); next_time = icalspanlist_next_free_time(spanl, icalcomponent_get_dtstart(c)); newc = icalcomponent_new_clone(c); icalcomponent_set_dtstart(newc, next_time.start); /* Hack, the duration of the counterproposed meeting may be longer than the free time available */ icalcomponent_set_duration(newc, icalcomponent_get_duration(c)); reply = icalmessage_new_counterpropose_reply( c, newc, this_user, "Unfortunately, I have another commitment that conflicts with " "this meeting. I am proposing a time that works better for me."); (void)icalset_add_component(out, reply); icalspanlist_free(spanl); icalcomponent_free(newc); } else { /* Decline the meeting */ reply = icalmessage_new_decline_reply( c, this_user, "I can't make it to this meeting"); (void)icalset_add_component(out, reply); } } icalcomponent_free(overlaps); break; } case ICAL_XLICCLASS_PUBLISHFREEBUSY:{ /* Store the busy time information in a file named after the sender */ break; } case ICAL_XLICCLASS_PUBLISHUPDATE:{ /* Only accept publish updates from self. If self, fall through to ICAL_XLICCLASS_REQUESTUPDATE */ } case ICAL_XLICCLASS_REQUESTUPDATE:{ /* always accept the changes */ break; } case ICAL_XLICCLASS_REQUESTRESCHEDULE:{ /* Use same rules as REQUEST_NEW */ (void)icalclassify_find_overlaps(cal, c); break; } case ICAL_XLICCLASS_REQUESTDELEGATE:{ break; } case ICAL_XLICCLASS_REQUESTNEWORGANIZER:{ break; } case ICAL_XLICCLASS_REQUESTFORWARD:{ break; } case ICAL_XLICCLASS_REQUESTSTATUS:{ break; } case ICAL_XLICCLASS_REQUESTFREEBUSY:{ break; } case ICAL_XLICCLASS_REPLYACCEPT:{ /* Change the PARTSTAT of the sender */ break; } case ICAL_XLICCLASS_REPLYDECLINE:{ /* Change the PARTSTAT of the sender */ break; } case ICAL_XLICCLASS_REPLYCRASHERACCEPT:{ /* Add the crasher to the ATTENDEE list with the appropriate PARTSTAT */ break; } case ICAL_XLICCLASS_REPLYCRASHERDECLINE:{ /* Add the crasher to the ATTENDEE list with the appropriate PARTSTAT */ break; } case ICAL_XLICCLASS_ADDINSTANCE:{ break; } case ICAL_XLICCLASS_CANCELEVENT:{ /* Remove the component */ break; } case ICAL_XLICCLASS_CANCELINSTANCE:{ break; } case ICAL_XLICCLASS_CANCELALL:{ /* Remove the component */ break; } case ICAL_XLICCLASS_REFRESH:{ /* Resend the latest copy of the request */ break; } case ICAL_XLICCLASS_COUNTER:{ break; } case ICAL_XLICCLASS_DECLINECOUNTER:{ break; } case ICAL_XLICCLASS_MALFORMED:{ /* Send back an error */ break; } case ICAL_XLICCLASS_OBSOLETE:{ printf(" ** Got an obsolete component:\n%s", icalcomponent_as_ical_string(c)); /* Send back an error */ break; } case ICAL_XLICCLASS_MISSEQUENCED:{ printf(" ** Got a missequenced component:\n%s", icalcomponent_as_ical_string(c)); /* Send back an error */ break; } case ICAL_XLICCLASS_UNKNOWN:{ printf(" ** Don't know what to do with this component:\n%s", icalcomponent_as_ical_string(c)); /* Send back an error */ break; } case ICAL_XLICCLASS_X: case ICAL_XLICCLASS_REPLYDELEGATE: default:{ } } #if 0 if (reply != 0) { /* Don't send the reply if the RSVP parameter indicates not to */ icalcomponent *reply_inner; icalproperty *attendee; icalparameter *rsvp; reply_inner = icalcomponent_get_first_real_component(reply); attendee = icalcomponent_get_first_property(reply_inner, ICAL_ATTENDEE_PROPERTY); rsvp = icalproperty_get_first_parameter(attendee, ICAL_RSVP_PARAMETER); if (rsvp == 0 || icalparameter_get_rsvp(rsvp) == 1) { icalrestriction_check(reply); send_message(reply, this_user); } icalcomponent_free(reply); } #endif if (reply != 0) { printf("%s\n", icalcomponent_as_ical_string(reply)); } next_c = icalset_get_next_component(f); if (dont_remove == 0) { /*icalset_remove_component(f,c); icalset_add_component(trash,c); */ } } #if 0 for (c = icalset_get_first_component(out); c != 0; c = icalset_get_next_component(out)) { printf("%s", icalcomponent_as_ical_string(c)); } #endif icalset_free(f); icalset_free(trash); icalset_free(cal); icalset_free(out); return 0; }