1 /*======================================================================
2  FILE: icalclassify.c
3  CREATOR: ebusboom 23 aug 2000
4 
5  (C) COPYRIGHT 2000, Eric Busboom <eric@civicknowledge.com>
6 
7  This library is free software; you can redistribute it and/or modify
8  it under the terms of either:
9 
10     The LGPL as published by the Free Software Foundation, version
11     2.1, available at: https://www.gnu.org/licenses/lgpl-2.1.html
12 
13  Or:
14 
15     The Mozilla Public License Version 2.0. You may obtain a copy of
16     the License at https://www.mozilla.org/MPL/
17 ======================================================================*/
18 
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22 
23 #include "icalclassify.h"
24 #include "icalmemory.h"
25 
26 #include <ctype.h>
27 #include <stdlib.h>
28 
29 struct icalclassify_parts
30 {
31     icalcomponent *c;
32     icalcomponent_kind inner_kind;
33     icalproperty_method method;
34     char *organizer;
35     icalparameter_partstat reply_partstat;
36     char *reply_attendee;
37     char *uid;
38     int sequence;
39     struct icaltimetype dtstamp;
40     struct icaltimetype recurrence_id;
41 };
42 
icalclassify_lowercase(const char * str)43 char *icalclassify_lowercase(const char *str)
44 {
45     char *p = 0;
46     char *xnew;
47 
48     if (str == 0) {
49         return 0;
50     }
51 
52     xnew = icalmemory_strdup(str);
53     for (p = xnew; *p != 0; p++) {
54         *p = tolower((int)*p);
55     }
56 
57     return xnew;
58 }
59 
60 /* Return a set of components that intersect in time with comp. For
61 component X and Y to intersect:
62     X.DTSTART < Y.DTEND && X.DTEND > Y.DTSTART
63 */
64 
icalclassify_find_overlaps(icalset * set,icalcomponent * comp)65 icalcomponent *icalclassify_find_overlaps(icalset *set, icalcomponent *comp)
66 {
67     icalcomponent *return_set;
68     icalcomponent *c;
69     struct icaltime_span span, compspan;
70 
71     icalerror_clear_errno();
72     compspan = icalcomponent_get_span(comp);
73 
74     if (icalerrno != ICAL_NO_ERROR) {
75         return 0;
76     }
77 
78     return_set = icalcomponent_new(ICAL_XROOT_COMPONENT);
79 
80     for (c = icalset_get_first_component(set); c != 0; c = icalset_get_next_component(set)) {
81 
82         icalerror_clear_errno();
83 
84         span = icalcomponent_get_span(c);
85 
86         if (icalerrno != ICAL_NO_ERROR) {
87             continue;
88         }
89 
90         if (compspan.start < span.end && compspan.end > span.start) {
91 
92             icalcomponent *clone = icalcomponent_new_clone(c);
93 
94             icalcomponent_add_component(return_set, clone);
95         }
96     }
97 
98     if (icalcomponent_count_components(return_set, ICAL_ANY_COMPONENT) != 0) {
99         return return_set;
100     } else {
101         icalcomponent_free(return_set);
102         return 0;
103     }
104 }
105 
icalclassify_find_attendee(icalcomponent * c,const char * attendee)106 icalproperty *icalclassify_find_attendee(icalcomponent *c, const char *attendee)
107 {
108     icalproperty *p;
109     icalcomponent *inner;
110     char *lattendee;
111     char *upn;
112 
113     if (attendee == 0) {
114         return 0;
115     }
116 
117     lattendee = icalclassify_lowercase(attendee);
118     upn = strchr(lattendee, ':');
119 
120     if (upn == 0) {
121         upn = lattendee;
122     } else {
123         upn++;  /* skip the ";" */
124     }
125 
126     inner = icalcomponent_get_first_real_component(c);
127 
128     for (p = icalcomponent_get_first_property(inner, ICAL_ATTENDEE_PROPERTY);
129          p != 0;
130          p = icalcomponent_get_next_property(inner, ICAL_ATTENDEE_PROPERTY)) {
131         char *this_upn;
132         char *this_attendee = icalclassify_lowercase(icalproperty_get_attendee(p));
133 
134         if (!this_attendee)
135             continue;
136 
137         this_upn = strchr(this_attendee, ':');
138 
139         if (this_upn == 0) {
140             free(this_attendee);
141             continue;
142         } else {
143             this_upn++;
144         }
145 
146         if (strcmp(this_upn, upn) == 0) {
147             free(lattendee);
148             free(this_attendee);
149             return p;
150         }
151 
152         free(this_attendee);
153     }
154     free(lattendee);
155 
156     return 0;
157 }
158 
icalssutil_free_parts(struct icalclassify_parts * parts)159 void icalssutil_free_parts(struct icalclassify_parts *parts)
160 {
161     if (parts == 0) {
162         return;
163     }
164 
165     if (parts->organizer != 0) {
166         free(parts->organizer);
167     }
168 
169     if (parts->uid != 0) {
170         free(parts->uid);
171     }
172 
173     if (parts->reply_attendee) {
174         free(parts->reply_attendee);
175     }
176 }
177 
icalssutil_get_parts(icalcomponent * c,struct icalclassify_parts * parts)178 void icalssutil_get_parts(icalcomponent *c, struct icalclassify_parts *parts)
179 {
180     icalproperty *p;
181     icalcomponent *inner;
182 
183     memset(parts, 0, sizeof(struct icalclassify_parts));
184 
185     parts->method = ICAL_METHOD_NONE;
186     parts->sequence = 0;
187     parts->reply_partstat = ICAL_PARTSTAT_NONE;
188 
189     if (c == 0) {
190         return;
191     }
192 
193     parts->c = c;
194 
195     p = icalcomponent_get_first_property(c, ICAL_METHOD_PROPERTY);
196     if (p != 0) {
197         parts->method = icalproperty_get_method(p);
198     }
199 
200     inner = icalcomponent_get_first_real_component(c);
201 
202     parts->inner_kind = icalcomponent_isa(inner);
203 
204     p = icalcomponent_get_first_property(inner, ICAL_ORGANIZER_PROPERTY);
205     if (p != 0) {
206         const char *p_organizer = icalproperty_get_organizer(p);
207 
208         if (p_organizer != 0) {
209             parts->organizer = strdup(p_organizer);
210         }
211     }
212 
213     p = icalcomponent_get_first_property(inner, ICAL_SEQUENCE_PROPERTY);
214     if (p != 0) {
215         parts->sequence = icalproperty_get_sequence(p);
216     }
217 
218     p = icalcomponent_get_first_property(inner, ICAL_UID_PROPERTY);
219     if (p != 0) {
220         const char *p_uid = icalproperty_get_uid(p);
221 
222         if (p_uid != 0) {
223             parts->uid = strdup(p_uid);
224         }
225     }
226 
227     p = icalcomponent_get_first_property(inner, ICAL_RECURRENCEID_PROPERTY);
228     if (p != 0) {
229         parts->recurrence_id = icalproperty_get_recurrenceid(p);
230     }
231 
232     p = icalcomponent_get_first_property(inner, ICAL_DTSTAMP_PROPERTY);
233     if (p != 0) {
234         parts->dtstamp = icalproperty_get_dtstamp(p);
235     }
236 
237     if (parts->method == ICAL_METHOD_REPLY) {
238         icalparameter *param;
239 
240         p = icalcomponent_get_first_property(inner, ICAL_ATTENDEE_PROPERTY);
241 
242         if (p != 0) {
243             const char *attendee = 0;
244 
245             param = icalproperty_get_first_parameter(p, ICAL_PARTSTAT_PARAMETER);
246 
247             if (param != 0) {
248                 parts->reply_partstat = icalparameter_get_partstat(param);
249             }
250             attendee = icalproperty_get_attendee(p);
251             if (attendee)
252                 parts->reply_attendee = strdup(attendee);
253         }
254     }
255 }
256 
icalssutil_is_rescheduled(icalcomponent * a,icalcomponent * b)257 int icalssutil_is_rescheduled(icalcomponent *a, icalcomponent *b)
258 {
259     icalproperty *p1, *p2;
260     icalcomponent *i1, *i2;
261     char *temp1, *temp2;
262     int i;
263 
264     icalproperty_kind kind_array[] = {
265         ICAL_DTSTART_PROPERTY,
266         ICAL_DTEND_PROPERTY,
267         ICAL_DURATION_PROPERTY,
268         ICAL_DUE_PROPERTY,
269         ICAL_RRULE_PROPERTY,
270         ICAL_RDATE_PROPERTY,
271         ICAL_EXRULE_PROPERTY,
272         ICAL_EXDATE_PROPERTY,
273         ICAL_NO_PROPERTY
274     };
275 
276     i1 = icalcomponent_get_first_real_component(a);
277     i2 = icalcomponent_get_first_real_component(b);
278 
279     for (i = 0; kind_array[i] != ICAL_NO_PROPERTY; i++) {
280         int cmp;
281 
282         p1 = icalcomponent_get_first_property(i1, kind_array[i]);
283         p2 = icalcomponent_get_first_property(i2, kind_array[i]);
284 
285         if ((p1 != 0) ^ (p2 != 0)) {
286             /* Return true if the property exists in one component and not
287                the other */
288             return 1;
289         } else if (!p1 && !p2)
290             continue;
291 
292         temp1 = icalproperty_as_ical_string_r(p1);
293         temp2 = icalproperty_as_ical_string_r(p2);
294         cmp = strcmp(temp1, temp2);
295         free(temp1);
296         free(temp2);
297 
298         if (p1 && cmp != 0) {
299             return 1;
300         }
301     }
302 
303     return 0;
304 }
305 
306 #define icalclassify_pre \
307 int rtrn = 0;
308 
309 #define icalclassify_post \
310 return rtrn;
311 
icalclassify_publish_new(struct icalclassify_parts * comp,struct icalclassify_parts * match,const char * user)312 int icalclassify_publish_new(struct icalclassify_parts *comp,
313                              struct icalclassify_parts *match, const char *user)
314 {
315     icalclassify_pre;
316     _unused(user);
317 
318     if (comp->method == ICAL_METHOD_PUBLISH &&
319         match == 0 && comp->inner_kind != ICAL_VFREEBUSY_COMPONENT) {
320         rtrn = 1;
321     }
322 
323     icalclassify_post;
324 }
325 
icalclassify_publish_update(struct icalclassify_parts * comp,struct icalclassify_parts * match,const char * user)326 int icalclassify_publish_update(struct icalclassify_parts *comp,
327                                 struct icalclassify_parts *match, const char *user)
328 {
329     icalclassify_pre;
330     _unused(user);
331 
332     if (comp->method == ICAL_METHOD_PUBLISH &&
333         match != 0 && comp->inner_kind != ICAL_VFREEBUSY_COMPONENT) {
334         rtrn = 1;
335     }
336 
337     icalclassify_post;
338 }
339 
icalclassify_publish_freebusy(struct icalclassify_parts * comp,struct icalclassify_parts * match,const char * user)340 int icalclassify_publish_freebusy(struct icalclassify_parts *comp,
341                                   struct icalclassify_parts *match, const char *user)
342 {
343     icalclassify_pre;
344 
345     _unused(match);
346     _unused(user);
347     if (comp->method == ICAL_METHOD_PUBLISH && comp->inner_kind == ICAL_VFREEBUSY_COMPONENT) {
348         rtrn = 1;
349     }
350 
351     icalclassify_post;
352 }
353 
icalclassify_request_new(struct icalclassify_parts * comp,struct icalclassify_parts * match,const char * user)354 int icalclassify_request_new(struct icalclassify_parts *comp,
355                              struct icalclassify_parts *match, const char *user)
356 {
357     /* Method is  REQUEST, and there is no match */
358 
359     icalclassify_pre _unused(user);
360 
361     if (match->c == 0 && comp->method == ICAL_METHOD_REQUEST) {
362         rtrn = 1;
363     }
364 
365 icalclassify_post}
366 
icalclassify_request_update(struct icalclassify_parts * comp,struct icalclassify_parts * match,const char * user)367 int icalclassify_request_update(struct icalclassify_parts *comp,
368                                 struct icalclassify_parts *match, const char *user)
369 {
370     /* REQUEST method, Higher SEQUENCE than match, and all
371        time-related properties are unchanged */
372 
373     icalclassify_pre _unused(user);
374 
375     if (match != 0 &&
376         comp->sequence >= match->sequence && !icalssutil_is_rescheduled(comp->c, match->c)) {
377         rtrn = 1;
378     }
379 
380 icalclassify_post}
381 
icalclassify_request_reschedule(struct icalclassify_parts * comp,struct icalclassify_parts * match,const char * user)382 int icalclassify_request_reschedule(struct icalclassify_parts *comp,
383                                     struct icalclassify_parts *match, const char *user)
384 {
385     /* REQUEST method, Higher SEQUENCE than match, and one or more
386        time-related properties are changed */
387     icalclassify_pre _unused(user);
388 
389     if (match->c != 0 &&
390         comp->sequence > match->sequence && icalssutil_is_rescheduled(comp->c, match->c)) {
391         rtrn = 1;
392     }
393 
394 icalclassify_post}
395 
icalclassify_request_delegate(struct icalclassify_parts * comp,struct icalclassify_parts * match,const char * user)396 int icalclassify_request_delegate(struct icalclassify_parts *comp,
397                                   struct icalclassify_parts *match, const char *user)
398 {
399     icalproperty *attendee;
400     icalparameter *param;
401 
402     icalclassify_pre;
403     _unused(match);
404 
405     attendee = icalclassify_find_attendee(comp->c, user);
406 
407     if (attendee == 0) {
408         return 0;
409     }
410 
411     param = icalproperty_get_first_parameter(attendee, ICAL_DELEGATEDFROM_PARAMETER);
412 
413     if (param != 0) {
414         rtrn = 1;
415     }
416 
417 icalclassify_post}
418 
icalclassify_request_new_organizer(struct icalclassify_parts * comp,struct icalclassify_parts * match,const char * user)419 int icalclassify_request_new_organizer(struct icalclassify_parts *comp,
420                                        struct icalclassify_parts *match, const char *user)
421 {
422     /*   Organizer has changed between match and component */
423     icalclassify_pre _unused(comp);
424     _unused(match);
425     _unused(user);
426 
427     icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR);
428 icalclassify_post}
429 
icalclassify_request_status(struct icalclassify_parts * comp,struct icalclassify_parts * match,const char * user)430 int icalclassify_request_status(struct icalclassify_parts *comp,
431                                 struct icalclassify_parts *match, const char *user)
432 {
433     icalclassify_pre _unused(comp);
434     _unused(match);
435     _unused(user);
436 
437     icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR);
438 icalclassify_post}
439 
icalclassify_request_forward(struct icalclassify_parts * comp,struct icalclassify_parts * match,const char * user)440 int icalclassify_request_forward(struct icalclassify_parts *comp,
441                                  struct icalclassify_parts *match, const char *user)
442 {
443     icalclassify_pre _unused(comp);
444     _unused(match);
445     _unused(user);
446 
447     icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR);
448 icalclassify_post}
449 
icalclassify_request_freebusy(struct icalclassify_parts * comp,struct icalclassify_parts * match,const char * user)450 int icalclassify_request_freebusy(struct icalclassify_parts *comp,
451                                   struct icalclassify_parts *match, const char *user)
452 {
453     icalclassify_pre _unused(comp);
454     _unused(match);
455     _unused(user);
456 
457     icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR);
458 icalclassify_post}
459 
icalclassify_reply_accept(struct icalclassify_parts * comp,struct icalclassify_parts * match,const char * user)460 int icalclassify_reply_accept(struct icalclassify_parts *comp,
461                               struct icalclassify_parts *match, const char *user)
462 {
463     icalproperty *attendee;
464 
465     icalclassify_pre;
466     _unused(user);
467 
468     attendee = icalclassify_find_attendee(match->c, comp->reply_attendee);
469 
470     if (attendee != 0 && comp->reply_partstat == ICAL_PARTSTAT_ACCEPTED) {
471         rtrn = 1;
472     }
473 
474 icalclassify_post}
475 
icalclassify_reply_decline(struct icalclassify_parts * comp,struct icalclassify_parts * match,const char * user)476 int icalclassify_reply_decline(struct icalclassify_parts *comp,
477                                struct icalclassify_parts *match, const char *user)
478 {
479     icalproperty *attendee;
480 
481     icalclassify_pre;
482     _unused(user);
483 
484     attendee = icalclassify_find_attendee(match->c, comp->reply_attendee);
485 
486     if (attendee != 0 && comp->reply_partstat == ICAL_PARTSTAT_DECLINED) {
487         rtrn = 1;
488     }
489 icalclassify_post}
490 
icalclassify_reply_delegate(struct icalclassify_parts * comp,struct icalclassify_parts * match,const char * user)491 int icalclassify_reply_delegate(struct icalclassify_parts *comp,
492                                 struct icalclassify_parts *match, const char *user)
493 {
494     icalproperty *attendee;
495 
496     icalclassify_pre;
497     _unused(user);
498 
499     attendee = icalclassify_find_attendee(match->c, comp->reply_attendee);
500 
501     if (attendee != 0 && comp->reply_partstat == ICAL_PARTSTAT_DELEGATED) {
502         rtrn = 1;
503     }
504 icalclassify_post}
505 
icalclassify_reply_crasher_accept(struct icalclassify_parts * comp,struct icalclassify_parts * match,const char * user)506 int icalclassify_reply_crasher_accept(struct icalclassify_parts *comp,
507                                       struct icalclassify_parts *match, const char *user)
508 {
509     icalproperty *attendee;
510 
511     icalclassify_pre;
512     _unused(user);
513 
514     attendee = icalclassify_find_attendee(match->c, comp->reply_attendee);
515 
516     if (attendee == 0 && comp->reply_partstat == ICAL_PARTSTAT_ACCEPTED) {
517         rtrn = 1;
518     }
519 icalclassify_post}
520 
icalclassify_reply_crasher_decline(struct icalclassify_parts * comp,struct icalclassify_parts * match,const char * user)521 int icalclassify_reply_crasher_decline(struct icalclassify_parts *comp,
522                                        struct icalclassify_parts *match, const char *user)
523 {
524     icalproperty *attendee;
525 
526     icalclassify_pre;
527     _unused(user);
528 
529     attendee = icalclassify_find_attendee(match->c, comp->reply_attendee);
530 
531     if (attendee == 0 && comp->reply_partstat == ICAL_PARTSTAT_DECLINED) {
532         rtrn = 1;
533     }
534 icalclassify_post}
535 
icalclassify_add_instance(struct icalclassify_parts * comp,struct icalclassify_parts * match,const char * user)536 int icalclassify_add_instance(struct icalclassify_parts *comp,
537                               struct icalclassify_parts *match, const char *user)
538 {
539     icalclassify_pre _unused(match);
540     _unused(user);
541 
542     if (comp->method == ICAL_METHOD_ADD) {
543         rtrn = 1;
544     }
545 icalclassify_post}
546 
icalclassify_cancel_event(struct icalclassify_parts * comp,struct icalclassify_parts * match,const char * user)547 int icalclassify_cancel_event(struct icalclassify_parts *comp,
548                               struct icalclassify_parts *match, const char *user)
549 {
550     icalclassify_pre _unused(match);
551     _unused(user);
552     if (comp->method == ICAL_METHOD_CANCEL) {
553         rtrn = 1;
554     }
555 icalclassify_post}
556 
icalclassify_cancel_instance(struct icalclassify_parts * comp,struct icalclassify_parts * match,const char * user)557 int icalclassify_cancel_instance(struct icalclassify_parts *comp,
558                                  struct icalclassify_parts *match, const char *user)
559 {
560     icalclassify_pre _unused(match);
561     _unused(user);
562     if (comp->method == ICAL_METHOD_CANCEL) {
563         rtrn = 1;
564     }
565 icalclassify_post}
566 
icalclassify_cancel_all(struct icalclassify_parts * comp,struct icalclassify_parts * match,const char * user)567 int icalclassify_cancel_all(struct icalclassify_parts *comp,
568                             struct icalclassify_parts *match, const char *user)
569 {
570     icalclassify_pre _unused(match);
571     _unused(user);
572     if (comp->method == ICAL_METHOD_CANCEL) {
573         rtrn = 1;
574     }
575 icalclassify_post}
576 
icalclassify_refesh(struct icalclassify_parts * comp,struct icalclassify_parts * match,const char * user)577 int icalclassify_refesh(struct icalclassify_parts *comp,
578                         struct icalclassify_parts *match, const char *user)
579 {
580     icalclassify_pre _unused(match);
581     _unused(user);
582     if (comp->method == ICAL_METHOD_REFRESH) {
583         rtrn = 1;
584     }
585 icalclassify_post}
586 
icalclassify_counter(struct icalclassify_parts * comp,struct icalclassify_parts * match,const char * user)587 int icalclassify_counter(struct icalclassify_parts *comp,
588                          struct icalclassify_parts *match, const char *user)
589 {
590     icalclassify_pre _unused(match);
591     _unused(user);
592     if (comp->method == ICAL_METHOD_COUNTER) {
593         rtrn = 1;
594     }
595 icalclassify_post}
596 
icalclassify_delinecounter(struct icalclassify_parts * comp,struct icalclassify_parts * match,const char * user)597 int icalclassify_delinecounter(struct icalclassify_parts *comp,
598                                struct icalclassify_parts *match, const char *user)
599 {
600     icalclassify_pre _unused(match);
601     _unused(user);
602 
603     if (comp->method == ICAL_METHOD_DECLINECOUNTER) {
604         rtrn = 1;
605     }
606 
607 icalclassify_post}
608 
609 static const struct icalclassify_map
610 {
611     icalproperty_method method;
612     int (*fn) (struct icalclassify_parts *comp, struct icalclassify_parts *match,
613                const char *user);
614     icalproperty_xlicclass class;
615 } icalclassify_map[] = {
616   {ICAL_METHOD_PUBLISH, icalclassify_publish_new, ICAL_XLICCLASS_PUBLISHNEW},
617   {ICAL_METHOD_PUBLISH, icalclassify_publish_update, ICAL_XLICCLASS_PUBLISHUPDATE},
618   {ICAL_METHOD_PUBLISH, icalclassify_publish_freebusy, ICAL_XLICCLASS_PUBLISHFREEBUSY},
619   {ICAL_METHOD_REQUEST, icalclassify_request_delegate, ICAL_XLICCLASS_REQUESTDELEGATE},
620   {ICAL_METHOD_REQUEST, icalclassify_request_new, ICAL_XLICCLASS_REQUESTNEW},
621   {ICAL_METHOD_REQUEST, icalclassify_request_update, ICAL_XLICCLASS_REQUESTUPDATE},
622   {ICAL_METHOD_REQUEST, icalclassify_request_reschedule, ICAL_XLICCLASS_REQUESTRESCHEDULE},
623   {ICAL_METHOD_REQUEST, icalclassify_request_new_organizer, ICAL_XLICCLASS_REQUESTNEWORGANIZER},
624   {ICAL_METHOD_REQUEST, icalclassify_request_forward, ICAL_XLICCLASS_REQUESTFORWARD},
625   {ICAL_METHOD_REQUEST, icalclassify_request_status, ICAL_XLICCLASS_REQUESTSTATUS},
626   {ICAL_METHOD_REQUEST, icalclassify_request_freebusy, ICAL_XLICCLASS_REQUESTFREEBUSY},
627   {ICAL_METHOD_REPLY, icalclassify_reply_accept, ICAL_XLICCLASS_REPLYACCEPT},
628   {ICAL_METHOD_REPLY, icalclassify_reply_decline, ICAL_XLICCLASS_REPLYDECLINE},
629   {ICAL_METHOD_REPLY, icalclassify_reply_delegate, ICAL_XLICCLASS_REPLYDELEGATE},
630   {ICAL_METHOD_REPLY, icalclassify_reply_crasher_accept, ICAL_XLICCLASS_REPLYCRASHERACCEPT},
631   {ICAL_METHOD_REPLY, icalclassify_reply_crasher_decline, ICAL_XLICCLASS_REPLYCRASHERDECLINE},
632   {ICAL_METHOD_ADD, icalclassify_add_instance, ICAL_XLICCLASS_ADDINSTANCE},
633   {ICAL_METHOD_CANCEL, icalclassify_cancel_event, ICAL_XLICCLASS_CANCELEVENT},
634   {ICAL_METHOD_CANCEL, icalclassify_cancel_instance, ICAL_XLICCLASS_CANCELINSTANCE},
635   {ICAL_METHOD_CANCEL, icalclassify_cancel_all, ICAL_XLICCLASS_CANCELALL},
636   {ICAL_METHOD_REFRESH, icalclassify_refesh, ICAL_XLICCLASS_REFRESH},
637   {ICAL_METHOD_COUNTER, icalclassify_counter, ICAL_XLICCLASS_COUNTER},
638   {ICAL_METHOD_DECLINECOUNTER, icalclassify_delinecounter, ICAL_XLICCLASS_DECLINECOUNTER},
639   {ICAL_METHOD_NONE, NULL, ICAL_XLICCLASS_NONE}
640 };
641 
icalclassify(icalcomponent * c,icalcomponent * match,const char * user)642 icalproperty_xlicclass icalclassify(icalcomponent *c, icalcomponent *match, const char *user)
643 {
644     icalcomponent *inner;
645     icalproperty *p;
646     icalproperty_method method;
647     icalproperty_xlicclass class = ICAL_XLICCLASS_UNKNOWN;
648 
649     int i;
650 
651     struct icalclassify_parts comp_parts;
652     struct icalclassify_parts match_parts;
653 
654     inner = icalcomponent_get_first_real_component(c);
655 
656     if (inner == 0) {
657         return ICAL_XLICCLASS_NONE;
658     }
659 
660     icalssutil_get_parts(c, &comp_parts);
661     icalssutil_get_parts(match, &match_parts);
662 
663     /* Determine if the incoming component is obsoleted by the match */
664     if (match != 0 && (comp_parts.method == ICAL_METHOD_REQUEST)) {
665         assert(!((icaltime_is_utc(comp_parts.dtstamp) == 1) ^
666                  (icaltime_is_utc(match_parts.dtstamp) == 1)));
667 
668         if (comp_parts.sequence < match_parts.sequence &&
669             icaltime_compare(comp_parts.dtstamp, match_parts.dtstamp) > 0) {
670             /* comp has a smaller sequence and a later DTSTAMP */
671             class = ICAL_XLICCLASS_MISSEQUENCED;
672             goto CLEANUP;
673         }
674 
675         if ((comp_parts.sequence < match_parts.sequence) ||
676             (comp_parts.sequence == match_parts.sequence &&
677              icaltime_compare(comp_parts.dtstamp, match_parts.dtstamp) <= 0)) {
678             class = ICAL_XLICCLASS_OBSOLETE;
679             goto CLEANUP;
680         }
681     }
682 
683     p = icalcomponent_get_first_property(c, ICAL_METHOD_PROPERTY);
684     if (p == 0) {
685         class = ICAL_XLICCLASS_UNKNOWN;
686         goto CLEANUP;
687     }
688     method = icalproperty_get_method(p);
689 
690     for (i = 0; icalclassify_map[i].method != ICAL_METHOD_NONE; i++) {
691         if (icalclassify_map[i].method == method) {
692             if ((*(icalclassify_map[i].fn)) (&comp_parts, &match_parts, user) == 1) {
693                 class = icalclassify_map[i].class;
694                 break;
695             }
696         }
697     }
698 
699   CLEANUP:
700     icalssutil_free_parts(&comp_parts);
701     icalssutil_free_parts(&match_parts);
702 
703     return class;
704 }
705