Add support for tab-completion when selecting by rule
[alpine.git] / pith / ical.c
blob64817e640aa428b1f248eea414b681460b370fbd
1 #include "../pith/headers.h"
2 #include "../pith/mailpart.h"
3 #include "../pith/store.h"
4 #include "../pith/string.h"
5 #include "../pith/ical.h"
7 typedef struct ical_iana_comp_s {
8 char *comp; /* component name */
9 size_t len; /* size of component name (strlen(x->comp)) */
10 int pos; /* position of this component in comp array */
11 void *(*parse)(char **); /* parser */
12 void (*give)(void **); /* free memory */
13 } ICAL_COMP_S;
15 typedef struct ical_iana_prop_s {
16 char *prop; /* component PROPerty name */
17 size_t len; /* size of component name (strlen(x->prop)) */
18 int pos; /* location of this component in the prop array */
19 void *(*parse)(); /* parser */
20 void (*give)(void **); /* free memory */
21 } ICAL_PROP_S;
23 int ical_january_first(int); /* args: year */
24 void ical_adjust_date(struct tm *, VTIMEZONE_S *);
26 void ical_initialize(void);
28 int ical_non_ascii_valid(unsigned char);
29 char *ical_unfold_line(char *);
30 ICLINE_S *ical_parse_line(char **, char *);
32 ICLINE_S *ical_cline_cpy(ICLINE_S *);
33 ICAL_PARAMETER_S *ical_parameter_cpy(ICAL_PARAMETER_S *param);
35 char *ical_get_value(char **);
36 unsigned char *ical_decode(char *, unsigned short);
38 /* parse component */
39 void *ical_parse_vcalendar(char **);
40 void *ical_parse_vevent(char **);
41 void *ical_parse_vtodo(char **);
42 void *ical_parse_vjournal(char **);
43 void *ical_parse_vfreebusy(char **);
44 void *ical_parse_vtimezone(char **);
45 void *ical_parse_valarm(char **);
46 void *ical_parse_timezone(char **);
47 ICAL_S *ical_parse_unknown_comp(char **, int);
48 ICAL_S *ical_parse_generic_comp(char **, int);
50 /* free components */
51 void ical_free_vevent(void **);
52 void ical_free_vtodo(void **);
53 void ical_free_vjournal(void **);
54 void ical_free_vfreebusy(void **);
55 void ical_free_vtimezone(void **);
56 void ical_free_timezone(void **);
57 void ical_free_valarm(void **);
58 void ical_free_unknown_comp(ICAL_S **);
60 /* parse properties */
61 void *ical_cline_from_token(void *, char **, char *);
62 void *ical_gencline_from_token(void *, char **, char *);
64 void *ical_parse_rrule(void *, char **, char *);
65 void *ical_parse_time(void *, char **, char *);
66 void *ical_parse_offset(void *, char **, char *);
68 void *ical_parse_freq(void *, char *);
69 void *ical_parse_until(void *, char *);
70 void *ical_parse_count(void *, char *);
71 void *ical_parse_interval(void *, char *);
72 void *ical_parse_weekday_list(void *, char *);
73 void *ical_parse_number_list(void *, char *);
75 int ical_get_number_value(char *, int, int);
76 void ical_set_date(ICLINE_S *, VTIMEZONE_S *);
77 void ical_set_date_vevent(void *, void *);
79 /* free properties */
80 void ical_free_prop(void ***, ICAL_PROP_S *, int);
81 void ical_free_cline(void **);
82 void ical_free_param(ICAL_PARAMETER_S **);
83 void ical_free_gencline(void **);
84 void ical_free_rrule(void **);
85 void ical_fs_give(void **);
86 void ical_free_weekday_list(void **);
88 /* utility functions */
89 void ical_date_time (char *, size_t, struct tm *);
90 char *ical_get_tzid(ICAL_PARAMETER_S *);
92 /* globals */
93 struct tm day_zero; /* date for january 1, 1601 */
94 int month_len[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
96 #define UTF8_COMPLETE (1)
97 #define NEED_MORE (2)
99 char *ical_buf;
100 unsigned long ical_buf_len;
103 /* parsing structures */
105 /* this is the list of V-components to a Calendar from RFC 5545 */
106 ICAL_COMP_S ical_comp[] = {
107 {"VCALENDAR", 9, VCalendar, ical_parse_vcalendar, ical_free_vcalendar},
108 {"VTIMEZONE", 9, VTimeZone, ical_parse_vtimezone, ical_free_vtimezone},
109 {"VEVENT", 6, VEvent, ical_parse_vevent, ical_free_vevent},
110 {"VTODO", 5, VTodo, ical_parse_vtodo, ical_free_vtodo},
111 {"VJOURNAL", 8, VJournal, ical_parse_vjournal, ical_free_vjournal},
112 {"VALARM", 6, VAlarm, ical_parse_valarm, ical_free_valarm},
113 {"VFREEBUSY", 9, VFreeBusy, ical_parse_vfreebusy, ical_free_vfreebusy},
114 {NULL, 0, VUnknown, NULL, 0}
117 /* array for properties */
118 ICAL_PROP_S rrule_prop[] = {
119 {"FREQ", 4, RRFreq, ical_parse_freq, ical_fs_give},
120 {"UNTIL", 5, RRUntil, ical_parse_until, ical_fs_give},
121 {"COUNT", 5, RRCount, ical_parse_count, ical_fs_give},
122 {"INTERVAL", 8, RRInterval, ical_parse_interval, ical_fs_give},
123 {"BYSECOND", 8, RRBysecond, ical_parse_number_list, ical_free_weekday_list},
124 {"BYMINUTE", 8, RRByminute, ical_parse_number_list, ical_free_weekday_list},
125 {"BYHOUR", 6, RRByhour, ical_parse_number_list, ical_free_weekday_list},
126 {"BYDAY", 5, RRByday, ical_parse_weekday_list,ical_free_weekday_list},
127 {"BYWEEKNO", 8, RRByweekno, 0, 0},
128 {"BYMONTH", 7, RRBymonth, ical_parse_number_list, ical_free_weekday_list},
129 {"BYSETPOS", 8, RRBysetpos, 0, 0},
130 {"BYWKST", 6, RRWkst, 0, 0},
131 {"BYMONTHDAY",
132 10, RRBymonthday, 0, 0},
133 {"BYYEARDAY", 9, RRByyearday, 0, 0},
134 {NULL, 0, RRUnknown, 0, 0}
137 ICAL_PROP_S event_prop[] = {
138 {"DTSTAMP", 7, EvDtstamp, ical_cline_from_token, ical_free_cline},
139 {"UID", 3, EvUid, ical_cline_from_token, ical_free_cline},
140 {"DTSTART", 7, EvDtstart, ical_cline_from_token, ical_free_cline},
141 {"CLASS", 5, EvClass, ical_cline_from_token, ical_free_cline},
142 {"CREATED", 7, EvCreated, ical_cline_from_token, ical_free_cline},
143 {"DESCRIPTION", 11, EvDescription, ical_cline_from_token, ical_free_cline},
144 {"GEO", 3, EvGeo, ical_cline_from_token, ical_free_cline},
145 {"LASTMOD", 7, EvLastMod, ical_cline_from_token, ical_free_cline},
146 {"LOCATION", 8, EvLocation, ical_cline_from_token, ical_free_cline},
147 {"ORGANIZER", 9, EvOrganizer, ical_cline_from_token, ical_free_cline},
148 {"PRIORITY", 8, EvPriority, ical_cline_from_token, ical_free_cline},
149 {"SEQUENCE", 8, EvSequence, ical_cline_from_token, ical_free_cline},
150 {"STATUS", 6, EvStatus, ical_cline_from_token, ical_free_cline},
151 {"SUMMARY", 7, EvSummary, ical_cline_from_token, ical_free_cline},
152 {"TRANSP", 6, EvTransp, ical_cline_from_token, ical_free_cline},
153 {"URL", 3, EvUrl, ical_cline_from_token, ical_free_cline},
154 {"RECURRENCE-ID", 13, EvRecurrence, ical_cline_from_token, ical_free_cline},
155 {"RRULE", 5, EvRrule, ical_parse_rrule, ical_free_rrule},
156 {"DTEND", 5, EvDtend, ical_cline_from_token, ical_free_cline},
157 {"DURATION", 8, EvDuration, ical_cline_from_token, ical_free_cline},
158 {"ATTACH", 6, EvAttach, ical_gencline_from_token, ical_free_gencline},
159 {"ATTENDEE", 8, EvAttendee, ical_gencline_from_token, ical_free_gencline},
160 {"CATEGORIES", 10, EvCategories, ical_gencline_from_token, ical_free_gencline},
161 {"COMMENT", 7, EvComment, ical_gencline_from_token, ical_free_gencline},
162 {"CONTACT", 7, EvContact, ical_gencline_from_token, ical_free_gencline},
163 {"EXDATE", 6, EvExdate, ical_gencline_from_token, ical_free_gencline},
164 {"RSTATUS", 7, EvRstatus, ical_gencline_from_token, ical_free_gencline},
165 {"RELATED", 7, EvRelated, ical_gencline_from_token, ical_free_gencline},
166 {"RESOURCES", 9, EvResources, ical_gencline_from_token, ical_free_gencline},
167 {"RDATE", 5, EvRdate, ical_gencline_from_token, ical_free_gencline},
168 {NULL, 0, EvUnknown, 0, 0}
171 ICAL_PROP_S tz_comp[] = {
172 {"TZID", 4, TZCid, ical_cline_from_token, ical_free_cline},
173 {"LAST-MODIFIED", 13, TZCLastMod, ical_cline_from_token, ical_free_cline},
174 {"TZURL", 5, TZCUrl, ical_cline_from_token, ical_free_cline},
175 {NULL, 0, TZCUnknown, 0, 0}
178 ICAL_PROP_S tz_prop[] = {
179 {"DTSTART", 7, TZPDtstart, ical_parse_time, ical_fs_give},
180 {"TZOFFSETTO", 10, TZPOffsetto, ical_parse_offset, ical_fs_give},
181 {"TZOFFSETFROM", 12, TZPOffsetfrom, ical_parse_offset, ical_fs_give},
182 {"RRULE", 5, TZPRrule, ical_parse_rrule, ical_free_rrule},
183 {"COMMENT", 7, TZPComment, ical_gencline_from_token, ical_free_gencline},
184 {"RDATE", 5, TZPRdate, ical_gencline_from_token, ical_free_gencline},
185 {"TZNAME", 6, TZPTzname, ical_gencline_from_token, ical_free_gencline},
186 {NULL, 0, TZPUnknown, 0, 0}
189 ICAL_PROP_S alarm_prop[] = {
190 {"ACTION", 6, AlAction, ical_cline_from_token, ical_free_cline},
191 {"TRIGGER", 7, AlTrigger, ical_cline_from_token, ical_free_cline},
192 {"DURATION", 8, AlDuration, ical_cline_from_token, ical_free_cline},
193 {"REPEAT", 6, AlRepeat, ical_cline_from_token, ical_free_cline},
194 {"DESCRIPTION", 11, AlDescription, ical_cline_from_token, ical_free_cline},
195 {"SUMMARY", 7, AlSummary, ical_cline_from_token, ical_free_cline},
196 {"ATTACH", 6, AlAttach, ical_gencline_from_token, ical_free_gencline},
197 {"ATTENDEE", 8, AlAttendee, ical_gencline_from_token, ical_free_gencline},
198 {NULL, 0, AlUnknown, 0, 0}
201 /* some useful macros for character analysis */
203 #define ical_wspace(X) \
204 ((X) == ' ' || (X) == '\t')
206 #define ical_name_allowed_char(X) \
207 (((X) >= 'A' && (X) <= 'Z') || \
208 ((X) >= 'a' && (X) <= 'z') || \
209 (X) == '-' )
211 #define ical_control(X) \
212 (((X) >= 0x00 && (X) <= 0x08) || \
213 ((X) >= 0x0A && (X) <= 0x1F) || \
214 (X) == 0x7F)
216 #define ical_safe_char(X) \
217 (ical_non_ascii_valid(X) \
218 || ical_wspace(X) \
219 || (X) == 0x21 \
220 || ((X) >= 0x23 && (X) <= 0x2B) \
221 || ((X) >= 0x2D && (X) <= 0x39) \
222 || ((X) >= 0x3C && (X) <= 0x7E))
224 #define ical_qsafe_char(X) \
225 (ical_non_ascii_valid((X)) \
226 || ical_wspace(X) \
227 || (X) == 0x21 \
228 || ((X) >= 0x23 && (X) <= 0x7E))
230 #define ical_value_char(X) \
231 (ical_non_ascii_valid(X) \
232 || ical_wspace(X) \
233 || ((X) >= 0x21 && (X) <= 0x7E))
235 /* Finally, here begins the code. */
237 unsigned char *
238 ical_decode(char *text, unsigned short encoding)
240 unsigned char *t;
241 unsigned long callen;
242 size_t tlen;
243 if(encoding == ENCQUOTEDPRINTABLE){
244 t = rfc822_qprint ((unsigned char *) text,strlen(text),&callen);
245 if(t != NULL){
246 tlen = strlen(text) + 1;
247 strncpy(text, (char *) t, tlen);
248 text[tlen - 1] = '\0';
249 fs_give((void **) &t);
252 return (unsigned char *) text;
256 /* Return code:
257 0 - if no errors
258 -1 - if an error occurred
259 Args: a pointer to the text. If there is an error, the text is not modified.
262 ical_remove_escapes(char **textp)
264 char *text, *s, *t;
265 int rv = 0;
266 int escaped;
267 size_t tlen;
269 if(textp == NULL) return 0;
271 t = cpystr(*textp); /* work on a copy of the text */
272 tlen = strlen(*textp) + 1; /* and record its size */
273 /* the variable text below points to the beginning of the filtered text */
274 for (text = s = t, escaped = 0; rv == 0 && *s != '\0'; s++){
275 if(*s == '\\' && escaped == 0){
276 escaped = 1;
277 continue;
279 if(escaped){
280 switch(*s){
281 case '\\':
282 case ',':
283 case ';':
284 *t++ = *s;
285 break;
287 case 'n':
288 case 'N':
289 *t++ = '\n';
290 break;
291 default: rv = -1;
292 break;
294 escaped = 0;
296 else *t++ = *s;
298 *t = '\0'; /* tie off filtered text */
299 t = text; /* reset t to the beginning */
300 if(rv == 0){
301 strncpy(*textp, t, tlen); /* overwrite given text with filtered text */
302 (*textp)[tlen - 1] = '\0';
304 fs_give((void **) &t);
305 return rv;
308 void
309 ical_debug(char *fcn, char *text)
311 char piece[50];
312 strncpy(piece, text, 49);
313 piece[sizeof(piece)-1] = '\0';
314 dprint((2, "%s: %s\n", fcn, piece));
317 /***
318 *** FREE MEMORY FUNCTIONS
319 ***/
321 void
322 ical_free_param(ICAL_PARAMETER_S **param)
324 if(param == NULL || *param == NULL)
325 return;
327 if((*param)->name) fs_give((void **) &(*param)->name);
328 if((*param)->value) fs_give((void **) &(*param)->value);
329 if((*param)->next) ical_free_param(&(*param)->next);
330 fs_give((void **)param);
333 void
334 ical_free_cline(void **icv)
336 ICLINE_S **ic = (ICLINE_S **) icv;
338 if(ic == NULL || *ic == NULL)
339 return;
341 if((*ic)->token) fs_give((void **) &(*ic)->token);
342 if((*ic)->param) ical_free_param(&(*ic)->param);
343 if((*ic)->value) fs_give((void **) &(*ic)->value);
344 fs_give(icv);
347 void
348 ical_free_gencline(void **giclpv)
350 GEN_ICLINE_S **giclp = (GEN_ICLINE_S **) giclpv;
352 if(giclp == NULL || *giclp == NULL) return;
354 if((*giclp)->cline) ical_free_cline((void **) &(*giclp)->cline);
355 if((*giclp)->next) ical_free_gencline((void **) &(*giclp)->next);
356 fs_give((void **)giclp);
359 void
360 ical_free_vcalendar(void **vcalpv)
362 VCALENDAR_S **vcalp = (VCALENDAR_S **)vcalpv;
364 if(vcalp == NULL || *vcalp == NULL) return;
366 if((*vcalp)->prodid) ical_free_cline((void **) &(*vcalp)->prodid);
367 if((*vcalp)->version) ical_free_cline((void **) &(*vcalp)->version);
368 if((*vcalp)->calscale) ical_free_cline((void **) &(*vcalp)->calscale);
369 if((*vcalp)->method) ical_free_cline((void **) &(*vcalp)->method);
370 if((*vcalp)->uk_prop) ical_free_gencline((void **) &(*vcalp)->uk_prop);
371 if((*vcalp)->comp){
372 Cal_comp i;
373 for(i = 0; i < VUnknown; i++)
374 if((*vcalp)->comp[i]) (ical_comp[i].give)(&(*vcalp)->comp[i]);
375 fs_give((void **) &(*vcalp)->comp);
377 if((*vcalp)->uk_comp) ical_free_unknown_comp(&(*vcalp)->uk_comp);
378 fs_give(vcalpv);
381 void
382 ical_free_vevent(void **veventpv)
384 VEVENT_S **veventp = (VEVENT_S **) veventpv;
386 if(veventp == NULL || *veventp == NULL) return;
388 ical_free_prop(&(*veventp)->prop, event_prop, EvUnknown);
389 if((*veventp)->uk_prop) ical_free_gencline((void **) &(*veventp)->uk_prop);
390 if((*veventp)->valarm) ical_free_valarm((void **) &(*veventp)->valarm);
391 if((*veventp)->next) ical_free_vevent((void **) &(*veventp)->next);
392 fs_give(veventpv);
395 void
396 ical_fs_give(void **x)
398 if(x != NULL && *x != NULL)
399 fs_give(x);
402 void
403 ical_free_rrule(void **rrulepv)
405 RRULE_S **rrulep = (RRULE_S **) rrulepv;
407 if(rrulep && *rrulep){
408 ical_free_prop(&(*rrulep)->prop, rrule_prop, RRUnknown);
409 ical_free_param(&(*rrulep)->param);
410 fs_give(rrulepv);
414 void
415 ical_free_weekday_list(void **wkdylv)
417 BYWKDY_S **wkdyl = (BYWKDY_S **) wkdylv;
419 if(wkdyl == NULL) return;
421 if((*wkdyl)->next)
422 ical_free_weekday_list((void **) &(*wkdyl)->next);
424 fs_give(wkdylv);
428 void
429 ical_free_vtodo(void **vtodopv)
433 void
434 ical_free_vjournal(void **vjournalpv)
438 void
439 ical_free_vfreebusy(void **vfbpv)
443 void
444 ical_free_prop(void ***propv, ICAL_PROP_S *aux_comp, int max)
446 int i, j;
448 if(propv == NULL || *propv == NULL) return;
450 for(i = 0; i < max; i++)
451 if((*propv)[i]){
452 for(j = 0; aux_comp[j].prop != NULL && aux_comp[j].pos != i; j++);
453 if(aux_comp[j].give) (aux_comp[j].give)(&(*propv)[i]);
455 fs_give((void **) propv);
459 void
460 ical_free_vtimezone(void **vtzpv)
462 VTIMEZONE_S **vtzp = (VTIMEZONE_S **) vtzpv;
464 if(vtzp == NULL || *vtzp == NULL) return;
466 ical_free_prop(&(*vtzp)->prop, tz_comp, TZCUnknown);
467 if((*vtzp)->uk_prop) ical_free_gencline((void **) &(*vtzp)->uk_prop);
468 if((*vtzp)->standardc) ical_free_timezone((void **) &(*vtzp)->standardc);
469 if((*vtzp)->daylightc) ical_free_timezone((void **) &(*vtzp)->daylightc);
470 fs_give(vtzpv);
473 void
474 ical_free_timezone(void **tzpv)
476 ICAL_TZPROP_S **tzp = (ICAL_TZPROP_S **) tzpv;
478 if(tzp == NULL || *tzp == NULL) return;
480 ical_free_prop(&(*tzp)->prop, tz_prop, TZPUnknown);
481 if((*tzp)->uk_prop) ical_free_gencline((void **) &(*tzp)->uk_prop);
482 if((*tzp)->next) ical_free_timezone((void **) &(*tzp)->next);
483 fs_give(tzpv);
486 void
487 ical_free_valarm(void **valarmpv)
489 VALARM_S **valarmp = (VALARM_S **) valarmpv;
491 if(valarmp == NULL || *valarmp == NULL) return;
493 ical_free_prop(&(*valarmp)->prop, alarm_prop, AlUnknown);
494 if((*valarmp)->uk_prop) ical_free_gencline((void **) &(*valarmp)->uk_prop);
495 if((*valarmp)->next) ical_free_timezone((void **) &(*valarmp)->next);
496 fs_give(valarmpv);
499 void
500 ical_free_unknown_comp(ICAL_S **icalp)
502 int i;
503 if(icalp == NULL || *icalp == NULL) return;
504 for(i = 0; ical_comp[i].comp && strucmp((*icalp)->comp,ical_comp[i].comp); i++);
505 if(ical_comp[i].give)
506 (ical_comp[i].give)(&(*icalp)->value);
507 else
508 ical_free_gencline((void **) &(*icalp)->value);
509 fs_give((void **)&(*icalp)->comp);
510 ical_free_unknown_comp(&(*icalp)->next);
511 ical_free_unknown_comp(&(*icalp)->branch);
512 fs_give((void **)icalp);
515 char *
516 ical_unfold_line(char *line)
518 int i, j;
520 if(line == NULL)
521 return NULL;
523 for(i = 0, j = 0; line[j] != '\0';)
524 switch(line[j]){
525 case '\r': if(line[j+1] == '\n' && ical_wspace(line[j+2])){
526 j += 3; /* get past white space */
527 continue;
529 case '\n': if(ical_wspace(line[j+1])){
530 j += 2; /* get past white space */
531 continue;
533 default : line[i++] = line[j++];
535 line[i] = '\0';
536 return line;
539 ICAL_PARAMETER_S *
540 ical_get_parameter(char **line)
542 ICAL_PARAMETER_S *param = NULL;
543 char *s;
545 if(line == NULL || *line == NULL)
546 return NULL;
548 for(s = *line; s && *s && ical_name_allowed_char(*s) ; s++);
550 if(*s == '='){
551 int quoted;
552 char c;
554 param = fs_get(sizeof(ICAL_PARAMETER_S));
555 memset((void *)param, 0, sizeof(ICAL_PARAMETER_S));
556 *s = '\0';
557 param->name = cpystr(*line);
558 *s = '=';
559 *line = s+1; /* step over '=' */
560 quoted = **line == '"' ? 1 : 0;
561 if(quoted != 0){
562 for(s = ++*line; s && *s && ical_qsafe_char((unsigned char) *s); s++);
563 if(*s != '"'){ /* error, do not parse this line */
564 ical_free_param(&param);
565 *line = strchr(s, ':'); /* reset line to closest ':' */
566 return NULL;
569 else
570 for(s = *line; s && *s && (ical_safe_char((unsigned char) *s)); s++);
571 c = *s;
572 *s = '\0';
573 param->value = cpystr(*line);
574 *s = c; /* restore character */
575 *line = quoted ? s + 1 : s;
577 if(**line == ';'){
578 ++*line;
579 param->next = ical_get_parameter(line);
582 return param;
585 char *
586 ical_get_value(char **line)
588 char *s, *t;
590 if(line == NULL || *line == NULL)
591 return NULL;
593 for (s = *line; *s && ical_value_char((unsigned char) *s); s++);
594 if(*s == '\r'){
595 *s = '\0';
596 t = cpystr(*line);
597 *s = '\r';
598 *line = s+2;
600 else{
601 t = NULL;
602 s = strchr(*line, '\r');
603 if(s != NULL)
604 *line = s + 2;
606 return t;
609 ICAL_PARAMETER_S *
610 ical_parameter_cpy(ICAL_PARAMETER_S *param)
612 ICAL_PARAMETER_S *rv;
614 if(param == NULL) return NULL;
616 rv = fs_get(sizeof(ICAL_PARAMETER_S));
617 memset((void *)rv, 0, sizeof(ICAL_PARAMETER_S));
619 if(param->name) rv->name = cpystr(param->name);
620 if(param->value) rv->value = cpystr(param->value);
621 if(param->next) rv->next = ical_parameter_cpy(param->next);
623 return rv;
626 ICLINE_S *
627 ical_cline_cpy(ICLINE_S *icl)
629 ICLINE_S *rv;
631 if(icl == NULL)
632 return NULL;
634 rv = fs_get(sizeof(ICLINE_S));
635 memset((void *)rv, 0, sizeof(ICLINE_S));
637 if(icl->token) rv->token = cpystr(icl->token);
638 if(icl->param) rv->param = ical_parameter_cpy(icl->param);
639 if(icl->value) rv->value = cpystr(icl->value);
641 return rv;
644 /* Given a \r\n-ending line (called *text), isolate the occurrence
645 * of the token in that line.
646 * Return the token, and modify the pointer to *text to point to the
647 * end of the token. Modify sep to contain the character following
648 * the token
649 * ical-line = token ':'/';' rest of the line\r\n
650 * on error return null, and set *text to the next line, if possible.
652 char *
653 ical_isolate_token(char **text, char *sep)
655 char *s, *t;
657 for(t = s = *text; *t && ical_name_allowed_char(*s); s++);
658 /* only followed by parameter or value */
659 if(*s == ':' || *s == ';'){
660 *sep = *s;
661 *s = '\0'; /* isolate token at pointer s */
662 *text = s;
664 else{ /* bad data - bail out of here */
665 t = NULL;
666 if(*s == '\0' || (s = strstr(s, "\r\n")) == NULL)
667 *text = NULL;
668 else /* move to next line */
669 *text = s + 2;
671 return t;
675 VCALENDAR_S *
676 ical_parse_text(char *text)
678 char *s;
679 VCALENDAR_S *vcal = NULL;
681 ical_debug("ical_parse_text", text);
682 ical_initialize();
684 text = ical_unfold_line(text);
685 for(s = text; s && *s != '\0'; s++){
686 if(*s != 'B' && *s != 'b')
687 continue;
688 if(!struncmp(s+1, "EGIN:VCALENDAR\r\n", 16)){
689 s += 17; /* 17 = strlen("BEGIN:VCALENDAR\r\n") */
690 vcal = (VCALENDAR_S *) ical_parse_vcalendar(&s);
691 break;
694 return vcal;
697 void *
698 ical_parse_time(void *ic_datep, char **text, char *token)
700 struct tm *datep;
701 ICLINE_S *icl;
703 datep = fs_get(sizeof(struct tm));
704 icl = ical_parse_line(text, token);
705 ical_parse_date(icl->value, datep);
706 ical_free_cline((void **) &icl);
707 ic_datep = (void *) datep;
709 return ic_datep;
712 void *
713 ical_parse_interval(void *longvp, char *value)
716 unsigned long *longp;
718 longp = fs_get(sizeof(unsigned long));
719 *longp = atoi(value);
720 longvp = (void *) longp;
722 return longvp;
726 void *
727 ical_parse_offset(void *offsetv, char **text, char *token)
729 ICLINE_S *icl;
730 char *value;
731 int h, m, *offset;
733 offset = fs_get(sizeof(int));
735 icl = ical_parse_line(text, token);
737 if(*icl->value == '+' || *icl->value == '-')
738 value = icl->value + 1;
739 else
740 value = icl->value;
742 h = ical_get_number_value(value, 0, 2);
743 m = ical_get_number_value(value, 2, 4);
745 *offset = 60*(60*h + m);
746 if(*icl->value == '-')
747 *offset *= -1;
749 ical_free_cline((void **) &icl);
750 offsetv = (void *) offset;
752 return offsetv;
755 /* This function processes the information in *text, and returns
756 * a pointer to the information in iclp, but only if iclp is NULL
757 * otherwise, it simply returns the current value and advances the
758 * pointer to *text.
759 * Call this function as follows
760 * rv = (cast here *) ical_cline_from_token((void *)rv, &text, token);
762 void *
763 ical_cline_from_token(void *iclp, char **text, char *token)
765 ICLINE_S *icl;
767 ical_debug("ical_cline_from_token", *text);
769 icl = ical_parse_line(text, token);
771 if(iclp != NULL)
772 ical_free_cline((void **)&icl);
773 else
774 iclp = (void *) icl;
776 return iclp;
779 void *
780 ical_gencline_from_token(void *giclv, char **text, char *token)
782 GEN_ICLINE_S *gicl= NULL;
784 if(!struncmp(*text, token, strlen(token))){
785 gicl = fs_get(sizeof(GEN_ICLINE_S));
786 memset((void *) gicl, 0, sizeof(GEN_ICLINE_S));
787 gicl->cline = ical_parse_line(text, token);
788 // gicl->line = (ICLINE_S *) ical_cline_from_token((void *) gicl->cline, text, token);
789 gicl->next = (GEN_ICLINE_S *) ical_gencline_from_token((void *) gicl->next, text, token);
792 if(giclv != NULL)
793 ical_free_gencline((void **) &gicl);
794 else
795 giclv = (void *) gicl;
797 return giclv;
800 /***
801 *** PARSE COMPONENT FUNCTIONS
802 ***/
804 void *
805 ical_parse_vcalendar(char **text)
807 char *s, *t;
808 char c;
809 VCALENDAR_S *vcal;
810 void *v;
812 dprint((9, "ical_parse_vcalendar:\n"));
813 ical_debug("ical_parse_vcalendar", *text);
815 vcal = fs_get(sizeof(VCALENDAR_S));
816 memset((void *) vcal, 0, sizeof(VCALENDAR_S));
818 /* s must always point the the beginning of a line */
819 for(s = *text; s && *s != '\0';){
820 t = s;
821 s = ical_isolate_token(&t, &c);
822 if(s == NULL){
823 if(t != NULL)
824 s = t;
825 continue;
828 *t = c; /* restore character */
829 if(s){ /* figure out the token */
830 int ukn = 0; /* unknown token */
831 int i;
832 switch(*s){
833 case 'B':
834 case 'b': if(!struncmp(s+1, "EGIN", 4)){
835 s += 6; /* 6 = strlen("BEGIN:") */
836 for(i = 0; ical_comp[i].comp
837 && (struncmp(s, ical_comp[i].comp, ical_comp[i].len)
838 || struncmp(s + ical_comp[i].len, "\r\n", 2)); i++);
840 if(ical_comp[i].parse){
841 s += ical_comp[i].len + 2;
842 v = (ical_comp[i].parse)(&s);
843 if(vcal->comp == NULL){
844 vcal->comp = fs_get((VUnknown+1)*sizeof(void *));
845 memset((void *) vcal->comp, 0, (VUnknown+1)*sizeof(void *));
848 if(vcal->comp[ical_comp[i].pos] == NULL)
849 vcal->comp[ical_comp[i].pos] = v;
850 else{
851 if((vcal->method && vcal->method->value
852 && strucmp(vcal->method->value, "PUBLISH"))
853 || struncmp(ical_comp[i].comp, "VEVENT", 6))
854 (ical_comp[i].give)(&v);
855 else{
856 VEVENT_S *vevent = (VEVENT_S *) vcal->comp[VEvent];
857 for(; vevent && vevent->next; vevent = vevent->next);
858 vevent->next = v;
861 } else {
862 v = (void *) ical_parse_unknown_comp(&s, 0);
863 if(vcal->uk_comp == NULL)
864 vcal->uk_comp = (ICAL_S *) v;
865 else{
866 ICAL_S *ic;
867 for(ic = vcal->uk_comp; ic && ic->branch; ic = ic->branch);
868 ic->branch = (ICAL_S *) v;
871 } else ukn++;
872 break;
874 case 'C':
875 case 'c': if(!struncmp(s+1, "ALSCALE", 7)){
876 v = (void *) vcal->calscale;
877 v = ical_cline_from_token(v, &s, "CALSCALE");
878 vcal->calscale = (ICLINE_S *) v;
880 else ukn++;
881 break;
883 case 'E':
884 case 'e': if(!struncmp(s+1, "ND", 2)){
885 *t = c;
886 s += 4; /* 4 = strlen("END:") */
887 if(!struncmp(s, "VCALENDAR\r\n", 11)){
888 *text = s + 11; /* 11 = strlen("VCALENDAR\r\n") */
889 return (void *) vcal;
891 // else ukn++; FIX THIS, this is not quite right
892 } else ukn++;
893 break;
895 case 'M':
896 case 'm': if(!struncmp(s+1, "ETHOD", 5)){
897 v = (void *) vcal->method;
898 v = ical_cline_from_token(v, &s, "METHOD");
899 vcal->method = (ICLINE_S *) v;
901 else ukn++;
902 break;
904 case 'P':
905 case 'p': if(!struncmp(s+1, "RODID", 5)){
906 v = (void *) vcal->prodid;
907 v = ical_cline_from_token(v, &s, "PRODID");
908 vcal->prodid = (ICLINE_S *) v;
910 else ukn++;
911 break;
913 case 'V':
914 case 'v': if(!struncmp(s+1, "ERSION", 6)){
915 v = (void *) vcal->version;
916 v = ical_cline_from_token(v, &s, "VERSION");
917 vcal->version = (ICLINE_S *) v;
918 } else ukn++;
919 break;
921 default : ukn++;
922 break;
923 } /* end of switch(*s) */
924 if(ukn){
925 if(ical_buf_len < t - s){
926 fs_resize((void **)&ical_buf, t-s+1);
927 ical_buf_len = t-s;
929 *t = '\0';
930 strcpy(ical_buf, s);
931 *t = c;
932 if(vcal->uk_prop == NULL){
933 vcal->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
934 memset((void *)vcal->uk_prop, 0, sizeof(GEN_ICLINE_S));
935 vcal->uk_prop->cline = ical_parse_line(&s, ical_buf);
937 else{
938 GEN_ICLINE_S *gcl;
939 for (gcl = vcal->uk_prop; gcl && gcl->next; gcl = gcl->next);
940 gcl->next = fs_get(sizeof(GEN_ICLINE_S));
941 memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
942 gcl->next->cline = ical_parse_line(&s, ical_buf);
945 } /* end of if(s) */
948 *text = s;
950 /* ok, we have parsed the vcalendar, now parse some special properties */
951 /* start by parsing dates */
952 ical_set_date_vevent(vcal->comp[VEvent], vcal->comp[VTimeZone]);
953 return (void *) vcal;
956 void *
957 ical_parse_vevent(char **text)
959 char *s, *t;
960 char c;
961 VEVENT_S *vevent;
963 ical_debug("ical_parse_vevent", *text);
964 vevent = fs_get(sizeof(VEVENT_S));
965 memset((void *)vevent, 0, sizeof(VEVENT_S));
967 /* s must always point the the beginning of a line */
968 for(s = *text; s && *s != '\0';){
969 t = s;
970 s = ical_isolate_token(&t, &c);
971 if(s == NULL){
972 if(t != NULL)
973 s = t;
974 continue;
976 *t = c; /* restore separator */
978 if(s){ /* figure out the token */
979 int ukn = 0; /* unknown token */
980 if(!struncmp(s, "BEGIN", 5)){
981 s += 6; /* 6 = strlen("BEGIN:") */
982 if(!struncmp(s, "VALARM\r\n", 8)){
983 s += 8; /* 8 = strlen("VALARM\r\n"); */
984 if(vevent->valarm == NULL)
985 vevent->valarm = ical_parse_valarm(&s);
986 else{
987 VALARM_S *valrm;
988 for(valrm = vevent->valarm; valrm && valrm->next;
989 valrm = valrm->next);
990 valrm->next = ical_parse_valarm(&s);
992 } else {
993 ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0);
994 ical_free_unknown_comp(&uk_comp);
996 } else if(!struncmp(s, "END", t-s-1)){
997 s += 4; /* 4 = strlen("END:") */
998 if(!struncmp(s, "VEVENT\r\n",8)){
999 *text = s + 8; /* 8 = strlen("VCALENDAR\r\n") */
1000 return (void *) vevent;
1002 } else{ Event_prop i;
1003 for(i = 0; i < EvUnknown; i++)
1004 if(!struncmp(s, event_prop[i].prop, t-s))
1005 break;
1006 if(event_prop[i].parse){
1007 void *v;
1008 if(vevent->prop == NULL){
1009 vevent->prop = fs_get((EvUnknown+1)*sizeof(void *));
1010 memset((void *)vevent->prop, 0, (EvUnknown+1)*sizeof(void *));
1012 v = vevent->prop[event_prop[i].pos];
1013 v = (event_prop[i].parse)(v , &s, event_prop[i].prop);
1014 vevent->prop[event_prop[i].pos] = v;
1016 else
1017 ukn++;
1020 if(ukn){
1021 if(ical_buf_len < t - s){
1022 fs_resize((void **)&ical_buf, t-s+1);
1023 ical_buf_len = t-s;
1025 *t = '\0';
1026 strcpy(ical_buf, s);
1027 *t = c;
1028 if(vevent->uk_prop == NULL){
1029 vevent->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
1030 memset((void *)vevent->uk_prop, 0, sizeof(GEN_ICLINE_S));
1031 vevent->uk_prop->cline = ical_parse_line(&s, ical_buf);
1033 else{
1034 GEN_ICLINE_S *gcl;
1035 for (gcl = vevent->uk_prop; gcl && gcl->next; gcl = gcl->next);
1036 gcl->next = fs_get(sizeof(GEN_ICLINE_S));
1037 memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
1038 gcl->next->cline = ical_parse_line(&s, ical_buf);
1041 } /* end of if(s) */
1044 *text = s;
1045 return (void *) vevent;
1048 void *
1049 ical_parse_vtimezone(char **text)
1051 char *s, *t;
1052 char c;
1053 void *v;
1054 VTIMEZONE_S *vtz;
1056 ical_debug("ical_parse_vtimezone", *text);
1057 vtz = fs_get(sizeof(VTIMEZONE_S));
1058 memset((void *)vtz, 0, sizeof(VTIMEZONE_S));
1060 /* s must always point the the beginning of a line */
1061 for(s = *text; s && *s != '\0';){
1062 t = s;
1063 s = ical_isolate_token(&t, &c);
1064 if(s == NULL){
1065 if(t != NULL)
1066 s = t;
1067 continue;
1069 *t = c; /* restore separator */
1071 if(s){ /* figure out the token */
1072 int ukn = 0; /* unknown token */
1073 if(!struncmp(s, "BEGIN", 5)){
1074 s += 6; /* 6 = strlen("BEGIN:") */
1075 if(!struncmp(s, "STANDARD\r\n", 10)){
1076 s += 10; /* 10 = strlen("STANDARD\r\n"); */
1077 v = ical_parse_timezone(&s);
1078 if(vtz->standardc == NULL)
1079 vtz->standardc = (ICAL_TZPROP_S *) v;
1080 else{
1081 ICAL_TZPROP_S *dl;
1082 for(dl = vtz->standardc; dl && dl->next; dl = dl->next);
1083 dl->next = (ICAL_TZPROP_S *) v;
1085 } else if(!struncmp(s, "DAYLIGHT\r\n", 10)){
1086 s += 10; /* 10 = strlen("DAYLIGHT\r\n"); */
1087 v = ical_parse_timezone(&s);
1088 if(vtz->daylightc == NULL)
1089 vtz->daylightc = (ICAL_TZPROP_S *) v;
1090 else{
1091 ICAL_TZPROP_S *dl;
1092 for(dl = vtz->daylightc; dl && dl->next; dl = dl->next);
1093 dl->next = (ICAL_TZPROP_S *) v;
1095 } else {
1096 ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0);
1097 ical_free_unknown_comp(&uk_comp);
1099 } else if(!struncmp(s, "END", t-s-1)){
1100 s += 4; /* 4 = strlen("END:") */
1101 if(!struncmp(s, "VTIMEZONE\r\n",11)){
1102 *text = s + 11; /* 11 = strlen("VTIMEZONE\r\n") */
1103 return (void *) vtz;
1105 } else{ TZ_comp i;
1106 for(i = 0; i < TZCUnknown; i++)
1107 if(!struncmp(s, tz_comp[i].prop, t-s))
1108 break;
1109 if(tz_comp[i].parse){
1110 void *v;
1111 if(vtz->prop == NULL){
1112 vtz->prop = fs_get(TZCUnknown*sizeof(void *));
1113 memset((void *)vtz->prop, 0, TZCUnknown*sizeof(void *));
1115 v = vtz->prop[tz_comp[i].pos];
1116 v = (tz_comp[i].parse)(v, &s, tz_comp[i].prop);
1117 vtz->prop[tz_comp[i].pos] = v;
1119 else
1120 ukn++;
1123 if(ukn){
1124 if(ical_buf_len < t - s){
1125 fs_resize((void **)&ical_buf, t-s+1);
1126 ical_buf_len = t-s;
1128 *t = '\0';
1129 strcpy(ical_buf, s);
1130 *t = c;
1131 if(vtz->uk_prop == NULL){
1132 vtz->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
1133 memset((void *)vtz->uk_prop, 0, sizeof(GEN_ICLINE_S));
1134 vtz->uk_prop->cline = ical_parse_line(&s, ical_buf);
1136 else{
1137 GEN_ICLINE_S *gcl;
1138 for (gcl = vtz->uk_prop; gcl && gcl->next; gcl = gcl->next);
1139 gcl->next = fs_get(sizeof(GEN_ICLINE_S));
1140 memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
1141 gcl->next->cline = ical_parse_line(&s, ical_buf);
1144 } /* end of if(s) */
1147 *text = s;
1148 return (void *) vtz;
1151 void *
1152 ical_parse_timezone(char **text)
1154 char *s, *t;
1155 char c;
1156 ICAL_TZPROP_S *tzprop;
1158 ical_debug("ical_parse_timezone", *text);
1159 tzprop = fs_get(sizeof(ICAL_TZPROP_S));
1160 memset((void *)tzprop, 0, sizeof(ICAL_TZPROP_S));
1162 /* s must always point the the beginning of a line */
1163 for(s = *text; s && *s != '\0';){
1164 t = s;
1165 s = ical_isolate_token(&t, &c);
1166 if(s == NULL){
1167 if(t != NULL)
1168 s = t;
1169 continue;
1171 *t = c; /* restore separator */
1173 if(s){ /* figure out the token */
1174 int ukn = 0; /* unknown token */
1175 if(!struncmp(s, "BEGIN", 5)){
1176 ICAL_S *uk_comp;
1177 s += 6; /* 6 = strlen("BEGIN:") */
1178 uk_comp = ical_parse_unknown_comp(&s, 0);
1179 ical_free_unknown_comp(&uk_comp);
1180 } else if(!struncmp(s, "END", t-s-1)){
1181 s += 4; /* 4 = strlen("END:") */
1182 if(!struncmp(s, "STANDARD\r\n", 10)
1183 || !struncmp(s, "DAYLIGHT\r\n", 10)){
1184 *text = s + 10; /* 10 = strlen("STANDARD\r\n") */
1185 return (void *) tzprop;
1187 } else{ TZ_prop i;
1188 for(i = 0; i < TZPUnknown; i++)
1189 if(!struncmp(s, tz_prop[i].prop, t-s))
1190 break;
1191 if(tz_prop[i].parse){
1192 void *v;
1193 if(tzprop->prop == NULL){
1194 tzprop->prop = fs_get(TZPUnknown*sizeof(void *));
1195 memset((void *)tzprop->prop, 0, TZPUnknown*sizeof(void *));
1197 v = tzprop->prop[tz_prop[i].pos];
1198 v = (tz_prop[i].parse)(v, &s, tz_prop[i].prop);
1199 tzprop->prop[tz_prop[i].pos] = v;
1201 else
1202 ukn++;
1205 if(ukn){
1206 if(ical_buf_len < t - s){
1207 fs_resize((void **)&ical_buf, t-s+1);
1208 ical_buf_len = t-s;
1210 *t = '\0';
1211 strcpy(ical_buf, s);
1212 *t = c;
1213 if(tzprop->uk_prop == NULL){
1214 tzprop->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
1215 memset((void *)tzprop->uk_prop, 0, sizeof(GEN_ICLINE_S));
1216 tzprop->uk_prop->cline = ical_parse_line(&s, ical_buf);
1218 else{
1219 GEN_ICLINE_S *gcl;
1220 for (gcl = tzprop->uk_prop; gcl && gcl->next; gcl = gcl->next);
1221 gcl->next = fs_get(sizeof(GEN_ICLINE_S));
1222 memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
1223 gcl->next->cline = ical_parse_line(&s, ical_buf);
1226 } /* end of if(s) */
1229 *text = s;
1230 return (void *) tzprop;
1233 void *
1234 ical_parse_valarm(char **text)
1236 char *s, *t;
1237 char c;
1238 VALARM_S *valarm;
1240 ical_debug("ical_parse_valarm", *text);
1241 valarm = fs_get(sizeof(VALARM_S));
1242 memset((void *)valarm, 0, sizeof(VALARM_S));
1244 /* s must always point the the beginning of a line */
1245 for(s = *text; s && *s != '\0';){
1246 t = s;
1247 s = ical_isolate_token(&t, &c);
1248 if(s == NULL){
1249 if(t != NULL)
1250 s = t;
1251 continue;
1253 *t = c; /* restore separator */
1255 if(s){ /* figure out the token */
1256 int ukn = 0; /* unknown token */
1257 if(!struncmp(s, "BEGIN", 5)){
1258 ICAL_S *uk_comp;
1259 s += 6; /* 6 = strlen("BEGIN:") */
1260 uk_comp = ical_parse_unknown_comp(&s, 0);
1261 ical_free_unknown_comp(&uk_comp);
1262 } else if(!struncmp(s, "END", t-s-1)){
1263 s += 4; /* 4 = strlen("END:") */
1264 if(!struncmp(s, "VALARM\r\n", 8)){
1265 *text = s + 8; /* 8 = strlen("VALARM\r\n") */
1266 return (void *) valarm;
1268 } else{ Alarm_prop i;
1269 for(i = 0; i < AlUnknown; i++)
1270 if(!struncmp(s, alarm_prop[i].prop, t-s))
1271 break;
1272 if(alarm_prop[i].parse){
1273 void *v;
1274 if(valarm->prop == NULL){
1275 valarm->prop = fs_get((AlUnknown+1)*sizeof(void *));
1276 memset((void *)valarm->prop, 0, (AlUnknown+1)*sizeof(void *));
1278 v = valarm->prop[alarm_prop[i].pos];
1279 v = (alarm_prop[i].parse)(v, &s, alarm_prop[i].prop);
1280 valarm->prop[alarm_prop[i].pos] = v;
1282 else
1283 ukn++;
1286 if(ukn){
1287 if(ical_buf_len < t - s){
1288 fs_resize((void **)&ical_buf, t-s+1);
1289 ical_buf_len = t-s;
1291 *t = '\0';
1292 strcpy(ical_buf, s);
1293 *t = c;
1294 if(valarm->uk_prop == NULL){
1295 valarm->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
1296 memset((void *)valarm->uk_prop, 0, sizeof(GEN_ICLINE_S));
1297 valarm->uk_prop->cline = ical_parse_line(&s, ical_buf);
1299 else{
1300 GEN_ICLINE_S *gcl;
1301 for (gcl = valarm->uk_prop; gcl && gcl->next; gcl = gcl->next);
1302 gcl->next = fs_get(sizeof(GEN_ICLINE_S));
1303 memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
1304 gcl->next->cline = ical_parse_line(&s, ical_buf);
1307 } /* end of if(s) */
1310 *text = s;
1311 return (void *) valarm;
1314 void *
1315 ical_parse_vtodo(char **text)
1317 return NULL;
1320 void *
1321 ical_parse_vjournal(char **text)
1323 return NULL;
1326 void *
1327 ical_parse_vfreebusy(char **text)
1329 return NULL;
1332 ICAL_S *
1333 ical_parse_generic_comp(char **text, int level)
1335 ICAL_S *ical;
1336 char *s, *t;
1337 char *token = NULL;
1338 GEN_ICLINE_S *gcl = NULL;
1339 char c;
1341 ical_debug("ical_parse_generic_comp", *text);
1342 ical = fs_get(sizeof(ICAL_S));
1343 memset((void *)ical, 0, sizeof(ICAL_S));
1345 ical->comp = ical_get_value(text);
1346 token = fs_get(strlen(ical->comp) + 2 + 1);
1347 sprintf(token, "%s\r\n", ical->comp); /* this is allocated memory */
1349 /* s must always point the the beginning of a line */
1350 for(s = *text; s && *s != '\0';){
1351 t = s;
1352 s = ical_isolate_token(&t, &c);
1353 if(s == NULL){
1354 if(t != NULL)
1355 s = t;
1356 continue;
1359 *t = c; /* restore character */
1360 if(s){ /* figure out the token */
1361 int ukn = 0; /* unknown token */
1362 switch(*s){
1363 case 'B':
1364 case 'b': if(!struncmp(s+1, "EGIN", 4)){
1365 s += 6; /* 6 = strlen("BEGIN:") */
1366 if(ical->next == NULL)
1367 ical->next = ical_parse_unknown_comp(&s, level+1);
1368 else{
1369 ICAL_S *b;
1370 int i;
1372 for(i = 0, b = ical; i <= level && b && b->next; b = b->next, i++);
1373 if(b->branch == NULL)
1374 b->branch = ical_parse_unknown_comp(&s, level+1);
1375 else {
1376 for(; b && b->branch; b = b->branch);
1377 b->branch = ical_parse_unknown_comp(&s, level+1);
1380 } else ukn++;
1381 break;
1383 case 'E':
1384 case 'e': if(!struncmp(s+1, "ND", 2)){
1385 *t = c;
1386 s += 4; /* 4 = strlen("END:") */
1387 if(!struncmp(s, token, strlen(token))){
1388 *text = s + strlen(token);
1389 ical->value = (void *) gcl;
1390 return ical;
1392 } else ukn++;
1393 break;
1395 default : ukn++;
1396 break;
1397 } /* end of switch(*s) */
1398 if(ukn){
1399 if(ical_buf_len < t - s){
1400 fs_resize((void **)&ical_buf, t-s+1);
1401 ical_buf_len = t-s;
1403 *t = '\0';
1404 strcpy(ical_buf, s);
1405 *t = c;
1406 if(gcl == NULL){
1407 gcl = fs_get(sizeof(GEN_ICLINE_S));
1408 memset((void *)gcl, 0, sizeof(GEN_ICLINE_S));
1409 gcl->cline = ical_parse_line(&s, ical_buf);
1411 else{
1412 GEN_ICLINE_S *gencl;
1413 for (gencl = gcl; gencl && gencl->next; gencl = gencl->next);
1414 gencl->next = fs_get(sizeof(GEN_ICLINE_S));
1415 memset((void *)gencl->next, 0, sizeof(GEN_ICLINE_S));
1416 gencl->next->cline = ical_parse_line(&s, ical_buf);
1419 } /* end of if(s) */
1422 ical->value = (void *) gcl;
1423 *text = s;
1424 return ical;
1427 ICAL_S *
1428 ical_parse_unknown_comp(char **text, int level)
1430 ICAL_S *ical;
1431 int i;
1433 ical_debug("ical_parse_unknown_comp", *text);
1434 for(i = 0; ical_comp[i].comp
1435 && (struncmp(*text, ical_comp[i].comp, ical_comp[i].len)
1436 || struncmp(*text + ical_comp[i].len, "\r\n", 2)); i++);
1438 if(ical_comp[i].parse){
1439 *text += ical_comp[i].len + 2;
1440 ical = fs_get(sizeof(ICAL_S));
1441 memset((void *)ical, 0, sizeof(ICAL_S));
1442 ical->comp = cpystr(ical_comp[i].comp);
1443 ical->value = (ical_comp[i].parse)(text);
1444 } else
1445 ical = ical_parse_generic_comp(text, level);
1447 return ical;
1450 ICLINE_S *
1451 ical_parse_line(char **text, char *name)
1453 ICLINE_S *ic;
1454 char *s = *text;
1456 ic = fs_get(sizeof(ICLINE_S));
1457 memset((void *)ic, 0, sizeof(ICLINE_S));
1459 ic->token = cpystr(name);
1460 s += strlen(name);
1461 if(*s == ';'){
1462 s++;
1463 ic->param = ical_get_parameter(&s);
1465 if(*s == ':'){
1466 s++;
1467 ic->value = ical_get_value(&s);
1470 *text = s;
1471 return ic;
1474 /***
1475 *** PARSE PROPERTY FUNCTIONS
1476 ***/
1478 void *
1479 ical_parse_freq(void *fvalp, char *text)
1481 Freq_value *fval;
1483 fval = fs_get(sizeof(Freq_value));
1485 *fval = FUnknown;
1487 if(text == NULL) return fvalp;
1489 if(!strucmp(text, "SECONDLY")) *fval = FSecondly;
1490 else if(!strucmp(text, "MINUTELY")) *fval = FMinutely;
1491 else if(!strucmp(text, "HOURLY")) *fval = FHourly;
1492 else if(!strucmp(text, "DAILY")) *fval = FDaily;
1493 else if(!strucmp(text, "WEEKLY")) *fval = FWeekly;
1494 else if(!strucmp(text, "MONTHLY")) *fval = FMonthly;
1495 else if(!strucmp(text, "YEARLY")) *fval = FYearly;
1497 fvalp = (void *) fval;
1499 return fvalp;
1502 void *
1503 ical_parse_until(void *Tmp, char *text)
1505 struct tm *Tm;
1507 if(text != NULL){
1508 Tm = fs_get(sizeof(struct tm));
1509 ical_parse_date(text, Tm);
1510 Tmp = (void *) Tm;
1513 return Tmp;
1516 void *
1517 ical_parse_count(void *countp, char *text)
1519 int *count;
1521 if(text != NULL){
1522 count = fs_get(sizeof(int));
1523 *count = atoi(text);
1524 countp = (void *) count;
1527 return countp;
1530 void *
1531 ical_parse_weekday_list(void *bywkdyp, char *wklist)
1533 BYWKDY_S *bywkdy, *w;
1534 char *s, *t, c;
1535 int done;
1536 size_t len;
1538 bywkdy = NULL;
1539 bywkdyp = (void *) bywkdy;
1541 if(wklist == NULL) return bywkdyp;
1543 done = 0;
1544 for(t = s = wklist; done == 0; s++){
1545 if(*s != ',' && *s != '\0')
1546 continue;
1547 c = *s;
1548 if(c == ',')
1549 *s = '\0';
1550 else /* c == '\0' */
1551 done++;
1552 len = strlen(t);
1553 if(len > 1){
1554 for(w = bywkdy; w && w->next; w = w->next);
1555 w = fs_get(sizeof(BYWKDY_S));
1556 memset((void *)w, 0, sizeof(BYWKDY_S));
1557 if(!strucmp(t+len-2, "SU")) w->wd = Sunday;
1558 else if(!strucmp(t+len-2, "MO")) w->wd = Monday;
1559 else if(!strucmp(t+len-2, "TU")) w->wd = Tuesday;
1560 else if(!strucmp(t+len-2, "WE")) w->wd = Wednesday;
1561 else if(!strucmp(t+len-2, "TH")) w->wd = Thursday;
1562 else if(!strucmp(t+len-2, "FR")) w->wd = Friday;
1563 else if(!strucmp(t+len-2, "SA")) w->wd = Saturday;
1564 // t[len - 2] = '\0';
1565 if(*t != '\0')
1566 w->value = strtoul(t, &t, 10);
1567 if(bywkdy == NULL)
1568 bywkdy = w;
1570 *s = c;
1571 if(*s == ',')
1572 t = s + 1;
1575 if(bywkdyp)
1576 ical_free_weekday_list((void **)&bywkdy);
1577 else
1578 bywkdyp = (void *) bywkdy;
1580 return bywkdyp;
1583 void *
1584 ical_parse_number_list(void *bynop, char *nolist)
1586 BYWKDY_S *byno, *n;
1587 char *s, *t, c;
1588 int done = 0;
1590 byno = NULL;
1591 bynop = (void *) byno;
1593 if(nolist == NULL) return bynop;
1595 for(t = s = nolist; done == 0; s++){
1596 if(*s != ',' && *s != '\0')
1597 continue;
1598 c = *s;
1599 if(c == ',')
1600 *s = '\0';
1601 else /* c == '\0' */
1602 done++;
1604 for(n = byno; n && n->next; n = n->next);
1605 n = fs_get(sizeof(BYWKDY_S));
1606 memset((void *)n, 0, sizeof(BYWKDY_S));
1607 n->value = strtoul(t, &t, 10);
1608 if(byno == NULL)
1609 byno = n;
1610 *s = c;
1611 if(*s == ',')
1612 t = s + 1;
1615 if(bynop)
1616 ical_free_weekday_list((void **)&byno);
1617 else
1618 bynop = (void *) byno;
1620 return bynop;
1623 void *
1624 ical_parse_rrule(void *rrulep, char **text, char *token)
1626 RRULE_S *rrule;
1627 ICLINE_S *icl;
1628 char *s;
1629 ICAL_PARAMETER_S *param, *p;
1630 int i;
1632 if(text == NULL || *text == NULL || struncmp(*text, "RRULE", 5))
1633 return rrulep;
1635 rrule = fs_get(sizeof(RRULE_S));
1636 memset((void *) rrule, 0, sizeof(RRULE_S));
1638 /* recurring rules are special. First, we parse the icline that contains it */
1639 icl = ical_parse_line(text, token);
1641 /* now we copy the parameters that it contains */
1642 rrule->param = ical_parameter_cpy(icl->param);
1644 /* then we parse icl->value as if it was a parameter */
1645 s = icl->value;
1646 param = ical_get_parameter(&s);
1648 /* now we check which values were given, and fill the prop array */
1649 rrule->prop = fs_get((RRUnknown+1)*sizeof(void *));
1650 memset((void *) rrule->prop, 0, (RRUnknown+1)*sizeof(void *));
1652 for(p = param; p != NULL; p = p->next){
1653 for(i = 0; rrule_prop[i].prop != NULL && strucmp(p->name, rrule_prop[i].prop); i++);
1654 if(rrule_prop[i].parse){
1655 void *v = rrule->prop[rrule_prop[i].pos];
1656 v = (rrule_prop[i].parse)(v, p->value);
1657 rrule->prop[rrule_prop[i].pos] = v;
1660 rrule->prop[RRUnknown] = NULL;
1662 ical_free_param(&param);
1663 ical_free_cline((void **)&icl);
1665 if(rrulep)
1666 ical_free_rrule((void **)&rrule);
1667 else
1668 rrulep = (void *) rrule;
1670 return rrulep;
1673 /*** UTF-8 for ICAL ***/
1676 ical_non_ascii_valid(unsigned char c)
1678 static unsigned char icu[6];
1679 static int utf8_len = 0;
1680 static int utf8_type = 0;
1681 int rv = 0;
1683 if(utf8_len == 0)
1684 utf8_type = (c >= 0xF0 && c <= 0xF4)
1685 ? 4 : (c >= 0xE0 && c <= 0xEF)
1686 ? 3 : (c >= 0xC2 && c <= 0xDF)
1687 ? 2 : 0;
1689 if(utf8_type == 0)
1690 return 0;
1692 icu[utf8_len++] = c; /* count it */
1694 if(utf8_type == 2){
1695 if(utf8_len < 2)
1696 rv = NEED_MORE;
1697 else if(utf8_len == 2){
1698 rv = (icu[0] >= 0xC2 && icu[0] <= 0xDF)
1699 && (icu[1] >= 0x80 && icu[1] <= 0xBF) ? UTF8_COMPLETE : 0;
1700 utf8_len = 0;
1702 } else if (utf8_type == 3){
1703 if(utf8_len < 3)
1704 rv = NEED_MORE;
1705 else{
1706 if(icu[0] == 0xE0)
1707 rv = (icu[1] >= 0xA0 && icu[1] <= 0xBF)
1708 && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0;
1709 else if(icu[0] >= 0xE1 && icu[0] <= 0xEC)
1710 rv = (icu[1] >= 0x80 && icu[1] <= 0xBF)
1711 && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0;
1712 else if(icu[0] == 0xED)
1713 rv = (icu[1] >= 0x80 && icu[1] <= 0x9F)
1714 && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0;
1715 else if(icu[0] >= 0xE1 && icu[0] <= 0xEC)
1716 rv = (icu[1] >= 0x80 && icu[1] <= 0xBF)
1717 && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0;
1718 utf8_len = 0;
1720 } else if (utf8_type == 4){
1721 if(utf8_len < 4)
1722 rv = NEED_MORE;
1723 else{
1724 if(icu[0] == 0xF0)
1725 rv = (icu[1] >= 0x90 && 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] >= 0xF1 && icu[0] <= 0xF3)
1729 rv = (icu[1] >= 0x80 && icu[1] <= 0xBF)
1730 && (icu[2] >= 0x80 && icu[2] <= 0xBF)
1731 && (icu[3] >= 0x80 && icu[3] <= 0xBF) ? UTF8_COMPLETE : 0;
1732 else if(icu[0] == 0xF4)
1733 rv = (icu[1] >= 0x80 && icu[1] <= 0x8F)
1734 && (icu[2] >= 0x80 && icu[2] <= 0xBF)
1735 && (icu[3] >= 0x80 && icu[3] <= 0xBF) ? UTF8_COMPLETE : 0;
1736 utf8_len = 0;
1739 return rv;
1743 ical_get_number_value(char *value, int beg_pos, int end_pos)
1745 char c, *err;
1746 int rv;
1748 c = value[end_pos];
1749 value[end_pos] = '\0';
1750 rv = strtoul(value + beg_pos, &err, 10);
1751 if(err != NULL && *err != '\0') return -1;
1752 value[end_pos] = c;
1753 return rv;
1756 void
1757 ical_free_duration(ICAL_DURATION_S **ic_d)
1759 if(ic_d == NULL || *ic_d == NULL)
1760 return;
1762 if((*ic_d)->next) ical_free_duration(&(*ic_d)->next);
1763 fs_give((void **)ic_d);
1766 /* returns 0 if no error, -1 if some error */
1768 ical_parse_duration(char *value, ICAL_DURATION_S *ic_d)
1770 int i, j = 0, rv = 0;
1772 if(value == NULL || ic_d == NULL) return -1;
1774 memset((void *)ic_d, 0, sizeof(ICAL_DURATION_S));
1776 if(value[i = 0] == '-'){
1777 i++;
1778 ic_d->sign = 1;
1779 } else if(value[i] == '+')
1780 i++;
1782 if(value[i++] == 'P'){
1783 for(j = i; value[j] != '\0' && value[j] != ','; j++){
1784 if(!isdigit(value[j]))
1785 switch(value[j]){
1786 case 'W': ic_d->weeks = ical_get_number_value(value, i, j-1);
1787 i = ++j;
1788 break;
1789 case 'D': ic_d->days = ical_get_number_value(value, i, j-1);
1790 i = ++j;
1791 break;
1792 case 'H': ic_d->hours = ical_get_number_value(value, i, j-1);
1793 i = ++j;
1794 break;
1795 case 'M': ic_d->minutes = ical_get_number_value(value, i, j-1);
1796 i = ++j;
1797 break;
1798 case 'S': ic_d->seconds = ical_get_number_value(value, i, j-1);
1799 i = ++j;
1800 break;
1801 case 'T': i = j + 1;
1802 break;
1803 default: rv = -1;
1804 break;
1808 else
1809 rv = -1;
1811 if(value[j++] == ','){
1812 ICAL_DURATION_S next;
1813 rv = ical_parse_duration(value+j, &next);
1814 ic_d->next = &next;
1817 return rv;
1820 /* return -1 if any error,
1821 ICAL_DATE_TIME if value has the DATE-TIME form
1822 ICAL_DATE if value has the DATE form only
1823 ICAL_DATE_TIME_GMT if value has the DATE-TIME form and is in GMT.
1826 ical_parse_date(char *value, struct tm *t)
1828 int i, rv;
1829 struct tm Tm;
1831 rv = -1;
1832 if(t == NULL) return rv;
1833 memset((void *)&Tm, 0, sizeof(struct tm));
1835 if(value == NULL) return rv;
1837 rv = ICAL_DATE_TIME; /* assume DATE-TIME format */
1838 /* a simple check for the format of the string */
1839 for(i = 0; isdigit(value[i]); i++);
1840 if (i == 8 && value[i] == '\0')
1841 rv = ICAL_DATE;
1842 else
1843 if (i != 8 || value[i++] != 'T') return -1;
1844 if(rv == ICAL_DATE_TIME) {
1845 for(; isdigit(value[i]); i++);
1846 if(i != 15 || (value[i] != '\0' && (value[i] != 'Z' || value[i+1] != '\0')))
1847 return -1;
1848 if(i == 15 && value[i] == 'Z')
1849 rv = ICAL_DATE_TIME_GMT;
1852 Tm.tm_year = ical_get_number_value(value, 0, 4) - 1900;
1853 Tm.tm_mon = ical_get_number_value(value, 4, 6) - 1;
1854 Tm.tm_mday = ical_get_number_value(value, 6, 8);
1855 if(rv != ICAL_DATE){
1856 Tm.tm_hour = ical_get_number_value(value, 9, 11);
1857 Tm.tm_min = ical_get_number_value(value, 11, 13);
1858 Tm.tm_sec = ical_get_number_value(value, 13, 15);
1859 Tm.tm_isdst = rv | ICAL_DST_UNKNOWN;
1861 else
1862 Tm.tm_isdst = rv;
1863 *t = Tm;
1865 return (t->tm_mon > 11 || t->tm_mon < 0
1866 || t->tm_mday > 31 || t->tm_mday < 0
1867 || t->tm_hour > 23 || t->tm_hour < 0
1868 || t->tm_min > 59 || t->tm_min < 0
1869 || t->tm_sec > 60 || t->tm_sec < 0)
1870 ? - 1 : rv;
1873 void
1874 ical_set_date(ICLINE_S *icl, VTIMEZONE_S *vtz)
1876 int date_form; /* date forms from section 3.3.4 in RFC 5545 */
1877 ICAL_PARAMETER_S *param;
1878 char *tz = NULL;
1879 struct tm ic_date;
1881 if(icl == NULL) return;
1883 for(param = icl->param; param != NULL; param = param->next)
1884 if(!strucmp(param->name, "TZID"))
1885 tz = param->value;
1887 if(tz != NULL)
1888 date_form = 3; /* local time with timezone */
1889 else if(icl->value[strlen(icl->value)-1] == 'Z')
1890 date_form = 2; /* utc time */
1891 else date_form = 1; /* local time */
1893 ical_parse_date(icl->value, &ic_date);
1894 ic_date.tm_wday = ical_day_of_week(ic_date); /* find out day of the week */
1896 switch(date_form){
1897 case 1: break;
1898 case 2: ical_adjust_date(&ic_date, vtz);
1899 break;
1900 case 3: break;
1901 default: alpine_panic ("Impossible date_form");
1905 ICAL_TZPROP_S *
1906 ical_std_or_daylight(struct tm *date, VTIMEZONE_S *vtz)
1908 struct tm standard, daylight;
1909 ICLINE_S *tzid = (ICLINE_S *) vtz->prop[TZCid];
1911 // standard = daylight;
1913 return NULL;
1918 /* adjusts time to given time zone */
1919 void
1920 ical_adjust_date(struct tm *date, VTIMEZONE_S *vtz)
1922 char *tzname = NULL;
1923 ICLINE_S *icl;
1924 ICAL_TZPROP_S *cur_std_day;
1926 if(vtz == NULL)
1927 return;
1929 if(vtz->prop){
1930 if((icl = (ICLINE_S *)vtz->prop[TZCid]) != NULL)
1931 tzname = cpystr(icl->value);
1934 cur_std_day = ical_std_or_daylight(date, vtz);
1937 void
1938 ical_set_date_vevent(void *veventv, void *vtzv)
1940 VEVENT_S *vevent = (VEVENT_S *) veventv;
1941 VTIMEZONE_S *vtz = (VTIMEZONE_S *) vtzv;
1943 if(vevent){
1944 ical_set_date(vevent->prop[EvDtstamp], vtz);
1945 ical_set_date(vevent->prop[EvDtstart], vtz);
1946 ical_set_date(vevent->prop[EvDtend], vtz);
1950 #define LEAP_YEAR(X) ((((X) % 4 == 0) \
1951 && (((X) % 100 != 0) || ((X) % 400 == 0))) \
1952 || (X) == 1700)
1954 #define CAL_OFFSET(X) (((X) == 1752) ? 5 : (LEAP_YEAR((X)) ? 2 : 1))
1956 /* given a year, after day_zero, return the day
1957 * of the week of the first of january of that year. On error,
1958 * return a negative number.
1959 * Assumption: day_zero is the date of january 1, of some year.
1962 ical_january_first(int year)
1964 int i, january_first;
1966 if(year < day_zero.tm_year) return -1; /* not supported */
1968 year += 1900;
1969 january_first = day_zero.tm_wday;
1970 for(i = 1900 + day_zero.tm_year + 1; i <= year; i++)
1971 january_first += CAL_OFFSET(i-1);
1973 return january_first % 7;
1976 /* given a month, week day, and year, return all days of the month
1977 * that have that day as the week day. For example, return all
1978 * sundays in november 2012.
1980 int *
1981 ical_day_from_week(int month, Weekday day, int year)
1983 int *rv = NULL;
1984 int fday, nday;
1985 Weekday wday;
1986 int i;
1988 fday = ical_first_of_month(month, year);
1989 year += 1900; /* restore year */
1990 if(year == 1752 && month == 8){
1991 fday = 9;
1992 } else {
1993 for(nday = 1, wday = (Weekday) fday; wday != day; wday = (wday+1) % 7, nday++)
1995 rv = fs_get(6*sizeof(int));
1996 memset((void *) rv, 0, 6*sizeof(int));
1997 for(i = 0; nday <= month_len[month]; i++){
1998 rv[i] = nday;
1999 nday += 7;
2001 if(LEAP_YEAR(year) && month == 1 && nday == 29)
2002 rv[i] = nday;
2005 return rv;
2009 /* given a month and a year, return the weekday of the first of the
2010 * month in that year.
2011 * return value: on error -1, otherwise the day of the week.
2014 ical_first_of_month(int month, int year)
2016 int i, d;
2018 if((d = ical_january_first(year)) < 0)
2019 return -1;
2021 year += 1900;
2022 for(i = 0; i < month; i++)
2023 d += month_len[i];
2025 if(LEAP_YEAR(year) && month >= 2)
2026 d += 1;
2028 if(year == 1752 && month >= 9)
2029 d -= 11;
2031 return d % 7;
2034 /* given a day, month and year, return the weekday of that day
2035 * return value: on error -1, otherwise the day of the week.
2038 ical_day_of_week(struct tm date)
2040 int d;
2042 if((d = ical_first_of_month(date.tm_mon, date.tm_year)) < 0)
2043 return -1;
2045 d += date.tm_mday - 1;
2047 if(date.tm_year + 1900 == 1752){
2048 if(date.tm_mday > 2 && date.tm_mday < 14)
2049 return -1;
2050 if(date.tm_mday >= 14)
2051 d -= 11;
2053 return d % 7;
2057 /* given an initial date dtstart, and a recurring rule, rrule,
2058 * adjust the date to the first date on the same year, when
2059 * the rule actually starts
2061 struct tm
2062 adjust_date_rrule(struct tm *dtstart, RRULE_S *rrule)
2064 struct tm t;
2066 memset((void *) &t, 0, sizeof(struct tm));
2067 t.tm_year = dtstart->tm_year; /* same year */
2068 if(rrule->prop[RRFreq]){
2070 if(rrule->prop[RRCount]){
2072 else if(rrule->prop[RRInterval]){
2074 if(rrule->prop[RRBysecond]){
2075 BYWKDY_S *sec = (BYWKDY_S *) rrule->prop[RRBysecond], *seco;
2076 for (seco = sec; seco != NULL; seco = seco->next)
2077 if(seco == sec) t.tm_sec = seco->value;
2078 else if (seco->value < t.tm_sec)
2079 t.tm_sec = seco->value;
2081 if (rrule->prop[RRByminute]){
2082 BYWKDY_S *sec = (BYWKDY_S *) rrule->prop[RRByminute], *seco;
2083 for (seco = sec; seco != NULL; seco = seco->next)
2084 if(seco == sec) t.tm_min = seco->value;
2085 else if (seco->value < t.tm_sec)
2086 t.tm_min = seco->value;
2088 if (rrule->prop[RRByhour]){
2089 BYWKDY_S *sec = (BYWKDY_S *) rrule->prop[RRByhour], *seco;
2090 for (seco = sec; seco != NULL; seco = seco->next)
2091 if(seco == sec) t.tm_hour = seco->value;
2092 else if (seco->value < t.tm_sec)
2093 t.tm_hour = seco->value;
2095 if (rrule->prop[RRByday]){
2097 if (rrule->prop[RRByweekno]){
2099 if (rrule->prop[RRBymonthday]){
2101 if (rrule->prop[RRByyearday]){
2103 if (rrule->prop[RRByweekno]){
2105 if (rrule->prop[RRBymonth]){
2106 BYWKDY_S *m = (BYWKDY_S *) rrule->prop[RRBymonth], *mo;
2107 for (mo = m; mo != NULL; mo = mo->next)
2108 if(mo == m) t.tm_mon = mo->value - 1;
2109 else if (mo->value - 1 < t.tm_mon)
2110 t.tm_mon = mo->value - 1;
2112 if (rrule->prop[RRBysetpos]){
2114 if (rrule->prop[RRWkst]){
2116 return t;
2119 void
2120 ical_initialize(void)
2122 static int inited = 0;
2124 if(inited != 0)
2125 return;
2127 ical_buf_len = 1024;
2128 ical_buf = fs_get(ical_buf_len+1);
2130 memset((void *) &day_zero, 0, sizeof(struct tm));
2131 day_zero.tm_year = 1601 - 1900;
2132 day_zero.tm_mday = 1;
2133 day_zero.tm_wday = 4;
2135 inited++;
2138 /* At this time, we are going to print the date in 24 hour format
2139 * if there is no string for AM or PM, but we use AM or PM when available.
2140 * We plan to make this user configurable, but not today...
2142 void
2143 ical_date_time (char *tmp, size_t len, struct tm *ic_datep)
2145 /* test of the AM/PM string is available */
2146 our_strftime(tmp, len, "%p", ic_datep);
2148 if(tmp[0])
2149 our_strftime(tmp, len, "%a %x %I:%M %p", ic_datep);
2150 else
2151 our_strftime(tmp, len, "%a %x %H:%M", ic_datep);
2154 /* If the icline has a TZID parameter, return its value, otherwise, return
2155 * NULL. Returned value freed by caller.
2157 char *
2158 ical_get_tzid(ICAL_PARAMETER_S *param)
2160 char *tzid = NULL;
2162 if(param == NULL)
2163 return tzid;
2165 if(strucmp(param->name, "TZID") == 0)
2166 tzid = cpystr(param->value);
2167 else
2168 tzid = ical_get_tzid(param->next);
2170 return tzid;
2173 /* we create a summary of the event, and pass that back as
2174 an ical parameter
2176 VEVENT_SUMMARY_S *
2177 ical_vevent_summary(VCALENDAR_S *vcal)
2179 VEVENT_SUMMARY_S *rv, *vsummary= NULL;
2180 ICLINE_S *method;
2181 VEVENT_S *vevent;
2182 GEN_ICLINE_S *gicl;
2183 ICLINE_S *icl;
2184 static int offset = 1000000; /* impossible value */
2185 static int dst;
2186 time_t ourtime;
2188 if(vcal == NULL) return NULL;
2190 if(offset == 1000000)
2191 offset = timezone_offset_to_gmt(&dst);
2193 method = vcal->method;
2195 vevent = (VEVENT_S *) vcal->comp[VEvent];
2196 if(vevent == NULL || vevent->prop == NULL)
2197 return NULL;
2199 for(vevent = (VEVENT_S *) vcal->comp[VEvent];
2200 vevent != NULL && vevent->prop != NULL;
2201 vevent = vevent->next, rv = rv->next){
2203 rv = fs_get(sizeof(VEVENT_SUMMARY_S));
2204 memset((void *) rv, 0, sizeof(VEVENT_SUMMARY_S));
2206 if(method != NULL && !strucmp(method->value, "CANCEL"))
2207 rv->cancel++;
2209 if((icl = (ICLINE_S *) vevent->prop[EvPriority]) != NULL)
2210 rv->priority = atoi(icl->value);
2212 if((icl = (ICLINE_S *) vevent->prop[EvSummary]) != NULL){
2213 rv->summary = cpystr(icl->value ? icl->value : _("No Summary"));
2214 ical_remove_escapes(&rv->summary);
2217 if((icl = (ICLINE_S *) vevent->prop[EvClass]) != NULL)
2218 rv->class = cpystr(icl->value ? icl->value : _("PUBLIC"));
2219 else
2220 rv->class = cpystr(_("PUBLIC"));
2222 if((icl = (ICLINE_S *) vevent->prop[EvOrganizer]) != NULL){
2223 char *cn, *sender, *address;
2224 ICAL_PARAMETER_S *param;
2226 cn = sender = address = NULL;
2227 for(param = icl->param; param != NULL; param = param->next)
2228 if(!strucmp(param->name, "CN"))
2229 cn = param->value;
2230 else if(!strucmp(param->name, "SENT-BY"))
2231 sender = param->value;
2233 if(sender != NULL){
2234 if(!struncmp(sender, "MAILTO:", 7))
2235 sender += 7;
2236 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "<%s>", sender);
2237 rv->sender = cpystr(tmp_20k_buf);
2240 if((address = icl->value) != NULL){
2241 if(!struncmp(address, "MAILTO:", 7))
2242 address += 7;
2243 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s<%s>",
2244 cn ? cn : "", cn ? " " : "",
2245 address ? address : _("Unknown address"));
2246 rv->organizer = cpystr(tmp_20k_buf);
2248 } /* end of if(organizer) */
2250 if((icl = (ICLINE_S *) vevent->prop[EvLocation]) != NULL){
2251 rv->location = cpystr(icl->value ? icl->value : _("Location undisclosed"));
2252 ical_remove_escapes(&rv->location);
2255 if((icl = (ICLINE_S *) vevent->prop[EvDtstart]) != NULL){
2256 struct tm ic_date;
2257 char tmp[200], *tzid;
2258 int icd; /* ical date return value */
2260 memset((void *)&ic_date, 0, sizeof(struct tm));
2261 icd = ical_parse_date(icl->value, &ic_date) & 0x00fff;
2262 tzid = ical_get_tzid(icl->param);
2263 if(icd >= 0){
2264 ic_date.tm_wday = ical_day_of_week(ic_date);
2265 if(ic_date.tm_isdst & ICAL_DATE_TIME_GMT){ /* GMT time */
2266 ic_date.tm_isdst = dst;
2267 ourtime = mktime(&ic_date);
2268 if(ourtime != (time_t) -1){
2269 ourtime += offset;
2270 ic_date = *localtime(&ourtime);
2273 ic_date.tm_isdst = dst;
2274 switch(icd){
2275 case ICAL_DATE_TIME: /* DATE-TIME */
2276 ical_date_time(tmp, sizeof(tmp), &ic_date);
2277 break;
2278 case ICAL_DATE: /* DATE */
2279 our_strftime(tmp, sizeof(tmp), "%a %x", &ic_date);
2280 break;
2281 case ICAL_DATE_TIME_GMT: /* DATE-TIME in GMT */
2282 our_strftime(tmp, sizeof(tmp), "%a %x %I:%M %p", &ic_date);
2283 break;
2284 default: alpine_panic("Unhandled ical date format");
2285 break;
2288 else{
2289 strncpy(tmp, _("Error while parsing event date"), sizeof(tmp));
2290 tmp[sizeof(tmp) - 1] = '\0';
2293 if(icl->value == NULL)
2294 rv->evstart = cpystr(_("Unknown Start Date"));
2295 else{
2296 size_t len = strlen(tmp) + 1;
2298 if(tzid != NULL)
2299 len += strlen(tzid) + 3; /* 3 = strlen(" ()") */
2301 rv->evstart = fs_get(len*sizeof(char));
2302 snprintf(rv->evstart, len, "%s%s%s%s", tmp,
2303 tzid != NULL ? " (" : "",
2304 tzid != NULL ? tzid : "",
2305 tzid != NULL ? ")" : "");
2306 rv->evstart[len-1] = '\0';
2308 if(tzid)
2309 fs_give((void **)&tzid);
2310 } /* end of if dtstart */
2312 if((icl = (ICLINE_S *) vevent->prop[EvDuration]) != NULL){
2313 int i, done = 0;
2314 ICAL_DURATION_S ic_d, icd2;
2315 if(ical_parse_duration(icl->value, &ic_d) == 0){
2316 char tmp[MAILTMPLEN+1];
2318 for(i = 1, icd2 = ic_d; icd2.next != NULL; icd2 = *icd2.next, i++);
2319 rv->duration = fs_get((i+1)*sizeof(char *));
2320 i = 0;
2322 do {
2323 tmp[0] = '\0';
2325 if(ic_d.weeks > 0)
2326 utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
2327 "%d %s ", ic_d.weeks, ic_d.weeks == 1 ? _("week") : _("weeks"));
2328 if(ic_d.days > 0)
2329 utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
2330 "%d %s ", ic_d.days, ic_d.days == 1 ? _("day") : _("days"));
2331 if(ic_d.hours > 0)
2332 utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
2333 "%d %s ", ic_d.hours, ic_d.hours == 1 ? _("hour") : _("hours"));
2334 if(ic_d.minutes > 0)
2335 utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
2336 "%d %s ", ic_d.minutes, ic_d.minutes == 1 ? _("minute") : _("minutes"));
2337 if(ic_d.seconds > 0)
2338 utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
2339 "%d %s ", ic_d.seconds, ic_d.seconds == 1 ? _("second") : _("seconds"));
2341 tmp[MAILTMPLEN] = '\0';
2342 rv->duration[i++] = cpystr(tmp);
2344 if(ic_d.next != NULL)
2345 ic_d = *ic_d.next;
2346 else
2347 done++;
2348 } while (done == 0);
2349 rv->duration[i] = NULL;
2351 } /* end of DURATION */
2352 else if((icl = (ICLINE_S *) vevent->prop[EvDtend]) != NULL){
2353 struct tm ic_date;
2354 char tmp[200], *tzid;
2355 int icd;
2357 memset((void *)&ic_date, 0, sizeof(struct tm));
2358 icd = ical_parse_date(icl->value, &ic_date) & 0x00fff;
2359 tzid = ical_get_tzid(icl->param);
2360 if(icd >= 0){
2361 ic_date.tm_wday = ical_day_of_week(ic_date);
2362 if(ic_date.tm_isdst & ICAL_DATE_TIME_GMT){ /* GMT time */
2363 ic_date.tm_isdst = dst;
2364 ourtime = mktime(&ic_date);
2365 if(ourtime != (time_t) -1){
2366 ourtime += offset;
2367 ic_date = *localtime(&ourtime);
2370 ic_date.tm_isdst = dst;
2371 switch(icd){
2372 case ICAL_DATE_TIME: /* DATE-TIME */
2373 ical_date_time(tmp, sizeof(tmp), &ic_date);
2374 break;
2375 case ICAL_DATE: /* DATE */
2376 our_strftime(tmp, sizeof(tmp), "%a %x", &ic_date);
2377 break;
2378 case ICAL_DATE_TIME_GMT: /* DATE-TIME in GMT */
2379 our_strftime(tmp, sizeof(tmp), "%a %x %I:%M %p", &ic_date);
2380 break;
2381 default: alpine_panic("Unhandled ical date format");
2382 break;
2385 else{
2386 strncpy(tmp, _("Error while parsing event date"), sizeof(tmp));
2387 tmp[sizeof(tmp) - 1] = '\0';
2390 if(icl->value == NULL)
2391 rv->evend = cpystr(_("Unknown End Date"));
2392 else{
2393 size_t len = strlen(tmp) + 1;
2395 if(tzid != NULL)
2396 len += strlen(tzid) + 3; /* 3 = strlen(" ()") */
2398 rv->evend = fs_get(len*sizeof(char));
2399 snprintf(rv->evend, len, "%s%s%s%s", tmp,
2400 tzid != NULL ? " (" : "",
2401 tzid != NULL ? tzid : "",
2402 tzid != NULL ? ")" : "");
2403 rv->evend[len-1] = '\0';
2405 if(tzid)
2406 fs_give((void **)&tzid);
2407 } /* end of if dtend */
2409 if((icl = (ICLINE_S *) vevent->prop[EvDtstamp]) != NULL){
2410 struct tm ic_date;
2411 char tmp[200], *tzid;
2412 int icd;
2414 memset((void *)&ic_date, 0, sizeof(struct tm));
2415 icd = ical_parse_date(icl->value, &ic_date) & 0x00fff;
2416 tzid = ical_get_tzid(icl->param);
2417 if(icd >= 0){
2418 ic_date.tm_wday = ical_day_of_week(ic_date);
2419 if(ic_date.tm_isdst & ICAL_DATE_TIME_GMT){ /* GMT time */
2420 ic_date.tm_isdst = dst;
2421 ourtime = mktime(&ic_date);
2422 if(ourtime != (time_t) -1){
2423 ourtime += offset;
2424 ic_date = *localtime(&ourtime);
2427 ic_date.tm_isdst = dst;
2428 switch(icd){
2429 case ICAL_DATE_TIME: /* DATE-TIME */
2430 ical_date_time(tmp, sizeof(tmp), &ic_date);
2431 break;
2432 case ICAL_DATE: /* DATE */
2433 our_strftime(tmp, sizeof(tmp), "%a %x", &ic_date);
2434 break;
2435 case ICAL_DATE_TIME_GMT: /* DATE-TIME in GMT */
2436 our_strftime(tmp, sizeof(tmp), "%a %x %I:%M %p", &ic_date);
2437 break;
2438 default: alpine_panic("Unhandled ical date format");
2439 break;
2442 else{
2443 strncpy(tmp, _("Error while parsing event date"), sizeof(tmp));
2444 tmp[sizeof(tmp) - 1] = '\0';
2446 if(icl->value == NULL)
2447 rv->dtstamp = cpystr(_("Unknown when event was scheduled"));
2448 else{
2449 size_t len = strlen(tmp) + 1;
2451 if(tzid != NULL)
2452 len += strlen(tzid) + 3; /* 3 = strlen(" ()") */
2454 rv->dtstamp = fs_get(len*sizeof(char));
2455 snprintf(rv->dtstamp, len, "%s%s%s%s", tmp,
2456 tzid != NULL ? " (" : "",
2457 tzid != NULL ? tzid : "",
2458 tzid != NULL ? ")" : "");
2459 rv->dtstamp[len-1] = '\0';
2461 } /* end of if dtstamp */
2463 if((gicl = (GEN_ICLINE_S *) vevent->prop[EvAttendee]) != NULL){
2464 int nattendees, i;
2466 for(nattendees = 0; gicl != NULL; gicl = gicl->next, nattendees++);
2467 rv->attendee = fs_get((nattendees+1)*sizeof(char *));
2469 gicl = (GEN_ICLINE_S *) vevent->prop[EvAttendee];
2470 for(i = 0; gicl != NULL; gicl = gicl->next, i++){
2471 char *role, *partstat, *rsvp;
2472 char *cn, *mailto;
2473 ICAL_PARAMETER_S *param;
2475 icl = gicl->cline;
2476 role = partstat = rsvp = cn = mailto = NULL;
2477 for(param = icl->param; param != NULL; param = param->next){
2478 if(!strucmp(param->name, "ROLE")){
2479 if(!strucmp(param->value, "REQ-PARTICIPANT"))
2480 role = _("[Required]");
2481 else if(!strucmp(param->value, "OPT-PARTICIPANT"))
2482 role = _("[Optional]");
2483 else if(!strucmp(param->value, "NON-PARTICIPANT"))
2484 role = _("[Informed]");
2485 else if(!strucmp(param->value, "CHAIR"))
2486 role = _("[ Chair ]");
2487 else
2488 role = param->value;
2490 else if(!strucmp(param->name, "PARTSTAT")){
2491 if(!strucmp(param->value, "NEEDS-ACTION"))
2492 partstat = _("[Need-Reply]");
2493 else if(!strucmp(param->value, "ACCEPTED"))
2494 partstat = _("[ Accepted ]");
2495 else if(!strucmp(param->value, "DECLINED"))
2496 partstat = _("[ Declined ]");
2497 else if(!strucmp(param->value, "TENTATIVE"))
2498 partstat = _("[ Tentative]");
2499 else if(!strucmp(param->value, "DELEGATED"))
2500 partstat = _("[ Delegated]");
2501 else
2502 partstat = param->value;
2504 else if(!strucmp(param->name, "RSVP"))
2505 rsvp = param->value;
2506 else if(!strucmp(param->name, "CN"))
2507 cn = param->value;
2509 if(icl->value && !struncmp(icl->value, "MAILTO:", strlen("MAILTO:")))
2510 mailto = icl->value + 7; /* 7 = strlen("MAILTO:") */
2511 if(!strucmp(cn, mailto))
2512 cn = "";
2513 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s%s%s%s<%s>",
2514 role && *role ? role : "",
2515 role && *role ? " " : "",
2516 partstat ? partstat : _("[Unknown Reply]"),
2517 " ",
2518 cn && *cn ? cn : "",
2519 cn && *cn ? " " : "",
2520 mailto ? mailto : _("Unknown address"));
2521 rv->attendee[i] = cpystr(tmp_20k_buf);
2523 rv->attendee[i] = NULL;
2524 } /* end of ATTENDEES */
2526 if((icl = (ICLINE_S *) vevent->prop[EvDescription]) != NULL){
2527 char *s, *t, *u, *v;
2528 int i, escaped;
2530 if(icl->value == NULL){
2531 free_vevent_summary(&rv);
2532 return vsummary;
2535 v = cpystr(icl->value); /* process a copy of icl->value */
2537 for(i = 1, escaped = 0, s = v; s && *s; s++){
2538 if(*s == '\\' && escaped == 0){ escaped = 1; continue; }
2539 if(escaped){
2540 if(!(*s == '\\' || *s == ',' || *s == 'n' || *s == 'N' || *s == ';')){
2541 free_vevent_summary(&rv);
2542 fs_give((void **)&v);
2543 return vsummary;
2545 escaped = 0;
2546 continue;
2548 if(*s == ',') i++; /* a non-scaped comma is a new value for text */
2551 rv->description = fs_get((i+1)*sizeof(unsigned char *));
2552 i = 0;
2553 for (s = t = u = v, escaped = 0; *t != '\0'; t++){
2554 if(*t == '\\' && escaped == 0){ escaped = 1; continue; }
2555 if(escaped){
2556 switch(*t){
2557 case '\\':
2558 case ',':
2559 case ';':
2560 *u++ = *t;
2561 break;
2562 case 'n':
2563 case 'N':
2564 *u++ = '\n';
2565 break;
2566 default: free_vevent_summary(&rv);
2567 fs_give((void **)&v);
2568 return NULL;
2570 escaped = 0;
2571 continue;
2573 if(*t == ','){
2574 *u = '\0';
2575 rv->description[i++] = (unsigned char *) cpystr((char *) ical_decode(s, vcal->encoding));
2576 s = u = t+1;
2577 } else
2578 *u++ = *t;
2580 *u = '\0';
2581 rv->description[i++] = (unsigned char *) cpystr((char *) ical_decode(s, vcal->encoding));
2582 rv->description[i] = NULL;
2583 fs_give((void **)&v);
2584 } /* end of if(description) */
2585 /* last instruction of the loop */
2586 if(vsummary == NULL)
2587 vsummary = rv;
2588 else{
2589 VEVENT_SUMMARY_S *vesy;
2590 for(vesy = vsummary; vesy && vesy->next; vesy = vesy->next);
2591 vesy->next = rv;
2593 } /* end of "for" loop */
2594 return vsummary;
2597 void
2598 free_vevent_summary(VEVENT_SUMMARY_S **vesy)
2600 int i;
2601 if(vesy == NULL || *vesy == NULL) return;
2603 if((*vesy)->class) fs_give((void **)&(*vesy)->class);
2604 if((*vesy)->summary) fs_give((void **)&(*vesy)->summary);
2605 if((*vesy)->sender) fs_give((void **)&(*vesy)->sender);
2606 if((*vesy)->organizer) fs_give((void **)&(*vesy)->organizer);
2607 if((*vesy)->location) fs_give((void **)&(*vesy)->location);
2608 if((*vesy)->evstart) fs_give((void **)&(*vesy)->evstart);
2609 if((*vesy)->evend) fs_give((void **)&(*vesy)->evend);
2610 if((*vesy)->dtstamp) fs_give((void **)&(*vesy)->dtstamp);
2611 if((*vesy)->duration){
2612 for(i = 0; (*vesy)->duration[i] != NULL; i++)
2613 fs_give((void **) &(*vesy)->duration[i]);
2614 fs_give((void **) (*vesy)->duration);
2616 if((*vesy)->attendee){
2617 for(i = 0; (*vesy)->attendee[i] != NULL; i++)
2618 fs_give((void **) &(*vesy)->attendee[i]);
2619 fs_give((void **) &(*vesy)->attendee);
2621 if((*vesy)->description){
2622 for(i = 0; (*vesy)->description[i] != NULL; i++)
2623 fs_give((void **) &(*vesy)->description[i]);
2624 fs_give((void **) &(*vesy)->description);
2626 if((*vesy)->next) free_vevent_summary(&(*vesy)->next);
2627 fs_give((void **) vesy);
2630 void
2631 ical_free_all(void)
2633 if(ical_buf)
2634 fs_give((void **)&ical_buf);