1/*  -*- Mode: C -*- */
2/*  ======================================================================
3  File: icalrestriction.c
4
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of either:
7
8    The LGPL as published by the Free Software Foundation, version
9    2.1, available at: http://www.fsf.org/copyleft/lesser.html
10
11  Or:
12
13    The Mozilla Public License Version 1.0. You may obtain a copy of
14    the License at http://www.mozilla.org/MPL/
15
16 (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org
17 ======================================================================*/
18/*#line 7 "icalrestriction.c.in"*/
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include "icalrestriction.h"
25#include "icalenums.h"
26#include "icalerror.h"
27
28#include <assert.h>
29#include <stdio.h> /* For snprintf */
30
31#ifdef WIN32
32#if defined(_MSC_VER) && (_MSC_VER < 1900)
33#define snprintf _snprintf
34#endif
35#endif
36
37
38#define TMP_BUF_SIZE 1024
39
40/* Define the structs for the restrictions. these data are filled out
41in machine generated code below */
42
43struct icalrestriction_property_record;
44
45typedef const char* (*restriction_func)(const struct icalrestriction_property_record* rec,icalcomponent* comp,icalproperty* prop);
46
47
48typedef struct icalrestriction_property_record {
49	icalproperty_method method;
50	icalcomponent_kind component;
51	icalproperty_kind property;
52	icalrestriction_kind restriction;
53	restriction_func function;
54} icalrestriction_property_record;
55
56
57typedef struct icalrestriction_component_record {
58	icalproperty_method method;
59	icalcomponent_kind component;
60	icalcomponent_kind subcomponent;
61	icalrestriction_kind restriction;
62	restriction_func function;
63} icalrestriction_component_record;
64
65static const icalrestriction_property_record*
66icalrestriction_get_property_restriction(icalproperty_method method,
67					 icalcomponent_kind component,
68					 icalproperty_kind property);
69static const icalrestriction_component_record*
70icalrestriction_get_component_restriction(icalproperty_method method,
71					  icalcomponent_kind component,
72					  icalcomponent_kind subcomponent);
73
74icalrestriction_property_record null_prop_record =   {ICAL_METHOD_NONE,ICAL_NO_COMPONENT,ICAL_NO_PROPERTY,ICAL_RESTRICTION_UNKNOWN,0};
75icalrestriction_component_record null_comp_record =   {ICAL_METHOD_NONE,ICAL_NO_COMPONENT,ICAL_NO_COMPONENT,ICAL_RESTRICTION_UNKNOWN,0};
76
77
78/** Each row gives the result of comparing a restriction against a count.
79   The columns in each row represent 0,1,2+. '-1' indicates
80   'invalid, 'don't care' or 'needs more analysis' So, for
81   ICAL_RESTRICTION_ONE, if there is 1 of a property with that
82   restriction, it passes, but if there are 0 or 2+, it fails. */
83
84char compare_map[ICAL_RESTRICTION_UNKNOWN+1][3] = {
85    { 1, 1, 1},/*ICAL_RESTRICTION_NONE*/
86    { 1, 0, 0},/*ICAL_RESTRICTION_ZERO*/
87    { 0, 1, 0},/*ICAL_RESTRICTION_ONE*/
88    { 1, 1, 1},/*ICAL_RESTRICTION_ZEROPLUS*/
89    { 0, 1, 1},/*ICAL_RESTRICTION_ONEPLUS*/
90    { 1, 1, 0},/*ICAL_RESTRICTION_ZEROORONE*/
91    { 1, 1, 0},/*ICAL_RESTRICTION_ONEEXCLUSIVE*/
92    { 1, 1, 0},/*ICAL_RESTRICTION_ONEMUTUAL*/
93    { 1, 1, 1} /*ICAL_RESTRICTION_UNKNOWN*/
94};
95
96const char restr_string_map[ICAL_RESTRICTION_UNKNOWN+1][60] = {
97    "unknown number",/*ICAL_RESTRICTION_NONE*/
98    "0",/*ICAL_RESTRICTION_ZERO*/
99    "1",/*ICAL_RESTRICTION_ONE*/
100    "zero or more",/*ICAL_RESTRICTION_ZEROPLUS*/
101    "one or more" ,/*ICAL_RESTRICTION_ONEPLUS*/
102    "zero or one",/*ICAL_RESTRICTION_ZEROORONE*/
103    "zero or one, exclusive with another property",/*ICAL_RESTRICTION_ONEEXCLUSIVE*/
104    "zero or one, mutual with another property",/*ICAL_RESTRICTION_ONEMUTUAL*/
105    "unknown number" /*ICAL_RESTRICTION_UNKNOWN*/
106};
107
108
109int
110icalrestriction_compare(icalrestriction_kind restr, int count){
111
112    /* restr is an unsigned int, ICAL_RESTRICTION_NONE == 0, so the check will always return false */
113    if ( /*restr < ICAL_RESTRICTION_NONE ||*/ restr > ICAL_RESTRICTION_UNKNOWN
114	 || count < 0){
115	return -1;
116    }
117
118    if (count > 2) {
119	count = 2;
120    }
121
122    return compare_map[restr][count];
123
124}
125
126/* Special case routines */
127
128const char* icalrestriction_may_be_draft_final_canceled(
129    const icalrestriction_property_record *rec,
130    icalcomponent* comp,
131    icalproperty* prop)
132{
133    icalproperty_status stat = icalproperty_get_status(prop);
134    (void)rec;
135    (void)comp;
136
137    if( !( stat == ICAL_STATUS_DRAFT ||
138	   stat == ICAL_STATUS_FINAL ||
139	   stat == ICAL_STATUS_CANCELLED )){
140
141	return "Failed iTIP restrictions for STATUS property. Value must be one of DRAFT, FINAL, or CANCELED";
142
143    }
144
145    return 0;
146}
147
148const char* icalrestriction_may_be_comp_need_process(
149    const icalrestriction_property_record *rec,
150    icalcomponent* comp,
151    icalproperty* prop)
152{
153    icalproperty_status stat = icalproperty_get_status(prop);
154    (void)rec;
155    (void)comp;
156
157    if( !( stat == ICAL_STATUS_COMPLETED ||
158	   stat == ICAL_STATUS_NEEDSACTION ||
159	   stat == ICAL_STATUS_INPROCESS )){
160
161	return "Failed iTIP restrictions for STATUS property. Value must be one of COMPLETED, NEEDS-ACTION or IN-PROCESS";
162
163    }
164
165    return 0;
166}
167const char* icalrestriction_may_be_tent_conf(const icalrestriction_property_record *rec,
168				       icalcomponent* comp,
169				       icalproperty* prop)
170{
171    icalproperty_status stat = icalproperty_get_status(prop);
172
173    (void)rec;
174    (void)comp;
175
176    if( !( stat == ICAL_STATUS_TENTATIVE ||
177	   stat == ICAL_STATUS_CONFIRMED )){
178
179	return "Failed iTIP restrictions for STATUS property. Value must be one of TENTATIVE or CONFIRMED";
180
181    }
182
183    return 0;
184}
185const char* icalrestriction_may_be_tent_conf_cancel(
186    const icalrestriction_property_record *rec,
187    icalcomponent* comp,
188    icalproperty* prop)
189{
190    icalproperty_status stat = icalproperty_get_status(prop);
191
192    (void)rec;
193    (void)comp;
194
195    if( !( stat == ICAL_STATUS_TENTATIVE ||
196	   stat == ICAL_STATUS_CONFIRMED ||
197	   stat == ICAL_STATUS_CANCELLED )){
198
199	return "Failed iTIP restrictions for STATUS property. Value must be one of TENTATIVE, CONFIRMED or CANCELED";
200
201    }
202
203    return 0;
204}
205
206const char* icalrestriction_must_be_cancel_if_present(
207    const icalrestriction_property_record *rec,
208    icalcomponent* comp,
209    icalproperty* prop)
210{
211    /* This routine will not be called if prop == 0 */
212    icalproperty_status stat = icalproperty_get_status(prop);
213    (void)rec;
214    (void)comp;
215
216    if( stat != ICAL_STATUS_CANCELLED)
217    {
218	return "Failed iTIP restrictions for STATUS property. Value must be CANCELLED";
219
220    }
221
222
223    return 0;
224}
225
226const char* icalrestriction_must_be_canceled_no_attendee(
227    const icalrestriction_property_record *rec,
228    icalcomponent* comp,
229    icalproperty* prop)
230{
231    (void)rec;
232    (void)comp;
233    (void)prop;
234
235    /* Hack. see rfc2446, 3.2.5 CANCEL for porperty STATUS. I don't
236       understand the note */
237
238    return 0;
239}
240
241const char* icalrestriction_must_be_recurring(
242		const icalrestriction_property_record *rec,
243		icalcomponent* comp,
244		icalproperty* prop){
245    /* Hack */
246    (void)rec;
247    (void)comp;
248    (void)prop;
249    return 0;
250}
251
252const char* icalrestriction_must_have_duration(
253	const icalrestriction_property_record *rec,
254	icalcomponent* comp,
255	icalproperty* prop)
256{
257    if( !icalcomponent_get_first_property(comp,ICAL_DURATION_PROPERTY)){
258
259	return "Failed iTIP restrictions for DURATION property. This component must have a DURATION property";
260
261    }
262
263    return 0;
264}
265
266const char* icalrestriction_must_have_repeat(
267		const icalrestriction_property_record *rec,
268		icalcomponent* comp,
269		icalproperty* prop)
270{
271    (void)rec;
272    (void)prop;
273    if( !icalcomponent_get_first_property(comp,ICAL_REPEAT_PROPERTY)){
274
275	return "Failed iTIP restrictions for REPEAT property. This component must have a REPEAT property";
276
277    }
278
279    return 0;
280}
281
282const char* icalrestriction_must_if_tz_ref(
283		const icalrestriction_property_record *rec,
284		icalcomponent* comp,
285		icalproperty* prop){
286
287    /* Hack */
288    (void)rec;
289    (void)comp;
290    (void)prop;
291    return 0;
292}
293
294const char* icalrestriction_no_dtend(
295		const icalrestriction_property_record *rec,
296		icalcomponent* comp,
297		icalproperty* prop)
298{
299
300    (void)rec;
301    (void)prop;
302    if(icalcomponent_get_first_property(comp,ICAL_DTEND_PROPERTY)){
303
304	return "Failed iTIP restrictions for DTEND property. The component must not have both DURATION and DTEND";
305
306    }
307
308    return 0;
309}
310
311const char* icalrestriction_no_duration(
312		const icalrestriction_property_record *rec,
313		icalcomponent* comp,
314		icalproperty* prop)
315{
316    (void)rec;
317    (void)comp;
318    (void)prop;
319
320    /* _no_dtend takes care of this one */
321    return 0;
322}
323
324const char* icalrestriction_must_be_email(
325    const icalrestriction_property_record *rec,
326    icalcomponent* comp,
327    icalproperty* prop)
328{
329    icalproperty_action stat = icalproperty_get_action(prop);
330
331    (void)rec;
332    (void)comp;
333
334    if( !( stat == ICAL_ACTION_EMAIL)){
335
336	return "Failed iTIP restrictions for ACTION property. Value must be EMAIL.";
337
338    }
339
340    return 0;
341}
342
343int icalrestriction_check_component(icalproperty_method method,
344				    icalcomponent* comp)
345{
346    icalproperty_kind kind;
347    icalcomponent_kind comp_kind;
348    icalrestriction_kind restr;
349    const icalrestriction_property_record *prop_record;
350    const char* funcr = 0;
351    icalproperty *prop;
352
353    int count;
354    int compare;
355    int valid = 1;
356
357    comp_kind = icalcomponent_isa(comp);
358
359    /* Check all of the properties in this component */
360
361    for(kind = ICAL_ANY_PROPERTY+1; kind != ICAL_NO_PROPERTY; kind++){
362	count = icalcomponent_count_properties(comp, kind);
363
364	prop_record = icalrestriction_get_property_restriction(method,
365							 comp_kind,
366							 kind);
367
368	restr = prop_record->restriction;
369
370	if(restr == ICAL_RESTRICTION_ONEEXCLUSIVE ||
371	    restr == ICAL_RESTRICTION_ONEMUTUAL) {
372
373	    /* First treat is as a 0/1 restriction */
374	    restr = ICAL_RESTRICTION_ZEROORONE;
375	    compare = icalrestriction_compare(restr,count);
376
377	} else {
378
379	    compare = icalrestriction_compare(restr,count);
380	}
381
382	assert(compare != -1);
383
384	if (compare == 0){
385	    char temp[TMP_BUF_SIZE];
386
387	    snprintf(temp, TMP_BUF_SIZE,"Failed iTIP restrictions for %s property. Expected %s instances of the property and got %d",
388		    icalenum_property_kind_to_string(kind),
389		    restr_string_map[restr], count);
390
391	    icalcomponent_add_property
392		(comp,
393		 icalproperty_vanew_xlicerror(
394		     temp,
395		     icalparameter_new_xlicerrortype(ICAL_XLICERRORTYPE_INVALIDITIP),
396		     0));
397	}
398
399
400	prop = icalcomponent_get_first_property(comp, kind);
401
402	if (prop != 0 && prop_record->function !=0  ){
403	    funcr =  prop_record->function(prop_record,comp,prop);
404	}
405
406	if(funcr !=0){
407	    icalcomponent_add_property
408		(comp,
409		 icalproperty_vanew_xlicerror(
410		     funcr,
411		     icalparameter_new_xlicerrortype(
412			 ICAL_XLICERRORTYPE_INVALIDITIP),
413		     0));
414
415	    compare = 0;
416	}
417
418	valid = valid && compare;
419    }
420
421
422
423    return valid;
424
425
426}
427
428int icalrestriction_check(icalcomponent* outer_comp)
429{
430    icalcomponent_kind comp_kind;
431    icalproperty_method method;
432    icalcomponent* inner_comp;
433    icalproperty *method_prop;
434    int valid;
435
436    icalerror_check_arg_rz( (outer_comp!=0), "outer comp");
437
438
439    /* Get the Method value from the outer component */
440
441    comp_kind = icalcomponent_isa(outer_comp);
442
443    if (comp_kind != ICAL_VCALENDAR_COMPONENT){
444	icalerror_set_errno(ICAL_BADARG_ERROR);
445	return 0;
446    }
447
448    method_prop = icalcomponent_get_first_property(outer_comp,
449						   ICAL_METHOD_PROPERTY);
450
451    if (method_prop == 0){
452	method = ICAL_METHOD_NONE;
453    } else {
454	method = icalproperty_get_method(method_prop);
455    }
456
457
458    /* Check the VCALENDAR wrapper */
459    valid = icalrestriction_check_component(ICAL_METHOD_NONE,outer_comp);
460
461
462    /* Now check the inner components */
463
464    for(inner_comp= icalcomponent_get_first_component(outer_comp,
465						      ICAL_ANY_COMPONENT);
466	inner_comp != 0;
467	inner_comp= icalcomponent_get_next_component(outer_comp,
468						     ICAL_ANY_COMPONENT)){
469
470	valid = valid && icalrestriction_check_component(method,inner_comp);
471
472    }
473
474
475    return valid;
476
477}
478
479<insert_code_here>
480
481static const icalrestriction_property_record*
482icalrestriction_get_property_restriction(icalproperty_method method,
483					 icalcomponent_kind component,
484					 icalproperty_kind property)
485{
486    int i;
487
488    for(i = 0;
489	icalrestriction_property_records[i].restriction != ICAL_RESTRICTION_NONE;
490	i++){
491
492	if (method == icalrestriction_property_records[i].method &&
493	    component == icalrestriction_property_records[i].component &&
494	    property ==  icalrestriction_property_records[i].property ){
495	    return  &icalrestriction_property_records[i];
496	}
497    }
498
499    return &null_prop_record;
500}
501
502
503static const icalrestriction_component_record*
504icalrestriction_get_component_restriction(icalproperty_method method,
505					  icalcomponent_kind component,
506					  icalcomponent_kind subcomponent)
507{
508
509    int i;
510
511    for(i = 0;
512	icalrestriction_component_records[i].restriction != ICAL_RESTRICTION_NONE;
513	i++){
514
515	if (method == icalrestriction_component_records[i].method &&
516	    component == icalrestriction_component_records[i].component &&
517	    subcomponent ==  icalrestriction_component_records[i].subcomponent ){
518	    return  &icalrestriction_component_records[i];
519	}
520    }
521
522    return &null_comp_record;
523}
524