* Nothing to see here, this is just reformatting of the code to
[alpine.git] / pith / ical.c
blobcff1f54233cd9afad18a27be46bfd1e1e47458b2
1 #include "../pith/headers.h"
2 #include "../pith/mailpart.h"
3 #include "../pith/store.h"
4 #include "../pith/ical.h"
6 typedef struct ical_iana_comp_s {
7 char *comp; /* component name */
8 size_t len; /* size of component name (strlen(x->comp)) */
9 int pos; /* position of this component in comp array */
10 void *(*parse)(char **); /* parser */
11 void (*give)(void **); /* free memory */
12 } ICAL_COMP_S;
14 typedef struct ical_iana_prop_s {
15 char *prop; /* component PROPerty name */
16 size_t len; /* size of component name (strlen(x->prop)) */
17 int pos; /* location of this component in the prop array */
18 void *(*parse)(); /* parser */
19 void (*give)(void **); /* free memory */
20 } ICAL_PROP_S;
22 int ical_january_first(int); /* args: year */
23 void ical_adjust_date(struct tm *, VTIMEZONE_S *);
25 void ical_initialize(void);
27 int ical_non_ascii_valid(unsigned char);
28 char *ical_unfold_line(char *);
29 ICLINE_S *ical_parse_line(char **, char *);
31 ICLINE_S *ical_cline_cpy(ICLINE_S *);
32 ICAL_PARAMETER_S *ical_parameter_cpy(ICAL_PARAMETER_S *param);
34 char *ical_get_value(char **);
35 unsigned char *ical_decode(char *, unsigned short);
37 /* pase component */
38 void *ical_parse_vcalendar(char **);
39 void *ical_parse_vevent(char **);
40 void *ical_parse_vtodo(char **);
41 void *ical_parse_vjournal(char **);
42 void *ical_parse_vfreebusy(char **);
43 void *ical_parse_vtimezone(char **);
44 void *ical_parse_valarm(char **);
45 void *ical_parse_timezone(char **);
46 ICAL_S *ical_parse_unknown_comp(char **, int);
47 ICAL_S *ical_parse_generic_comp(char **, int);
49 /* free components */
50 void ical_free_vevent(void **);
51 void ical_free_vtodo(void **);
52 void ical_free_vjournal(void **);
53 void ical_free_vfreebusy(void **);
54 void ical_free_vtimezone(void **);
55 void ical_free_timezone(void **);
56 void ical_free_valarm(void **);
57 void ical_free_unknown_comp(ICAL_S **);
59 /* parse properties */
60 void *ical_cline_from_token(void *, char **, char *);
61 void *ical_gencline_from_token(void *, char **, char *);
63 void *ical_parse_rrule(void *, char **, char *);
64 void *ical_parse_time(void *, char **, char *);
65 void *ical_parse_offset(void *, char **, char *);
67 void *ical_parse_freq(void *, char *);
68 void *ical_parse_until(void *, char *);
69 void *ical_parse_count(void *, char *);
70 void *ical_parse_interval(void *, char *);
71 void *ical_parse_weekday_list(void *, char *);
72 void *ical_parse_number_list(void *, char *);
74 int ical_get_number_value(char *, int, int);
75 void ical_set_date(ICLINE_S *, VTIMEZONE_S *);
76 void ical_set_date_vevent(void *, void *);
78 /* free properties */
79 void ical_free_prop(void ***, ICAL_PROP_S *, int);
80 void ical_free_cline(void **);
81 void ical_free_param(ICAL_PARAMETER_S **);
82 void ical_free_gencline(void **);
83 void ical_free_rrule(void **);
84 void ical_fs_give(void **);
85 void ical_free_weekday_list(void **);
87 /* utility functions */
88 void ical_date_time (char *, size_t, struct tm *);
89 char *ical_get_tzid(ICAL_PARAMETER_S *);
91 /* globals */
92 struct tm day_zero; /* date for january 1, 1601 */
93 int month_len[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
95 #define UTF8_COMPLETE (1)
96 #define NEED_MORE (2)
98 char *ical_buf;
99 unsigned long ical_buf_len;
102 /* parsing structures */
104 /* this is the list of V-components to a Calendar from RFC 5545 */
105 ICAL_COMP_S ical_comp[] = {
106 {"VCALENDAR", 9, VCalendar, ical_parse_vcalendar, ical_free_vcalendar},
107 {"VTIMEZONE", 9, VTimeZone, ical_parse_vtimezone, ical_free_vtimezone},
108 {"VEVENT", 6, VEvent, ical_parse_vevent, ical_free_vevent},
109 {"VTODO", 5, VTodo, ical_parse_vtodo, ical_free_vtodo},
110 {"VJOURNAL", 8, VJournal, ical_parse_vjournal, ical_free_vjournal},
111 {"VALARM", 6, VAlarm, ical_parse_valarm, ical_free_valarm},
112 {"VFREEBUSY", 9, VFreeBusy, ical_parse_vfreebusy, ical_free_vfreebusy},
113 {NULL, 0, VUnknown, NULL, 0}
116 /* array for properties */
117 ICAL_PROP_S rrule_prop[] = {
118 {"FREQ", 4, RRFreq, ical_parse_freq, ical_fs_give},
119 {"UNTIL", 5, RRUntil, ical_parse_until, ical_fs_give},
120 {"COUNT", 5, RRCount, ical_parse_count, ical_fs_give},
121 {"INTERVAL", 8, RRInterval, ical_parse_interval, ical_fs_give},
122 {"BYSECOND", 8, RRBysecond, ical_parse_number_list, ical_free_weekday_list},
123 {"BYMINUTE", 8, RRByminute, ical_parse_number_list, ical_free_weekday_list},
124 {"BYHOUR", 6, RRByhour, ical_parse_number_list, ical_free_weekday_list},
125 {"BYDAY", 5, RRByday, ical_parse_weekday_list,ical_free_weekday_list},
126 {"BYWEEKNO", 8, RRByweekno, 0, 0},
127 {"BYMONTH", 7, RRBymonth, ical_parse_number_list, ical_free_weekday_list},
128 {"BYSETPOS", 8, RRBysetpos, 0, 0},
129 {"BYWKST", 6, RRWkst, 0, 0},
130 {"BYMONTHDAY",
131 10, RRBymonthday, 0, 0},
132 {"BYYEARDAY", 9, RRByyearday, 0, 0},
133 {NULL, 0, RRUnknown, 0, 0}
136 ICAL_PROP_S event_prop[] = {
137 {"DTSTAMP", 7, EvDtstamp, ical_cline_from_token, ical_free_cline},
138 {"UID", 3, EvUid, ical_cline_from_token, ical_free_cline},
139 {"DTSTART", 7, EvDtstart, ical_cline_from_token, ical_free_cline},
140 {"CLASS", 5, EvClass, ical_cline_from_token, ical_free_cline},
141 {"CREATED", 7, EvCreated, ical_cline_from_token, ical_free_cline},
142 {"DESCRIPTION", 11, EvDescription, ical_cline_from_token, ical_free_cline},
143 {"GEO", 3, EvGeo, ical_cline_from_token, ical_free_cline},
144 {"LASTMOD", 7, EvLastMod, ical_cline_from_token, ical_free_cline},
145 {"LOCATION", 8, EvLocation, ical_cline_from_token, ical_free_cline},
146 {"ORGANIZER", 9, EvOrganizer, ical_cline_from_token, ical_free_cline},
147 {"PRIORITY", 8, EvPriority, ical_cline_from_token, ical_free_cline},
148 {"SEQUENCE", 8, EvSequence, ical_cline_from_token, ical_free_cline},
149 {"STATUS", 6, EvStatus, ical_cline_from_token, ical_free_cline},
150 {"SUMMARY", 7, EvSummary, ical_cline_from_token, ical_free_cline},
151 {"TRANSP", 6, EvTransp, ical_cline_from_token, ical_free_cline},
152 {"URL", 3, EvUrl, ical_cline_from_token, ical_free_cline},
153 {"RECURRENCE-ID", 13, EvRecurrence, ical_cline_from_token, ical_free_cline},
154 {"RRULE", 5, EvRrule, ical_parse_rrule, ical_free_rrule},
155 {"DTEND", 5, EvDtend, ical_cline_from_token, ical_free_cline},
156 {"DURATION", 8, EvDuration, ical_cline_from_token, ical_free_cline},
157 {"ATTACH", 6, EvAttach, ical_gencline_from_token, ical_free_gencline},
158 {"ATTENDEE", 8, EvAttendee, ical_gencline_from_token, ical_free_gencline},
159 {"CATEGORIES", 10, EvCategories, ical_gencline_from_token, ical_free_gencline},
160 {"COMMENT", 7, EvComment, ical_gencline_from_token, ical_free_gencline},
161 {"CONTACT", 7, EvContact, ical_gencline_from_token, ical_free_gencline},
162 {"EXDATE", 6, EvExdate, ical_gencline_from_token, ical_free_gencline},
163 {"RSTATUS", 7, EvRstatus, ical_gencline_from_token, ical_free_gencline},
164 {"RELATED", 7, EvRelated, ical_gencline_from_token, ical_free_gencline},
165 {"RESOURCES", 9, EvResources, ical_gencline_from_token, ical_free_gencline},
166 {"RDATE", 5, EvRdate, ical_gencline_from_token, ical_free_gencline},
167 {NULL, 0, EvUnknown, 0, 0}
170 ICAL_PROP_S tz_comp[] = {
171 {"TZID", 4, TZCid, ical_cline_from_token, ical_free_cline},
172 {"LAST-MODIFIED", 13, TZCLastMod, ical_cline_from_token, ical_free_cline},
173 {"TZURL", 5, TZCUrl, ical_cline_from_token, ical_free_cline},
174 {NULL, 0, TZCUnknown, 0, 0}
177 ICAL_PROP_S tz_prop[] = {
178 {"DTSTART", 7, TZPDtstart, ical_parse_time, ical_fs_give},
179 {"TZOFFSETTO", 10, TZPOffsetto, ical_parse_offset, ical_fs_give},
180 {"TZOFFSETFROM", 12, TZPOffsetfrom, ical_parse_offset, ical_fs_give},
181 {"RRULE", 5, TZPRrule, ical_parse_rrule, ical_free_rrule},
182 {"COMMENT", 7, TZPComment, ical_gencline_from_token, ical_free_gencline},
183 {"RDATE", 5, TZPRdate, ical_gencline_from_token, ical_free_gencline},
184 {"TZNAME", 6, TZPTzname, ical_gencline_from_token, ical_free_gencline},
185 {NULL, 0, TZPUnknown, 0, 0}
188 ICAL_PROP_S alarm_prop[] = {
189 {"ACTION", 6, AlAction, ical_cline_from_token, ical_free_cline},
190 {"TRIGGER", 7, AlTrigger, ical_cline_from_token, ical_free_cline},
191 {"DURATION", 8, AlDuration, ical_cline_from_token, ical_free_cline},
192 {"REPEAT", 6, AlRepeat, ical_cline_from_token, ical_free_cline},
193 {"DESCRIPTION", 11, AlDescription, ical_cline_from_token, ical_free_cline},
194 {"SUMMARY", 7, AlSummary, ical_cline_from_token, ical_free_cline},
195 {"ATTACH", 6, AlAttach, ical_gencline_from_token, ical_free_gencline},
196 {"ATTENDEE", 8, AlAttendee, ical_gencline_from_token, ical_free_gencline},
197 {NULL, 0, AlUnknown, 0, 0}
200 /* some useful macros for character analysis */
202 #define ical_wspace(X) \
203 ((X) == ' ' || (X) == '\t')
205 #define ical_name_allowed_char(X) \
206 (((X) >= 'A' && (X) <= 'Z') || \
207 ((X) >= 'a' && (X) <= 'z') || \
208 (X) == '-' )
210 #define ical_control(X) \
211 (((X) >= 0x00 && (X) <= 0x08) || \
212 ((X) >= 0x0A && (X) <= 0x1F) || \
213 (X) == 0x7F)
215 #define ical_safe_char(X) \
216 (ical_non_ascii_valid(X) \
217 || ical_wspace(X) \
218 || (X) == 0x21 \
219 || ((X) >= 0x23 && (X) <= 0x2B) \
220 || ((X) >= 0x2D && (X) <= 0x39) \
221 || ((X) >= 0x3C && (X) <= 0x7E))
223 #define ical_qsafe_char(X) \
224 (ical_non_ascii_valid((X)) \
225 || ical_wspace(X) \
226 || (X) == 0x21 \
227 || ((X) >= 0x23 && (X) <= 0x7E))
229 #define ical_value_char(X) \
230 (ical_non_ascii_valid(X) \
231 || ical_wspace(X) \
232 || ((X) >= 0x21 && (X) <= 0x7E))
234 /* Finally, here begins the code. */
236 unsigned char *
237 ical_decode(char *text, unsigned short encoding)
239 unsigned char *t;
240 unsigned long callen;
241 if(encoding == ENCQUOTEDPRINTABLE){
242 t = rfc822_qprint ((unsigned char *) text,strlen(text),&callen);
243 if(t != NULL){
244 strncpy(text, t, strlen(t));
245 fs_give((void **) &t);
248 return (unsigned char *) text;
252 /* Return code:
253 0 - if no errors
254 -1 - if an error occured
255 Args: a pointer to the text. If there is an error, the text is not modified.
258 ical_remove_escapes(char **textp)
260 char *text, *s, *t;
261 int rv = 0;
262 int escaped;
264 if(textp == NULL) return 0;
266 t = cpystr(*textp); /* work on a copy of the text */
267 /* the variable text below points to the beginning of the filtered text */
268 for (text = s = t, escaped = 0; rv == 0 && *s != '\0'; s++){
269 if(*s == '\\' && escaped == 0){
270 escaped = 1;
271 continue;
273 if(escaped){
274 switch(*s){
275 case '\\':
276 case ',':
277 case ';':
278 *t++ = *s;
279 break;
281 case 'n':
282 case 'N':
283 *t++ = '\n';
284 break;
285 default: rv = -1;
286 break;
288 escaped = 0;
290 else *t++ = *s;
292 *t = '\0'; /* tie off filtered text */
293 t = text; /* reset t to the beginning */
294 if(rv == 0){
295 strncpy(*textp, t, strlen(t)); /* overwrite given text with filtered text */
296 (*textp)[strlen(t)] = '\0';
298 fs_give((void **) &t);
299 return rv;
302 void
303 ical_debug(char *fcn, char *text)
305 char piece[50];
306 strncpy(piece, text, 49);
307 piece[sizeof(piece)-1] = '\0';
308 dprint((2, "%s: %s\n", fcn, piece));
311 /***
312 *** FREE MEMORY FUNCTIONS
313 ***/
315 void
316 ical_free_param(ICAL_PARAMETER_S **param)
318 if(param == NULL || *param == NULL)
319 return;
321 if((*param)->name) fs_give((void **) &(*param)->name);
322 if((*param)->value) fs_give((void **) &(*param)->value);
323 if((*param)->next) ical_free_param(&(*param)->next);
324 fs_give((void **)param);
327 void
328 ical_free_cline(void **icv)
330 ICLINE_S **ic = (ICLINE_S **) icv;
332 if(ic == NULL || *ic == NULL)
333 return;
335 if((*ic)->token) fs_give((void **) &(*ic)->token);
336 if((*ic)->param) ical_free_param(&(*ic)->param);
337 if((*ic)->value) fs_give((void **) &(*ic)->value);
338 fs_give(icv);
341 void
342 ical_free_gencline(void **giclpv)
344 GEN_ICLINE_S **giclp = (GEN_ICLINE_S **) giclpv;
346 if(giclp == NULL || *giclp == NULL) return;
348 if((*giclp)->cline) ical_free_cline((void **) &(*giclp)->cline);
349 if((*giclp)->next) ical_free_gencline((void **) &(*giclp)->next);
350 fs_give((void **)giclp);
353 void
354 ical_free_vcalendar(void **vcalpv)
356 VCALENDAR_S **vcalp = (VCALENDAR_S **)vcalpv;
358 if(vcalp == NULL || *vcalp == NULL) return;
360 if((*vcalp)->prodid) ical_free_cline((void **) &(*vcalp)->prodid);
361 if((*vcalp)->version) ical_free_cline((void **) &(*vcalp)->version);
362 if((*vcalp)->calscale) ical_free_cline((void **) &(*vcalp)->calscale);
363 if((*vcalp)->method) ical_free_cline((void **) &(*vcalp)->method);
364 if((*vcalp)->uk_prop) ical_free_gencline((void **) &(*vcalp)->uk_prop);
365 if((*vcalp)->comp){
366 Cal_comp i;
367 for(i = 0; i < VUnknown; i++)
368 if((*vcalp)->comp[i]) (ical_comp[i].give)(&(*vcalp)->comp[i]);
369 fs_give((void **) &(*vcalp)->comp);
371 if((*vcalp)->uk_comp) ical_free_unknown_comp(&(*vcalp)->uk_comp);
372 fs_give(vcalpv);
375 void
376 ical_free_vevent(void **veventpv)
378 VEVENT_S **veventp = (VEVENT_S **) veventpv;
380 if(veventp == NULL || *veventp == NULL) return;
382 ical_free_prop(&(*veventp)->prop, event_prop, EvUnknown);
383 if((*veventp)->uk_prop) ical_free_gencline((void **) &(*veventp)->uk_prop);
384 if((*veventp)->valarm) ical_free_valarm((void **) &(*veventp)->valarm);
385 if((*veventp)->next) ical_free_vevent((void **) &(*veventp)->next);
386 fs_give(veventpv);
389 void
390 ical_fs_give(void **x)
392 if(x != NULL && *x != NULL)
393 fs_give(x);
396 void
397 ical_free_rrule(void **rrulepv)
399 RRULE_S **rrulep = (RRULE_S **) rrulepv;
401 if(rrulep && *rrulep){
402 ical_free_prop(&(*rrulep)->prop, rrule_prop, RRUnknown);
403 ical_free_param(&(*rrulep)->param);
404 fs_give(rrulepv);
408 void
409 ical_free_weekday_list(void **wkdylv)
411 BYWKDY_S **wkdyl = (BYWKDY_S **) wkdylv;
413 if(wkdyl == NULL) return;
415 if((*wkdyl)->next)
416 ical_free_weekday_list((void **) &(*wkdyl)->next);
418 fs_give(wkdylv);
422 void
423 ical_free_vtodo(void **vtodopv)
427 void
428 ical_free_vjournal(void **vjournalpv)
432 void
433 ical_free_vfreebusy(void **vfbpv)
437 void
438 ical_free_prop(void ***propv, ICAL_PROP_S *aux_comp, int max)
440 int i, j;
442 if(propv == NULL || *propv == NULL) return;
444 for(i = 0; i < max; i++)
445 if((*propv)[i]){
446 for(j = 0; aux_comp[j].prop != NULL && aux_comp[j].pos != i; j++);
447 if(aux_comp[j].give) (aux_comp[j].give)(&(*propv)[i]);
449 fs_give((void **) propv);
453 void
454 ical_free_vtimezone(void **vtzpv)
456 VTIMEZONE_S **vtzp = (VTIMEZONE_S **) vtzpv;
457 TZ_comp i,j;
459 if(vtzp == NULL || *vtzp == NULL) return;
461 ical_free_prop(&(*vtzp)->prop, tz_comp, TZCUnknown);
462 if((*vtzp)->uk_prop) ical_free_gencline((void **) &(*vtzp)->uk_prop);
463 if((*vtzp)->standardc) ical_free_timezone((void **) &(*vtzp)->standardc);
464 if((*vtzp)->daylightc) ical_free_timezone((void **) &(*vtzp)->daylightc);
465 fs_give(vtzpv);
468 void
469 ical_free_timezone(void **tzpv)
471 ICAL_TZPROP_S **tzp = (ICAL_TZPROP_S **) tzpv;
473 if(tzp == NULL || *tzp == NULL) return;
475 ical_free_prop(&(*tzp)->prop, tz_prop, TZPUnknown);
476 if((*tzp)->uk_prop) ical_free_gencline((void **) &(*tzp)->uk_prop);
477 if((*tzp)->next) ical_free_timezone((void **) &(*tzp)->next);
478 fs_give(tzpv);
481 void
482 ical_free_valarm(void **valarmpv)
484 VALARM_S **valarmp = (VALARM_S **) valarmpv;
485 int i, j;
487 if(valarmp == NULL || *valarmp == NULL) return;
489 ical_free_prop(&(*valarmp)->prop, alarm_prop, AlUnknown);
490 if((*valarmp)->uk_prop) ical_free_gencline((void **) &(*valarmp)->uk_prop);
491 if((*valarmp)->next) ical_free_timezone((void **) &(*valarmp)->next);
492 fs_give(valarmpv);
495 void
496 ical_free_unknown_comp(ICAL_S **icalp)
498 int i;
499 if(icalp == NULL || *icalp == NULL) return;
500 for(i = 0; ical_comp[i].comp && strucmp((*icalp)->comp,ical_comp[i].comp); i++);
501 if(ical_comp[i].give)
502 (ical_comp[i].give)(&(*icalp)->value);
503 else
504 ical_free_gencline((void **) &(*icalp)->value);
505 fs_give((void **)&(*icalp)->comp);
506 ical_free_unknown_comp(&(*icalp)->next);
507 ical_free_unknown_comp(&(*icalp)->branch);
508 fs_give((void **)icalp);
511 char *
512 ical_unfold_line(char *line)
514 int i, j;
516 if(line == NULL)
517 return NULL;
519 for(i = 0, j = 0; line[j] != '\0';)
520 switch(line[j]){
521 case '\r': if(line[j+1] == '\n' && ical_wspace(line[j+2])){
522 j += 3; /* get past white space */
523 continue;
525 case '\n': if(ical_wspace(line[j+1])){
526 j += 2; /* get past white space */
527 continue;
529 default : line[i++] = line[j++];
531 line[i] = '\0';
532 return line;
535 ICAL_PARAMETER_S *
536 ical_get_parameter(char **line)
538 ICAL_PARAMETER_S *param = NULL;
539 char *s;
541 if(line == NULL || *line == NULL)
542 return NULL;
544 for(s = *line; s && *s && ical_name_allowed_char(*s) ; s++);
546 if(*s == '='){
547 int quoted;
548 char c;
550 param = fs_get(sizeof(ICAL_PARAMETER_S));
551 memset((void *)param, 0, sizeof(ICAL_PARAMETER_S));
552 *s = '\0';
553 param->name = cpystr(*line);
554 *s = '=';
555 *line = s+1; /* step over '=' */
556 quoted = **line == '"' ? 1 : 0;
557 if(quoted != 0){
558 for(s = ++*line; s && *s && ical_qsafe_char((unsigned char) *s); s++);
559 if(*s != '"'){ /* error, do not parse this line */
560 ical_free_param(&param);
561 *line = strchr(s, ':'); /* reset line to closest ':' */
562 return NULL;
565 else
566 for(s = *line; s && *s && (ical_safe_char((unsigned char) *s)); s++);
567 c = *s;
568 *s = '\0';
569 param->value = cpystr(*line);
570 *s = c; /* restore character */
571 *line = quoted ? s + 1 : s;
573 if(**line == ';'){
574 ++*line;
575 param->next = ical_get_parameter(line);
578 return param;
581 char *
582 ical_get_value(char **line)
584 char *s, *t;
586 if(line == NULL || *line == NULL)
587 return NULL;
589 for (s = *line; *s && ical_value_char((unsigned char) *s); s++);
590 if(*s == '\r'){
591 *s = '\0';
592 t = cpystr(*line);
593 *s = '\r';
594 *line = s+2;
596 else{
597 t = NULL;
598 s = strchr(*line, '\r');
599 if(s != NULL)
600 *line = s + 2;
602 return t;
605 ICAL_PARAMETER_S *
606 ical_parameter_cpy(ICAL_PARAMETER_S *param)
608 ICAL_PARAMETER_S *rv;
610 if(param == NULL) return NULL;
612 rv = fs_get(sizeof(ICAL_PARAMETER_S));
613 memset((void *)rv, 0, sizeof(ICAL_PARAMETER_S));
615 if(param->name) rv->name = cpystr(param->name);
616 if(param->value) rv->value = cpystr(param->value);
617 if(param->next) rv->next = ical_parameter_cpy(param->next);
619 return rv;
622 ICLINE_S *
623 ical_cline_cpy(ICLINE_S *icl)
625 ICLINE_S *rv;
627 if(icl == NULL)
628 return NULL;
630 rv = fs_get(sizeof(ICLINE_S));
631 memset((void *)rv, 0, sizeof(ICLINE_S));
633 if(icl->token) rv->token = cpystr(icl->token);
634 if(icl->param) rv->param = ical_parameter_cpy(icl->param);
635 if(icl->value) rv->value = cpystr(icl->value);
637 return rv;
640 /* Given a \r\n-ending line (called *text), isolate the ocurrence
641 * of the token in that line.
642 * Return the token, and modify the pointer to *text to point to the
643 * end of the token. Modify sep to contain the character following
644 * the token
645 * ical-line = token ':'/';' rest of the line\r\n
646 * on error return null, and set *text to the next line, if possible.
648 char *
649 ical_isolate_token(char **text, char *sep)
651 char *s, *t;
653 for(t = s = *text; *t && ical_name_allowed_char(*s); s++);
654 /* only followed by parameter or value */
655 if(*s == ':' || *s == ';'){
656 *sep = *s;
657 *s = '\0'; /* isolate token at pointer s */
658 *text = s;
660 else{ /* bad data - bail out of here */
661 t = NULL;
662 if(*s == '\0' || (s = strstr(s, "\r\n")) == NULL)
663 *text = NULL;
664 else /* move to next line */
665 *text = s + 2;
667 return t;
671 VCALENDAR_S *
672 ical_parse_text(char *text)
674 char *s;
675 VCALENDAR_S *vcal = NULL;
677 ical_debug("ical_parse_text", text);
678 ical_initialize();
680 text = ical_unfold_line(text);
681 for(s = text; s && *s != '\0'; s++){
682 if(*s != 'B' && *s != 'b')
683 continue;
684 if(!struncmp(s+1, "EGIN:VCALENDAR\r\n", 16)){
685 s += 17; /* 17 = strlen("BEGIN:VCALENDAR\r\n") */
686 vcal = (VCALENDAR_S *) ical_parse_vcalendar(&s);
687 break;
690 return vcal;
693 void *
694 ical_parse_time(void *ic_datep, char **text, char *token)
696 struct tm *datep;
697 ICLINE_S *icl;
699 datep = fs_get(sizeof(struct tm));
700 icl = ical_parse_line(text, token);
701 ical_parse_date(icl->value, datep);
702 ical_free_cline((void **) &icl);
703 ic_datep = (void *) datep;
705 return ic_datep;
708 void *
709 ical_parse_interval(void *longvp, char *value)
712 unsigned long *longp;
714 longp = fs_get(sizeof(unsigned long));
715 *longp = atoi(value);
716 longvp = (void *) longp;
718 return longvp;
722 void *
723 ical_parse_offset(void *offsetv, char **text, char *token)
725 ICLINE_S *icl;
726 char *value;
727 int h, m, *offset;
729 offset = fs_get(sizeof(int));
731 icl = ical_parse_line(text, token);
733 if(*icl->value == '+' || *icl->value == '-')
734 value = icl->value + 1;
735 else
736 value = icl->value;
738 h = ical_get_number_value(value, 0, 2);
739 m = ical_get_number_value(value, 2, 4);
741 *offset = 60*(60*h + m);
742 if(*icl->value == '-')
743 *offset *= -1;
745 ical_free_cline((void **) &icl);
746 offsetv = (void *) offset;
748 return offsetv;
751 /* This function processes the information in *text, and returns
752 * a pointer to the information in iclp, but only if iclp is NULL
753 * otherwise, it simply returns the current value and advances the
754 * pointer to *text.
755 * Call this function as follows
756 * rv = (cast here *) ical_cline_from_token((void *)rv, &text, token);
758 void *
759 ical_cline_from_token(void *iclp, char **text, char *token)
761 ICLINE_S *icl;
763 ical_debug("ical_cline_from_token", *text);
765 icl = ical_parse_line(text, token);
767 if(iclp != NULL)
768 ical_free_cline((void **)&icl);
769 else
770 iclp = (void *) icl;
772 return iclp;
775 void *
776 ical_gencline_from_token(void *giclv, char **text, char *token)
778 GEN_ICLINE_S *gicl= NULL;
780 if(!struncmp(*text, token, strlen(token))){
781 gicl = fs_get(sizeof(GEN_ICLINE_S));
782 memset((void *) gicl, 0, sizeof(GEN_ICLINE_S));
783 gicl->cline = ical_parse_line(text, token);
784 // gicl->line = (ICLINE_S *) ical_cline_from_token((void *) gicl->cline, text, token);
785 gicl->next = (GEN_ICLINE_S *) ical_gencline_from_token((void *) gicl->next, text, token);
788 if(giclv != NULL)
789 ical_free_gencline((void **) &gicl);
790 else
791 giclv = (void *) gicl;
793 return giclv;
796 /***
797 *** PARSE COMPONENT FUNCTIONS
798 ***/
800 void *
801 ical_parse_vcalendar(char **text)
803 char *s, *t;
804 char c;
805 VCALENDAR_S *vcal;
806 void *v;
808 dprint((9, "ical_parse_vcalendar:\n"));
809 ical_debug("ical_parse_vcalendar", *text);
811 vcal = fs_get(sizeof(VCALENDAR_S));
812 memset((void *) vcal, 0, sizeof(VCALENDAR_S));
814 /* s must always point the the beginning of a line */
815 for(s = *text; s && *s != '\0';){
816 t = s;
817 s = ical_isolate_token(&t, &c);
818 if(s == NULL){
819 if(t != NULL)
820 s = t;
821 continue;
824 *t = c; /* restore character */
825 if(s){ /* figure out the token */
826 int ukn = 0; /* unknown token */
827 int i;
828 switch(*s){
829 case 'B':
830 case 'b': if(!struncmp(s+1, "EGIN", 4)){
831 s += 6; /* 6 = strlen("BEGIN:") */
832 for(i = 0; ical_comp[i].comp
833 && (struncmp(s, ical_comp[i].comp, ical_comp[i].len)
834 || struncmp(s + ical_comp[i].len, "\r\n", 2)); i++);
836 if(ical_comp[i].parse){
837 s += ical_comp[i].len + 2;
838 v = (ical_comp[i].parse)(&s);
839 if(vcal->comp == NULL){
840 vcal->comp = fs_get((VUnknown+1)*sizeof(void *));
841 memset((void *) vcal->comp, 0, (VUnknown+1)*sizeof(void *));
844 if(vcal->comp[ical_comp[i].pos] == NULL)
845 vcal->comp[ical_comp[i].pos] = v;
846 else{
847 if((vcal->method && vcal->method->value
848 && strucmp(vcal->method->value, "PUBLISH"))
849 || struncmp(ical_comp[i].comp, "VEVENT", 6))
850 (ical_comp[i].give)(&v);
851 else{
852 VEVENT_S *vevent = (VEVENT_S *) vcal->comp[VEvent];
853 for(; vevent && vevent->next; vevent = vevent->next);
854 vevent->next = v;
857 } else {
858 v = (void *) ical_parse_unknown_comp(&s, 0);
859 if(vcal->uk_comp == NULL)
860 vcal->uk_comp = (ICAL_S *) v;
861 else{
862 ICAL_S *ic;
863 for(ic = vcal->uk_comp; ic && ic->branch; ic = ic->branch);
864 ic->branch = (ICAL_S *) v;
867 } else ukn++;
868 break;
870 case 'C':
871 case 'c': if(!struncmp(s+1, "ALSCALE", 7)){
872 v = (void *) vcal->calscale;
873 v = ical_cline_from_token(v, &s, "CALSCALE");
874 vcal->calscale = (ICLINE_S *) v;
876 else ukn++;
877 break;
879 case 'E':
880 case 'e': if(!struncmp(s+1, "ND", 2)){
881 *t = c;
882 s += 4; /* 4 = strlen("END:") */
883 if(!struncmp(s, "VCALENDAR\r\n", 11)){
884 *text = s + 11; /* 11 = strlen("VCALENDAR\r\n") */
885 return (void *) vcal;
887 // else ukn++; FIX THIS, this is not quite right
888 } else ukn++;
889 break;
891 case 'M':
892 case 'm': if(!struncmp(s+1, "ETHOD", 5)){
893 v = (void *) vcal->method;
894 v = ical_cline_from_token(v, &s, "METHOD");
895 vcal->method = (ICLINE_S *) v;
897 else ukn++;
898 break;
900 case 'P':
901 case 'p': if(!struncmp(s+1, "RODID", 5)){
902 v = (void *) vcal->prodid;
903 v = ical_cline_from_token(v, &s, "PRODID");
904 vcal->prodid = (ICLINE_S *) v;
906 else ukn++;
907 break;
909 case 'V':
910 case 'v': if(!struncmp(s+1, "ERSION", 6)){
911 v = (void *) vcal->version;
912 v = ical_cline_from_token(v, &s, "VERSION");
913 vcal->version = (ICLINE_S *) v;
914 } else ukn++;
915 break;
917 default : ukn++;
918 break;
919 } /* end of switch(*s) */
920 if(ukn){
921 if(ical_buf_len < t - s){
922 fs_resize((void **)&ical_buf, t-s+1);
923 ical_buf_len = t-s;
925 *t = '\0';
926 strcpy(ical_buf, s);
927 *t = c;
928 if(vcal->uk_prop == NULL){
929 vcal->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
930 memset((void *)vcal->uk_prop, 0, sizeof(GEN_ICLINE_S));
931 vcal->uk_prop->cline = ical_parse_line(&s, ical_buf);
933 else{
934 GEN_ICLINE_S *gcl;
935 for (gcl = vcal->uk_prop; gcl && gcl->next; gcl = gcl->next);
936 gcl->next = fs_get(sizeof(GEN_ICLINE_S));
937 memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
938 gcl->next->cline = ical_parse_line(&s, ical_buf);
941 } /* end of if(s) */
944 *text = s;
946 /* ok, we have parsed the vcalendar, now parse some special properties */
947 /* start by parsing dates */
948 ical_set_date_vevent(vcal->comp[VEvent], vcal->comp[VTimeZone]);
949 return (void *) vcal;
952 void *
953 ical_parse_vevent(char **text)
955 char *s, *t;
956 char c;
957 VEVENT_S *vevent;
959 ical_debug("ical_parse_vevent", *text);
960 vevent = fs_get(sizeof(VEVENT_S));
961 memset((void *)vevent, 0, sizeof(VEVENT_S));
963 /* s must always point the the beginning of a line */
964 for(s = *text; s && *s != '\0';){
965 t = s;
966 s = ical_isolate_token(&t, &c);
967 if(s == NULL){
968 if(t != NULL)
969 s = t;
970 continue;
972 *t = c; /* restore separator */
974 if(s){ /* figure out the token */
975 int ukn = 0; /* unknown token */
976 if(!struncmp(s, "BEGIN", 5)){
977 s += 6; /* 6 = strlen("BEGIN:") */
978 if(!struncmp(s, "VALARM\r\n", 8)){
979 s += 8; /* 8 = strlen("VALARM\r\n"); */
980 if(vevent->valarm == NULL)
981 vevent->valarm = ical_parse_valarm(&s);
982 else{
983 VALARM_S *valrm;
984 for(valrm = vevent->valarm; valrm && valrm->next;
985 valrm = valrm->next);
986 valrm->next = ical_parse_valarm(&s);
988 } else {
989 ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0);
990 ical_free_unknown_comp(&uk_comp);
992 } else if(!struncmp(s, "END", t-s-1)){
993 s += 4; /* 4 = strlen("END:") */
994 if(!struncmp(s, "VEVENT\r\n",8)){
995 *text = s + 8; /* 8 = strlen("VCALENDAR\r\n") */
996 return (void *) vevent;
998 } else{ Event_prop i;
999 for(i = 0; i < EvUnknown; i++)
1000 if(!struncmp(s, event_prop[i].prop, t-s))
1001 break;
1002 if(event_prop[i].parse){
1003 void *v;
1004 if(vevent->prop == NULL){
1005 vevent->prop = fs_get((EvUnknown+1)*sizeof(void *));
1006 memset((void *)vevent->prop, 0, (EvUnknown+1)*sizeof(void *));
1008 v = vevent->prop[event_prop[i].pos];
1009 v = (event_prop[i].parse)(v , &s, event_prop[i].prop);
1010 vevent->prop[event_prop[i].pos] = v;
1012 else
1013 ukn++;
1016 if(ukn){
1017 if(ical_buf_len < t - s){
1018 fs_resize((void **)&ical_buf, t-s+1);
1019 ical_buf_len = t-s;
1021 *t = '\0';
1022 strcpy(ical_buf, s);
1023 *t = c;
1024 if(vevent->uk_prop == NULL){
1025 vevent->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
1026 memset((void *)vevent->uk_prop, 0, sizeof(GEN_ICLINE_S));
1027 vevent->uk_prop->cline = ical_parse_line(&s, ical_buf);
1029 else{
1030 GEN_ICLINE_S *gcl;
1031 for (gcl = vevent->uk_prop; gcl && gcl->next; gcl = gcl->next);
1032 gcl->next = fs_get(sizeof(GEN_ICLINE_S));
1033 memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
1034 gcl->next->cline = ical_parse_line(&s, ical_buf);
1037 } /* end of if(s) */
1040 *text = s;
1041 return (void *) vevent;
1044 void *
1045 ical_parse_vtimezone(char **text)
1047 char *s, *t;
1048 char c;
1049 void *v;
1050 VTIMEZONE_S *vtz;
1052 ical_debug("ical_parse_vtimezone", *text);
1053 vtz = fs_get(sizeof(VTIMEZONE_S));
1054 memset((void *)vtz, 0, sizeof(VTIMEZONE_S));
1056 /* s must always point the the beginning of a line */
1057 for(s = *text; s && *s != '\0';){
1058 t = s;
1059 s = ical_isolate_token(&t, &c);
1060 if(s == NULL){
1061 if(t != NULL)
1062 s = t;
1063 continue;
1065 *t = c; /* restore separator */
1067 if(s){ /* figure out the token */
1068 int ukn = 0; /* unknown token */
1069 if(!struncmp(s, "BEGIN", 5)){
1070 s += 6; /* 6 = strlen("BEGIN:") */
1071 if(!struncmp(s, "STANDARD\r\n", 10)){
1072 s += 10; /* 10 = strlen("STANDARD\r\n"); */
1073 v = ical_parse_timezone(&s);
1074 if(vtz->standardc == NULL)
1075 vtz->standardc = (ICAL_TZPROP_S *) v;
1076 else{
1077 ICAL_TZPROP_S *dl;
1078 for(dl = vtz->standardc; dl && dl->next; dl = dl->next);
1079 dl->next = (ICAL_TZPROP_S *) v;
1081 } else if(!struncmp(s, "DAYLIGHT\r\n", 10)){
1082 s += 10; /* 10 = strlen("DAYLIGHT\r\n"); */
1083 v = ical_parse_timezone(&s);
1084 if(vtz->daylightc == NULL)
1085 vtz->daylightc = (ICAL_TZPROP_S *) v;
1086 else{
1087 ICAL_TZPROP_S *dl;
1088 for(dl = vtz->daylightc; dl && dl->next; dl = dl->next);
1089 dl->next = (ICAL_TZPROP_S *) v;
1091 } else {
1092 ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0);
1093 ical_free_unknown_comp(&uk_comp);
1095 } else if(!struncmp(s, "END", t-s-1)){
1096 s += 4; /* 4 = strlen("END:") */
1097 if(!struncmp(s, "VTIMEZONE\r\n",11)){
1098 *text = s + 11; /* 11 = strlen("VTIMEZONE\r\n") */
1099 return (void *) vtz;
1101 } else{ TZ_comp i;
1102 for(i = 0; i < TZCUnknown; i++)
1103 if(!struncmp(s, tz_comp[i].prop, t-s))
1104 break;
1105 if(tz_comp[i].parse){
1106 void *v;
1107 if(vtz->prop == NULL){
1108 vtz->prop = fs_get(TZCUnknown*sizeof(void *));
1109 memset((void *)vtz->prop, 0, TZCUnknown*sizeof(void *));
1111 v = vtz->prop[tz_comp[i].pos];
1112 v = (tz_comp[i].parse)(v, &s, tz_comp[i].prop);
1113 vtz->prop[tz_comp[i].pos] = v;
1115 else
1116 ukn++;
1119 if(ukn){
1120 if(ical_buf_len < t - s){
1121 fs_resize((void **)&ical_buf, t-s+1);
1122 ical_buf_len = t-s;
1124 *t = '\0';
1125 strcpy(ical_buf, s);
1126 *t = c;
1127 if(vtz->uk_prop == NULL){
1128 vtz->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
1129 memset((void *)vtz->uk_prop, 0, sizeof(GEN_ICLINE_S));
1130 vtz->uk_prop->cline = ical_parse_line(&s, ical_buf);
1132 else{
1133 GEN_ICLINE_S *gcl;
1134 for (gcl = vtz->uk_prop; gcl && gcl->next; gcl = gcl->next);
1135 gcl->next = fs_get(sizeof(GEN_ICLINE_S));
1136 memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
1137 gcl->next->cline = ical_parse_line(&s, ical_buf);
1140 } /* end of if(s) */
1143 *text = s;
1144 return (void *) vtz;
1147 void *
1148 ical_parse_timezone(char **text)
1150 char *s, *t;
1151 char c;
1152 ICAL_TZPROP_S *tzprop;
1154 ical_debug("ical_parse_timezone", *text);
1155 tzprop = fs_get(sizeof(ICAL_TZPROP_S));
1156 memset((void *)tzprop, 0, sizeof(ICAL_TZPROP_S));
1158 /* s must always point the the beginning of a line */
1159 for(s = *text; s && *s != '\0';){
1160 t = s;
1161 s = ical_isolate_token(&t, &c);
1162 if(s == NULL){
1163 if(t != NULL)
1164 s = t;
1165 continue;
1167 *t = c; /* restore separator */
1169 if(s){ /* figure out the token */
1170 int ukn = 0; /* unknown token */
1171 if(!struncmp(s, "BEGIN", 5)){
1172 ICAL_S *uk_comp;
1173 s += 6; /* 6 = strlen("BEGIN:") */
1174 uk_comp = ical_parse_unknown_comp(&s, 0);
1175 ical_free_unknown_comp(&uk_comp);
1176 } else if(!struncmp(s, "END", t-s-1)){
1177 s += 4; /* 4 = strlen("END:") */
1178 if(!struncmp(s, "STANDARD\r\n", 10)
1179 || !struncmp(s, "DAYLIGHT\r\n", 10)){
1180 *text = s + 10; /* 10 = strlen("STANDARD\r\n") */
1181 return (void *) tzprop;
1183 } else{ TZ_prop i;
1184 for(i = 0; i < TZPUnknown; i++)
1185 if(!struncmp(s, tz_prop[i].prop, t-s))
1186 break;
1187 if(tz_prop[i].parse){
1188 void *v;
1189 if(tzprop->prop == NULL){
1190 tzprop->prop = fs_get(TZPUnknown*sizeof(void *));
1191 memset((void *)tzprop->prop, 0, TZPUnknown*sizeof(void *));
1193 v = tzprop->prop[tz_prop[i].pos];
1194 v = (tz_prop[i].parse)(v, &s, tz_prop[i].prop);
1195 tzprop->prop[tz_prop[i].pos] = v;
1197 else
1198 ukn++;
1201 if(ukn){
1202 if(ical_buf_len < t - s){
1203 fs_resize((void **)&ical_buf, t-s+1);
1204 ical_buf_len = t-s;
1206 *t = '\0';
1207 strcpy(ical_buf, s);
1208 *t = c;
1209 if(tzprop->uk_prop == NULL){
1210 tzprop->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
1211 memset((void *)tzprop->uk_prop, 0, sizeof(GEN_ICLINE_S));
1212 tzprop->uk_prop->cline = ical_parse_line(&s, ical_buf);
1214 else{
1215 GEN_ICLINE_S *gcl;
1216 for (gcl = tzprop->uk_prop; gcl && gcl->next; gcl = gcl->next);
1217 gcl->next = fs_get(sizeof(GEN_ICLINE_S));
1218 memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
1219 gcl->next->cline = ical_parse_line(&s, ical_buf);
1222 } /* end of if(s) */
1225 *text = s;
1226 return (void *) tzprop;
1229 void *
1230 ical_parse_valarm(char **text)
1232 char *s, *t;
1233 char c;
1234 VALARM_S *valarm;
1236 ical_debug("ical_parse_valarm", *text);
1237 valarm = fs_get(sizeof(VALARM_S));
1238 memset((void *)valarm, 0, sizeof(VALARM_S));
1240 /* s must always point the the beginning of a line */
1241 for(s = *text; s && *s != '\0';){
1242 t = s;
1243 s = ical_isolate_token(&t, &c);
1244 if(s == NULL){
1245 if(t != NULL)
1246 s = t;
1247 continue;
1249 *t = c; /* restore separator */
1251 if(s){ /* figure out the token */
1252 int ukn = 0; /* unknown token */
1253 if(!struncmp(s, "BEGIN", 5)){
1254 ICAL_S *uk_comp;
1255 s += 6; /* 6 = strlen("BEGIN:") */
1256 uk_comp = ical_parse_unknown_comp(&s, 0);
1257 ical_free_unknown_comp(&uk_comp);
1258 } else if(!struncmp(s, "END", t-s-1)){
1259 s += 4; /* 4 = strlen("END:") */
1260 if(!struncmp(s, "VALARM\r\n", 8)){
1261 *text = s + 8; /* 8 = strlen("VALARM\r\n") */
1262 return (void *) valarm;
1264 } else{ Alarm_prop i;
1265 for(i = 0; i < AlUnknown; i++)
1266 if(!struncmp(s, alarm_prop[i].prop, t-s))
1267 break;
1268 if(alarm_prop[i].parse){
1269 void *v;
1270 if(valarm->prop == NULL){
1271 valarm->prop = fs_get((AlUnknown+1)*sizeof(void *));
1272 memset((void *)valarm->prop, 0, (AlUnknown+1)*sizeof(void *));
1274 v = valarm->prop[alarm_prop[i].pos];
1275 v = (alarm_prop[i].parse)(v, &s, alarm_prop[i].prop);
1276 valarm->prop[alarm_prop[i].pos] = v;
1278 else
1279 ukn++;
1282 if(ukn){
1283 if(ical_buf_len < t - s){
1284 fs_resize((void **)&ical_buf, t-s+1);
1285 ical_buf_len = t-s;
1287 *t = '\0';
1288 strcpy(ical_buf, s);
1289 *t = c;
1290 if(valarm->uk_prop == NULL){
1291 valarm->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
1292 memset((void *)valarm->uk_prop, 0, sizeof(GEN_ICLINE_S));
1293 valarm->uk_prop->cline = ical_parse_line(&s, ical_buf);
1295 else{
1296 GEN_ICLINE_S *gcl;
1297 for (gcl = valarm->uk_prop; gcl && gcl->next; gcl = gcl->next);
1298 gcl->next = fs_get(sizeof(GEN_ICLINE_S));
1299 memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
1300 gcl->next->cline = ical_parse_line(&s, ical_buf);
1303 } /* end of if(s) */
1306 *text = s;
1307 return (void *) valarm;
1310 void *
1311 ical_parse_vtodo(char **text)
1313 return NULL;
1316 void *
1317 ical_parse_vjournal(char **text)
1319 return NULL;
1322 void *
1323 ical_parse_vfreebusy(char **text)
1325 return NULL;
1328 ICAL_S *
1329 ical_parse_generic_comp(char **text, int level)
1331 ICAL_S *ical;
1332 char *s, *t;
1333 char *token = NULL;
1334 GEN_ICLINE_S *gcl = NULL;
1335 char c;
1337 ical_debug("ical_parse_generic_comp", *text);
1338 ical = fs_get(sizeof(ICAL_S));
1339 memset((void *)ical, 0, sizeof(ICAL_S));
1341 ical->comp = ical_get_value(text);
1342 token = fs_get(strlen(ical->comp) + 2 + 1);
1343 sprintf(token, "%s\r\n", ical->comp); /* this is allocated memory */
1345 /* s must always point the the beginning of a line */
1346 for(s = *text; s && *s != '\0';){
1347 t = s;
1348 s = ical_isolate_token(&t, &c);
1349 if(s == NULL){
1350 if(t != NULL)
1351 s = t;
1352 continue;
1355 *t = c; /* restore character */
1356 if(s){ /* figure out the token */
1357 int ukn = 0; /* unknown token */
1358 switch(*s){
1359 case 'B':
1360 case 'b': if(!struncmp(s+1, "EGIN", 4)){
1361 s += 6; /* 6 = strlen("BEGIN:") */
1362 if(ical->next == NULL)
1363 ical->next = ical_parse_unknown_comp(&s, level+1);
1364 else{
1365 ICAL_S *b;
1366 int i;
1368 for(i = 0, b = ical; i <= level && b && b->next; b = b->next, i++);
1369 if(b->branch == NULL)
1370 b->branch = ical_parse_unknown_comp(&s, level+1);
1371 else {
1372 for(; b && b->branch; b = b->branch);
1373 b->branch = ical_parse_unknown_comp(&s, level+1);
1376 } else ukn++;
1377 break;
1379 case 'E':
1380 case 'e': if(!struncmp(s+1, "ND", 2)){
1381 *t = c;
1382 s += 4; /* 4 = strlen("END:") */
1383 if(!struncmp(s, token, strlen(token))){
1384 *text = s + strlen(token);
1385 ical->value = (void *) gcl;
1386 return ical;
1388 } else ukn++;
1389 break;
1391 default : ukn++;
1392 break;
1393 } /* end of switch(*s) */
1394 if(ukn){
1395 if(ical_buf_len < t - s){
1396 fs_resize((void **)&ical_buf, t-s+1);
1397 ical_buf_len = t-s;
1399 *t = '\0';
1400 strcpy(ical_buf, s);
1401 *t = c;
1402 if(gcl == NULL){
1403 gcl = fs_get(sizeof(GEN_ICLINE_S));
1404 memset((void *)gcl, 0, sizeof(GEN_ICLINE_S));
1405 gcl->cline = ical_parse_line(&s, ical_buf);
1407 else{
1408 GEN_ICLINE_S *gencl;
1409 for (gencl = gcl; gencl && gencl->next; gencl = gencl->next);
1410 gencl->next = fs_get(sizeof(GEN_ICLINE_S));
1411 memset((void *)gencl->next, 0, sizeof(GEN_ICLINE_S));
1412 gencl->next->cline = ical_parse_line(&s, ical_buf);
1415 } /* end of if(s) */
1418 ical->value = (void *) gcl;
1419 *text = s;
1420 return ical;
1423 ICAL_S *
1424 ical_parse_unknown_comp(char **text, int level)
1426 ICAL_S *ical;
1427 int i;
1429 ical_debug("ical_parse_unknown_comp", *text);
1430 for(i = 0; ical_comp[i].comp
1431 && (struncmp(*text, ical_comp[i].comp, ical_comp[i].len)
1432 || struncmp(*text + ical_comp[i].len, "\r\n", 2)); i++);
1434 if(ical_comp[i].parse){
1435 *text += ical_comp[i].len + 2;
1436 ical = fs_get(sizeof(ICAL_S));
1437 memset((void *)ical, 0, sizeof(ICAL_S));
1438 ical->comp = cpystr(ical_comp[i].comp);
1439 ical->value = (ical_comp[i].parse)(text);
1440 } else
1441 ical = ical_parse_generic_comp(text, level);
1443 return ical;
1446 ICLINE_S *
1447 ical_parse_line(char **text, char *name)
1449 ICLINE_S *ic;
1450 char *s = *text;
1452 ic = fs_get(sizeof(ICLINE_S));
1453 memset((void *)ic, 0, sizeof(ICLINE_S));
1455 ic->token = cpystr(name);
1456 s += strlen(name);
1457 if(*s == ';'){
1458 s++;
1459 ic->param = ical_get_parameter(&s);
1461 if(*s == ':'){
1462 s++;
1463 ic->value = ical_get_value(&s);
1466 *text = s;
1467 return ic;
1470 /***
1471 *** PARSE PROPERTY FUNCTIONS
1472 ***/
1474 void *
1475 ical_parse_freq(void *fvalp, char *text)
1477 Freq_value *fval;
1479 fval = fs_get(sizeof(Freq_value));
1481 *fval = FUnknown;
1483 if(text == NULL) return fvalp;
1485 if(!strucmp(text, "SECONDLY")) *fval = FSecondly;
1486 else if(!strucmp(text, "MINUTELY")) *fval = FMinutely;
1487 else if(!strucmp(text, "HOURLY")) *fval = FHourly;
1488 else if(!strucmp(text, "DAILY")) *fval = FDaily;
1489 else if(!strucmp(text, "WEEKLY")) *fval = FWeekly;
1490 else if(!strucmp(text, "MONTHLY")) *fval = FMonthly;
1491 else if(!strucmp(text, "YEARLY")) *fval = FYearly;
1493 fvalp = (void *) fval;
1495 return fvalp;
1498 void *
1499 ical_parse_until(void *Tmp, char *text)
1501 struct tm *Tm;
1503 if(text != NULL){
1504 Tm = fs_get(sizeof(struct tm));
1505 ical_parse_date(text, Tm);
1506 Tmp = (void *) Tm;
1509 return Tmp;
1512 void *
1513 ical_parse_count(void *countp, char *text)
1515 int *count;
1517 if(text != NULL){
1518 count = fs_get(sizeof(int));
1519 *count = atoi(text);
1520 countp = (void *) count;
1523 return countp;
1526 void *
1527 ical_parse_weekday_list(void *bywkdyp, char *wklist)
1529 BYWKDY_S *bywkdy, *w;
1530 char *s, *t, c;
1531 int done;
1532 size_t len;
1534 bywkdy = NULL;
1535 bywkdyp = (void *) bywkdy;
1537 if(wklist == NULL) return bywkdyp;
1539 done = 0;
1540 for(t = s = wklist; done == 0; s++){
1541 if(*s != ',' && *s != '\0')
1542 continue;
1543 c = *s;
1544 if(c == ',')
1545 *s = '\0';
1546 else /* c == '\0' */
1547 done++;
1548 len = strlen(t);
1549 if(len > 1){
1550 for(w = bywkdy; w && w->next; w = w->next);
1551 w = fs_get(sizeof(BYWKDY_S));
1552 memset((void *)w, 0, sizeof(BYWKDY_S));
1553 if(!strucmp(t+len-2, "SU")) w->wd = Sunday;
1554 else if(!strucmp(t+len-2, "MO")) w->wd = Monday;
1555 else if(!strucmp(t+len-2, "TU")) w->wd = Tuesday;
1556 else if(!strucmp(t+len-2, "WE")) w->wd = Wednesday;
1557 else if(!strucmp(t+len-2, "TH")) w->wd = Thursday;
1558 else if(!strucmp(t+len-2, "FR")) w->wd = Friday;
1559 else if(!strucmp(t+len-2, "SA")) w->wd = Saturday;
1560 // t[len - 2] = '\0';
1561 if(*t != '\0')
1562 w->value = strtoul(t, &t, 10);
1563 if(bywkdy == NULL)
1564 bywkdy = w;
1566 *s = c;
1567 if(*s == ',')
1568 t = s + 1;
1571 if(bywkdyp)
1572 ical_free_weekday_list((void **)&bywkdy);
1573 else
1574 bywkdyp = (void *) bywkdy;
1576 return bywkdyp;
1579 void *
1580 ical_parse_number_list(void *bynop, char *nolist)
1582 BYWKDY_S *byno, *n;
1583 char *s, *t, c;
1584 int done = 0;
1586 byno = NULL;
1587 bynop = (void *) byno;
1589 if(nolist == NULL) return bynop;
1591 for(t = s = nolist; done == 0; s++){
1592 if(*s != ',' && *s != '\0')
1593 continue;
1594 c = *s;
1595 if(c == ',')
1596 *s = '\0';
1597 else /* c == '\0' */
1598 done++;
1600 for(n = byno; n && n->next; n = n->next);
1601 n = fs_get(sizeof(BYWKDY_S));
1602 memset((void *)n, 0, sizeof(BYWKDY_S));
1603 n->value = strtoul(t, &t, 10);
1604 if(byno == NULL)
1605 byno = n;
1606 *s = c;
1607 if(*s == ',')
1608 t = s + 1;
1611 if(bynop)
1612 ical_free_weekday_list((void **)&byno);
1613 else
1614 bynop = (void *) byno;
1616 return bynop;
1619 void *
1620 ical_parse_rrule(void *rrulep, char **text, char *token)
1622 RRULE_S *rrule;
1623 ICLINE_S *icl;
1624 char *s;
1625 ICAL_PARAMETER_S *param, *p;
1626 int i;
1628 if(text == NULL || *text == NULL || struncmp(*text, "RRULE", 5))
1629 return rrulep;
1631 rrule = fs_get(sizeof(RRULE_S));
1632 memset((void *) rrule, 0, sizeof(RRULE_S));
1634 /* recurring rules are special. First, we parse the icline that contains it */
1635 icl = ical_parse_line(text, token);
1637 /* now we copy the parameters that it contains */
1638 rrule->param = ical_parameter_cpy(icl->param);
1640 /* then we parse icl->value as if it was a parameter */
1641 s = icl->value;
1642 param = ical_get_parameter(&s);
1644 /* now we check which values were given, and fill the prop array */
1645 rrule->prop = fs_get((RRUnknown+1)*sizeof(void *));
1646 memset((void *) rrule->prop, 0, (RRUnknown+1)*sizeof(void *));
1648 for(p = param; p != NULL; p = p->next){
1649 for(i = 0; rrule_prop[i].prop != NULL && strucmp(p->name, rrule_prop[i].prop); i++);
1650 if(rrule_prop[i].parse){
1651 void *v = rrule->prop[rrule_prop[i].pos];
1652 v = (rrule_prop[i].parse)(v, p->value);
1653 rrule->prop[rrule_prop[i].pos] = v;
1656 rrule->prop[RRUnknown] = NULL;
1658 ical_free_param(&param);
1659 ical_free_cline((void **)&icl);
1661 if(rrulep)
1662 ical_free_rrule((void **)&rrule);
1663 else
1664 rrulep = (void *) rrule;
1666 return rrulep;
1669 /*** UTF-8 for ICAL ***/
1672 ical_non_ascii_valid(unsigned char c)
1674 static unsigned char icu[6];
1675 static int utf8_len = 0;
1676 static int utf8_type = 0;
1677 int rv;
1679 if(utf8_len == 0)
1680 utf8_type = (c >= 0xF0 && c <= 0xF4)
1681 ? 4 : (c >= 0xE0 && c <= 0xEF)
1682 ? 3 : (c >= 0xC2 && c <= 0xDF)
1683 ? 2 : 0;
1685 if(utf8_type == 0)
1686 return 0;
1688 icu[utf8_len++] = c; /* count it */
1690 if(utf8_type == 2){
1691 if(utf8_len < 2)
1692 rv = NEED_MORE;
1693 else if(utf8_len == 2){
1694 rv = (icu[0] >= 0xC2 && icu[0] <= 0xDF)
1695 && (icu[1] >= 0x80 && icu[1] <= 0xBF) ? UTF8_COMPLETE : 0;
1696 utf8_len = 0;
1698 } else if (utf8_type == 3){
1699 if(utf8_len < 3)
1700 rv = NEED_MORE;
1701 else{
1702 if(icu[0] == 0xE0)
1703 rv = (icu[1] >= 0xA0 && icu[1] <= 0xBF)
1704 && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0;
1705 else if(icu[0] >= 0xE1 && icu[0] <= 0xEC)
1706 rv = (icu[1] >= 0x80 && icu[1] <= 0xBF)
1707 && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0;
1708 else if(icu[0] == 0xED)
1709 rv = (icu[1] >= 0x80 && icu[1] <= 0x9F)
1710 && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0;
1711 else if(icu[0] >= 0xE1 && icu[0] <= 0xEC)
1712 rv = (icu[1] >= 0x80 && icu[1] <= 0xBF)
1713 && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0;
1714 utf8_len = 0;
1716 } else if (utf8_type == 4){
1717 if(utf8_len < 4)
1718 rv = NEED_MORE;
1719 else{
1720 if(icu[0] == 0xF0)
1721 rv = (icu[1] >= 0x90 && icu[1] <= 0xBF)
1722 && (icu[2] >= 0x80 && icu[2] <= 0xBF)
1723 && (icu[3] >= 0x80 && icu[3] <= 0xBF) ? UTF8_COMPLETE : 0;
1724 else if(icu[0] >= 0xF1 && icu[0] <= 0xF3)
1725 rv = (icu[1] >= 0x80 && icu[1] <= 0xBF)
1726 && (icu[2] >= 0x80 && icu[2] <= 0xBF)
1727 && (icu[3] >= 0x80 && icu[3] <= 0xBF) ? UTF8_COMPLETE : 0;
1728 else if(icu[0] == 0xF4)
1729 rv = (icu[1] >= 0x80 && icu[1] <= 0x8F)
1730 && (icu[2] >= 0x80 && icu[2] <= 0xBF)
1731 && (icu[3] >= 0x80 && icu[3] <= 0xBF) ? UTF8_COMPLETE : 0;
1732 utf8_len = 0;
1735 return rv;
1739 ical_get_number_value(char *value, int beg_pos, int end_pos)
1741 char c, *err;
1742 int rv;
1744 c = value[end_pos];
1745 value[end_pos] = '\0';
1746 rv = strtoul(value + beg_pos, &err, 10);
1747 if(err != NULL && *err != '\0') return -1;
1748 value[end_pos] = c;
1749 return rv;
1752 void
1753 ical_free_duration(ICAL_DURATION_S **ic_d)
1755 if(ic_d == NULL || *ic_d == NULL)
1756 return;
1758 if((*ic_d)->next) ical_free_duration(&(*ic_d)->next);
1759 fs_give((void **)ic_d);
1762 /* returns 0 if no error, -1 if some error */
1764 ical_parse_duration(char *value, ICAL_DURATION_S *ic_d)
1766 int i, j, rv = 0;
1768 if(value == NULL || ic_d == NULL) return -1;
1770 memset((void *)ic_d, 0, sizeof(ICAL_DURATION_S));
1772 if(value[i = 0] == '-'){
1773 i++;
1774 ic_d->sign = 1;
1775 } else if(value[i] == '+')
1776 i++;
1778 if(value[i++] == 'P'){
1779 for(j = i; value[j] != '\0' && value[j] != ','; j++){
1780 if(!isdigit(value[j]))
1781 switch(value[j]){
1782 case 'W': ic_d->weeks = ical_get_number_value(value, i, j-1);
1783 i = ++j;
1784 break;
1785 case 'D': ic_d->days = ical_get_number_value(value, i, j-1);
1786 i = ++j;
1787 break;
1788 case 'H': ic_d->hours = ical_get_number_value(value, i, j-1);
1789 i = ++j;
1790 break;
1791 case 'M': ic_d->minutes = ical_get_number_value(value, i, j-1);
1792 i = ++j;
1793 break;
1794 case 'S': ic_d->seconds = ical_get_number_value(value, i, j-1);
1795 i = ++j;
1796 break;
1797 case 'T': i = j + 1;
1798 break;
1799 default: rv = -1;
1800 break;
1804 else
1805 rv = -1;
1807 if(value[j++] == ','){
1808 ICAL_DURATION_S next;
1809 rv = ical_parse_duration(value+j, &next);
1810 ic_d->next = &next;
1813 return rv;
1816 /* return -1 if any error,
1817 0 if value has the DATE-TIME form
1818 1 if value has the DATE form only
1819 2 if value has the DATE-TIME form and is in GMT.
1822 ical_parse_date(char *value, struct tm *t)
1824 int i, rv;
1825 struct tm Tm;
1827 rv = -1;
1828 if(t == NULL) return rv;
1829 memset((void *)&Tm, 0, sizeof(struct tm));
1831 if(value == NULL) return rv;
1833 rv = 0; /* assume DATE-TIME format */
1834 /* a simple check for the format of the string */
1835 for(i = 0; isdigit(value[i]); i++);
1836 if (i == 8 && value[i] == '\0')
1837 rv = 1;
1838 else
1839 if (i != 8 || value[i++] != 'T') return -1;
1840 if(rv == 0) {
1841 for(; isdigit(value[i]); i++);
1842 if(i != 15 || (value[i] != '\0' && (value[i] != 'Z' || value[i+1] != '\0')))
1843 return -1;
1844 if(i == 15 && value[i] == 'Z')
1845 rv = 2;
1848 Tm.tm_year = ical_get_number_value(value, 0, 4) - 1900;
1849 Tm.tm_mon = ical_get_number_value(value, 4, 6) - 1;
1850 Tm.tm_mday = ical_get_number_value(value, 6, 8);
1851 if(rv != 1){
1852 Tm.tm_hour = ical_get_number_value(value, 9, 11);
1853 Tm.tm_min = ical_get_number_value(value, 11, 13);
1854 Tm.tm_sec = ical_get_number_value(value, 13, 15);
1855 Tm.tm_isdst = ICAL_DST_UNKNOWN;
1857 else
1858 Tm.tm_isdst = -1;
1859 *t = Tm;
1861 return (t->tm_mon > 11 || t->tm_mon < 0
1862 || t->tm_mday > 31 || t->tm_mday < 0
1863 || t->tm_hour > 23 || t->tm_hour < 0
1864 || t->tm_min > 59 || t->tm_min < 0
1865 || t->tm_sec > 60 || t->tm_sec < 0)
1866 ? - 1 : rv;
1869 void
1870 ical_set_date(ICLINE_S *icl, VTIMEZONE_S *vtz)
1872 int date_form; /* date forms from section 3.3.4 in RFC 5545 */
1873 ICAL_PARAMETER_S *param;
1874 char *tz = NULL;
1875 struct tm ic_date;
1876 time_t t;
1878 if(icl == NULL) return;
1880 for(param = icl->param; param != NULL; param = param->next)
1881 if(!strucmp(param->name, "TZID"))
1882 tz = param->value;
1884 if(tz != NULL)
1885 date_form = 3; /* local time with timezone */
1886 else if(icl->value[strlen(icl->value)-1] == 'Z')
1887 date_form = 2; /* utc time */
1888 else date_form = 1; /* local time */
1890 ical_parse_date(icl->value, &ic_date);
1891 ic_date.tm_wday = ical_day_of_week(ic_date); /* find out day of the week */
1893 switch(date_form){
1894 case 1: break;
1895 case 2: ical_adjust_date(&ic_date, vtz);
1896 break;
1897 case 3: break;
1898 default: alpine_panic ("Impossible date_form");
1902 ICAL_TZPROP_S *
1903 ical_std_or_daylight(struct tm *date, VTIMEZONE_S *vtz)
1905 struct tm standard, daylight;
1906 ICLINE_S *tzid = (ICLINE_S *) vtz->prop[TZCid];
1908 // standard = daylight;
1910 return NULL;
1915 /* adjusts time to given time zone */
1916 void
1917 ical_adjust_date(struct tm *date, VTIMEZONE_S *vtz)
1919 char *tzname = NULL;
1920 ICLINE_S *icl;
1921 ICAL_TZPROP_S *cur_std_day;
1923 if(vtz == NULL)
1924 return;
1926 if(vtz->prop){
1927 if((icl = (ICLINE_S *)vtz->prop[TZCid]) != NULL)
1928 tzname = cpystr(icl->value);
1931 cur_std_day = ical_std_or_daylight(date, vtz);
1934 void
1935 ical_set_date_vevent(void *veventv, void *vtzv)
1937 VEVENT_S *vevent = (VEVENT_S *) veventv;
1938 VTIMEZONE_S *vtz = (VTIMEZONE_S *) vtzv;
1940 if(vevent){
1941 ical_set_date(vevent->prop[EvDtstamp], vtz);
1942 ical_set_date(vevent->prop[EvDtstart], vtz);
1943 ical_set_date(vevent->prop[EvDtend], vtz);
1947 #define LEAP_YEAR(X) ((((X) % 4 == 0) \
1948 && (((X) % 100 != 0) || ((X) % 400 == 0))) \
1949 || (X) == 1700)
1951 #define CAL_OFFSET(X) (((X) == 1752) ? 5 : (LEAP_YEAR((X)) ? 2 : 1))
1953 /* given a year, after day_zero, return the day
1954 * of the week of the first of january of that year. On error,
1955 * return a negative number.
1956 * Assumption: day_zero is the date of january 1, of some year.
1959 ical_january_first(int year)
1961 int i, january_first;
1963 if(year < day_zero.tm_year) return -1; /* not supported */
1965 year += 1900;
1966 january_first = day_zero.tm_wday;
1967 for(i = 1900 + day_zero.tm_year + 1; i <= year; i++)
1968 january_first += CAL_OFFSET(i-1);
1970 return january_first % 7;
1973 /* given a month, week day, and year, return all days of the month
1974 * that have that day as the week day. For example, return all
1975 * sundays in november 2012.
1977 int *
1978 ical_day_from_week(int month, Weekday day, int year)
1980 int *rv = NULL;
1981 int fday, nday;
1982 Weekday wday;
1983 int i;
1985 fday = ical_first_of_month(month, year);
1986 year += 1900; /* restore year */
1987 if(year == 1752 && month == 8){
1988 fday = 9;
1989 } else {
1990 for(nday = 1, wday = (Weekday) fday; wday != day; wday = (wday+1) % 7, nday++)
1992 rv = fs_get(6*sizeof(int));
1993 memset((void *) rv, 0, 6*sizeof(int));
1994 for(i = 0; nday <= month_len[month]; i++){
1995 rv[i] = nday;
1996 nday += 7;
1998 if(LEAP_YEAR(year) && month == 1 && nday == 29)
1999 rv[i] = nday;
2002 return rv;
2006 /* given a month and a year, return the weekday of the first of the
2007 * month in that year.
2008 * return value: on error -1, otherwise the day of the week.
2011 ical_first_of_month(int month, int year)
2013 int i, d;
2015 if((d = ical_january_first(year)) < 0)
2016 return -1;
2018 year += 1900;
2019 for(i = 0; i < month; i++)
2020 d += month_len[i];
2022 if(LEAP_YEAR(year) && month >= 2)
2023 d += 1;
2025 if(year == 1752 && month >= 9)
2026 d -= 11;
2028 return d % 7;
2031 /* given a day, month and year, return the weekday of that day
2032 * return value: on error -1, otherwise the day of the week.
2035 ical_day_of_week(struct tm date)
2037 int d;
2039 if((d = ical_first_of_month(date.tm_mon, date.tm_year)) < 0)
2040 return -1;
2042 d += date.tm_mday - 1;
2044 if(date.tm_year + 1900 == 1752){
2045 if(date.tm_mday > 2 && date.tm_mday < 14)
2046 return -1;
2047 if(date.tm_mday >= 14)
2048 d -= 11;
2050 return d % 7;
2054 /* given an initial date dtstart, and a recurring rule, rrule,
2055 * adjust the date to the first date on the same year, when
2056 * the rule actually starts
2058 struct tm
2059 adjust_date_rrule(struct tm *dtstart, RRULE_S *rrule)
2061 struct tm t;
2063 memset((void *) &t, 0, sizeof(struct tm));
2064 t.tm_year = dtstart->tm_year; /* same year */
2065 if(rrule->prop[RRFreq]){
2067 if(rrule->prop[RRCount]){
2069 else if(rrule->prop[RRInterval]){
2071 if(rrule->prop[RRBysecond]){
2072 BYWKDY_S *sec = (BYWKDY_S *) rrule->prop[RRBysecond], *seco;
2073 for (seco = sec; seco != NULL; seco = seco->next)
2074 if(seco == sec) t.tm_sec = seco->value;
2075 else if (seco->value < t.tm_sec)
2076 t.tm_sec = seco->value;
2078 if (rrule->prop[RRByminute]){
2079 BYWKDY_S *sec = (BYWKDY_S *) rrule->prop[RRByminute], *seco;
2080 for (seco = sec; seco != NULL; seco = seco->next)
2081 if(seco == sec) t.tm_min = seco->value;
2082 else if (seco->value < t.tm_sec)
2083 t.tm_min = seco->value;
2085 if (rrule->prop[RRByhour]){
2086 BYWKDY_S *sec = (BYWKDY_S *) rrule->prop[RRByhour], *seco;
2087 for (seco = sec; seco != NULL; seco = seco->next)
2088 if(seco == sec) t.tm_hour = seco->value;
2089 else if (seco->value < t.tm_sec)
2090 t.tm_hour = seco->value;
2092 if (rrule->prop[RRByday]){
2094 if (rrule->prop[RRByweekno]){
2096 if (rrule->prop[RRBymonthday]){
2098 if (rrule->prop[RRByyearday]){
2100 if (rrule->prop[RRByweekno]){
2102 if (rrule->prop[RRBymonth]){
2103 BYWKDY_S *m = (BYWKDY_S *) rrule->prop[RRBymonth], *mo;
2104 for (mo = m; mo != NULL; mo = mo->next)
2105 if(mo == m) t.tm_mon = mo->value - 1;
2106 else if (mo->value - 1 < t.tm_mon)
2107 t.tm_mon = mo->value - 1;
2109 if (rrule->prop[RRBysetpos]){
2111 if (rrule->prop[RRWkst]){
2113 return t;
2116 void
2117 ical_initialize(void)
2119 static int inited = 0;
2121 if(inited != 0)
2122 return;
2124 ical_buf_len = 1024;
2125 ical_buf = fs_get(ical_buf_len+1);
2127 memset((void *) &day_zero, 0, sizeof(struct tm));
2128 day_zero.tm_year = 1601 - 1900;
2129 day_zero.tm_mday = 1;
2130 day_zero.tm_wday = 4;
2132 inited++;
2135 /* At this time, we are going to print the date in 24 hour format
2136 * if there is no string for AM or PM, but we use AM or PM when available.
2137 * We plan to make this user configurable, but not today...
2139 void
2140 ical_date_time (char *tmp, size_t len, struct tm *ic_datep)
2142 /* test of the AM/PM string is available */
2143 our_strftime(tmp, len, "%p", ic_datep);
2145 if(tmp[0])
2146 our_strftime(tmp, len, "%a %x %I:%M %p", ic_datep);
2147 else
2148 our_strftime(tmp, len, "%a %x %H:%M", ic_datep);
2151 /* If the icline has a TZID parameter, return its value, otherwise, return
2152 * NULL. Returned value freed by caller.
2154 char *
2155 ical_get_tzid(ICAL_PARAMETER_S *param)
2157 char *tzid = NULL;
2159 if(param == NULL)
2160 return tzid;
2162 if(strucmp(param->name, "TZID") == 0)
2163 tzid = cpystr(param->value);
2164 else
2165 tzid = ical_get_tzid(param->next);
2167 return tzid;
2170 /* we create a summary of the event, and pass that back as
2171 an ical parameter
2173 VEVENT_SUMMARY_S *
2174 ical_vevent_summary(VCALENDAR_S *vcal)
2176 VEVENT_SUMMARY_S *rv, *vsummary= NULL;
2177 ICLINE_S *method;
2178 VEVENT_S *vevent;
2179 GEN_ICLINE_S *gicl;
2180 ICLINE_S *icl;
2181 char *k;
2183 if(vcal == NULL) return NULL;
2185 method = vcal->method;
2187 vevent = (VEVENT_S *) vcal->comp[VEvent];
2188 if(vevent == NULL || vevent->prop == NULL)
2189 return NULL;
2191 for(vevent = (VEVENT_S *) vcal->comp[VEvent];
2192 vevent != NULL && vevent->prop != NULL;
2193 vevent = vevent->next, rv = rv->next){
2195 rv = fs_get(sizeof(VEVENT_SUMMARY_S));
2196 memset((void *) rv, 0, sizeof(VEVENT_SUMMARY_S));
2198 if(method != NULL && !strucmp(method->value, "CANCEL"))
2199 rv->cancel++;
2201 if((icl = (ICLINE_S *) vevent->prop[EvPriority]) != NULL)
2202 rv->priority = atoi(icl->value);
2204 if((icl = (ICLINE_S *) vevent->prop[EvSummary]) != NULL)
2205 rv->summary = cpystr(icl->value ? icl->value : _("No Summary"));
2207 if((icl = (ICLINE_S *) vevent->prop[EvClass]) != NULL)
2208 rv->class = cpystr(icl->value ? icl->value : _("PUBLIC"));
2209 else
2210 rv->class = cpystr(_("PUBLIC"));
2212 if((icl = (ICLINE_S *) vevent->prop[EvOrganizer]) != NULL){
2213 char *cn, *sender, *address;
2214 ICAL_PARAMETER_S *param;
2216 cn = sender = address = NULL;
2217 for(param = icl->param; param != NULL; param = param->next)
2218 if(!strucmp(param->name, "CN"))
2219 cn = param->value;
2220 else if(!strucmp(param->name, "SENT-BY"))
2221 sender = param->value;
2223 if(sender != NULL){
2224 if(!struncmp(sender, "MAILTO:", 7))
2225 sender += 7;
2226 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "<%s>", sender);
2227 rv->sender = cpystr(tmp_20k_buf);
2230 if((address = icl->value) != NULL){
2231 if(!struncmp(address, "MAILTO:", 7))
2232 address += 7;
2233 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s<%s>",
2234 cn ? cn : "", cn ? " " : "",
2235 address ? address : _("Unknown address"));
2236 rv->organizer = cpystr(tmp_20k_buf);
2238 } /* end of if(organizer) */
2240 if((icl = (ICLINE_S *) vevent->prop[EvLocation]) != NULL)
2241 rv->location = cpystr(icl->value ? icl->value : _("Location undisclosed"));
2243 if((icl = (ICLINE_S *) vevent->prop[EvDtstart]) != NULL){
2244 struct tm ic_date;
2245 char tmp[200], *tzid;
2246 int icd; /* ical date return value */
2248 memset((void *)&ic_date, 0, sizeof(struct tm));
2249 icd = ical_parse_date(icl->value, &ic_date);
2250 tzid = ical_get_tzid(icl->param);
2251 if(icd >= 0){
2252 ic_date.tm_wday = ical_day_of_week(ic_date);
2253 switch(icd){
2254 case 0: /* DATE-TIME */
2255 ical_date_time(tmp, sizeof(tmp), &ic_date);
2256 break;
2257 case 1: /* DATE */
2258 our_strftime(tmp, sizeof(tmp), "%a %x", &ic_date);
2259 break;
2260 case 2: /* DATE-TIME in GMT, Bug: add adjust to time zone */
2261 our_strftime(tmp, sizeof(tmp), "%a %x %I:%M %p", &ic_date);
2262 break;
2263 default: alpine_panic("Unhandled ical date format");
2264 break;
2267 else{
2268 strncpy(tmp, _("Error while parsing event date"), sizeof(tmp));
2269 tmp[sizeof(tmp) - 1] = '\0';
2272 if(icl->value == NULL)
2273 rv->evstart = cpystr(_("Unknown Start Date"));
2274 else{
2275 size_t len = strlen(tmp) + 1;
2277 if(tzid != NULL)
2278 len += strlen(tzid) + 3; /* 3 = strlen(" ()") */
2280 rv->evstart = fs_get(len*sizeof(char));
2281 snprintf(rv->evstart, len, "%s%s%s%s", tmp,
2282 tzid != NULL ? " (" : "",
2283 tzid != NULL ? tzid : "",
2284 tzid != NULL ? ")" : "");
2285 rv->evstart[len-1] = '\0';
2287 if(tzid)
2288 fs_give((void **)&tzid);
2289 } /* end of if dtstart */
2291 if((icl = (ICLINE_S *) vevent->prop[EvDuration]) != NULL){
2292 int i, done = 0;
2293 ICAL_DURATION_S ic_d, icd2;
2294 if(ical_parse_duration(icl->value, &ic_d) == 0){
2295 char tmp[MAILTMPLEN+1];
2297 for(i = 1, icd2 = ic_d; icd2.next != NULL; icd2 = *icd2.next, i++);
2298 rv->duration = fs_get((i+1)*sizeof(char *));
2299 i = 0;
2301 do {
2302 tmp[0] = '\0';
2304 if(ic_d.weeks > 0)
2305 utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
2306 "%d %s ", ic_d.weeks, ic_d.weeks == 1 ? _("week") : _("weeks"));
2307 if(ic_d.days > 0)
2308 utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
2309 "%d %s ", ic_d.days, ic_d.days == 1 ? _("day") : _("days"));
2310 if(ic_d.hours > 0)
2311 utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
2312 "%d %s ", ic_d.hours, ic_d.hours == 1 ? _("hour") : _("hours"));
2313 if(ic_d.minutes > 0)
2314 utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
2315 "%d %s ", ic_d.minutes, ic_d.minutes == 1 ? _("minute") : _("minutes"));
2316 if(ic_d.seconds > 0)
2317 utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
2318 "%d %s ", ic_d.seconds, ic_d.seconds == 1 ? _("second") : _("seconds"));
2320 tmp[MAILTMPLEN] = '\0';
2321 rv->duration[i++] = cpystr(tmp);
2323 if(ic_d.next != NULL)
2324 ic_d = *ic_d.next;
2325 else
2326 done++;
2327 } while (done == 0);
2328 rv->duration[i] = NULL;
2330 } /* end of DURATION */
2331 else if((icl = (ICLINE_S *) vevent->prop[EvDtend]) != NULL){
2332 struct tm ic_date;
2333 char tmp[200], *tzid;
2334 int icd;
2336 memset((void *)&ic_date, 0, sizeof(struct tm));
2337 icd = ical_parse_date(icl->value, &ic_date);
2338 tzid = ical_get_tzid(icl->param);
2339 if(icd >= 0){
2340 ic_date.tm_wday = ical_day_of_week(ic_date);
2341 switch(icd){
2342 case 0: /* DATE-TIME */
2343 ical_date_time(tmp, sizeof(tmp), &ic_date);
2344 break;
2345 case 1: /* DATE */
2346 our_strftime(tmp, sizeof(tmp), "%a %x", &ic_date);
2347 break;
2348 case 2: /* DATE-TIME in GMT, Bug: add adjust to time zone */
2349 our_strftime(tmp, sizeof(tmp), "%a %x %I:%M %p", &ic_date);
2350 break;
2351 default: alpine_panic("Unhandled ical date format");
2352 break;
2355 else{
2356 strncpy(tmp, _("Error while parsing event date"), sizeof(tmp));
2357 tmp[sizeof(tmp) - 1] = '\0';
2360 if(icl->value == NULL)
2361 rv->evend = cpystr(_("Unknown End Date"));
2362 else{
2363 size_t len = strlen(tmp) + 1;
2365 if(tzid != NULL)
2366 len += strlen(tzid) + 3; /* 3 = strlen(" ()") */
2368 rv->evend = fs_get(len*sizeof(char));
2369 snprintf(rv->evend, len, "%s%s%s%s", tmp,
2370 tzid != NULL ? " (" : "",
2371 tzid != NULL ? tzid : "",
2372 tzid != NULL ? ")" : "");
2373 rv->evend[len-1] = '\0';
2375 if(tzid)
2376 fs_give((void **)&tzid);
2377 } /* end of if dtend */
2379 if((icl = (ICLINE_S *) vevent->prop[EvDtstamp]) != NULL){
2380 struct tm ic_date;
2381 char tmp[200], *tzid;
2382 int icd;
2384 memset((void *)&ic_date, 0, sizeof(struct tm));
2385 icd = ical_parse_date(icl->value, &ic_date);
2386 tzid = ical_get_tzid(icl->param);
2387 if(icd >= 0){
2388 ic_date.tm_wday = ical_day_of_week(ic_date);
2389 switch(icd){
2390 case 0: /* DATE-TIME */
2391 ical_date_time(tmp, sizeof(tmp), &ic_date);
2392 break;
2393 case 1: /* DATE */
2394 our_strftime(tmp, sizeof(tmp), "%a %x", &ic_date);
2395 break;
2396 case 2: /* DATE-TIME in GMT, Bug: add adjust to time zone */
2397 our_strftime(tmp, sizeof(tmp), "%a %x %I:%M %p", &ic_date);
2398 break;
2399 default: alpine_panic("Unhandled ical date format");
2400 break;
2403 else{
2404 strncpy(tmp, _("Error while parsing event date"), sizeof(tmp));
2405 tmp[sizeof(tmp) - 1] = '\0';
2407 if(icl->value == NULL)
2408 rv->dtstamp = cpystr(_("Unknown when event was scheduled"));
2409 else{
2410 size_t len = strlen(tmp) + 1;
2412 if(tzid != NULL)
2413 len += strlen(tzid) + 3; /* 3 = strlen(" ()") */
2415 rv->dtstamp = fs_get(len*sizeof(char));
2416 snprintf(rv->dtstamp, len, "%s%s%s%s", tmp,
2417 tzid != NULL ? " (" : "",
2418 tzid != NULL ? tzid : "",
2419 tzid != NULL ? ")" : "");
2420 rv->dtstamp[len-1] = '\0';
2422 } /* end of if dtstamp */
2424 if((gicl = (GEN_ICLINE_S *) vevent->prop[EvAttendee]) != NULL){
2425 int nattendees, i;
2427 for(nattendees = 0; gicl != NULL; gicl = gicl->next, nattendees++);
2428 rv->attendee = fs_get((nattendees+1)*sizeof(char *));
2430 gicl = (GEN_ICLINE_S *) vevent->prop[EvAttendee];
2431 for(i = 0; gicl != NULL; gicl = gicl->next, i++){
2432 char *role, *partstat, *rsvp;
2433 char *cn, *mailto;
2434 ICAL_PARAMETER_S *param;
2436 icl = gicl->cline;
2437 role = partstat = rsvp = cn = mailto = NULL;
2438 for(param = icl->param; param != NULL; param = param->next){
2439 if(!strucmp(param->name, "ROLE")){
2440 if(!strucmp(param->value, "REQ-PARTICIPANT"))
2441 role = _("[Required]");
2442 else if(!strucmp(param->value, "OPT-PARTICIPANT"))
2443 role = _("[Optional]");
2444 else if(!strucmp(param->value, "NON-PARTICIPANT"))
2445 role = _("[Informed]");
2446 else if(!strucmp(param->value, "CHAIR"))
2447 role = _("[ Chair ]");
2448 else
2449 role = param->value;
2451 else if(!strucmp(param->name, "PARTSTAT")){
2452 if(!strucmp(param->value, "NEEDS-ACTION"))
2453 partstat = _("[Need-Reply]");
2454 else if(!strucmp(param->value, "ACCEPTED"))
2455 partstat = _("[ Accepted ]");
2456 else if(!strucmp(param->value, "DECLINED"))
2457 partstat = _("[ Declined ]");
2458 else if(!strucmp(param->value, "TENTATIVE"))
2459 partstat = _("[ Tentative]");
2460 else if(!strucmp(param->value, "DELEGATED"))
2461 partstat = _("[ Delegated]");
2462 else
2463 partstat = param->value;
2465 else if(!strucmp(param->name, "RSVP"))
2466 rsvp = param->value;
2467 else if(!strucmp(param->name, "CN"))
2468 cn = param->value;
2470 if(icl->value && !struncmp(icl->value, "MAILTO:", strlen("MAILTO:")))
2471 mailto = icl->value + 7; /* 7 = strlen("MAILTO:") */
2472 if(!strucmp(cn, mailto))
2473 cn = "";
2474 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s%s%s%s<%s>",
2475 role && *role ? role : "",
2476 role && *role ? " " : "",
2477 partstat ? partstat : _("[Unknown Reply]"),
2478 " ",
2479 cn && *cn ? cn : "",
2480 cn && *cn ? " " : "",
2481 mailto ? mailto : _("Unknown address"));
2482 rv->attendee[i] = cpystr(tmp_20k_buf);
2484 rv->attendee[i] = NULL;
2485 } /* end of ATTENDEES */
2487 if((icl = (ICLINE_S *) vevent->prop[EvDescription]) != NULL){
2488 char *s, *t, *u, *v;
2489 int i, escaped;
2491 if(icl->value == NULL){
2492 free_vevent_summary(&rv);
2493 return vsummary;
2496 v = cpystr(icl->value); /* process a copy of icl->value */
2498 for(i = 1, escaped = 0, s = v; s && *s; s++){
2499 if(*s == '\\' && escaped == 0){ escaped = 1; continue; }
2500 if(escaped){
2501 if(!(*s == '\\' || *s == ',' || *s == 'n' || *s == 'N' || *s == ';')){
2502 free_vevent_summary(&rv);
2503 fs_give((void **)&v);
2504 return vsummary;
2506 escaped = 0;
2507 continue;
2509 if(*s == ',') i++; /* a non-scaped comma is a new value for text */
2512 rv->description = fs_get((i+1)*sizeof(unsigned char *));
2513 i = 0;
2514 for (s = t = u = v, escaped = 0; *t != '\0'; t++){
2515 if(*t == '\\' && escaped == 0){ escaped = 1; continue; }
2516 if(escaped){
2517 switch(*t){
2518 case '\\':
2519 case ',':
2520 case ';':
2521 *u++ = *t;
2522 break;
2523 case 'n':
2524 case 'N':
2525 *u++ = '\n';
2526 break;
2527 default: free_vevent_summary(&rv);
2528 fs_give((void **)&v);
2529 return NULL;
2531 escaped = 0;
2532 continue;
2534 if(*t == ','){
2535 *u = '\0';
2536 rv->description[i++] = cpystr(ical_decode(s, vcal->encoding));
2537 s = u = t+1;
2538 } else
2539 *u++ = *t;
2541 *u = '\0';
2542 rv->description[i++] = cpystr(ical_decode(s, vcal->encoding));
2543 rv->description[i] = NULL;
2544 fs_give((void **)&v);
2545 } /* end of if(description) */
2546 /* last instruction of the loop */
2547 if(vsummary == NULL)
2548 vsummary = rv;
2549 else{
2550 VEVENT_SUMMARY_S *vesy;
2551 for(vesy = vsummary; vesy && vesy->next; vesy = vesy->next);
2552 vesy->next = rv;
2554 } /* end of "for" loop */
2555 return vsummary;
2558 void
2559 free_vevent_summary(VEVENT_SUMMARY_S **vesy)
2561 int i;
2562 if(vesy == NULL || *vesy == NULL) return;
2564 if((*vesy)->class) fs_give((void **)&(*vesy)->class);
2565 if((*vesy)->summary) fs_give((void **)&(*vesy)->summary);
2566 if((*vesy)->sender) fs_give((void **)&(*vesy)->sender);
2567 if((*vesy)->organizer) fs_give((void **)&(*vesy)->organizer);
2568 if((*vesy)->location) fs_give((void **)&(*vesy)->location);
2569 if((*vesy)->evstart) fs_give((void **)&(*vesy)->evstart);
2570 if((*vesy)->evend) fs_give((void **)&(*vesy)->evend);
2571 if((*vesy)->dtstamp) fs_give((void **)&(*vesy)->dtstamp);
2572 if((*vesy)->duration){
2573 for(i = 0; (*vesy)->duration[i] != NULL; i++)
2574 fs_give((void **) &(*vesy)->duration[i]);
2575 fs_give((void **) (*vesy)->duration);
2577 if((*vesy)->attendee){
2578 for(i = 0; (*vesy)->attendee[i] != NULL; i++)
2579 fs_give((void **) &(*vesy)->attendee[i]);
2580 fs_give((void **) &(*vesy)->attendee);
2582 if((*vesy)->description){
2583 for(i = 0; (*vesy)->description[i] != NULL; i++)
2584 fs_give((void **) &(*vesy)->description[i]);
2585 fs_give((void **) &(*vesy)->description);
2587 if((*vesy)->next) free_vevent_summary(&(*vesy)->next);
2588 fs_give((void **) vesy);
2591 void
2592 ical_free_all(void)
2594 if(ical_buf)
2595 fs_give((void **)&ical_buf);