/*====================================================================== FILE: icalgauge.c CREATOR: eric 23 December 1999 (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: https://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 https://www.mozilla.org/MPL/ The Original Code is eric. The Initial Developer of the Original Code is Eric Busboom ======================================================================*/ #ifdef HAVE_CONFIG_H #include #endif #include "icalgauge.h" #include "icalgaugeimpl.h" #include "icalerror.h" #include "icalvalue.h" #include extern int ssparse(void); extern char *input_buffer; extern char *input_buffer_p; struct icalgauge_impl *icalss_yy_gauge; icalgauge *icalgauge_new_from_sql(const char *sql, int expand) { struct icalgauge_impl *impl; int r; if ((impl = (struct icalgauge_impl *)malloc(sizeof(struct icalgauge_impl))) == 0) { icalerror_set_errno(ICAL_NEWFAILED_ERROR); return 0; } impl->select = pvl_newlist(); impl->from = pvl_newlist(); impl->where = pvl_newlist(); impl->expand = expand; icalss_yy_gauge = impl; input_buffer = input_buffer_p = (char *)sql; r = ssparse(); if (r == 0) { return impl; } else { icalgauge_free(impl); return NULL; } } int icalgauge_get_expand(icalgauge *gauge) { icalerror_check_arg_rz((gauge != 0), "gauge"); if (gauge != 0) { return gauge->expand; } return -1; } void icalgauge_free(icalgauge *gauge) { struct icalgauge_where *w; assert(gauge->select != 0); assert(gauge->where != 0); assert(gauge->from != 0); if (gauge->select) { while ((w = pvl_pop(gauge->select)) != 0) { if (w->value != 0) { free(w->value); } free(w); } pvl_free(gauge->select); gauge->select = 0; } if (gauge->where) { while ((w = pvl_pop(gauge->where)) != 0) { if (w->value != 0) { free(w->value); } free(w); } pvl_free(gauge->where); gauge->where = 0; } if (gauge->from) { pvl_free(gauge->from); gauge->from = 0; } free(gauge); } /** Converts a VQUERY component into a gauge */ icalcomponent *icalgauge_make_gauge(icalcomponent *query); /** icaldirset_test compares a component against a gauge, and returns true if the component passes the test The gauge is a VCALENDAR component that specifies how to test the target components. The gauge holds a collection of VEVENT, VTODO or VJOURNAL sub-components. Each of the sub-components has a collection of properties that are compared to corresponding properties in the target component, according to the X-LIC-COMPARETYPE parameters to the gauge's properties. When a gauge has several sub-components, the results of testing the target against each of them is ORed together - the target component will pass if it matches any of the sub-components in the gauge. However, the results of matching the properties in a sub-component are ANDed -- the target must match every property in a gauge sub-component to match the sub-component. Here is an example: BEGIN:XROOT DTSTART;X-LIC-COMPARETYPE=LESS:19981025T020000 ORGANIZER;X-LIC-COMPARETYPE=EQUAL:mrbig@host.com END:XROOT BEGIN:XROOT LOCATION;X-LIC-COMPARETYPE=EQUAL:McNary's Pub END:XROOT This gauge has two sub-components; one which will match a VEVENT based on start time, and organizer, and another that matches based on LOCATION. A target component will pass the test if it matched either of the sub-components. */ /*non-static,recursive*/ int icalgauge_compare_recurse(icalcomponent *comp, icalcomponent *gauge) { int pass = 1, localpass = 0; icalproperty *p; icalcomponent *child, *subgauge; icalcomponent_kind gaugekind, compkind; icalerror_check_arg_rz((comp != 0), "comp"); icalerror_check_arg_rz((gauge != 0), "gauge"); gaugekind = icalcomponent_isa(gauge); compkind = icalcomponent_isa(comp); if (!(gaugekind == compkind || gaugekind == ICAL_ANY_COMPONENT)) { return 0; } /* Test properties. For each property in the gauge, search through the component for a similar property. If one is found, compare the two properties value with the comparison specified in the gauge with the X-LIC-COMPARETYPE parameter */ for (p = icalcomponent_get_first_property(gauge, ICAL_ANY_PROPERTY); p != 0; p = icalcomponent_get_next_property(gauge, ICAL_ANY_PROPERTY)) { icalproperty *targetprop; icalparameter *compareparam; icalparameter_xliccomparetype compare; icalparameter_xliccomparetype rel; /* The relationship between the gauge and target values. */ /* Extract the comparison type from the gauge. If there is no comparison type, assume that it is "EQUAL" */ compareparam = icalproperty_get_first_parameter(p, ICAL_XLICCOMPARETYPE_PARAMETER); if (compareparam != 0) { compare = icalparameter_get_xliccomparetype(compareparam); } else { compare = ICAL_XLICCOMPARETYPE_EQUAL; } /* Find a property in the component that has the same type as the gauge property. HACK -- multiples of a single property type in the gauge will match only the first instance in the component */ targetprop = icalcomponent_get_first_property(comp, icalproperty_isa(p)); if (targetprop != 0) { /* Compare the values of the gauge property and the target property */ rel = icalvalue_compare(icalproperty_get_value(p), icalproperty_get_value(targetprop)); /* Now see if the comparison is equavalent to the comparison specified in the gauge */ if (rel == compare) { localpass++; } else if (compare == ICAL_XLICCOMPARETYPE_LESSEQUAL && (rel == ICAL_XLICCOMPARETYPE_LESS || rel == ICAL_XLICCOMPARETYPE_EQUAL)) { localpass++; } else if (compare == ICAL_XLICCOMPARETYPE_GREATEREQUAL && (rel == ICAL_XLICCOMPARETYPE_GREATER || rel == ICAL_XLICCOMPARETYPE_EQUAL)) { localpass++; } else if (compare == ICAL_XLICCOMPARETYPE_NOTEQUAL && (rel == ICAL_XLICCOMPARETYPE_GREATER || rel == ICAL_XLICCOMPARETYPE_LESS)) { localpass++; } else { localpass = 0; } pass = pass && (localpass > 0); } } /* Test subcomponents. Look for a child component that has a counterpart in the gauge. If one is found, recursively call icaldirset_test */ for (subgauge = icalcomponent_get_first_component(gauge, ICAL_ANY_COMPONENT); subgauge != 0; subgauge = icalcomponent_get_next_component(gauge, ICAL_ANY_COMPONENT)) { gaugekind = icalcomponent_isa(subgauge); if (gaugekind == ICAL_ANY_COMPONENT) { child = icalcomponent_get_first_component(comp, ICAL_ANY_COMPONENT); } else { child = icalcomponent_get_first_component(comp, gaugekind); } if (child != 0) { localpass = icalgauge_compare_recurse(child, subgauge); pass = pass && localpass; } else { pass = 0; } } return pass; } int icalgauge_compare(icalgauge *gauge, icalcomponent *comp) { icalcomponent *inner; int local_pass = 0; int last_clause = 1, this_clause = 1; pvl_elem e; icalcomponent_kind kind; icalproperty *rrule; int compare_recur = 0; icalerror_check_arg_rz((comp != 0), "comp"); icalerror_check_arg_rz((gauge != 0), "gauge"); inner = icalcomponent_get_first_real_component(comp); if (inner == 0) { /* Wally Yau: our component is not always wrapped with * a . It's not an error. * icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); * return 0; */ kind = icalcomponent_isa(comp); if (kind == ICAL_VEVENT_COMPONENT || kind == ICAL_VTODO_COMPONENT || kind == ICAL_VJOURNAL_COMPONENT || kind == ICAL_VQUERY_COMPONENT || kind == ICAL_VAGENDA_COMPONENT) { inner = comp; } else { icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); return 0; } } /* Check that this component is one of the FROM types */ local_pass = 0; for (e = pvl_head(gauge->from); e != 0; e = pvl_next(e)) { icalcomponent_kind k = (icalcomponent_kind) pvl_data(e); if (k == icalcomponent_isa(inner)) { local_pass = 1; } } if (local_pass == 0) { return 0; } /**** Check each where clause against the component ****/ for (e = pvl_head(gauge->where); e != 0; e = pvl_next(e)) { struct icalgauge_where *w = pvl_data(e); icalcomponent *sub_comp; icalvalue *v; icalproperty *prop; icalvalue_kind vk; if (!w || w->prop == ICAL_NO_PROPERTY || w->value == 0) { icalerror_set_errno(ICAL_INTERNAL_ERROR); return 0; } /* First, create a value from the gauge */ vk = icalenum_property_kind_to_value_kind(w->prop); if (vk == ICAL_NO_VALUE) { icalerror_set_errno(ICAL_INTERNAL_ERROR); return 0; } if (w->compare == ICALGAUGECOMPARE_ISNULL || w->compare == ICALGAUGECOMPARE_ISNOTNULL) { v = icalvalue_new(vk); } else { v = icalvalue_new_from_string(vk, w->value); } if (v == 0) { /* Keep error set by icalvalue_from-string */ return 0; } /* Now find the corresponding property in the component, descending into a sub-component if necessary */ if (w->comp == ICAL_NO_COMPONENT) { sub_comp = inner; } else { sub_comp = icalcomponent_get_first_component(inner, w->comp); if (sub_comp == 0) { icalvalue_free(v); return 0; } } /* check if it is a recurring */ rrule = icalcomponent_get_first_property(sub_comp, ICAL_RRULE_PROPERTY); if (gauge->expand && rrule) { if (w->prop == ICAL_DTSTART_PROPERTY || w->prop == ICAL_DTEND_PROPERTY || w->prop == ICAL_DUE_PROPERTY) { /** needs to use recurrence-id to do comparison */ compare_recur = 1; } } local_pass = (w->compare == ICALGAUGECOMPARE_ISNULL) ? 1 : 0; for (prop = icalcomponent_get_first_property(sub_comp, w->prop); prop != 0; prop = icalcomponent_get_next_property(sub_comp, w->prop)) { icalvalue *prop_value; icalgaugecompare relation; if (w->compare == ICALGAUGECOMPARE_ISNULL) { local_pass = 0; break; } if (w->compare == ICALGAUGECOMPARE_ISNOTNULL) { local_pass = 1; break; } if (compare_recur) { icalproperty *p = icalcomponent_get_first_property(sub_comp, ICAL_RECURRENCEID_PROPERTY); prop_value = icalproperty_get_value(p); } else { /* prop value from this component */ prop_value = icalproperty_get_value(prop); } /* coverity[mixed_enums] */ relation = (icalgaugecompare) icalvalue_compare(prop_value, v); if (relation == w->compare) { local_pass++; } else if (w->compare == ICALGAUGECOMPARE_LESSEQUAL && (relation == ICALGAUGECOMPARE_LESS || relation == ICALGAUGECOMPARE_EQUAL)) { local_pass++; } else if (w->compare == ICALGAUGECOMPARE_GREATEREQUAL && (relation == ICALGAUGECOMPARE_GREATER || relation == ICALGAUGECOMPARE_EQUAL)) { local_pass++; } else if (w->compare == ICALGAUGECOMPARE_NOTEQUAL && (relation == ICALGAUGECOMPARE_GREATER || relation == ICALGAUGECOMPARE_LESS)) { local_pass++; } else { local_pass = 0; } } this_clause = local_pass > 0 ? 1 : 0; /* Now look at the logic operator for this clause to see how the value should be merge with the previous clause */ if (w->logic == ICALGAUGELOGIC_AND) { last_clause = this_clause && last_clause; } else if (w->logic == ICALGAUGELOGIC_OR) { last_clause = this_clause || last_clause; } else { last_clause = this_clause; } icalvalue_free(v); }/**** check next one in where clause ****/ return last_clause; } void icalgauge_dump(icalgauge *gauge) { pvl_elem p; printf("--- Select ---\n"); for (p = pvl_head(gauge->select); p != 0; p = pvl_next(p)) { struct icalgauge_where *w = pvl_data(p); if (!w) continue; if (w->comp != ICAL_NO_COMPONENT) { printf("%s ", icalenum_component_kind_to_string(w->comp)); } if (w->prop != ICAL_NO_PROPERTY) { printf("%s ", icalenum_property_kind_to_string(w->prop)); } if (w->compare != ICALGAUGECOMPARE_NONE) { printf("%d ", w->compare); } if (w->value != 0) { printf("%s", w->value); } printf("\n"); } printf("--- From ---\n"); for (p = pvl_head(gauge->from); p != 0; p = pvl_next(p)) { icalcomponent_kind k = (icalcomponent_kind) pvl_data(p); printf("%s\n", icalenum_component_kind_to_string(k)); } printf("--- Where ---\n"); for (p = pvl_head(gauge->where); p != 0; p = pvl_next(p)) { struct icalgauge_where *w = pvl_data(p); if (!w) continue; if (w->logic != ICALGAUGELOGIC_NONE) { printf("%d ", w->logic); } if (w->comp != ICAL_NO_COMPONENT) { printf("%s ", icalenum_component_kind_to_string(w->comp)); } if (w->prop != ICAL_NO_PROPERTY) { printf("%s ", icalenum_property_kind_to_string(w->prop)); } if (w->compare != ICALGAUGECOMPARE_NONE) { printf("%d ", w->compare); } if (w->value != 0) { printf("%s", w->value); } printf("\n"); } }