/*====================================================================== File: icalrestriction.c (C) COPYRIGHT 2000, Eric Busboom This library is free software; you can redistribute it and/or modify it under the terms of either: The LGPL as published by the Free Software Foundation, version 2.1, available at: http://www.gnu.org/licenses/lgpl-2.1.html Or: The Mozilla Public License Version 2.0. You may obtain a copy of the License at http://www.mozilla.org/MPL/ ======================================================================*/ /*#line 7 "icalrestriction.c.in"*/ #ifdef HAVE_CONFIG_H #include #endif #include "icalrestriction.h" #include "icalerror.h" #include /* Define the structs for the restrictions. these data are filled out in machine generated code below */ struct icalrestriction_property_record; typedef const char *(*restriction_func) (const struct icalrestriction_property_record * rec, icalcomponent *comp, icalproperty *prop); typedef struct icalrestriction_property_record { icalproperty_method method; icalcomponent_kind component; icalproperty_kind property; icalrestriction_kind restriction; restriction_func function; } icalrestriction_property_record; typedef struct icalrestriction_component_record { icalproperty_method method; icalcomponent_kind component; icalcomponent_kind subcomponent; icalrestriction_kind restriction; restriction_func function; } icalrestriction_component_record; static const icalrestriction_property_record *icalrestriction_get_property_restriction( icalproperty_method method, icalcomponent_kind component, icalproperty_kind property); static icalrestriction_property_record null_prop_record = { ICAL_METHOD_NONE, ICAL_NO_COMPONENT, ICAL_NO_PROPERTY, ICAL_RESTRICTION_UNKNOWN, NULL }; /** Each row gives the result of comparing a restriction against a count. The columns in each row represent 0,1,2+. '-1' indicates 'invalid, 'don't care' or 'needs more analysis' So, for ICAL_RESTRICTION_ONE, if there is 1 of a property with that restriction, it passes, but if there are 0 or 2+, it fails. */ static char compare_map[ICAL_RESTRICTION_UNKNOWN + 1][3] = { {1, 1, 1}, /*ICAL_RESTRICTION_NONE */ {1, 0, 0}, /*ICAL_RESTRICTION_ZERO */ {0, 1, 0}, /*ICAL_RESTRICTION_ONE */ {1, 1, 1}, /*ICAL_RESTRICTION_ZEROPLUS */ {0, 1, 1}, /*ICAL_RESTRICTION_ONEPLUS */ {1, 1, 0}, /*ICAL_RESTRICTION_ZEROORONE */ {1, 1, 0}, /*ICAL_RESTRICTION_ONEEXCLUSIVE */ {1, 1, 0}, /*ICAL_RESTRICTION_ONEMUTUAL */ {1, 1, 1} /*ICAL_RESTRICTION_UNKNOWN */ }; static const char restr_string_map[ICAL_RESTRICTION_UNKNOWN + 1][60] = { "unknown number", /*ICAL_RESTRICTION_NONE */ "0", /*ICAL_RESTRICTION_ZERO */ "1", /*ICAL_RESTRICTION_ONE */ "zero or more", /*ICAL_RESTRICTION_ZEROPLUS */ "one or more", /*ICAL_RESTRICTION_ONEPLUS */ "zero or one", /*ICAL_RESTRICTION_ZEROORONE */ "zero or one, exclusive with another property", /*ICAL_RESTRICTION_ONEEXCLUSIVE */ "zero or one, mutual with another property", /*ICAL_RESTRICTION_ONEMUTUAL */ "unknown number" /*ICAL_RESTRICTION_UNKNOWN */ }; int icalrestriction_compare(icalrestriction_kind restr, int count) { /* restr is an unsigned int and ICAL_RESTRICTION_NONE == 0, so no need to check if restr < ICAL_RESTRICTION_NONE */ if (restr > ICAL_RESTRICTION_UNKNOWN || count < 0) { return -1; } if (count > 2) { count = 2; } return compare_map[restr][count]; } /* Special case routines */ static const char *icalrestriction_may_be_draft_final_canceled( const icalrestriction_property_record *rec, icalcomponent *comp, icalproperty *prop) { icalproperty_status stat = icalproperty_get_status(prop); _unused(rec); _unused(comp); if (!(stat == ICAL_STATUS_DRAFT || stat == ICAL_STATUS_FINAL || stat == ICAL_STATUS_CANCELLED)) { return "Failed iTIP restrictions for STATUS property. " "Value must be one of DRAFT, FINAL, or CANCELED"; } return 0; } static const char *icalrestriction_may_be_comp_need_process(const icalrestriction_property_record * rec, icalcomponent *comp, icalproperty *prop) { icalproperty_status stat = icalproperty_get_status(prop); _unused(rec); _unused(comp); if (!(stat == ICAL_STATUS_COMPLETED || stat == ICAL_STATUS_NEEDSACTION || stat == ICAL_STATUS_INPROCESS)) { return "Failed iTIP restrictions for STATUS property. " "Value must be one of COMPLETED, NEEDS-ACTION or IN-PROCESS"; } return 0; } static const char *icalrestriction_may_be_tent_conf(const icalrestriction_property_record * rec, icalcomponent *comp, icalproperty *prop) { icalproperty_status stat = icalproperty_get_status(prop); _unused(rec); _unused(comp); if (!(stat == ICAL_STATUS_TENTATIVE || stat == ICAL_STATUS_CONFIRMED)) { return "Failed iTIP restrictions for STATUS property. " "Value must be one of TENTATIVE or CONFIRMED"; } return 0; } static const char *icalrestriction_may_be_tent_conf_cancel(const icalrestriction_property_record * rec, icalcomponent *comp, icalproperty *prop) { icalproperty_status stat = icalproperty_get_status(prop); _unused(rec); _unused(comp); if (!(stat == ICAL_STATUS_TENTATIVE || stat == ICAL_STATUS_CONFIRMED || stat == ICAL_STATUS_CANCELLED)) { return "Failed iTIP restrictions for STATUS property. " "Value must be one of TENTATIVE, CONFIRMED or CANCELED"; } return 0; } static const char *icalrestriction_must_be_cancel_if_present( const icalrestriction_property_record *rec, icalcomponent *comp, icalproperty *prop) { /* This routine will not be called if prop == 0 */ icalproperty_status stat = icalproperty_get_status(prop); _unused(rec); _unused(comp); if (stat != ICAL_STATUS_CANCELLED) { return "Failed iTIP restrictions for STATUS property. Value must be CANCELLED"; } return 0; } static const char *icalrestriction_must_be_canceled_no_attendee( const icalrestriction_property_record *rec, icalcomponent *comp, icalproperty *prop) { _unused(rec); _unused(comp); _unused(prop); /* Hack. see rfc5546, 3.2.5 CANCEL for property STATUS. I don't understand the note */ return 0; } static const char *icalrestriction_must_be_recurring(const icalrestriction_property_record * rec, icalcomponent *comp, icalproperty *prop) { _unused(rec); _unused(comp); _unused(prop); return 0; } static const char *icalrestriction_must_have_duration(const icalrestriction_property_record * rec, icalcomponent *comp, icalproperty *prop) { _unused(rec); _unused(prop); if (!icalcomponent_get_first_property(comp, ICAL_DURATION_PROPERTY)) { return "Failed iTIP restrictions for DURATION property. " "This component must have a DURATION property"; } return 0; } static const char *icalrestriction_must_have_repeat(const icalrestriction_property_record * rec, icalcomponent *comp, icalproperty *prop) { _unused(rec); _unused(prop); if (!icalcomponent_get_first_property(comp, ICAL_REPEAT_PROPERTY)) { return "Failed iTIP restrictions for REPEAT property. " "This component must have a REPEAT property"; } return 0; } const char *icalrestriction_must_if_tz_ref(const icalrestriction_property_record * rec, icalcomponent *comp, icalproperty *prop) { _unused(rec); _unused(comp); _unused(prop); return 0; } static const char *icalrestriction_no_dtend(const icalrestriction_property_record * rec, icalcomponent *comp, icalproperty *prop) { _unused(rec); _unused(prop); if (icalcomponent_get_first_property(comp, ICAL_DTEND_PROPERTY)) { return "Failed iTIP restrictions for DTEND property. " "The component must not have both DURATION and DTEND"; } return 0; } static const char *icalrestriction_no_duration(const icalrestriction_property_record * rec, icalcomponent *comp, icalproperty *prop) { _unused(rec); _unused(comp); _unused(prop); /* _no_dtend takes care of this one */ return 0; } static const char *icalrestriction_must_be_email(const icalrestriction_property_record * rec, icalcomponent *comp, icalproperty *prop) { icalproperty_action stat = icalproperty_get_action(prop); _unused(rec); _unused(comp); if (!(stat == ICAL_ACTION_EMAIL)) { return "Failed iTIP restrictions for ACTION property. Value must be EMAIL."; } return 0; } static int icalrestriction_check_component(icalproperty_method method, icalcomponent *comp) { icalproperty_kind kind; icalcomponent_kind comp_kind; icalrestriction_kind restr; const icalrestriction_property_record *prop_record; const char *funcr = 0; icalproperty *prop; int count; int compare; int valid = 1; comp_kind = icalcomponent_isa(comp); /* Check all of the properties in this component */ for (kind = ICAL_ANY_PROPERTY + 1; kind != ICAL_NO_PROPERTY; kind++) { count = icalcomponent_count_properties(comp, kind); prop_record = icalrestriction_get_property_restriction(method, comp_kind, kind); restr = prop_record->restriction; if (restr == ICAL_RESTRICTION_ONEEXCLUSIVE || restr == ICAL_RESTRICTION_ONEMUTUAL) { /* First treat is as a 0/1 restriction */ restr = ICAL_RESTRICTION_ZEROORONE; compare = icalrestriction_compare(restr, count); } else { compare = icalrestriction_compare(restr, count); } assert(compare != -1); if (compare == 0) { #define TMP_BUF_SIZE 1024 char temp[TMP_BUF_SIZE]; icalproperty *errProp; icalparameter *errParam; snprintf(temp, TMP_BUF_SIZE, "Failed iTIP restrictions for %s property. " "Expected %s instances of the property and got %d", icalenum_property_kind_to_string(kind), restr_string_map[restr], count); errParam = icalparameter_new_xlicerrortype(ICAL_XLICERRORTYPE_INVALIDITIP); errProp = icalproperty_vanew_xlicerror(temp, errParam, 0); icalcomponent_add_property(comp, errProp); icalproperty_free(errProp); } prop = icalcomponent_get_first_property(comp, kind); if (prop != 0 && prop_record->function != NULL) { funcr = prop_record->function(prop_record, comp, prop); } if (funcr != 0) { icalproperty *errProp; icalparameter *errParam; errParam = icalparameter_new_xlicerrortype(ICAL_XLICERRORTYPE_INVALIDITIP); errProp = icalproperty_vanew_xlicerror(funcr, errParam, 0); icalcomponent_add_property(comp, errProp); icalproperty_free(errProp); compare = 0; } valid = valid && compare; } return valid; } int icalrestriction_check(icalcomponent *outer_comp) { icalcomponent_kind comp_kind; icalproperty_method method; icalcomponent *inner_comp; icalproperty *method_prop; int valid; icalerror_check_arg_rz((outer_comp != 0), "outer comp"); /* Get the Method value from the outer component */ comp_kind = icalcomponent_isa(outer_comp); if (comp_kind != ICAL_VCALENDAR_COMPONENT) { icalerror_set_errno(ICAL_BADARG_ERROR); return 0; } method_prop = icalcomponent_get_first_property(outer_comp, ICAL_METHOD_PROPERTY); if (method_prop == 0) { method = ICAL_METHOD_NONE; } else { method = icalproperty_get_method(method_prop); } /* Check the VCALENDAR wrapper */ valid = icalrestriction_check_component(ICAL_METHOD_NONE, outer_comp); /* Now check the inner components */ for (inner_comp = icalcomponent_get_first_component(outer_comp, ICAL_ANY_COMPONENT); inner_comp != 0; inner_comp = icalcomponent_get_next_component(outer_comp, ICAL_ANY_COMPONENT)) { valid = valid && icalrestriction_check_component(method, inner_comp); } return valid; } static const icalrestriction_property_record *icalrestriction_get_property_restriction( icalproperty_method method, icalcomponent_kind component, icalproperty_kind property) { int i; for (i = 0; icalrestriction_property_records[i].restriction != ICAL_RESTRICTION_NONE; i++) { if (method == icalrestriction_property_records[i].method && component == icalrestriction_property_records[i].component && property == icalrestriction_property_records[i].property) { return &icalrestriction_property_records[i]; } } return &null_prop_record; }