Imported Upstream version 0.48
[platform/upstream/libical.git] / src / libicalss / icalgauge.c
1 /* -*- Mode: C -*- */
2 /*======================================================================
3  FILE: icalgauge.c
4  CREATOR: eric 23 December 1999
5
6
7  $Id: icalgauge.c,v 1.15 2008-01-02 20:07:40 dothebart Exp $
8  $Locker:  $
9
10  (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org
11
12  This program is free software; you can redistribute it and/or modify
13  it under the terms of either: 
14
15     The LGPL as published by the Free Software Foundation, version
16     2.1, available at: http://www.fsf.org/copyleft/lesser.html
17
18   Or:
19
20     The Mozilla Public License Version 1.0. You may obtain a copy of
21     the License at http://www.mozilla.org/MPL/
22
23  The Original Code is eric. The Initial Developer of the Original
24  Code is Eric Busboom
25
26
27 ======================================================================*/
28
29 #include <libical/ical.h>
30 #include "icalgauge.h"
31 #include "icalgaugeimpl.h"
32 #include <stdlib.h>
33
34 #include "icalssyacc.h"
35
36 int ssparse(void);
37
38 extern char *input_buffer;
39 extern char *input_buffer_p;
40
41 struct icalgauge_impl *icalss_yy_gauge;
42
43 icalgauge* icalgauge_new_from_sql(char* sql, int expand)
44 {
45     struct icalgauge_impl *impl;
46     int r;
47     
48     if ( ( impl = (struct icalgauge_impl*)
49            malloc(sizeof(struct icalgauge_impl))) == 0) {
50         icalerror_set_errno(ICAL_NEWFAILED_ERROR);
51         return 0;
52     }
53
54     impl->select = pvl_newlist();
55     impl->from = pvl_newlist();
56     impl->where = pvl_newlist();
57     impl->expand = expand;
58
59     icalss_yy_gauge = impl;
60     input_buffer = input_buffer_p = sql;
61
62     r = ssparse();
63
64         if (r == 0) {
65             return impl;
66         }
67         else {
68                  icalgauge_free(impl);
69                  return NULL;
70         }
71 }
72
73 int icalgauge_get_expand(icalgauge* gauge)
74 {
75 return (gauge->expand);
76
77 }
78
79 void icalgauge_free(icalgauge* gauge)
80 {
81       struct icalgauge_where *w;
82
83       assert(gauge->select != 0);
84       assert(gauge->where != 0);
85       assert(gauge->from != 0);
86
87       if(gauge->select){
88           while( (w=pvl_pop(gauge->select)) != 0){
89               if(w->value != 0){
90                   free(w->value);
91               }
92               free(w);
93           }
94           pvl_free(gauge->select);
95           gauge->select = 0;
96       }
97
98       if(gauge->where){
99           while( (w=pvl_pop(gauge->where)) != 0){
100               
101               if(w->value != 0){
102                   free(w->value);
103               }
104               free(w);
105           }
106           pvl_free(gauge->where);
107           gauge->where = 0;
108       }
109
110       if(gauge->from){
111           pvl_free(gauge->from);
112           gauge->from = 0;
113       }
114
115           free(gauge);
116       
117 }
118
119
120 /** Convert a VQUERY component into a gauge */
121 icalcomponent* icalgauge_make_gauge(icalcomponent* query);
122
123 /**
124    icaldirset_test compares a component against a gauge, and returns
125    true if the component passes the test 
126
127    The gauge is a VCALENDAR component that specifies how to test the
128    target components. The gauge holds a collection of VEVENT, VTODO or
129    VJOURNAL sub-components. Each of the sub-components has a
130    collection of properties that are compared to corresponding
131    properties in the target component, according to the
132    X-LIC-COMPARETYPE parameters to the gauge's properties.
133
134    When a gauge has several sub-components, the results of testing the
135    target against each of them is ORed together - the target
136    component will pass if it matches any of the sub-components in the
137    gauge. However, the results of matching the properties in a
138    sub-component are ANDed -- the target must match every property in
139    a gauge sub-component to match the sub-component.
140
141    Here is an example:
142
143    BEGIN:XROOT
144    DTSTART;X-LIC-COMPARETYPE=LESS:19981025T020000
145    ORGANIZER;X-LIC-COMPARETYPE=EQUAL:mrbig@host.com 
146    END:XROOT
147    BEGIN:XROOT
148    LOCATION;X-LIC-COMPARETYPE=EQUAL:McNary's Pub
149    END:XROOT
150
151    This gauge has two sub-components; one which will match a VEVENT
152    based on start time, and organizer, and another that matches based
153    on LOCATION. A target component will pass the test if it matched
154    either of the sub-components.
155    
156   */
157
158
159 int icalgauge_compare_recurse(icalcomponent* comp, icalcomponent* gauge)
160 {
161     int pass = 1,localpass = 0;
162     icalproperty *p;
163     icalcomponent *child,*subgauge; 
164     icalcomponent_kind gaugekind, compkind;
165
166     icalerror_check_arg_rz( (comp!=0), "comp");
167     icalerror_check_arg_rz( (gauge!=0), "gauge");
168
169     gaugekind = icalcomponent_isa(gauge);
170     compkind = icalcomponent_isa(comp);
171
172     if( ! (gaugekind == compkind || gaugekind == ICAL_ANY_COMPONENT) ){
173         return 0;
174     }   
175
176     /* Test properties. For each property in the gauge, search through
177        the component for a similar property. If one is found, compare
178        the two properties value with the comparison specified in the
179        gauge with the X-LIC-COMPARETYPE parameter */
180     
181     for(p = icalcomponent_get_first_property(gauge,ICAL_ANY_PROPERTY);
182         p != 0;
183         p = icalcomponent_get_next_property(gauge,ICAL_ANY_PROPERTY)){
184         
185         icalproperty* targetprop; 
186         icalparameter* compareparam;
187         icalparameter_xliccomparetype compare;
188         int rel; /* The relationship between the gauge and target values.*/
189         
190         /* Extract the comparison type from the gauge. If there is no
191            comparison type, assume that it is "EQUAL" */
192         
193         compareparam = icalproperty_get_first_parameter(
194             p,
195             ICAL_XLICCOMPARETYPE_PARAMETER);
196         
197         if (compareparam!=0){
198             compare = icalparameter_get_xliccomparetype(compareparam);
199         } else {
200             compare = ICAL_XLICCOMPARETYPE_EQUAL;
201         }
202         
203         /* Find a property in the component that has the same type
204            as the gauge property. HACK -- multiples of a single
205            property type in the gauge will match only the first
206            instance in the component */
207         
208         targetprop = icalcomponent_get_first_property(comp,
209                                                       icalproperty_isa(p));
210         
211         if(targetprop != 0){
212         
213             /* Compare the values of the gauge property and the target
214                property */
215             
216             rel = icalvalue_compare(icalproperty_get_value(p),
217                                     icalproperty_get_value(targetprop));
218             
219             /* Now see if the comparison is equavalent to the comparison
220                specified in the gauge */
221             
222             if (rel == compare){ 
223                 localpass++; 
224             } else if (compare == ICAL_XLICCOMPARETYPE_LESSEQUAL && 
225                        ( rel == ICAL_XLICCOMPARETYPE_LESS ||
226                          rel == ICAL_XLICCOMPARETYPE_EQUAL)) {
227                 localpass++;
228             } else if (compare == ICAL_XLICCOMPARETYPE_GREATEREQUAL && 
229                        ( rel == ICAL_XLICCOMPARETYPE_GREATER ||
230                          rel == ICAL_XLICCOMPARETYPE_EQUAL)) {
231                 localpass++;
232             } else if (compare == ICAL_XLICCOMPARETYPE_NOTEQUAL && 
233                        ( rel == ICAL_XLICCOMPARETYPE_GREATER ||
234                          rel == ICAL_XLICCOMPARETYPE_LESS)) {
235                 localpass++;
236             } else {
237                 localpass = 0;
238             }
239             
240             pass = pass && (localpass>0);
241         }
242     }
243     
244     /* Test subcomponents. Look for a child component that has a
245        counterpart in the gauge. If one is found, recursively call
246        icaldirset_test */
247     
248     for(subgauge = icalcomponent_get_first_component(gauge,ICAL_ANY_COMPONENT);
249         subgauge != 0;
250         subgauge = icalcomponent_get_next_component(gauge,ICAL_ANY_COMPONENT)){
251         
252         gaugekind = icalcomponent_isa(subgauge);
253
254         if (gaugekind == ICAL_ANY_COMPONENT){
255             child = icalcomponent_get_first_component(comp,ICAL_ANY_COMPONENT);
256         } else {
257             child = icalcomponent_get_first_component(comp,gaugekind);
258         }
259         
260         if(child !=0){
261             localpass = icalgauge_compare_recurse(child,subgauge);
262             pass = pass && localpass;
263         } else {
264             pass = 0;
265         }
266     }
267     
268     return pass;   
269 }
270
271
272 int icalgauge_compare(icalgauge* gauge,icalcomponent* comp)
273 {
274
275     icalcomponent *inner; 
276     int local_pass = 0;
277     int last_clause = 1, this_clause = 1;
278     pvl_elem e;
279     icalcomponent_kind kind;
280     icalproperty *rrule;
281     int compare_recur = 0;
282
283
284     icalerror_check_arg_rz( (comp!=0), "comp");
285     icalerror_check_arg_rz( (gauge!=0), "gauge");
286     
287     inner = icalcomponent_get_first_real_component(comp);
288
289     if(inner == 0){
290         /* Wally Yau: our component is not always wrapped with
291          * a <VCALENDAR>. It's not an error. 
292          * icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
293          * return 0; */
294         kind = icalcomponent_isa(comp);
295         if(kind == ICAL_VEVENT_COMPONENT ||
296             kind == ICAL_VTODO_COMPONENT ||
297             kind == ICAL_VJOURNAL_COMPONENT ||
298             kind == ICAL_VQUERY_COMPONENT ||
299             kind == ICAL_VAGENDA_COMPONENT){
300                 inner = comp;
301             }
302         else {
303             icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
304             return 0;
305         }
306         inner = comp;
307     }
308
309     /* Check that this component is one of the FROM types */
310     local_pass = 0;
311     for(e = pvl_head(gauge->from);e!=0;e=pvl_next(e)){
312         icalcomponent_kind k = (icalcomponent_kind)pvl_data(e);
313
314         if(k == icalcomponent_isa(inner)){
315             local_pass=1;
316         }
317     }
318     
319     if(local_pass == 0){
320         return 0;
321     }
322
323     
324     /**** Check each where clause against the component ****/
325     for(e = pvl_head(gauge->where);e!=0;e=pvl_next(e)){
326         struct icalgauge_where *w = pvl_data(e);
327         icalcomponent *sub_comp;
328         icalvalue *v;
329         icalproperty *prop;
330         icalvalue_kind vk;
331
332         if(w->prop == ICAL_NO_PROPERTY || w->value == 0){
333             icalerror_set_errno(ICAL_INTERNAL_ERROR);
334             return 0;
335         }
336
337         /* First, create a value from the gauge */
338         vk = icalenum_property_kind_to_value_kind(w->prop);
339
340         if(vk == ICAL_NO_VALUE){
341             icalerror_set_errno(ICAL_INTERNAL_ERROR);
342             return 0;
343         }
344
345         if (w->compare == ICALGAUGECOMPARE_ISNULL || w->compare == ICALGAUGECOMPARE_ISNOTNULL)
346             v = icalvalue_new(vk);
347         else
348           v = icalvalue_new_from_string(vk,w->value);
349
350         if (v == 0){
351             /* Keep error set by icalvalue_from-string*/
352             return 0;
353         }
354             
355         /* Now find the corresponding property in the component,
356            descending into a sub-component if necessary */
357
358         if(w->comp == ICAL_NO_COMPONENT){
359             sub_comp = inner;
360         } else {
361             sub_comp = icalcomponent_get_first_component(inner,w->comp);
362             if(sub_comp == 0){
363                 icalvalue_free(v);
364                 return 0;
365             }
366         }          
367
368         /* check if it is a recurring */
369         rrule = icalcomponent_get_first_property(sub_comp,ICAL_RRULE_PROPERTY);
370
371         if (gauge->expand
372             && rrule) {
373
374             if (w->prop == ICAL_DTSTART_PROPERTY || 
375                 w->prop == ICAL_DTEND_PROPERTY || 
376                 w->prop == ICAL_DUE_PROPERTY){
377                 /** needs to use recurrence-id to do comparison */
378                 compare_recur = 1;
379             } 
380
381        }
382
383
384         this_clause = 0;
385         local_pass = (w->compare == ICALGAUGECOMPARE_ISNULL) ? 1 : 0;
386
387         for(prop = icalcomponent_get_first_property(sub_comp,w->prop);
388             prop != 0;
389             prop = icalcomponent_get_next_property(sub_comp,w->prop)){
390             icalvalue* prop_value;
391             icalgaugecompare relation;
392
393             if (w->compare == ICALGAUGECOMPARE_ISNULL) {
394                 local_pass = 0;
395                 break;
396             }
397
398             if (w->compare == ICALGAUGECOMPARE_ISNOTNULL) {
399                 local_pass = 1;
400                 break;
401             }
402
403             if (compare_recur) {
404                 icalproperty *p = icalcomponent_get_first_property(sub_comp, ICAL_RECURRENCEID_PROPERTY);   
405                 prop_value = icalproperty_get_value(p);
406             }
407             else /* prop value from this component */
408             prop_value = icalproperty_get_value(prop);
409
410             relation = (icalgaugecompare)icalvalue_compare(prop_value,v);
411             
412             if (relation  == w->compare){ 
413                 local_pass++; 
414             } else if (w->compare == ICALGAUGECOMPARE_LESSEQUAL && 
415                        ( relation  == ICALGAUGECOMPARE_LESS ||
416                          relation  == ICALGAUGECOMPARE_EQUAL)) {
417                 local_pass++;
418             } else if (w->compare == ICALGAUGECOMPARE_GREATEREQUAL && 
419                        ( relation  == ICALGAUGECOMPARE_GREATER ||
420                          relation  == ICALGAUGECOMPARE_EQUAL)) {
421                 local_pass++;
422             } else if (w->compare == ICALGAUGECOMPARE_NOTEQUAL && 
423                        ( relation  == ICALGAUGECOMPARE_GREATER ||
424                          relation  == ICALGAUGECOMPARE_LESS)) {
425                 local_pass++;
426             } else {
427                 local_pass = 0;
428             }
429         }
430     
431     
432         this_clause = local_pass > 0 ? 1 : 0;
433
434
435         /* Now look at the logic operator for this clause to see how
436            the value should be merge with the previous clause */
437
438         if(w->logic == ICALGAUGELOGIC_AND){
439             last_clause = this_clause && last_clause;
440         } else if(w->logic == ICALGAUGELOGIC_OR) {
441             last_clause = this_clause || last_clause;
442         } else {
443             last_clause = this_clause;
444         }
445
446         icalvalue_free(v);
447
448     }/**** check next one in where clause ****/
449
450     return last_clause;
451
452 }
453     
454 /** @brief Debug
455  * Print gauge information to stdout.
456  */
457
458 void icalgauge_dump(icalgauge* gauge)
459 {
460
461     pvl_elem p;
462   
463     printf("--- Select ---\n");
464     for(p = pvl_head(gauge->select);p!=0;p=pvl_next(p)){
465         struct icalgauge_where *w = pvl_data(p);
466
467         if(w->comp != ICAL_NO_COMPONENT){
468             printf("%s ",icalenum_component_kind_to_string(w->comp));
469         }
470
471         if(w->prop != ICAL_NO_PROPERTY){
472             printf("%s ",icalenum_property_kind_to_string(w->prop));
473         }
474         
475         if (w->compare != ICALGAUGECOMPARE_NONE){
476             printf("%d ",w->compare);
477         }
478
479
480         if (w->value!=0){
481             printf("%s",w->value);
482         }
483
484
485         printf("\n");
486     }
487
488     printf("--- From ---\n");
489     for(p = pvl_head(gauge->from);p!=0;p=pvl_next(p)){
490         icalcomponent_kind k = (icalcomponent_kind)pvl_data(p);
491
492         printf("%s\n",icalenum_component_kind_to_string(k));
493     }
494
495     printf("--- Where ---\n");
496     for(p = pvl_head(gauge->where);p!=0;p=pvl_next(p)){
497         struct icalgauge_where *w = pvl_data(p);
498
499         if(w->logic != ICALGAUGELOGIC_NONE){
500             printf("%d ",w->logic);
501         }
502         
503         if(w->comp != ICAL_NO_COMPONENT){
504             printf("%s ",icalenum_component_kind_to_string(w->comp));
505         }
506
507         if(w->prop != ICAL_NO_PROPERTY){
508             printf("%s ",icalenum_property_kind_to_string(w->prop));
509         }
510         
511         if (w->compare != ICALGAUGECOMPARE_NONE){
512             printf("%d ",w->compare);
513         }
514
515
516         if (w->value!=0){
517             printf("%s",w->value);
518         }
519
520
521         printf("\n");
522     }
523 }
524