* Remove dead code.
[alpine.git] / pith / ical.c
blob2d61c00efeecd220d4c53795019d94423240b1d8
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 /* parse 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 size_t tlen;
242 if(encoding == ENCQUOTEDPRINTABLE){
243 t = rfc822_qprint ((unsigned char *) text,strlen(text),&callen);
244 if(t != NULL){
245 tlen = strlen(text) + 1;
246 strncpy(text, (char *) t, tlen);
247 text[tlen - 1] = '\0';
248 fs_give((void **) &t);
251 return (unsigned char *) text;
255 /* Return code:
256 0 - if no errors
257 -1 - if an error occurred
258 Args: a pointer to the text. If there is an error, the text is not modified.
261 ical_remove_escapes(char **textp)
263 char *text, *s, *t;
264 int rv = 0;
265 int escaped;
266 size_t tlen;
268 if(textp == NULL) return 0;
270 t = cpystr(*textp); /* work on a copy of the text */
271 tlen = strlen(*textp) + 1; /* and record its size */
272 /* the variable text below points to the beginning of the filtered text */
273 for (text = s = t, escaped = 0; rv == 0 && *s != '\0'; s++){
274 if(*s == '\\' && escaped == 0){
275 escaped = 1;
276 continue;
278 if(escaped){
279 switch(*s){
280 case '\\':
281 case ',':
282 case ';':
283 *t++ = *s;
284 break;
286 case 'n':
287 case 'N':
288 *t++ = '\n';
289 break;
290 default: rv = -1;
291 break;
293 escaped = 0;
295 else *t++ = *s;
297 *t = '\0'; /* tie off filtered text */
298 t = text; /* reset t to the beginning */
299 if(rv == 0){
300 strncpy(*textp, t, tlen); /* overwrite given text with filtered text */
301 (*textp)[tlen - 1] = '\0';
303 fs_give((void **) &t);
304 return rv;
307 void
308 ical_debug(char *fcn, char *text)
310 char piece[50];
311 strncpy(piece, text, 49);
312 piece[sizeof(piece)-1] = '\0';
313 dprint((2, "%s: %s\n", fcn, piece));
316 /***
317 *** FREE MEMORY FUNCTIONS
318 ***/
320 void
321 ical_free_param(ICAL_PARAMETER_S **param)
323 if(param == NULL || *param == NULL)
324 return;
326 if((*param)->name) fs_give((void **) &(*param)->name);
327 if((*param)->value) fs_give((void **) &(*param)->value);
328 if((*param)->next) ical_free_param(&(*param)->next);
329 fs_give((void **)param);
332 void
333 ical_free_cline(void **icv)
335 ICLINE_S **ic = (ICLINE_S **) icv;
337 if(ic == NULL || *ic == NULL)
338 return;
340 if((*ic)->token) fs_give((void **) &(*ic)->token);
341 if((*ic)->param) ical_free_param(&(*ic)->param);
342 if((*ic)->value) fs_give((void **) &(*ic)->value);
343 fs_give(icv);
346 void
347 ical_free_gencline(void **giclpv)
349 GEN_ICLINE_S **giclp = (GEN_ICLINE_S **) giclpv;
351 if(giclp == NULL || *giclp == NULL) return;
353 if((*giclp)->cline) ical_free_cline((void **) &(*giclp)->cline);
354 if((*giclp)->next) ical_free_gencline((void **) &(*giclp)->next);
355 fs_give((void **)giclp);
358 void
359 ical_free_vcalendar(void **vcalpv)
361 VCALENDAR_S **vcalp = (VCALENDAR_S **)vcalpv;
363 if(vcalp == NULL || *vcalp == NULL) return;
365 if((*vcalp)->prodid) ical_free_cline((void **) &(*vcalp)->prodid);
366 if((*vcalp)->version) ical_free_cline((void **) &(*vcalp)->version);
367 if((*vcalp)->calscale) ical_free_cline((void **) &(*vcalp)->calscale);
368 if((*vcalp)->method) ical_free_cline((void **) &(*vcalp)->method);
369 if((*vcalp)->uk_prop) ical_free_gencline((void **) &(*vcalp)->uk_prop);
370 if((*vcalp)->comp){
371 Cal_comp i;
372 for(i = 0; i < VUnknown; i++)
373 if((*vcalp)->comp[i]) (ical_comp[i].give)(&(*vcalp)->comp[i]);
374 fs_give((void **) &(*vcalp)->comp);
376 if((*vcalp)->uk_comp) ical_free_unknown_comp(&(*vcalp)->uk_comp);
377 fs_give(vcalpv);
380 void
381 ical_free_vevent(void **veventpv)
383 VEVENT_S **veventp = (VEVENT_S **) veventpv;
385 if(veventp == NULL || *veventp == NULL) return;
387 ical_free_prop(&(*veventp)->prop, event_prop, EvUnknown);
388 if((*veventp)->uk_prop) ical_free_gencline((void **) &(*veventp)->uk_prop);
389 if((*veventp)->valarm) ical_free_valarm((void **) &(*veventp)->valarm);
390 if((*veventp)->next) ical_free_vevent((void **) &(*veventp)->next);
391 fs_give(veventpv);
394 void
395 ical_fs_give(void **x)
397 if(x != NULL && *x != NULL)
398 fs_give(x);
401 void
402 ical_free_rrule(void **rrulepv)
404 RRULE_S **rrulep = (RRULE_S **) rrulepv;
406 if(rrulep && *rrulep){
407 ical_free_prop(&(*rrulep)->prop, rrule_prop, RRUnknown);
408 ical_free_param(&(*rrulep)->param);
409 fs_give(rrulepv);
413 void
414 ical_free_weekday_list(void **wkdylv)
416 BYWKDY_S **wkdyl = (BYWKDY_S **) wkdylv;
418 if(wkdyl == NULL) return;
420 if((*wkdyl)->next)
421 ical_free_weekday_list((void **) &(*wkdyl)->next);
423 fs_give(wkdylv);
427 void
428 ical_free_vtodo(void **vtodopv)
432 void
433 ical_free_vjournal(void **vjournalpv)
437 void
438 ical_free_vfreebusy(void **vfbpv)
442 void
443 ical_free_prop(void ***propv, ICAL_PROP_S *aux_comp, int max)
445 int i, j;
447 if(propv == NULL || *propv == NULL) return;
449 for(i = 0; i < max; i++)
450 if((*propv)[i]){
451 for(j = 0; aux_comp[j].prop != NULL && aux_comp[j].pos != i; j++);
452 if(aux_comp[j].give) (aux_comp[j].give)(&(*propv)[i]);
454 fs_give((void **) propv);
458 void
459 ical_free_vtimezone(void **vtzpv)
461 VTIMEZONE_S **vtzp = (VTIMEZONE_S **) vtzpv;
462 TZ_comp i,j;
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;
490 int i, j;
492 if(valarmp == NULL || *valarmp == NULL) return;
494 ical_free_prop(&(*valarmp)->prop, alarm_prop, AlUnknown);
495 if((*valarmp)->uk_prop) ical_free_gencline((void **) &(*valarmp)->uk_prop);
496 if((*valarmp)->next) ical_free_timezone((void **) &(*valarmp)->next);
497 fs_give(valarmpv);
500 void
501 ical_free_unknown_comp(ICAL_S **icalp)
503 int i;
504 if(icalp == NULL || *icalp == NULL) return;
505 for(i = 0; ical_comp[i].comp && strucmp((*icalp)->comp,ical_comp[i].comp); i++);
506 if(ical_comp[i].give)
507 (ical_comp[i].give)(&(*icalp)->value);
508 else
509 ical_free_gencline((void **) &(*icalp)->value);
510 fs_give((void **)&(*icalp)->comp);
511 ical_free_unknown_comp(&(*icalp)->next);
512 ical_free_unknown_comp(&(*icalp)->branch);
513 fs_give((void **)icalp);
516 char *
517 ical_unfold_line(char *line)
519 int i, j;
521 if(line == NULL)
522 return NULL;
524 for(i = 0, j = 0; line[j] != '\0';)
525 switch(line[j]){
526 case '\r': if(line[j+1] == '\n' && ical_wspace(line[j+2])){
527 j += 3; /* get past white space */
528 continue;
530 case '\n': if(ical_wspace(line[j+1])){
531 j += 2; /* get past white space */
532 continue;
534 default : line[i++] = line[j++];
536 line[i] = '\0';
537 return line;
540 ICAL_PARAMETER_S *
541 ical_get_parameter(char **line)
543 ICAL_PARAMETER_S *param = NULL;
544 char *s;
546 if(line == NULL || *line == NULL)
547 return NULL;
549 for(s = *line; s && *s && ical_name_allowed_char(*s) ; s++);
551 if(*s == '='){
552 int quoted;
553 char c;
555 param = fs_get(sizeof(ICAL_PARAMETER_S));
556 memset((void *)param, 0, sizeof(ICAL_PARAMETER_S));
557 *s = '\0';
558 param->name = cpystr(*line);
559 *s = '=';
560 *line = s+1; /* step over '=' */
561 quoted = **line == '"' ? 1 : 0;
562 if(quoted != 0){
563 for(s = ++*line; s && *s && ical_qsafe_char((unsigned char) *s); s++);
564 if(*s != '"'){ /* error, do not parse this line */
565 ical_free_param(&param);
566 *line = strchr(s, ':'); /* reset line to closest ':' */
567 return NULL;
570 else
571 for(s = *line; s && *s && (ical_safe_char((unsigned char) *s)); s++);
572 c = *s;
573 *s = '\0';
574 param->value = cpystr(*line);
575 *s = c; /* restore character */
576 *line = quoted ? s + 1 : s;
578 if(**line == ';'){
579 ++*line;
580 param->next = ical_get_parameter(line);
583 return param;
586 char *
587 ical_get_value(char **line)
589 char *s, *t;
591 if(line == NULL || *line == NULL)
592 return NULL;
594 for (s = *line; *s && ical_value_char((unsigned char) *s); s++);
595 if(*s == '\r'){
596 *s = '\0';
597 t = cpystr(*line);
598 *s = '\r';
599 *line = s+2;
601 else{
602 t = NULL;
603 s = strchr(*line, '\r');
604 if(s != NULL)
605 *line = s + 2;
607 return t;
610 ICAL_PARAMETER_S *
611 ical_parameter_cpy(ICAL_PARAMETER_S *param)
613 ICAL_PARAMETER_S *rv;
615 if(param == NULL) return NULL;
617 rv = fs_get(sizeof(ICAL_PARAMETER_S));
618 memset((void *)rv, 0, sizeof(ICAL_PARAMETER_S));
620 if(param->name) rv->name = cpystr(param->name);
621 if(param->value) rv->value = cpystr(param->value);
622 if(param->next) rv->next = ical_parameter_cpy(param->next);
624 return rv;
627 ICLINE_S *
628 ical_cline_cpy(ICLINE_S *icl)
630 ICLINE_S *rv;
632 if(icl == NULL)
633 return NULL;
635 rv = fs_get(sizeof(ICLINE_S));
636 memset((void *)rv, 0, sizeof(ICLINE_S));
638 if(icl->token) rv->token = cpystr(icl->token);
639 if(icl->param) rv->param = ical_parameter_cpy(icl->param);
640 if(icl->value) rv->value = cpystr(icl->value);
642 return rv;
645 /* Given a \r\n-ending line (called *text), isolate the occurrence
646 * of the token in that line.
647 * Return the token, and modify the pointer to *text to point to the
648 * end of the token. Modify sep to contain the character following
649 * the token
650 * ical-line = token ':'/';' rest of the line\r\n
651 * on error return null, and set *text to the next line, if possible.
653 char *
654 ical_isolate_token(char **text, char *sep)
656 char *s, *t;
658 for(t = s = *text; *t && ical_name_allowed_char(*s); s++);
659 /* only followed by parameter or value */
660 if(*s == ':' || *s == ';'){
661 *sep = *s;
662 *s = '\0'; /* isolate token at pointer s */
663 *text = s;
665 else{ /* bad data - bail out of here */
666 t = NULL;
667 if(*s == '\0' || (s = strstr(s, "\r\n")) == NULL)
668 *text = NULL;
669 else /* move to next line */
670 *text = s + 2;
672 return t;
676 VCALENDAR_S *
677 ical_parse_text(char *text)
679 char *s;
680 VCALENDAR_S *vcal = NULL;
682 ical_debug("ical_parse_text", text);
683 ical_initialize();
685 text = ical_unfold_line(text);
686 for(s = text; s && *s != '\0'; s++){
687 if(*s != 'B' && *s != 'b')
688 continue;
689 if(!struncmp(s+1, "EGIN:VCALENDAR\r\n", 16)){
690 s += 17; /* 17 = strlen("BEGIN:VCALENDAR\r\n") */
691 vcal = (VCALENDAR_S *) ical_parse_vcalendar(&s);
692 break;
695 return vcal;
698 void *
699 ical_parse_time(void *ic_datep, char **text, char *token)
701 struct tm *datep;
702 ICLINE_S *icl;
704 datep = fs_get(sizeof(struct tm));
705 icl = ical_parse_line(text, token);
706 ical_parse_date(icl->value, datep);
707 ical_free_cline((void **) &icl);
708 ic_datep = (void *) datep;
710 return ic_datep;
713 void *
714 ical_parse_interval(void *longvp, char *value)
717 unsigned long *longp;
719 longp = fs_get(sizeof(unsigned long));
720 *longp = atoi(value);
721 longvp = (void *) longp;
723 return longvp;
727 void *
728 ical_parse_offset(void *offsetv, char **text, char *token)
730 ICLINE_S *icl;
731 char *value;
732 int h, m, *offset;
734 offset = fs_get(sizeof(int));
736 icl = ical_parse_line(text, token);
738 if(*icl->value == '+' || *icl->value == '-')
739 value = icl->value + 1;
740 else
741 value = icl->value;
743 h = ical_get_number_value(value, 0, 2);
744 m = ical_get_number_value(value, 2, 4);
746 *offset = 60*(60*h + m);
747 if(*icl->value == '-')
748 *offset *= -1;
750 ical_free_cline((void **) &icl);
751 offsetv = (void *) offset;
753 return offsetv;
756 /* This function processes the information in *text, and returns
757 * a pointer to the information in iclp, but only if iclp is NULL
758 * otherwise, it simply returns the current value and advances the
759 * pointer to *text.
760 * Call this function as follows
761 * rv = (cast here *) ical_cline_from_token((void *)rv, &text, token);
763 void *
764 ical_cline_from_token(void *iclp, char **text, char *token)
766 ICLINE_S *icl;
768 ical_debug("ical_cline_from_token", *text);
770 icl = ical_parse_line(text, token);
772 if(iclp != NULL)
773 ical_free_cline((void **)&icl);
774 else
775 iclp = (void *) icl;
777 return iclp;
780 void *
781 ical_gencline_from_token(void *giclv, char **text, char *token)
783 GEN_ICLINE_S *gicl= NULL;
785 if(!struncmp(*text, token, strlen(token))){
786 gicl = fs_get(sizeof(GEN_ICLINE_S));
787 memset((void *) gicl, 0, sizeof(GEN_ICLINE_S));
788 gicl->cline = ical_parse_line(text, token);
789 // gicl->line = (ICLINE_S *) ical_cline_from_token((void *) gicl->cline, text, token);
790 gicl->next = (GEN_ICLINE_S *) ical_gencline_from_token((void *) gicl->next, text, token);
793 if(giclv != NULL)
794 ical_free_gencline((void **) &gicl);
795 else
796 giclv = (void *) gicl;
798 return giclv;
801 /***
802 *** PARSE COMPONENT FUNCTIONS
803 ***/
805 void *
806 ical_parse_vcalendar(char **text)
808 char *s, *t;
809 char c;
810 VCALENDAR_S *vcal;
811 void *v;
813 dprint((9, "ical_parse_vcalendar:\n"));
814 ical_debug("ical_parse_vcalendar", *text);
816 vcal = fs_get(sizeof(VCALENDAR_S));
817 memset((void *) vcal, 0, sizeof(VCALENDAR_S));
819 /* s must always point the the beginning of a line */
820 for(s = *text; s && *s != '\0';){
821 t = s;
822 s = ical_isolate_token(&t, &c);
823 if(s == NULL){
824 if(t != NULL)
825 s = t;
826 continue;
829 *t = c; /* restore character */
830 if(s){ /* figure out the token */
831 int ukn = 0; /* unknown token */
832 int i;
833 switch(*s){
834 case 'B':
835 case 'b': if(!struncmp(s+1, "EGIN", 4)){
836 s += 6; /* 6 = strlen("BEGIN:") */
837 for(i = 0; ical_comp[i].comp
838 && (struncmp(s, ical_comp[i].comp, ical_comp[i].len)
839 || struncmp(s + ical_comp[i].len, "\r\n", 2)); i++);
841 if(ical_comp[i].parse){
842 s += ical_comp[i].len + 2;
843 v = (ical_comp[i].parse)(&s);
844 if(vcal->comp == NULL){
845 vcal->comp = fs_get((VUnknown+1)*sizeof(void *));
846 memset((void *) vcal->comp, 0, (VUnknown+1)*sizeof(void *));
849 if(vcal->comp[ical_comp[i].pos] == NULL)
850 vcal->comp[ical_comp[i].pos] = v;
851 else{
852 if((vcal->method && vcal->method->value
853 && strucmp(vcal->method->value, "PUBLISH"))
854 || struncmp(ical_comp[i].comp, "VEVENT", 6))
855 (ical_comp[i].give)(&v);
856 else{
857 VEVENT_S *vevent = (VEVENT_S *) vcal->comp[VEvent];
858 for(; vevent && vevent->next; vevent = vevent->next);
859 vevent->next = v;
862 } else {
863 v = (void *) ical_parse_unknown_comp(&s, 0);
864 if(vcal->uk_comp == NULL)
865 vcal->uk_comp = (ICAL_S *) v;
866 else{
867 ICAL_S *ic;
868 for(ic = vcal->uk_comp; ic && ic->branch; ic = ic->branch);
869 ic->branch = (ICAL_S *) v;
872 } else ukn++;
873 break;
875 case 'C':
876 case 'c': if(!struncmp(s+1, "ALSCALE", 7)){
877 v = (void *) vcal->calscale;
878 v = ical_cline_from_token(v, &s, "CALSCALE");
879 vcal->calscale = (ICLINE_S *) v;
881 else ukn++;
882 break;
884 case 'E':
885 case 'e': if(!struncmp(s+1, "ND", 2)){
886 *t = c;
887 s += 4; /* 4 = strlen("END:") */
888 if(!struncmp(s, "VCALENDAR\r\n", 11)){
889 *text = s + 11; /* 11 = strlen("VCALENDAR\r\n") */
890 return (void *) vcal;
892 // else ukn++; FIX THIS, this is not quite right
893 } else ukn++;
894 break;
896 case 'M':
897 case 'm': if(!struncmp(s+1, "ETHOD", 5)){
898 v = (void *) vcal->method;
899 v = ical_cline_from_token(v, &s, "METHOD");
900 vcal->method = (ICLINE_S *) v;
902 else ukn++;
903 break;
905 case 'P':
906 case 'p': if(!struncmp(s+1, "RODID", 5)){
907 v = (void *) vcal->prodid;
908 v = ical_cline_from_token(v, &s, "PRODID");
909 vcal->prodid = (ICLINE_S *) v;
911 else ukn++;
912 break;
914 case 'V':
915 case 'v': if(!struncmp(s+1, "ERSION", 6)){
916 v = (void *) vcal->version;
917 v = ical_cline_from_token(v, &s, "VERSION");
918 vcal->version = (ICLINE_S *) v;
919 } else ukn++;
920 break;
922 default : ukn++;
923 break;
924 } /* end of switch(*s) */
925 if(ukn){
926 if(ical_buf_len < t - s){
927 fs_resize((void **)&ical_buf, t-s+1);
928 ical_buf_len = t-s;
930 *t = '\0';
931 strcpy(ical_buf, s);
932 *t = c;
933 if(vcal->uk_prop == NULL){
934 vcal->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
935 memset((void *)vcal->uk_prop, 0, sizeof(GEN_ICLINE_S));
936 vcal->uk_prop->cline = ical_parse_line(&s, ical_buf);
938 else{
939 GEN_ICLINE_S *gcl;
940 for (gcl = vcal->uk_prop; gcl && gcl->next; gcl = gcl->next);
941 gcl->next = fs_get(sizeof(GEN_ICLINE_S));
942 memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
943 gcl->next->cline = ical_parse_line(&s, ical_buf);
946 } /* end of if(s) */
949 *text = s;
951 /* ok, we have parsed the vcalendar, now parse some special properties */
952 /* start by parsing dates */
953 ical_set_date_vevent(vcal->comp[VEvent], vcal->comp[VTimeZone]);
954 return (void *) vcal;
957 void *
958 ical_parse_vevent(char **text)
960 char *s, *t;
961 char c;
962 VEVENT_S *vevent;
964 ical_debug("ical_parse_vevent", *text);
965 vevent = fs_get(sizeof(VEVENT_S));
966 memset((void *)vevent, 0, sizeof(VEVENT_S));
968 /* s must always point the the beginning of a line */
969 for(s = *text; s && *s != '\0';){
970 t = s;
971 s = ical_isolate_token(&t, &c);
972 if(s == NULL){
973 if(t != NULL)
974 s = t;
975 continue;
977 *t = c; /* restore separator */
979 if(s){ /* figure out the token */
980 int ukn = 0; /* unknown token */
981 if(!struncmp(s, "BEGIN", 5)){
982 s += 6; /* 6 = strlen("BEGIN:") */
983 if(!struncmp(s, "VALARM\r\n", 8)){
984 s += 8; /* 8 = strlen("VALARM\r\n"); */
985 if(vevent->valarm == NULL)
986 vevent->valarm = ical_parse_valarm(&s);
987 else{
988 VALARM_S *valrm;
989 for(valrm = vevent->valarm; valrm && valrm->next;
990 valrm = valrm->next);
991 valrm->next = ical_parse_valarm(&s);
993 } else {
994 ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0);
995 ical_free_unknown_comp(&uk_comp);
997 } else if(!struncmp(s, "END", t-s-1)){
998 s += 4; /* 4 = strlen("END:") */
999 if(!struncmp(s, "VEVENT\r\n",8)){
1000 *text = s + 8; /* 8 = strlen("VCALENDAR\r\n") */
1001 return (void *) vevent;
1003 } else{ Event_prop i;
1004 for(i = 0; i < EvUnknown; i++)
1005 if(!struncmp(s, event_prop[i].prop, t-s))
1006 break;
1007 if(event_prop[i].parse){
1008 void *v;
1009 if(vevent->prop == NULL){
1010 vevent->prop = fs_get((EvUnknown+1)*sizeof(void *));
1011 memset((void *)vevent->prop, 0, (EvUnknown+1)*sizeof(void *));
1013 v = vevent->prop[event_prop[i].pos];
1014 v = (event_prop[i].parse)(v , &s, event_prop[i].prop);
1015 vevent->prop[event_prop[i].pos] = v;
1017 else
1018 ukn++;
1021 if(ukn){
1022 if(ical_buf_len < t - s){
1023 fs_resize((void **)&ical_buf, t-s+1);
1024 ical_buf_len = t-s;
1026 *t = '\0';
1027 strcpy(ical_buf, s);
1028 *t = c;
1029 if(vevent->uk_prop == NULL){
1030 vevent->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
1031 memset((void *)vevent->uk_prop, 0, sizeof(GEN_ICLINE_S));
1032 vevent->uk_prop->cline = ical_parse_line(&s, ical_buf);
1034 else{
1035 GEN_ICLINE_S *gcl;
1036 for (gcl = vevent->uk_prop; gcl && gcl->next; gcl = gcl->next);
1037 gcl->next = fs_get(sizeof(GEN_ICLINE_S));
1038 memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
1039 gcl->next->cline = ical_parse_line(&s, ical_buf);
1042 } /* end of if(s) */
1045 *text = s;
1046 return (void *) vevent;
1049 void *
1050 ical_parse_vtimezone(char **text)
1052 char *s, *t;
1053 char c;
1054 void *v;
1055 VTIMEZONE_S *vtz;
1057 ical_debug("ical_parse_vtimezone", *text);
1058 vtz = fs_get(sizeof(VTIMEZONE_S));
1059 memset((void *)vtz, 0, sizeof(VTIMEZONE_S));
1061 /* s must always point the the beginning of a line */
1062 for(s = *text; s && *s != '\0';){
1063 t = s;
1064 s = ical_isolate_token(&t, &c);
1065 if(s == NULL){
1066 if(t != NULL)
1067 s = t;
1068 continue;
1070 *t = c; /* restore separator */
1072 if(s){ /* figure out the token */
1073 int ukn = 0; /* unknown token */
1074 if(!struncmp(s, "BEGIN", 5)){
1075 s += 6; /* 6 = strlen("BEGIN:") */
1076 if(!struncmp(s, "STANDARD\r\n", 10)){
1077 s += 10; /* 10 = strlen("STANDARD\r\n"); */
1078 v = ical_parse_timezone(&s);
1079 if(vtz->standardc == NULL)
1080 vtz->standardc = (ICAL_TZPROP_S *) v;
1081 else{
1082 ICAL_TZPROP_S *dl;
1083 for(dl = vtz->standardc; dl && dl->next; dl = dl->next);
1084 dl->next = (ICAL_TZPROP_S *) v;
1086 } else if(!struncmp(s, "DAYLIGHT\r\n", 10)){
1087 s += 10; /* 10 = strlen("DAYLIGHT\r\n"); */
1088 v = ical_parse_timezone(&s);
1089 if(vtz->daylightc == NULL)
1090 vtz->daylightc = (ICAL_TZPROP_S *) v;
1091 else{
1092 ICAL_TZPROP_S *dl;
1093 for(dl = vtz->daylightc; dl && dl->next; dl = dl->next);
1094 dl->next = (ICAL_TZPROP_S *) v;
1096 } else {
1097 ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0);
1098 ical_free_unknown_comp(&uk_comp);
1100 } else if(!struncmp(s, "END", t-s-1)){
1101 s += 4; /* 4 = strlen("END:") */
1102 if(!struncmp(s, "VTIMEZONE\r\n",11)){
1103 *text = s + 11; /* 11 = strlen("VTIMEZONE\r\n") */
1104 return (void *) vtz;
1106 } else{ TZ_comp i;
1107 for(i = 0; i < TZCUnknown; i++)
1108 if(!struncmp(s, tz_comp[i].prop, t-s))
1109 break;
1110 if(tz_comp[i].parse){
1111 void *v;
1112 if(vtz->prop == NULL){
1113 vtz->prop = fs_get(TZCUnknown*sizeof(void *));
1114 memset((void *)vtz->prop, 0, TZCUnknown*sizeof(void *));
1116 v = vtz->prop[tz_comp[i].pos];
1117 v = (tz_comp[i].parse)(v, &s, tz_comp[i].prop);
1118 vtz->prop[tz_comp[i].pos] = v;
1120 else
1121 ukn++;
1124 if(ukn){
1125 if(ical_buf_len < t - s){
1126 fs_resize((void **)&ical_buf, t-s+1);
1127 ical_buf_len = t-s;
1129 *t = '\0';
1130 strcpy(ical_buf, s);
1131 *t = c;
1132 if(vtz->uk_prop == NULL){
1133 vtz->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
1134 memset((void *)vtz->uk_prop, 0, sizeof(GEN_ICLINE_S));
1135 vtz->uk_prop->cline = ical_parse_line(&s, ical_buf);
1137 else{
1138 GEN_ICLINE_S *gcl;
1139 for (gcl = vtz->uk_prop; gcl && gcl->next; gcl = gcl->next);
1140 gcl->next = fs_get(sizeof(GEN_ICLINE_S));
1141 memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
1142 gcl->next->cline = ical_parse_line(&s, ical_buf);
1145 } /* end of if(s) */
1148 *text = s;
1149 return (void *) vtz;
1152 void *
1153 ical_parse_timezone(char **text)
1155 char *s, *t;
1156 char c;
1157 ICAL_TZPROP_S *tzprop;
1159 ical_debug("ical_parse_timezone", *text);
1160 tzprop = fs_get(sizeof(ICAL_TZPROP_S));
1161 memset((void *)tzprop, 0, sizeof(ICAL_TZPROP_S));
1163 /* s must always point the the beginning of a line */
1164 for(s = *text; s && *s != '\0';){
1165 t = s;
1166 s = ical_isolate_token(&t, &c);
1167 if(s == NULL){
1168 if(t != NULL)
1169 s = t;
1170 continue;
1172 *t = c; /* restore separator */
1174 if(s){ /* figure out the token */
1175 int ukn = 0; /* unknown token */
1176 if(!struncmp(s, "BEGIN", 5)){
1177 ICAL_S *uk_comp;
1178 s += 6; /* 6 = strlen("BEGIN:") */
1179 uk_comp = ical_parse_unknown_comp(&s, 0);
1180 ical_free_unknown_comp(&uk_comp);
1181 } else if(!struncmp(s, "END", t-s-1)){
1182 s += 4; /* 4 = strlen("END:") */
1183 if(!struncmp(s, "STANDARD\r\n", 10)
1184 || !struncmp(s, "DAYLIGHT\r\n", 10)){
1185 *text = s + 10; /* 10 = strlen("STANDARD\r\n") */
1186 return (void *) tzprop;
1188 } else{ TZ_prop i;
1189 for(i = 0; i < TZPUnknown; i++)
1190 if(!struncmp(s, tz_prop[i].prop, t-s))
1191 break;
1192 if(tz_prop[i].parse){
1193 void *v;
1194 if(tzprop->prop == NULL){
1195 tzprop->prop = fs_get(TZPUnknown*sizeof(void *));
1196 memset((void *)tzprop->prop, 0, TZPUnknown*sizeof(void *));
1198 v = tzprop->prop[tz_prop[i].pos];
1199 v = (tz_prop[i].parse)(v, &s, tz_prop[i].prop);
1200 tzprop->prop[tz_prop[i].pos] = v;
1202 else
1203 ukn++;
1206 if(ukn){
1207 if(ical_buf_len < t - s){
1208 fs_resize((void **)&ical_buf, t-s+1);
1209 ical_buf_len = t-s;
1211 *t = '\0';
1212 strcpy(ical_buf, s);
1213 *t = c;
1214 if(tzprop->uk_prop == NULL){
1215 tzprop->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
1216 memset((void *)tzprop->uk_prop, 0, sizeof(GEN_ICLINE_S));
1217 tzprop->uk_prop->cline = ical_parse_line(&s, ical_buf);
1219 else{
1220 GEN_ICLINE_S *gcl;
1221 for (gcl = tzprop->uk_prop; gcl && gcl->next; gcl = gcl->next);
1222 gcl->next = fs_get(sizeof(GEN_ICLINE_S));
1223 memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
1224 gcl->next->cline = ical_parse_line(&s, ical_buf);
1227 } /* end of if(s) */
1230 *text = s;
1231 return (void *) tzprop;
1234 void *
1235 ical_parse_valarm(char **text)
1237 char *s, *t;
1238 char c;
1239 VALARM_S *valarm;
1241 ical_debug("ical_parse_valarm", *text);
1242 valarm = fs_get(sizeof(VALARM_S));
1243 memset((void *)valarm, 0, sizeof(VALARM_S));
1245 /* s must always point the the beginning of a line */
1246 for(s = *text; s && *s != '\0';){
1247 t = s;
1248 s = ical_isolate_token(&t, &c);
1249 if(s == NULL){
1250 if(t != NULL)
1251 s = t;
1252 continue;
1254 *t = c; /* restore separator */
1256 if(s){ /* figure out the token */
1257 int ukn = 0; /* unknown token */
1258 if(!struncmp(s, "BEGIN", 5)){
1259 ICAL_S *uk_comp;
1260 s += 6; /* 6 = strlen("BEGIN:") */
1261 uk_comp = ical_parse_unknown_comp(&s, 0);
1262 ical_free_unknown_comp(&uk_comp);
1263 } else if(!struncmp(s, "END", t-s-1)){
1264 s += 4; /* 4 = strlen("END:") */
1265 if(!struncmp(s, "VALARM\r\n", 8)){
1266 *text = s + 8; /* 8 = strlen("VALARM\r\n") */
1267 return (void *) valarm;
1269 } else{ Alarm_prop i;
1270 for(i = 0; i < AlUnknown; i++)
1271 if(!struncmp(s, alarm_prop[i].prop, t-s))
1272 break;
1273 if(alarm_prop[i].parse){
1274 void *v;
1275 if(valarm->prop == NULL){
1276 valarm->prop = fs_get((AlUnknown+1)*sizeof(void *));
1277 memset((void *)valarm->prop, 0, (AlUnknown+1)*sizeof(void *));
1279 v = valarm->prop[alarm_prop[i].pos];
1280 v = (alarm_prop[i].parse)(v, &s, alarm_prop[i].prop);
1281 valarm->prop[alarm_prop[i].pos] = v;
1283 else
1284 ukn++;
1287 if(ukn){
1288 if(ical_buf_len < t - s){
1289 fs_resize((void **)&ical_buf, t-s+1);
1290 ical_buf_len = t-s;
1292 *t = '\0';
1293 strcpy(ical_buf, s);
1294 *t = c;
1295 if(valarm->uk_prop == NULL){
1296 valarm->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
1297 memset((void *)valarm->uk_prop, 0, sizeof(GEN_ICLINE_S));
1298 valarm->uk_prop->cline = ical_parse_line(&s, ical_buf);
1300 else{
1301 GEN_ICLINE_S *gcl;
1302 for (gcl = valarm->uk_prop; gcl && gcl->next; gcl = gcl->next);
1303 gcl->next = fs_get(sizeof(GEN_ICLINE_S));
1304 memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
1305 gcl->next->cline = ical_parse_line(&s, ical_buf);
1308 } /* end of if(s) */
1311 *text = s;
1312 return (void *) valarm;
1315 void *
1316 ical_parse_vtodo(char **text)
1318 return NULL;
1321 void *
1322 ical_parse_vjournal(char **text)
1324 return NULL;
1327 void *
1328 ical_parse_vfreebusy(char **text)
1330 return NULL;
1333 ICAL_S *
1334 ical_parse_generic_comp(char **text, int level)
1336 ICAL_S *ical;
1337 char *s, *t;
1338 char *token = NULL;
1339 GEN_ICLINE_S *gcl = NULL;
1340 char c;
1342 ical_debug("ical_parse_generic_comp", *text);
1343 ical = fs_get(sizeof(ICAL_S));
1344 memset((void *)ical, 0, sizeof(ICAL_S));
1346 ical->comp = ical_get_value(text);
1347 token = fs_get(strlen(ical->comp) + 2 + 1);
1348 sprintf(token, "%s\r\n", ical->comp); /* this is allocated memory */
1350 /* s must always point the the beginning of a line */
1351 for(s = *text; s && *s != '\0';){
1352 t = s;
1353 s = ical_isolate_token(&t, &c);
1354 if(s == NULL){
1355 if(t != NULL)
1356 s = t;
1357 continue;
1360 *t = c; /* restore character */
1361 if(s){ /* figure out the token */
1362 int ukn = 0; /* unknown token */
1363 switch(*s){
1364 case 'B':
1365 case 'b': if(!struncmp(s+1, "EGIN", 4)){
1366 s += 6; /* 6 = strlen("BEGIN:") */
1367 if(ical->next == NULL)
1368 ical->next = ical_parse_unknown_comp(&s, level+1);
1369 else{
1370 ICAL_S *b;
1371 int i;
1373 for(i = 0, b = ical; i <= level && b && b->next; b = b->next, i++);
1374 if(b->branch == NULL)
1375 b->branch = ical_parse_unknown_comp(&s, level+1);
1376 else {
1377 for(; b && b->branch; b = b->branch);
1378 b->branch = ical_parse_unknown_comp(&s, level+1);
1381 } else ukn++;
1382 break;
1384 case 'E':
1385 case 'e': if(!struncmp(s+1, "ND", 2)){
1386 *t = c;
1387 s += 4; /* 4 = strlen("END:") */
1388 if(!struncmp(s, token, strlen(token))){
1389 *text = s + strlen(token);
1390 ical->value = (void *) gcl;
1391 return ical;
1393 } else ukn++;
1394 break;
1396 default : ukn++;
1397 break;
1398 } /* end of switch(*s) */
1399 if(ukn){
1400 if(ical_buf_len < t - s){
1401 fs_resize((void **)&ical_buf, t-s+1);
1402 ical_buf_len = t-s;
1404 *t = '\0';
1405 strcpy(ical_buf, s);
1406 *t = c;
1407 if(gcl == NULL){
1408 gcl = fs_get(sizeof(GEN_ICLINE_S));
1409 memset((void *)gcl, 0, sizeof(GEN_ICLINE_S));
1410 gcl->cline = ical_parse_line(&s, ical_buf);
1412 else{
1413 GEN_ICLINE_S *gencl;
1414 for (gencl = gcl; gencl && gencl->next; gencl = gencl->next);
1415 gencl->next = fs_get(sizeof(GEN_ICLINE_S));
1416 memset((void *)gencl->next, 0, sizeof(GEN_ICLINE_S));
1417 gencl->next->cline = ical_parse_line(&s, ical_buf);
1420 } /* end of if(s) */
1423 ical->value = (void *) gcl;
1424 *text = s;
1425 return ical;
1428 ICAL_S *
1429 ical_parse_unknown_comp(char **text, int level)
1431 ICAL_S *ical;
1432 int i;
1434 ical_debug("ical_parse_unknown_comp", *text);
1435 for(i = 0; ical_comp[i].comp
1436 && (struncmp(*text, ical_comp[i].comp, ical_comp[i].len)
1437 || struncmp(*text + ical_comp[i].len, "\r\n", 2)); i++);
1439 if(ical_comp[i].parse){
1440 *text += ical_comp[i].len + 2;
1441 ical = fs_get(sizeof(ICAL_S));
1442 memset((void *)ical, 0, sizeof(ICAL_S));
1443 ical->comp = cpystr(ical_comp[i].comp);
1444 ical->value = (ical_comp[i].parse)(text);
1445 } else
1446 ical = ical_parse_generic_comp(text, level);
1448 return ical;
1451 ICLINE_S *
1452 ical_parse_line(char **text, char *name)
1454 ICLINE_S *ic;
1455 char *s = *text;
1457 ic = fs_get(sizeof(ICLINE_S));
1458 memset((void *)ic, 0, sizeof(ICLINE_S));
1460 ic->token = cpystr(name);
1461 s += strlen(name);
1462 if(*s == ';'){
1463 s++;
1464 ic->param = ical_get_parameter(&s);
1466 if(*s == ':'){
1467 s++;
1468 ic->value = ical_get_value(&s);
1471 *text = s;
1472 return ic;
1475 /***
1476 *** PARSE PROPERTY FUNCTIONS
1477 ***/
1479 void *
1480 ical_parse_freq(void *fvalp, char *text)
1482 Freq_value *fval;
1484 fval = fs_get(sizeof(Freq_value));
1486 *fval = FUnknown;
1488 if(text == NULL) return fvalp;
1490 if(!strucmp(text, "SECONDLY")) *fval = FSecondly;
1491 else if(!strucmp(text, "MINUTELY")) *fval = FMinutely;
1492 else if(!strucmp(text, "HOURLY")) *fval = FHourly;
1493 else if(!strucmp(text, "DAILY")) *fval = FDaily;
1494 else if(!strucmp(text, "WEEKLY")) *fval = FWeekly;
1495 else if(!strucmp(text, "MONTHLY")) *fval = FMonthly;
1496 else if(!strucmp(text, "YEARLY")) *fval = FYearly;
1498 fvalp = (void *) fval;
1500 return fvalp;
1503 void *
1504 ical_parse_until(void *Tmp, char *text)
1506 struct tm *Tm;
1508 if(text != NULL){
1509 Tm = fs_get(sizeof(struct tm));
1510 ical_parse_date(text, Tm);
1511 Tmp = (void *) Tm;
1514 return Tmp;
1517 void *
1518 ical_parse_count(void *countp, char *text)
1520 int *count;
1522 if(text != NULL){
1523 count = fs_get(sizeof(int));
1524 *count = atoi(text);
1525 countp = (void *) count;
1528 return countp;
1531 void *
1532 ical_parse_weekday_list(void *bywkdyp, char *wklist)
1534 BYWKDY_S *bywkdy, *w;
1535 char *s, *t, c;
1536 int done;
1537 size_t len;
1539 bywkdy = NULL;
1540 bywkdyp = (void *) bywkdy;
1542 if(wklist == NULL) return bywkdyp;
1544 done = 0;
1545 for(t = s = wklist; done == 0; s++){
1546 if(*s != ',' && *s != '\0')
1547 continue;
1548 c = *s;
1549 if(c == ',')
1550 *s = '\0';
1551 else /* c == '\0' */
1552 done++;
1553 len = strlen(t);
1554 if(len > 1){
1555 for(w = bywkdy; w && w->next; w = w->next);
1556 w = fs_get(sizeof(BYWKDY_S));
1557 memset((void *)w, 0, sizeof(BYWKDY_S));
1558 if(!strucmp(t+len-2, "SU")) w->wd = Sunday;
1559 else if(!strucmp(t+len-2, "MO")) w->wd = Monday;
1560 else if(!strucmp(t+len-2, "TU")) w->wd = Tuesday;
1561 else if(!strucmp(t+len-2, "WE")) w->wd = Wednesday;
1562 else if(!strucmp(t+len-2, "TH")) w->wd = Thursday;
1563 else if(!strucmp(t+len-2, "FR")) w->wd = Friday;
1564 else if(!strucmp(t+len-2, "SA")) w->wd = Saturday;
1565 // t[len - 2] = '\0';
1566 if(*t != '\0')
1567 w->value = strtoul(t, &t, 10);
1568 if(bywkdy == NULL)
1569 bywkdy = w;
1571 *s = c;
1572 if(*s == ',')
1573 t = s + 1;
1576 if(bywkdyp)
1577 ical_free_weekday_list((void **)&bywkdy);
1578 else
1579 bywkdyp = (void *) bywkdy;
1581 return bywkdyp;
1584 void *
1585 ical_parse_number_list(void *bynop, char *nolist)
1587 BYWKDY_S *byno, *n;
1588 char *s, *t, c;
1589 int done = 0;
1591 byno = NULL;
1592 bynop = (void *) byno;
1594 if(nolist == NULL) return bynop;
1596 for(t = s = nolist; done == 0; s++){
1597 if(*s != ',' && *s != '\0')
1598 continue;
1599 c = *s;
1600 if(c == ',')
1601 *s = '\0';
1602 else /* c == '\0' */
1603 done++;
1605 for(n = byno; n && n->next; n = n->next);
1606 n = fs_get(sizeof(BYWKDY_S));
1607 memset((void *)n, 0, sizeof(BYWKDY_S));
1608 n->value = strtoul(t, &t, 10);
1609 if(byno == NULL)
1610 byno = n;
1611 *s = c;
1612 if(*s == ',')
1613 t = s + 1;
1616 if(bynop)
1617 ical_free_weekday_list((void **)&byno);
1618 else
1619 bynop = (void *) byno;
1621 return bynop;
1624 void *
1625 ical_parse_rrule(void *rrulep, char **text, char *token)
1627 RRULE_S *rrule;
1628 ICLINE_S *icl;
1629 char *s;
1630 ICAL_PARAMETER_S *param, *p;
1631 int i;
1633 if(text == NULL || *text == NULL || struncmp(*text, "RRULE", 5))
1634 return rrulep;
1636 rrule = fs_get(sizeof(RRULE_S));
1637 memset((void *) rrule, 0, sizeof(RRULE_S));
1639 /* recurring rules are special. First, we parse the icline that contains it */
1640 icl = ical_parse_line(text, token);
1642 /* now we copy the parameters that it contains */
1643 rrule->param = ical_parameter_cpy(icl->param);
1645 /* then we parse icl->value as if it was a parameter */
1646 s = icl->value;
1647 param = ical_get_parameter(&s);
1649 /* now we check which values were given, and fill the prop array */
1650 rrule->prop = fs_get((RRUnknown+1)*sizeof(void *));
1651 memset((void *) rrule->prop, 0, (RRUnknown+1)*sizeof(void *));
1653 for(p = param; p != NULL; p = p->next){
1654 for(i = 0; rrule_prop[i].prop != NULL && strucmp(p->name, rrule_prop[i].prop); i++);
1655 if(rrule_prop[i].parse){
1656 void *v = rrule->prop[rrule_prop[i].pos];
1657 v = (rrule_prop[i].parse)(v, p->value);
1658 rrule->prop[rrule_prop[i].pos] = v;
1661 rrule->prop[RRUnknown] = NULL;
1663 ical_free_param(&param);
1664 ical_free_cline((void **)&icl);
1666 if(rrulep)
1667 ical_free_rrule((void **)&rrule);
1668 else
1669 rrulep = (void *) rrule;
1671 return rrulep;
1674 /*** UTF-8 for ICAL ***/
1677 ical_non_ascii_valid(unsigned char c)
1679 static unsigned char icu[6];
1680 static int utf8_len = 0;
1681 static int utf8_type = 0;
1682 int rv;
1684 if(utf8_len == 0)
1685 utf8_type = (c >= 0xF0 && c <= 0xF4)
1686 ? 4 : (c >= 0xE0 && c <= 0xEF)
1687 ? 3 : (c >= 0xC2 && c <= 0xDF)
1688 ? 2 : 0;
1690 if(utf8_type == 0)
1691 return 0;
1693 icu[utf8_len++] = c; /* count it */
1695 if(utf8_type == 2){
1696 if(utf8_len < 2)
1697 rv = NEED_MORE;
1698 else if(utf8_len == 2){
1699 rv = (icu[0] >= 0xC2 && icu[0] <= 0xDF)
1700 && (icu[1] >= 0x80 && icu[1] <= 0xBF) ? UTF8_COMPLETE : 0;
1701 utf8_len = 0;
1703 } else if (utf8_type == 3){
1704 if(utf8_len < 3)
1705 rv = NEED_MORE;
1706 else{
1707 if(icu[0] == 0xE0)
1708 rv = (icu[1] >= 0xA0 && icu[1] <= 0xBF)
1709 && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0;
1710 else if(icu[0] >= 0xE1 && icu[0] <= 0xEC)
1711 rv = (icu[1] >= 0x80 && icu[1] <= 0xBF)
1712 && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0;
1713 else if(icu[0] == 0xED)
1714 rv = (icu[1] >= 0x80 && icu[1] <= 0x9F)
1715 && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0;
1716 else if(icu[0] >= 0xE1 && icu[0] <= 0xEC)
1717 rv = (icu[1] >= 0x80 && icu[1] <= 0xBF)
1718 && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0;
1719 utf8_len = 0;
1721 } else if (utf8_type == 4){
1722 if(utf8_len < 4)
1723 rv = NEED_MORE;
1724 else{
1725 if(icu[0] == 0xF0)
1726 rv = (icu[1] >= 0x90 && icu[1] <= 0xBF)
1727 && (icu[2] >= 0x80 && icu[2] <= 0xBF)
1728 && (icu[3] >= 0x80 && icu[3] <= 0xBF) ? UTF8_COMPLETE : 0;
1729 else if(icu[0] >= 0xF1 && icu[0] <= 0xF3)
1730 rv = (icu[1] >= 0x80 && icu[1] <= 0xBF)
1731 && (icu[2] >= 0x80 && icu[2] <= 0xBF)
1732 && (icu[3] >= 0x80 && icu[3] <= 0xBF) ? UTF8_COMPLETE : 0;
1733 else if(icu[0] == 0xF4)
1734 rv = (icu[1] >= 0x80 && icu[1] <= 0x8F)
1735 && (icu[2] >= 0x80 && icu[2] <= 0xBF)
1736 && (icu[3] >= 0x80 && icu[3] <= 0xBF) ? UTF8_COMPLETE : 0;
1737 utf8_len = 0;
1740 return rv;
1744 ical_get_number_value(char *value, int beg_pos, int end_pos)
1746 char c, *err;
1747 int rv;
1749 c = value[end_pos];
1750 value[end_pos] = '\0';
1751 rv = strtoul(value + beg_pos, &err, 10);
1752 if(err != NULL && *err != '\0') return -1;
1753 value[end_pos] = c;
1754 return rv;
1757 void
1758 ical_free_duration(ICAL_DURATION_S **ic_d)
1760 if(ic_d == NULL || *ic_d == NULL)
1761 return;
1763 if((*ic_d)->next) ical_free_duration(&(*ic_d)->next);
1764 fs_give((void **)ic_d);
1767 /* returns 0 if no error, -1 if some error */
1769 ical_parse_duration(char *value, ICAL_DURATION_S *ic_d)
1771 int i, j = 0, rv = 0;
1773 if(value == NULL || ic_d == NULL) return -1;
1775 memset((void *)ic_d, 0, sizeof(ICAL_DURATION_S));
1777 if(value[i = 0] == '-'){
1778 i++;
1779 ic_d->sign = 1;
1780 } else if(value[i] == '+')
1781 i++;
1783 if(value[i++] == 'P'){
1784 for(j = i; value[j] != '\0' && value[j] != ','; j++){
1785 if(!isdigit(value[j]))
1786 switch(value[j]){
1787 case 'W': ic_d->weeks = ical_get_number_value(value, i, j-1);
1788 i = ++j;
1789 break;
1790 case 'D': ic_d->days = ical_get_number_value(value, i, j-1);
1791 i = ++j;
1792 break;
1793 case 'H': ic_d->hours = ical_get_number_value(value, i, j-1);
1794 i = ++j;
1795 break;
1796 case 'M': ic_d->minutes = ical_get_number_value(value, i, j-1);
1797 i = ++j;
1798 break;
1799 case 'S': ic_d->seconds = ical_get_number_value(value, i, j-1);
1800 i = ++j;
1801 break;
1802 case 'T': i = j + 1;
1803 break;
1804 default: rv = -1;
1805 break;
1809 else
1810 rv = -1;
1812 if(value[j++] == ','){
1813 ICAL_DURATION_S next;
1814 rv = ical_parse_duration(value+j, &next);
1815 ic_d->next = &next;
1818 return rv;
1821 /* return -1 if any error,
1822 0 if value has the DATE-TIME form
1823 1 if value has the DATE form only
1824 2 if value has the DATE-TIME form and is in GMT.
1827 ical_parse_date(char *value, struct tm *t)
1829 int i, rv;
1830 struct tm Tm;
1832 rv = -1;
1833 if(t == NULL) return rv;
1834 memset((void *)&Tm, 0, sizeof(struct tm));
1836 if(value == NULL) return rv;
1838 rv = 0; /* assume DATE-TIME format */
1839 /* a simple check for the format of the string */
1840 for(i = 0; isdigit(value[i]); i++);
1841 if (i == 8 && value[i] == '\0')
1842 rv = 1;
1843 else
1844 if (i != 8 || value[i++] != 'T') return -1;
1845 if(rv == 0) {
1846 for(; isdigit(value[i]); i++);
1847 if(i != 15 || (value[i] != '\0' && (value[i] != 'Z' || value[i+1] != '\0')))
1848 return -1;
1849 if(i == 15 && value[i] == 'Z')
1850 rv = 2;
1853 Tm.tm_year = ical_get_number_value(value, 0, 4) - 1900;
1854 Tm.tm_mon = ical_get_number_value(value, 4, 6) - 1;
1855 Tm.tm_mday = ical_get_number_value(value, 6, 8);
1856 if(rv != 1){
1857 Tm.tm_hour = ical_get_number_value(value, 9, 11);
1858 Tm.tm_min = ical_get_number_value(value, 11, 13);
1859 Tm.tm_sec = ical_get_number_value(value, 13, 15);
1860 Tm.tm_isdst = ICAL_DST_UNKNOWN;
1862 else
1863 Tm.tm_isdst = -1;
1864 *t = Tm;
1866 return (t->tm_mon > 11 || t->tm_mon < 0
1867 || t->tm_mday > 31 || t->tm_mday < 0
1868 || t->tm_hour > 23 || t->tm_hour < 0
1869 || t->tm_min > 59 || t->tm_min < 0
1870 || t->tm_sec > 60 || t->tm_sec < 0)
1871 ? - 1 : rv;
1874 void
1875 ical_set_date(ICLINE_S *icl, VTIMEZONE_S *vtz)
1877 int date_form; /* date forms from section 3.3.4 in RFC 5545 */
1878 ICAL_PARAMETER_S *param;
1879 char *tz = NULL;
1880 struct tm ic_date;
1881 time_t t;
1883 if(icl == NULL) return;
1885 for(param = icl->param; param != NULL; param = param->next)
1886 if(!strucmp(param->name, "TZID"))
1887 tz = param->value;
1889 if(tz != NULL)
1890 date_form = 3; /* local time with timezone */
1891 else if(icl->value[strlen(icl->value)-1] == 'Z')
1892 date_form = 2; /* utc time */
1893 else date_form = 1; /* local time */
1895 ical_parse_date(icl->value, &ic_date);
1896 ic_date.tm_wday = ical_day_of_week(ic_date); /* find out day of the week */
1898 switch(date_form){
1899 case 1: break;
1900 case 2: ical_adjust_date(&ic_date, vtz);
1901 break;
1902 case 3: break;
1903 default: alpine_panic ("Impossible date_form");
1907 ICAL_TZPROP_S *
1908 ical_std_or_daylight(struct tm *date, VTIMEZONE_S *vtz)
1910 struct tm standard, daylight;
1911 ICLINE_S *tzid = (ICLINE_S *) vtz->prop[TZCid];
1913 // standard = daylight;
1915 return NULL;
1920 /* adjusts time to given time zone */
1921 void
1922 ical_adjust_date(struct tm *date, VTIMEZONE_S *vtz)
1924 char *tzname = NULL;
1925 ICLINE_S *icl;
1926 ICAL_TZPROP_S *cur_std_day;
1928 if(vtz == NULL)
1929 return;
1931 if(vtz->prop){
1932 if((icl = (ICLINE_S *)vtz->prop[TZCid]) != NULL)
1933 tzname = cpystr(icl->value);
1936 cur_std_day = ical_std_or_daylight(date, vtz);
1939 void
1940 ical_set_date_vevent(void *veventv, void *vtzv)
1942 VEVENT_S *vevent = (VEVENT_S *) veventv;
1943 VTIMEZONE_S *vtz = (VTIMEZONE_S *) vtzv;
1945 if(vevent){
1946 ical_set_date(vevent->prop[EvDtstamp], vtz);
1947 ical_set_date(vevent->prop[EvDtstart], vtz);
1948 ical_set_date(vevent->prop[EvDtend], vtz);
1952 #define LEAP_YEAR(X) ((((X) % 4 == 0) \
1953 && (((X) % 100 != 0) || ((X) % 400 == 0))) \
1954 || (X) == 1700)
1956 #define CAL_OFFSET(X) (((X) == 1752) ? 5 : (LEAP_YEAR((X)) ? 2 : 1))
1958 /* given a year, after day_zero, return the day
1959 * of the week of the first of january of that year. On error,
1960 * return a negative number.
1961 * Assumption: day_zero is the date of january 1, of some year.
1964 ical_january_first(int year)
1966 int i, january_first;
1968 if(year < day_zero.tm_year) return -1; /* not supported */
1970 year += 1900;
1971 january_first = day_zero.tm_wday;
1972 for(i = 1900 + day_zero.tm_year + 1; i <= year; i++)
1973 january_first += CAL_OFFSET(i-1);
1975 return january_first % 7;
1978 /* given a month, week day, and year, return all days of the month
1979 * that have that day as the week day. For example, return all
1980 * sundays in november 2012.
1982 int *
1983 ical_day_from_week(int month, Weekday day, int year)
1985 int *rv = NULL;
1986 int fday, nday;
1987 Weekday wday;
1988 int i;
1990 fday = ical_first_of_month(month, year);
1991 year += 1900; /* restore year */
1992 if(year == 1752 && month == 8){
1993 fday = 9;
1994 } else {
1995 for(nday = 1, wday = (Weekday) fday; wday != day; wday = (wday+1) % 7, nday++)
1997 rv = fs_get(6*sizeof(int));
1998 memset((void *) rv, 0, 6*sizeof(int));
1999 for(i = 0; nday <= month_len[month]; i++){
2000 rv[i] = nday;
2001 nday += 7;
2003 if(LEAP_YEAR(year) && month == 1 && nday == 29)
2004 rv[i] = nday;
2007 return rv;
2011 /* given a month and a year, return the weekday of the first of the
2012 * month in that year.
2013 * return value: on error -1, otherwise the day of the week.
2016 ical_first_of_month(int month, int year)
2018 int i, d;
2020 if((d = ical_january_first(year)) < 0)
2021 return -1;
2023 year += 1900;
2024 for(i = 0; i < month; i++)
2025 d += month_len[i];
2027 if(LEAP_YEAR(year) && month >= 2)
2028 d += 1;
2030 if(year == 1752 && month >= 9)
2031 d -= 11;
2033 return d % 7;
2036 /* given a day, month and year, return the weekday of that day
2037 * return value: on error -1, otherwise the day of the week.
2040 ical_day_of_week(struct tm date)
2042 int d;
2044 if((d = ical_first_of_month(date.tm_mon, date.tm_year)) < 0)
2045 return -1;
2047 d += date.tm_mday - 1;
2049 if(date.tm_year + 1900 == 1752){
2050 if(date.tm_mday > 2 && date.tm_mday < 14)
2051 return -1;
2052 if(date.tm_mday >= 14)
2053 d -= 11;
2055 return d % 7;
2059 /* given an initial date dtstart, and a recurring rule, rrule,
2060 * adjust the date to the first date on the same year, when
2061 * the rule actually starts
2063 struct tm
2064 adjust_date_rrule(struct tm *dtstart, RRULE_S *rrule)
2066 struct tm t;
2068 memset((void *) &t, 0, sizeof(struct tm));
2069 t.tm_year = dtstart->tm_year; /* same year */
2070 if(rrule->prop[RRFreq]){
2072 if(rrule->prop[RRCount]){
2074 else if(rrule->prop[RRInterval]){
2076 if(rrule->prop[RRBysecond]){
2077 BYWKDY_S *sec = (BYWKDY_S *) rrule->prop[RRBysecond], *seco;
2078 for (seco = sec; seco != NULL; seco = seco->next)
2079 if(seco == sec) t.tm_sec = seco->value;
2080 else if (seco->value < t.tm_sec)
2081 t.tm_sec = seco->value;
2083 if (rrule->prop[RRByminute]){
2084 BYWKDY_S *sec = (BYWKDY_S *) rrule->prop[RRByminute], *seco;
2085 for (seco = sec; seco != NULL; seco = seco->next)
2086 if(seco == sec) t.tm_min = seco->value;
2087 else if (seco->value < t.tm_sec)
2088 t.tm_min = seco->value;
2090 if (rrule->prop[RRByhour]){
2091 BYWKDY_S *sec = (BYWKDY_S *) rrule->prop[RRByhour], *seco;
2092 for (seco = sec; seco != NULL; seco = seco->next)
2093 if(seco == sec) t.tm_hour = seco->value;
2094 else if (seco->value < t.tm_sec)
2095 t.tm_hour = seco->value;
2097 if (rrule->prop[RRByday]){
2099 if (rrule->prop[RRByweekno]){
2101 if (rrule->prop[RRBymonthday]){
2103 if (rrule->prop[RRByyearday]){
2105 if (rrule->prop[RRByweekno]){
2107 if (rrule->prop[RRBymonth]){
2108 BYWKDY_S *m = (BYWKDY_S *) rrule->prop[RRBymonth], *mo;
2109 for (mo = m; mo != NULL; mo = mo->next)
2110 if(mo == m) t.tm_mon = mo->value - 1;
2111 else if (mo->value - 1 < t.tm_mon)
2112 t.tm_mon = mo->value - 1;
2114 if (rrule->prop[RRBysetpos]){
2116 if (rrule->prop[RRWkst]){
2118 return t;
2121 void
2122 ical_initialize(void)
2124 static int inited = 0;
2126 if(inited != 0)
2127 return;
2129 ical_buf_len = 1024;
2130 ical_buf = fs_get(ical_buf_len+1);
2132 memset((void *) &day_zero, 0, sizeof(struct tm));
2133 day_zero.tm_year = 1601 - 1900;
2134 day_zero.tm_mday = 1;
2135 day_zero.tm_wday = 4;
2137 inited++;
2140 /* At this time, we are going to print the date in 24 hour format
2141 * if there is no string for AM or PM, but we use AM or PM when available.
2142 * We plan to make this user configurable, but not today...
2144 void
2145 ical_date_time (char *tmp, size_t len, struct tm *ic_datep)
2147 /* test of the AM/PM string is available */
2148 our_strftime(tmp, len, "%p", ic_datep);
2150 if(tmp[0])
2151 our_strftime(tmp, len, "%a %x %I:%M %p", ic_datep);
2152 else
2153 our_strftime(tmp, len, "%a %x %H:%M", ic_datep);
2156 /* If the icline has a TZID parameter, return its value, otherwise, return
2157 * NULL. Returned value freed by caller.
2159 char *
2160 ical_get_tzid(ICAL_PARAMETER_S *param)
2162 char *tzid = NULL;
2164 if(param == NULL)
2165 return tzid;
2167 if(strucmp(param->name, "TZID") == 0)
2168 tzid = cpystr(param->value);
2169 else
2170 tzid = ical_get_tzid(param->next);
2172 return tzid;
2175 /* we create a summary of the event, and pass that back as
2176 an ical parameter
2178 VEVENT_SUMMARY_S *
2179 ical_vevent_summary(VCALENDAR_S *vcal)
2181 VEVENT_SUMMARY_S *rv, *vsummary= NULL;
2182 ICLINE_S *method;
2183 VEVENT_S *vevent;
2184 GEN_ICLINE_S *gicl;
2185 ICLINE_S *icl;
2186 char *k;
2188 if(vcal == NULL) return NULL;
2190 method = vcal->method;
2192 vevent = (VEVENT_S *) vcal->comp[VEvent];
2193 if(vevent == NULL || vevent->prop == NULL)
2194 return NULL;
2196 for(vevent = (VEVENT_S *) vcal->comp[VEvent];
2197 vevent != NULL && vevent->prop != NULL;
2198 vevent = vevent->next, rv = rv->next){
2200 rv = fs_get(sizeof(VEVENT_SUMMARY_S));
2201 memset((void *) rv, 0, sizeof(VEVENT_SUMMARY_S));
2203 if(method != NULL && !strucmp(method->value, "CANCEL"))
2204 rv->cancel++;
2206 if((icl = (ICLINE_S *) vevent->prop[EvPriority]) != NULL)
2207 rv->priority = atoi(icl->value);
2209 if((icl = (ICLINE_S *) vevent->prop[EvSummary]) != NULL){
2210 rv->summary = cpystr(icl->value ? icl->value : _("No Summary"));
2211 ical_remove_escapes(&rv->summary);
2214 if((icl = (ICLINE_S *) vevent->prop[EvClass]) != NULL)
2215 rv->class = cpystr(icl->value ? icl->value : _("PUBLIC"));
2216 else
2217 rv->class = cpystr(_("PUBLIC"));
2219 if((icl = (ICLINE_S *) vevent->prop[EvOrganizer]) != NULL){
2220 char *cn, *sender, *address;
2221 ICAL_PARAMETER_S *param;
2223 cn = sender = address = NULL;
2224 for(param = icl->param; param != NULL; param = param->next)
2225 if(!strucmp(param->name, "CN"))
2226 cn = param->value;
2227 else if(!strucmp(param->name, "SENT-BY"))
2228 sender = param->value;
2230 if(sender != NULL){
2231 if(!struncmp(sender, "MAILTO:", 7))
2232 sender += 7;
2233 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "<%s>", sender);
2234 rv->sender = cpystr(tmp_20k_buf);
2237 if((address = icl->value) != NULL){
2238 if(!struncmp(address, "MAILTO:", 7))
2239 address += 7;
2240 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s<%s>",
2241 cn ? cn : "", cn ? " " : "",
2242 address ? address : _("Unknown address"));
2243 rv->organizer = cpystr(tmp_20k_buf);
2245 } /* end of if(organizer) */
2247 if((icl = (ICLINE_S *) vevent->prop[EvLocation]) != NULL){
2248 rv->location = cpystr(icl->value ? icl->value : _("Location undisclosed"));
2249 ical_remove_escapes(&rv->location);
2252 if((icl = (ICLINE_S *) vevent->prop[EvDtstart]) != NULL){
2253 struct tm ic_date;
2254 char tmp[200], *tzid;
2255 int icd; /* ical date return value */
2257 memset((void *)&ic_date, 0, sizeof(struct tm));
2258 icd = ical_parse_date(icl->value, &ic_date);
2259 tzid = ical_get_tzid(icl->param);
2260 if(icd >= 0){
2261 ic_date.tm_wday = ical_day_of_week(ic_date);
2262 switch(icd){
2263 case 0: /* DATE-TIME */
2264 ical_date_time(tmp, sizeof(tmp), &ic_date);
2265 break;
2266 case 1: /* DATE */
2267 our_strftime(tmp, sizeof(tmp), "%a %x", &ic_date);
2268 break;
2269 case 2: /* DATE-TIME in GMT, Bug: add adjust to time zone */
2270 our_strftime(tmp, sizeof(tmp), "%a %x %I:%M %p", &ic_date);
2271 break;
2272 default: alpine_panic("Unhandled ical date format");
2273 break;
2276 else{
2277 strncpy(tmp, _("Error while parsing event date"), sizeof(tmp));
2278 tmp[sizeof(tmp) - 1] = '\0';
2281 if(icl->value == NULL)
2282 rv->evstart = cpystr(_("Unknown Start Date"));
2283 else{
2284 size_t len = strlen(tmp) + 1;
2286 if(tzid != NULL)
2287 len += strlen(tzid) + 3; /* 3 = strlen(" ()") */
2289 rv->evstart = fs_get(len*sizeof(char));
2290 snprintf(rv->evstart, len, "%s%s%s%s", tmp,
2291 tzid != NULL ? " (" : "",
2292 tzid != NULL ? tzid : "",
2293 tzid != NULL ? ")" : "");
2294 rv->evstart[len-1] = '\0';
2296 if(tzid)
2297 fs_give((void **)&tzid);
2298 } /* end of if dtstart */
2300 if((icl = (ICLINE_S *) vevent->prop[EvDuration]) != NULL){
2301 int i, done = 0;
2302 ICAL_DURATION_S ic_d, icd2;
2303 if(ical_parse_duration(icl->value, &ic_d) == 0){
2304 char tmp[MAILTMPLEN+1];
2306 for(i = 1, icd2 = ic_d; icd2.next != NULL; icd2 = *icd2.next, i++);
2307 rv->duration = fs_get((i+1)*sizeof(char *));
2308 i = 0;
2310 do {
2311 tmp[0] = '\0';
2313 if(ic_d.weeks > 0)
2314 utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
2315 "%d %s ", ic_d.weeks, ic_d.weeks == 1 ? _("week") : _("weeks"));
2316 if(ic_d.days > 0)
2317 utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
2318 "%d %s ", ic_d.days, ic_d.days == 1 ? _("day") : _("days"));
2319 if(ic_d.hours > 0)
2320 utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
2321 "%d %s ", ic_d.hours, ic_d.hours == 1 ? _("hour") : _("hours"));
2322 if(ic_d.minutes > 0)
2323 utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
2324 "%d %s ", ic_d.minutes, ic_d.minutes == 1 ? _("minute") : _("minutes"));
2325 if(ic_d.seconds > 0)
2326 utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
2327 "%d %s ", ic_d.seconds, ic_d.seconds == 1 ? _("second") : _("seconds"));
2329 tmp[MAILTMPLEN] = '\0';
2330 rv->duration[i++] = cpystr(tmp);
2332 if(ic_d.next != NULL)
2333 ic_d = *ic_d.next;
2334 else
2335 done++;
2336 } while (done == 0);
2337 rv->duration[i] = NULL;
2339 } /* end of DURATION */
2340 else if((icl = (ICLINE_S *) vevent->prop[EvDtend]) != NULL){
2341 struct tm ic_date;
2342 char tmp[200], *tzid;
2343 int icd;
2345 memset((void *)&ic_date, 0, sizeof(struct tm));
2346 icd = ical_parse_date(icl->value, &ic_date);
2347 tzid = ical_get_tzid(icl->param);
2348 if(icd >= 0){
2349 ic_date.tm_wday = ical_day_of_week(ic_date);
2350 switch(icd){
2351 case 0: /* DATE-TIME */
2352 ical_date_time(tmp, sizeof(tmp), &ic_date);
2353 break;
2354 case 1: /* DATE */
2355 our_strftime(tmp, sizeof(tmp), "%a %x", &ic_date);
2356 break;
2357 case 2: /* DATE-TIME in GMT, Bug: add adjust to time zone */
2358 our_strftime(tmp, sizeof(tmp), "%a %x %I:%M %p", &ic_date);
2359 break;
2360 default: alpine_panic("Unhandled ical date format");
2361 break;
2364 else{
2365 strncpy(tmp, _("Error while parsing event date"), sizeof(tmp));
2366 tmp[sizeof(tmp) - 1] = '\0';
2369 if(icl->value == NULL)
2370 rv->evend = cpystr(_("Unknown End Date"));
2371 else{
2372 size_t len = strlen(tmp) + 1;
2374 if(tzid != NULL)
2375 len += strlen(tzid) + 3; /* 3 = strlen(" ()") */
2377 rv->evend = fs_get(len*sizeof(char));
2378 snprintf(rv->evend, len, "%s%s%s%s", tmp,
2379 tzid != NULL ? " (" : "",
2380 tzid != NULL ? tzid : "",
2381 tzid != NULL ? ")" : "");
2382 rv->evend[len-1] = '\0';
2384 if(tzid)
2385 fs_give((void **)&tzid);
2386 } /* end of if dtend */
2388 if((icl = (ICLINE_S *) vevent->prop[EvDtstamp]) != NULL){
2389 struct tm ic_date;
2390 char tmp[200], *tzid;
2391 int icd;
2393 memset((void *)&ic_date, 0, sizeof(struct tm));
2394 icd = ical_parse_date(icl->value, &ic_date);
2395 tzid = ical_get_tzid(icl->param);
2396 if(icd >= 0){
2397 ic_date.tm_wday = ical_day_of_week(ic_date);
2398 switch(icd){
2399 case 0: /* DATE-TIME */
2400 ical_date_time(tmp, sizeof(tmp), &ic_date);
2401 break;
2402 case 1: /* DATE */
2403 our_strftime(tmp, sizeof(tmp), "%a %x", &ic_date);
2404 break;
2405 case 2: /* DATE-TIME in GMT, Bug: add adjust to time zone */
2406 our_strftime(tmp, sizeof(tmp), "%a %x %I:%M %p", &ic_date);
2407 break;
2408 default: alpine_panic("Unhandled ical date format");
2409 break;
2412 else{
2413 strncpy(tmp, _("Error while parsing event date"), sizeof(tmp));
2414 tmp[sizeof(tmp) - 1] = '\0';
2416 if(icl->value == NULL)
2417 rv->dtstamp = cpystr(_("Unknown when event was scheduled"));
2418 else{
2419 size_t len = strlen(tmp) + 1;
2421 if(tzid != NULL)
2422 len += strlen(tzid) + 3; /* 3 = strlen(" ()") */
2424 rv->dtstamp = fs_get(len*sizeof(char));
2425 snprintf(rv->dtstamp, len, "%s%s%s%s", tmp,
2426 tzid != NULL ? " (" : "",
2427 tzid != NULL ? tzid : "",
2428 tzid != NULL ? ")" : "");
2429 rv->dtstamp[len-1] = '\0';
2431 } /* end of if dtstamp */
2433 if((gicl = (GEN_ICLINE_S *) vevent->prop[EvAttendee]) != NULL){
2434 int nattendees, i;
2436 for(nattendees = 0; gicl != NULL; gicl = gicl->next, nattendees++);
2437 rv->attendee = fs_get((nattendees+1)*sizeof(char *));
2439 gicl = (GEN_ICLINE_S *) vevent->prop[EvAttendee];
2440 for(i = 0; gicl != NULL; gicl = gicl->next, i++){
2441 char *role, *partstat, *rsvp;
2442 char *cn, *mailto;
2443 ICAL_PARAMETER_S *param;
2445 icl = gicl->cline;
2446 role = partstat = rsvp = cn = mailto = NULL;
2447 for(param = icl->param; param != NULL; param = param->next){
2448 if(!strucmp(param->name, "ROLE")){
2449 if(!strucmp(param->value, "REQ-PARTICIPANT"))
2450 role = _("[Required]");
2451 else if(!strucmp(param->value, "OPT-PARTICIPANT"))
2452 role = _("[Optional]");
2453 else if(!strucmp(param->value, "NON-PARTICIPANT"))
2454 role = _("[Informed]");
2455 else if(!strucmp(param->value, "CHAIR"))
2456 role = _("[ Chair ]");
2457 else
2458 role = param->value;
2460 else if(!strucmp(param->name, "PARTSTAT")){
2461 if(!strucmp(param->value, "NEEDS-ACTION"))
2462 partstat = _("[Need-Reply]");
2463 else if(!strucmp(param->value, "ACCEPTED"))
2464 partstat = _("[ Accepted ]");
2465 else if(!strucmp(param->value, "DECLINED"))
2466 partstat = _("[ Declined ]");
2467 else if(!strucmp(param->value, "TENTATIVE"))
2468 partstat = _("[ Tentative]");
2469 else if(!strucmp(param->value, "DELEGATED"))
2470 partstat = _("[ Delegated]");
2471 else
2472 partstat = param->value;
2474 else if(!strucmp(param->name, "RSVP"))
2475 rsvp = param->value;
2476 else if(!strucmp(param->name, "CN"))
2477 cn = param->value;
2479 if(icl->value && !struncmp(icl->value, "MAILTO:", strlen("MAILTO:")))
2480 mailto = icl->value + 7; /* 7 = strlen("MAILTO:") */
2481 if(!strucmp(cn, mailto))
2482 cn = "";
2483 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s%s%s%s<%s>",
2484 role && *role ? role : "",
2485 role && *role ? " " : "",
2486 partstat ? partstat : _("[Unknown Reply]"),
2487 " ",
2488 cn && *cn ? cn : "",
2489 cn && *cn ? " " : "",
2490 mailto ? mailto : _("Unknown address"));
2491 rv->attendee[i] = cpystr(tmp_20k_buf);
2493 rv->attendee[i] = NULL;
2494 } /* end of ATTENDEES */
2496 if((icl = (ICLINE_S *) vevent->prop[EvDescription]) != NULL){
2497 char *s, *t, *u, *v;
2498 int i, escaped;
2500 if(icl->value == NULL){
2501 free_vevent_summary(&rv);
2502 return vsummary;
2505 v = cpystr(icl->value); /* process a copy of icl->value */
2507 for(i = 1, escaped = 0, s = v; s && *s; s++){
2508 if(*s == '\\' && escaped == 0){ escaped = 1; continue; }
2509 if(escaped){
2510 if(!(*s == '\\' || *s == ',' || *s == 'n' || *s == 'N' || *s == ';')){
2511 free_vevent_summary(&rv);
2512 fs_give((void **)&v);
2513 return vsummary;
2515 escaped = 0;
2516 continue;
2518 if(*s == ',') i++; /* a non-scaped comma is a new value for text */
2521 rv->description = fs_get((i+1)*sizeof(unsigned char *));
2522 i = 0;
2523 for (s = t = u = v, escaped = 0; *t != '\0'; t++){
2524 if(*t == '\\' && escaped == 0){ escaped = 1; continue; }
2525 if(escaped){
2526 switch(*t){
2527 case '\\':
2528 case ',':
2529 case ';':
2530 *u++ = *t;
2531 break;
2532 case 'n':
2533 case 'N':
2534 *u++ = '\n';
2535 break;
2536 default: free_vevent_summary(&rv);
2537 fs_give((void **)&v);
2538 return NULL;
2540 escaped = 0;
2541 continue;
2543 if(*t == ','){
2544 *u = '\0';
2545 rv->description[i++] = cpystr(ical_decode(s, vcal->encoding));
2546 s = u = t+1;
2547 } else
2548 *u++ = *t;
2550 *u = '\0';
2551 rv->description[i++] = cpystr(ical_decode(s, vcal->encoding));
2552 rv->description[i] = NULL;
2553 fs_give((void **)&v);
2554 } /* end of if(description) */
2555 /* last instruction of the loop */
2556 if(vsummary == NULL)
2557 vsummary = rv;
2558 else{
2559 VEVENT_SUMMARY_S *vesy;
2560 for(vesy = vsummary; vesy && vesy->next; vesy = vesy->next);
2561 vesy->next = rv;
2563 } /* end of "for" loop */
2564 return vsummary;
2567 void
2568 free_vevent_summary(VEVENT_SUMMARY_S **vesy)
2570 int i;
2571 if(vesy == NULL || *vesy == NULL) return;
2573 if((*vesy)->class) fs_give((void **)&(*vesy)->class);
2574 if((*vesy)->summary) fs_give((void **)&(*vesy)->summary);
2575 if((*vesy)->sender) fs_give((void **)&(*vesy)->sender);
2576 if((*vesy)->organizer) fs_give((void **)&(*vesy)->organizer);
2577 if((*vesy)->location) fs_give((void **)&(*vesy)->location);
2578 if((*vesy)->evstart) fs_give((void **)&(*vesy)->evstart);
2579 if((*vesy)->evend) fs_give((void **)&(*vesy)->evend);
2580 if((*vesy)->dtstamp) fs_give((void **)&(*vesy)->dtstamp);
2581 if((*vesy)->duration){
2582 for(i = 0; (*vesy)->duration[i] != NULL; i++)
2583 fs_give((void **) &(*vesy)->duration[i]);
2584 fs_give((void **) (*vesy)->duration);
2586 if((*vesy)->attendee){
2587 for(i = 0; (*vesy)->attendee[i] != NULL; i++)
2588 fs_give((void **) &(*vesy)->attendee[i]);
2589 fs_give((void **) &(*vesy)->attendee);
2591 if((*vesy)->description){
2592 for(i = 0; (*vesy)->description[i] != NULL; i++)
2593 fs_give((void **) &(*vesy)->description[i]);
2594 fs_give((void **) &(*vesy)->description);
2596 if((*vesy)->next) free_vevent_summary(&(*vesy)->next);
2597 fs_give((void **) vesy);
2600 void
2601 ical_free_all(void)
2603 if(ical_buf)
2604 fs_give((void **)&ical_buf);