Merge branch 'ical'
[alpine.git] / pith / ical.c
blobbfad80786361450fa4ee39e81805d907ea66f911
1 #include "../pith/headers.h"
2 #include "../pith/mailpart.h"
3 #include "../pith/store.h"
4 #include "../pith/ical.h"
5 #ifdef STANDALONE
6 #include "readfile.h"
7 #include "mem.h"
8 char *lf2crlf(char *);
9 #endif /* STANDALONE */
11 typedef struct ical_iana_comp_s {
12 char *comp; /* component name */
13 size_t len; /* size of component name (strlen(x->comp)) */
14 int pos; /* position of this component in comp array */
15 void *(*parse)(char **); /* parser */
16 void (*give)(void **); /* free memory */
17 } ICAL_COMP_S;
19 typedef struct ical_iana_prop_s {
20 char *prop; /* component name */
21 size_t len; /* size of component name (strlen(x->comp)) */
22 int pos; /* location of this component in the prop array */
23 void (*parse)(); /* parser */
24 void (*give)(void **); /* free memory */
25 } ICAL_PROP_S;
27 int ical_january_first(int); /* args: year */
28 void ical_adjust_date(struct tm *, VTIMEZONE_S *);
30 void ical_initialize(void);
32 int ical_non_ascii_valid(unsigned char);
33 char *ical_unfold_line(char *);
34 ICLINE_S *ical_parse_line(char **, char *);
36 ICLINE_S *ical_cline_cpy(ICLINE_S *);
37 ICAL_PARAMETER_S *ical_parameter_cpy(ICAL_PARAMETER_S *param);
39 char *ical_get_value(char **);
40 char *ical_decode(char *, unsigned short);
42 /* pase component */
43 void *ical_parse_vcalendar(char **);
44 void *ical_parse_vevent(char **);
45 void *ical_parse_vtodo(char **);
46 void *ical_parse_vjournal(char **);
47 void *ical_parse_vfreebusy(char **);
48 void *ical_parse_vtimezone(char **);
49 void *ical_parse_valarm(char **);
50 void *ical_parse_timezone(char **);
51 ICAL_S *ical_parse_unknown_comp(char **, int);
52 ICAL_S *ical_parse_generic_comp(char **, int);
54 /* free components */
55 void ical_free_vevent(void **);
56 void ical_free_vtodo(void **);
57 void ical_free_vjournal(void **);
58 void ical_free_vfreebusy(void **);
59 void ical_free_vtimezone(void **);
60 void ical_free_timezone(void **);
61 void ical_free_valarm(void **);
62 void ical_free_unknown_comp(ICAL_S **);
64 /* parse properties */
65 void ical_cline_from_token(void **, char **, char *);
66 void ical_gencline_from_token(void **, char **, char *);
68 void ical_parse_rrule(RRULE_S **, char **);
69 void ical_parse_time(struct tm *, char **, char *);
70 void ical_parse_offset(int *, char **, char *);
72 void ical_parse_freq(Freq_value *, char *);
73 void ical_parse_weekday_list(BYWKDY_S **, char *);
74 void ical_parse_number_list(BYWKDY_S **, char *);
75 void ical_parse_interval(unsigned long *, char *);
77 int ical_get_number_value(char *, int, int);
78 void ical_set_date(ICLINE_S *, VTIMEZONE_S *);
79 void ical_set_date_vevent(void *, void *);
81 /* free properties */
82 void ical_free_prop(void **, ICAL_PROP_S *);
83 void ical_free_null(void **);
84 void ical_free_cline(void **);
85 void ical_free_param(ICAL_PARAMETER_S **);
86 void ical_free_gencline(void **);
87 void ical_free_rrule(void **);
88 void ical_free_weekday_list(void **);
90 /* globals */
91 struct tm day_zero; /* date for january 1, 1601 */
92 int month_len[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
94 #define UTF8_COMPLETE (1)
95 #define NEED_MORE (2)
97 char *ical_buf;
98 unsigned long ical_buf_len;
101 /* parsing structures */
103 /* this is the list of V-components to a Calendar from RFC 5545 */
104 ICAL_COMP_S ical_comp[] = {
105 {"VCALENDAR", 9, VCalendar, ical_parse_vcalendar, ical_free_vcalendar},
106 {"VTIMEZONE", 9, VTimeZone, ical_parse_vtimezone, ical_free_vtimezone},
107 {"VEVENT", 6, VEvent, ical_parse_vevent, ical_free_vevent},
108 {"VTODO", 5, VTodo, ical_parse_vtodo, ical_free_vtodo},
109 {"VJOURNAL", 8, VJournal, ical_parse_vjournal, ical_free_vjournal},
110 {"VALARM", 6, VAlarm, ical_parse_valarm, ical_free_valarm},
111 {"VFREEBUSY", 9, VFreeBusy, ical_parse_vfreebusy, ical_free_vfreebusy},
112 {NULL, 0, VUnknown, NULL, 0}
115 /* array for properties */
116 ICAL_PROP_S rrule_prop[] = {
117 {"FREQ", 4, RRFreq, ical_parse_freq, ical_free_null},
118 {"UNTIL", 5, RRUntil, ical_parse_freq, 0},
119 {"COUNT", 5, RRCount, ical_cline_from_token, ical_free_cline},
120 {"INTERVAL", 8, RRInterval, ical_parse_interval, ical_free_null},
121 {"BYSECOND", 8, RRBysecond, ical_parse_number_list, ical_free_weekday_list},
122 {"BYMINUTE", 8, RRByminute, ical_parse_number_list, ical_free_weekday_list},
123 {"BYHOUR", 6, RRByhour, ical_parse_number_list, ical_free_weekday_list},
124 {"BYDAY", 5, RRByday, ical_parse_weekday_list,ical_free_weekday_list},
125 {"BYWEEKNO", 8, RRByweekno, 0, 0},
126 {"BYMONTH", 7, RRBymonth, ical_parse_number_list, ical_free_weekday_list},
127 {"BYSETPOS", 8, RRBysetpos, 0, 0},
128 {"BYWKST", 6, RRWkst, 0, 0},
129 {"BYMONTHDAY",
130 10, RRBymonthday, 0, 0},
131 {"BYYEARDAY", 9, RRByyearday, 0, 0},
132 {NULL, 0, RRUnknown, 0, 0}
135 ICAL_PROP_S event_prop[] = {
136 {"DTSTAMP", 7, EvDtstamp, ical_cline_from_token, ical_free_cline},
137 {"UID", 3, EvUid, ical_cline_from_token, ical_free_cline},
138 {"DTSTART", 7, EvDtstart, ical_cline_from_token, ical_free_cline},
139 {"CLASS", 5, EvClass, ical_cline_from_token, ical_free_cline},
140 {"CREATED", 7, EvCreated, ical_cline_from_token, ical_free_cline},
141 {"DESCRIPTION", 11, EvDescription, ical_cline_from_token, ical_free_cline},
142 {"GEO", 3, EvGeo, ical_cline_from_token, ical_free_cline},
143 {"LASTMOD", 7, EvLastMod, ical_cline_from_token, ical_free_cline},
144 {"LOCATION", 8, EvLocation, ical_cline_from_token, ical_free_cline},
145 {"ORGANIZER", 9, EvOrganizer, ical_cline_from_token, ical_free_cline},
146 {"PRIORITY", 8, EvPriority, ical_cline_from_token, ical_free_cline},
147 {"SEQUENCE", 8, EvSequence, ical_cline_from_token, ical_free_cline},
148 {"STATUS", 6, EvStatus, ical_cline_from_token, ical_free_cline},
149 {"SUMMARY", 7, EvSummary, ical_cline_from_token, ical_free_cline},
150 {"TRANSP", 6, EvTransp, ical_cline_from_token, ical_free_cline},
151 {"URL", 3, EvUrl, ical_cline_from_token, ical_free_cline},
152 {"RECURRENCE-ID", 13, EvRecurrence, ical_cline_from_token, ical_free_cline},
153 {"RRULE", 5, EvRrule, ical_parse_rrule, ical_free_rrule},
154 {"DTEND", 5, EvDtend, ical_cline_from_token, ical_free_cline},
155 {"DURATION", 8, EvDuration, ical_cline_from_token, ical_free_cline},
156 {"ATTACH", 6, EvAttach, ical_gencline_from_token, ical_free_gencline},
157 {"ATTENDEE", 8, EvAttendee, ical_gencline_from_token, ical_free_gencline},
158 {"CATEGORIES", 10, EvCategories, ical_gencline_from_token, ical_free_gencline},
159 {"COMMENT", 7, EvComment, ical_gencline_from_token, ical_free_gencline},
160 {"CONTACT", 7, EvContact, ical_gencline_from_token, ical_free_gencline},
161 {"EXDATE", 6, EvExdate, ical_gencline_from_token, ical_free_gencline},
162 {"RSTATUS", 7, EvRstatus, ical_gencline_from_token, ical_free_gencline},
163 {"RELATED", 7, EvRelated, ical_gencline_from_token, ical_free_gencline},
164 {"RESOURCES", 9, EvResources, ical_gencline_from_token, ical_free_gencline},
165 {"RDATE", 5, EvRdate, ical_gencline_from_token, ical_free_gencline},
166 {NULL, 0, EvUnknown, 0, 0}
169 ICAL_PROP_S tz_comp[] = {
170 {"TZID", 4, TZCid, ical_cline_from_token, ical_free_cline},
171 {"LAST-MODIFIED", 13, TZCLastMod, ical_cline_from_token, ical_free_cline},
172 {"TZURL", 5, TZCUrl, ical_cline_from_token, ical_free_cline},
173 {NULL, 0, TZCUnknown, 0, 0}
176 ICAL_PROP_S tz_prop[] = {
177 {"DTSTART", 7, TZPDtstart, ical_parse_time, 0},
178 {"TZOFFSETTO", 10, TZPOffsetto, ical_parse_offset, 0},
179 {"TZOFFSETFROM", 12, TZPOffsetfrom, ical_parse_offset, 0},
180 {"RRULE", 5, TZPRrule, ical_parse_rrule, ical_free_rrule},
181 {"COMMENT", 7, TZPComment, ical_gencline_from_token, ical_free_gencline},
182 {"RDATE", 5, TZPRdate, ical_gencline_from_token, ical_free_gencline},
183 {"TZNAME", 6, TZPTzname, ical_gencline_from_token, ical_free_gencline},
184 {NULL, 0, TZPUnknown, 0, 0}
187 ICAL_PROP_S alarm_prop[] = {
188 {"ACTION", 6, AlAction, ical_cline_from_token, ical_free_cline},
189 {"TRIGGER", 7, AlTrigger, ical_cline_from_token, ical_free_cline},
190 {"DURATION", 8, AlDuration, ical_cline_from_token, ical_free_cline},
191 {"REPEAT", 6, AlRepeat, ical_cline_from_token, ical_free_cline},
192 {"DESCRIPTION", 11, AlDescription, ical_cline_from_token, ical_free_cline},
193 {"SUMMARY", 7, AlSummary, ical_cline_from_token, ical_free_cline},
194 {"ATTACH", 6, AlAttach, ical_gencline_from_token, ical_free_gencline},
195 {"ATTENDEE", 8, AlAttendee, ical_gencline_from_token, ical_free_gencline},
196 {NULL, 0, AlUnknown, 0, 0}
199 /* some useful macros for character analysis */
201 #define ical_wspace(X) \
202 ((X) == ' ' || (X) == '\t')
204 #define ical_name_allowed_char(X) \
205 (((X) >= 'A' && (X) <= 'Z') || \
206 ((X) >= 'a' && (X) <= 'z') || \
207 (X) == '-' )
209 #define ical_control(X) \
210 (((X) >= 0x00 && (X) <= 0x08) || \
211 ((X) >= 0x0A && (X) <= 0x1F) || \
212 (X) == 0x7F)
214 #define ical_safe_char(X) \
215 (ical_non_ascii_valid(X) \
216 || ical_wspace(X) \
217 || (X) == 0x21 \
218 || ((X) >= 0x23 && (X) <= 0x2B) \
219 || ((X) >= 0x2D && (X) <= 0x39) \
220 || ((X) >= 0x3C && (X) <= 0x7E))
222 #define ical_qsafe_char(X) \
223 (ical_non_ascii_valid((X)) \
224 || ical_wspace(X) \
225 || (X) == 0x21 \
226 || ((X) >= 0x23 && (X) <= 0x7E))
228 #define ical_value_char(X) \
229 (ical_non_ascii_valid(X) \
230 || ical_wspace(X) \
231 || ((X) >= 0x21 && (X) <= 0x7E))
233 /* Finally, here begins the code. */
235 char *
236 ical_decode(char *text, unsigned short encoding)
238 char *t;
239 unsigned long callen;
240 if(encoding == ENCQUOTEDPRINTABLE){
241 t = rfc822_qprint ((unsigned char *) text,strlen(text),&callen);
242 if(t != NULL){
243 strncpy(text, t, strlen(t));
244 fs_give((void **) &t);
247 return text;
251 /* Return code:
252 0 - if no errors
253 -1 - if an error occured
254 Args: a pointer to the text. If there is an error, the text is not modified.
257 ical_remove_escapes(char **textp)
259 char *text, *s, *t;
260 int rv = 0;
261 int escaped;
263 if(textp == NULL) return 0;
265 t = cpystr(*textp); /* work on a copy of the text */
266 /* the variable text below points to the beginning of the filtered text */
267 for (text = s = t, escaped = 0; rv == 0 && *s != '\0'; s++){
268 if(*s == '\\' && escaped == 0){
269 escaped = 1;
270 continue;
272 if(escaped){
273 switch(*s){
274 case '\\':
275 case ',':
276 case ';':
277 *t++ = *s;
278 break;
280 case 'n':
281 case 'N':
282 *t++ = '\n';
283 break;
284 default: rv = -1;
285 break;
287 escaped = 0;
289 else *t++ = *s;
291 *t = '\0'; /* tie off filtered text */
292 t = text; /* reset t to the beginning */
293 if(rv == -1)
294 fs_give((void **) &t);
295 else{
296 strncpy(*textp, t, strlen(t)); /* overwrite given text with filtered text */
297 (*textp)[strlen(t)] = '\0';
299 return rv;
302 void ical_debug(char *fcn, char *text)
304 char piece[50];
305 strncpy(piece, text, 49);
306 dprint((9, "%s: %s", fcn, piece));
309 /***
310 *** FREE MEMORY FUNCTIONS
311 ***/
313 void
314 ical_free_param(ICAL_PARAMETER_S **param)
316 if(param == NULL || *param == NULL)
317 return;
319 if((*param)->name) fs_give((void **) &(*param)->name);
320 if((*param)->value) fs_give((void **) &(*param)->value);
321 if((*param)->next) ical_free_param(&(*param)->next);
322 fs_give((void **)param);
325 void
326 ical_free_null(void **n)
328 if(n != NULL) *n = NULL;
331 void
332 ical_free_cline(void **icv)
334 ICLINE_S **ic = (ICLINE_S **) icv;
336 if(ic == NULL || *ic == NULL)
337 return;
339 if((*ic)->token) fs_give((void **) &(*ic)->token);
340 if((*ic)->param) ical_free_param(&(*ic)->param);
341 if((*ic)->value) fs_give((void **) &(*ic)->value);
342 fs_give(icv);
345 void
346 ical_free_gencline(void **giclpv)
348 GEN_ICLINE_S **giclp = (GEN_ICLINE_S **) giclpv;
350 if(giclp == NULL || *giclp == NULL) return;
352 if((*giclp)->cline) ical_free_cline((void **) &(*giclp)->cline);
353 if((*giclp)->next) ical_free_gencline((void **) &(*giclp)->next);
356 void
357 ical_free_vcalendar(void **vcalpv)
359 VCALENDAR_S **vcalp = (VCALENDAR_S **)vcalpv;
361 if(vcalp == NULL || *vcalp == NULL) return;
363 if((*vcalp)->prodid) ical_free_cline((void **) &(*vcalp)->prodid);
364 if((*vcalp)->version) ical_free_cline((void **) &(*vcalp)->version);
365 if((*vcalp)->calscale) ical_free_cline((void **) &(*vcalp)->calscale);
366 if((*vcalp)->method) ical_free_cline((void **) &(*vcalp)->method);
367 if((*vcalp)->uk_prop) ical_free_gencline((void **) &(*vcalp)->uk_prop);
368 if((*vcalp)->comp){
369 Cal_comp i;
370 for(i = 0; i < VUnknown; i++)
371 if((*vcalp)->comp[i]) (ical_comp[i].give)(&(*vcalp)->comp[i]);
372 fs_give((*vcalp)->comp);
374 if((*vcalp)->uk_comp) ical_free_unknown_comp(&(*vcalp)->uk_comp);
375 fs_give(vcalpv);
378 void
379 ical_free_vevent(void **veventpv)
381 VEVENT_S **veventp = (VEVENT_S **) veventpv;
383 if(veventp == NULL || *veventp == NULL) return;
385 ical_free_prop((*veventp)->prop, event_prop);
386 if((*veventp)->uk_prop) ical_free_gencline((void **) &(*veventp)->uk_prop);
387 if((*veventp)->valarm) ical_free_valarm((void **) &(*veventp)->valarm);
388 fs_give(veventpv);
391 void
392 ical_free_rrule(void **rrulepv)
394 RRULE_S **rrulep = (RRULE_S **) rrulepv;
396 if(rrulep && *rrulep){
397 ical_free_prop((*rrulep)->prop, rrule_prop);
398 ical_free_param(&(*rrulep)->param);
399 fs_give(rrulepv);
403 void
404 ical_free_weekday_list(void **wkdylv)
406 BYWKDY_S **wkdyl = (BYWKDY_S **) wkdylv;
408 if(wkdyl == NULL) return;
410 if((*wkdyl)->next)
411 ical_free_weekday_list((void **) &(*wkdyl)->next);
413 fs_give(wkdylv);
417 void
418 ical_free_vtodo(void **vtodopv)
422 void
423 ical_free_vjournal(void **vjournalpv)
427 void
428 ical_free_vfreebusy(void **vfbpv)
432 void
433 ical_free_prop(void **propv, ICAL_PROP_S *aux_comp)
435 int i, j;
437 if(propv == NULL) return;
439 for(i = 0; aux_comp[i].prop != NULL; i++)
440 if(propv[i]){
441 for(j = 0; aux_comp[j].prop != NULL && aux_comp[j].pos != i; j++);
442 if(aux_comp[j].give) (aux_comp[j].give)(&propv[i]);
444 fs_give(propv);
448 void
449 ical_free_vtimezone(void **vtzpv)
451 VTIMEZONE_S **vtzp = (VTIMEZONE_S **) vtzpv;
452 TZ_comp i,j;
454 if(vtzp == NULL || *vtzp == NULL) return;
456 ical_free_prop((*vtzp)->prop, tz_comp);
458 if((*vtzp)->uk_prop) ical_free_gencline((void **) &(*vtzp)->uk_prop);
459 if((*vtzp)->standardc) ical_free_timezone((void **) &(*vtzp)->standardc);
460 if((*vtzp)->daylightc) ical_free_timezone((void **) &(*vtzp)->daylightc);
461 fs_give(vtzpv);
464 void
465 ical_free_timezone(void **tzpv)
467 ICAL_TZPROP_S **tzp = (ICAL_TZPROP_S **) tzpv;
469 if(tzp == NULL || *tzp == NULL) return;
471 ical_free_prop((*tzp)->prop, tz_prop);
472 if((*tzp)->uk_prop) ical_free_gencline((void **) &(*tzp)->uk_prop);
473 if((*tzp)->next) ical_free_timezone((void **) &(*tzp)->next);
474 fs_give(tzpv);
477 void ical_free_valarm(void **valarmpv)
481 void
482 ical_free_unknown_comp(ICAL_S **icalp)
484 int i;
485 if(icalp == NULL || *icalp == NULL) return;
486 for(i = 0; ical_comp[i].comp && strucmp((*icalp)->comp,ical_comp[i].comp); i++);
487 if(ical_comp[i].give)
488 (ical_comp[i].give)(&(*icalp)->value);
489 else
490 ical_free_gencline((void **) &(*icalp)->value);
491 fs_give((void **)&(*icalp)->comp);
492 ical_free_unknown_comp(&(*icalp)->next);
493 ical_free_unknown_comp(&(*icalp)->branch);
494 fs_give((void **)icalp);
497 char *ical_unfold_line(char *line)
499 int i, j;
501 if(line == NULL)
502 return NULL;
504 for(i = 0, j = 0; line[j] != '\0';)
505 switch(line[j]){
506 case '\r': if(line[j+1] == '\n' && ical_wspace(line[j+2])){
507 j += 3; /* get past white space */
508 continue;
510 default : line[i++] = line[j++];
512 line[i] = '\0';
513 return line;
516 ICAL_PARAMETER_S *
517 ical_get_parameter(char **line)
519 ICAL_PARAMETER_S *param = NULL;
520 char *s;
522 if(line == NULL || *line == NULL)
523 return NULL;
525 for(s = *line; s && *s && ical_name_allowed_char(*s) ; s++);
527 if(*s == '='){
528 int quoted;
529 char c;
531 param = fs_get(sizeof(ICAL_PARAMETER_S));
532 memset((void *)param, 0, sizeof(ICAL_PARAMETER_S));
533 *s = '\0';
534 param->name = cpystr(*line);
535 *s = '=';
536 *line = s+1; /* step over '=' */
537 quoted = **line == '"' ? 1 : 0;
538 if(quoted != 0){
539 for(s = ++*line; s && *s && ical_qsafe_char((unsigned char) *s); s++);
540 if(*s != '"'){ /* error, do not parse this line */
541 ical_free_param(&param);
542 *line = strchr(s, ':'); /* reset line to closest ':' */
543 return NULL;
546 else
547 for(s = *line; s && *s && (ical_safe_char((unsigned char) *s)); s++);
548 c = *s;
549 *s = '\0';
550 param->value = cpystr(*line);
551 *s = c; /* restore character */
552 *line = quoted ? s + 1 : s;
554 if(**line == ';'){
555 ++*line;
556 param->next = ical_get_parameter(line);
559 return param;
562 char *ical_get_value(char **line)
564 char *s, *t;
566 if(line == NULL || *line == NULL)
567 return NULL;
569 for (s = *line; *s && ical_value_char((unsigned char) *s); s++);
570 if(*s == '\r'){
571 *s = '\0';
572 t = cpystr(*line);
573 *s = '\r';
574 *line = s+2;
576 else{
577 t = NULL;
578 s = strchr(*line, '\r');
579 if(s != NULL)
580 *line = s + 2;
582 return t;
585 ICAL_PARAMETER_S *
586 ical_parameter_cpy(ICAL_PARAMETER_S *param)
588 ICAL_PARAMETER_S *rv;
590 if(param == NULL) return NULL;
592 rv = fs_get(sizeof(ICAL_PARAMETER_S));
593 memset((void *)rv, 0, sizeof(ICAL_PARAMETER_S));
595 if(param->name) rv->name = cpystr(param->name);
596 if(param->value) rv->value = cpystr(param->value);
597 if(param->next) rv->next = ical_parameter_cpy(param->next);
599 return rv;
602 ICLINE_S *
603 ical_cline_cpy(ICLINE_S *icl)
605 ICLINE_S *rv;
607 if(icl == NULL)
608 return NULL;
610 rv = fs_get(sizeof(ICLINE_S));
611 memset((void *)rv, 0, sizeof(ICLINE_S));
613 if(icl->token) rv->token = cpystr(icl->token);
614 if(icl->param) rv->param = ical_parameter_cpy(icl->param);
615 if(icl->value) rv->value = cpystr(icl->value);
617 return rv;
620 /* Given a \r\n-ending line (called *text), isolate the ocurrence
621 * of the token in that line.
622 * Return the token, and modify the pointer to *text to point to the
623 * end of the token. Modify sep to contain the character following
624 * the token
625 * ical-line = token ':'/';' rest of the line\r\n
626 * on error return null, and set *text to the next line, if possible.
628 char *
629 ical_isolate_token(char **text, char *sep)
631 char *s, *t;
633 for(t = s = *text; *t && ical_name_allowed_char(*s); s++);
634 /* only followed by parameter or value */
635 if(*s == ':' || *s == ';'){
636 *sep = *s;
637 *s = '\0'; /* isolate token at pointer s */
638 *text = s;
640 else{ /* bad data - bail out of here */
641 t = NULL;
642 if(*s == '\0' || (s = strstr(s, "\r\n")) == NULL)
643 *text = NULL;
644 else /* move to next line */
645 *text = s + 2;
647 return t;
651 VCALENDAR_S *
652 ical_parse_text(char *text)
654 char *s;
655 VCALENDAR_S *vcal = NULL;
657 ical_debug("ical_parse_text", text);
658 ical_initialize();
660 text = ical_unfold_line(text);
661 for(s = text; s && *s != '\0'; s++){
662 if(*s != 'B' && *s != 'b')
663 continue;
664 if(!struncmp(s+1, "EGIN:VCALENDAR\r\n", 16)){
665 s += 17; /* 17 = strlen("BEGIN:VCALENDAR\r\n") */
666 vcal = (VCALENDAR_S *) ical_parse_vcalendar(&s);
667 break;
670 return vcal;
673 void
674 ical_parse_time(struct tm *ic_date, char **text, char *token)
676 ICLINE_S *icl;
678 icl = ical_parse_line(text, token);
679 ical_parse_date(icl->value, ic_date);
680 ical_free_cline((void **) &icl);
683 void
684 ical_parse_interval(unsigned long *longv, char *value)
686 *longv = atoi(value);
690 void
691 ical_parse_offset(int *offsetv, char **text, char *token)
693 ICLINE_S *icl;
694 char *value;
695 int h, m, offset;
697 icl = ical_parse_line(text, token);
699 if(*icl->value == '+' || *icl->value == '-')
700 value = icl->value + 1;
701 else
702 value = icl->value;
704 h = ical_get_number_value(value, 0, 2);
705 m = ical_get_number_value(value, 2, 4);
707 offset = 60*(60*h + m);
708 if(*icl->value == '-')
709 offset *= -1;
711 *offsetv = offset;
712 ical_free_cline((void **) &icl);
715 void
716 ical_cline_from_token(void **iclv, char **text, char *token)
718 ICLINE_S **icl = (ICLINE_S **)iclv;
720 if(*icl == NULL)
721 *icl = ical_parse_line(text, token);
722 else {
723 ICLINE_S *ic = ical_parse_line(text, token);
724 ical_free_cline((void **)&ic);
728 void
729 ical_gencline_from_token(void **giclv, char **text, char *token)
731 GEN_ICLINE_S *gicl;
733 if(!struncmp(*text, token, strlen(token))){
734 gicl = fs_get(sizeof(GEN_ICLINE_S));
735 memset((void *) gicl, 0, sizeof(GEN_ICLINE_S));
736 ical_cline_from_token((void **) &gicl->cline, text, token);
737 ical_gencline_from_token((void **) &gicl->next, text, token);
738 *giclv = (void *) gicl;
742 /***
743 *** PARSE COMPONENT FUNCTIONS
744 ***/
746 void *
747 ical_parse_vcalendar(char **text)
749 char *s, *t;
750 char c;
751 VCALENDAR_S *vcal;
752 void *v;
754 dprint((9, "ical_parse_vcalendar:\n"));
755 ical_debug("ical_parse_vcalendar", *text);
757 vcal = fs_get(sizeof(VCALENDAR_S));
758 memset((void *)vcal, 0, sizeof(VCALENDAR_S));
760 /* s must always point the the beginning of a line */
761 for(s = *text; s && *s != '\0';){
762 t = s;
763 s = ical_isolate_token(&t, &c);
764 if(s == NULL){
765 if(t != NULL)
766 s = t;
767 continue;
770 *t = c; /* restore character */
771 if(s){ /* figure out the token */
772 int ukn = 0; /* unknown token */
773 int i;
774 switch(*s){
775 case 'B':
776 case 'b': if(!struncmp(s+1, "EGIN", 4)){
777 s += 6; /* 6 = strlen("BEGIN:") */
778 for(i = 0; ical_comp[i].comp
779 && (struncmp(s, ical_comp[i].comp, ical_comp[i].len)
780 || struncmp(s + ical_comp[i].len, "\r\n", 2)); i++);
782 if(ical_comp[i].parse){
783 s += ical_comp[i].len + 2;
784 v = (ical_comp[i].parse)(&s);
785 if(vcal->comp == NULL){
786 vcal->comp = fs_get(VUnknown*sizeof(void *));
787 memset((void *) vcal->comp, 0, VUnknown*sizeof(void *));
790 if(vcal->comp[ical_comp[i].pos] == NULL)
791 vcal->comp[ical_comp[i].pos] = v;
792 else
793 (ical_comp[i].give)(&v);
794 } else {
795 v = (void *) ical_parse_unknown_comp(&s, 0);
796 if(vcal->uk_comp == NULL)
797 vcal->uk_comp = (ICAL_S *) v;
798 else{
799 ICAL_S *ic;
800 for(ic = vcal->uk_comp; ic && ic->branch; ic = ic->branch);
801 ic->branch = (ICAL_S *) v;
804 } else ukn++;
805 break;
807 case 'C':
808 case 'c': if(!struncmp(s+1, "ALSCALE", 7))
809 ical_cline_from_token((void **) &vcal->calscale, &s, "CALSCALE");
810 else ukn++;
811 break;
813 case 'E':
814 case 'e': if(!struncmp(s+1, "ND", 2)){
815 *t = c;
816 s += 4; /* 4 = strlen("END:") */
817 if(!struncmp(s, "VCALENDAR\r\n", 11)){
818 *text = s + 11; /* 11 = strlen("VCALENDAR\r\n") */
819 return (void *) vcal;
821 // else ukn++; FIX THIS, this is not quite right
822 } else ukn++;
823 break;
825 case 'M':
826 case 'm': if(!struncmp(s+1, "ETHOD", 5))
827 ical_cline_from_token((void **) &vcal->method, &s, "METHOD");
828 else ukn++;
829 break;
831 case 'P':
832 case 'p': if(!struncmp(s+1, "RODID", 5))
833 ical_cline_from_token((void **) &vcal->prodid, &s, "PRODID");
834 else ukn++;
835 break;
837 case 'V':
838 case 'v': if(!struncmp(s+1, "ERSION", 6)){
839 ical_cline_from_token((void **) &vcal->version, &s, "VERSION");
840 } else ukn++;
841 break;
843 default : ukn++;
844 break;
845 } /* end of switch(*s) */
846 if(ukn){
847 if(ical_buf_len < t - s){
848 fs_resize((void **)&ical_buf, t-s+1);
849 ical_buf_len = t-s;
851 *t = '\0';
852 strcpy(ical_buf, s);
853 *t = c;
854 if(vcal->uk_prop == NULL){
855 vcal->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
856 memset((void *)vcal->uk_prop, 0, sizeof(GEN_ICLINE_S));
857 vcal->uk_prop->cline = ical_parse_line(&s, ical_buf);
859 else{
860 GEN_ICLINE_S *gcl;
861 for (gcl = vcal->uk_prop; gcl && gcl->next; gcl = gcl->next);
862 gcl->next = fs_get(sizeof(GEN_ICLINE_S));
863 memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
864 gcl->next->cline = ical_parse_line(&s, ical_buf);
867 } /* end of if(s) */
870 *text = s;
872 /* ok, we have parsed the vcalendar, now parse some special properties */
873 /* start by parsing dates */
874 ical_set_date_vevent(vcal->comp[VEvent], vcal->comp[VTimeZone]);
875 return (void *) vcal;
878 void *
879 ical_parse_vevent(char **text)
881 char *s, *t;
882 char c;
883 VEVENT_S *vevent;
885 ical_debug("ical_parse_vevent", *text);
886 vevent = fs_get(sizeof(VEVENT_S));
887 memset((void *)vevent, 0, sizeof(VEVENT_S));
889 /* s must always point the the beginning of a line */
890 for(s = *text; s && *s != '\0';){
891 t = s;
892 s = ical_isolate_token(&t, &c);
893 if(s == NULL){
894 if(t != NULL)
895 s = t;
896 continue;
898 *t = c; /* restore separator */
900 if(s){ /* figure out the token */
901 int ukn = 0; /* unknown token */
902 if(!struncmp(s, "BEGIN", 5)){
903 s += 6; /* 6 = strlen("BEGIN:") */
904 if(!struncmp(s, "VALARM\r\n", 8)){
905 s += 8; /* 8 = strlen("VALARM\r\n"); */
906 if(vevent->valarm == NULL)
907 vevent->valarm = ical_parse_valarm(&s);
908 else{
909 VALARM_S *valrm;
910 for(valrm = vevent->valarm; valrm && valrm->next;
911 valrm = valrm->next);
912 valrm->next = ical_parse_valarm(&s);
914 } else {
915 ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0);
916 ical_free_unknown_comp(&uk_comp);
918 } else if(!struncmp(s, "END", t-s-1)){
919 s += 4; /* 4 = strlen("END:") */
920 if(!struncmp(s, "VEVENT\r\n",8)){
921 *text = s + 8; /* 8 = strlen("VCALENDAR\r\n") */
922 return (void *) vevent;
924 } else{ Event_prop i;
925 for(i = 0; i < EvUnknown; i++)
926 if(!struncmp(s, event_prop[i].prop, t-s))
927 break;
928 if(event_prop[i].parse){
929 if(vevent->prop == NULL){
930 vevent->prop = fs_get(EvUnknown*sizeof(void *));
931 memset((void *)vevent->prop, 0, EvUnknown*sizeof(void *));
933 (event_prop[i].parse)(&vevent->prop[event_prop[i].pos], &s, event_prop[i].prop);
935 else
936 ukn++;
939 if(ukn){
940 if(ical_buf_len < t - s){
941 fs_resize((void **)&ical_buf, t-s+1);
942 ical_buf_len = t-s;
944 *t = '\0';
945 strcpy(ical_buf, s);
946 *t = c;
947 if(vevent->uk_prop == NULL){
948 vevent->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
949 memset((void *)vevent->uk_prop, 0, sizeof(GEN_ICLINE_S));
950 vevent->uk_prop->cline = ical_parse_line(&s, ical_buf);
952 else{
953 GEN_ICLINE_S *gcl;
954 for (gcl = vevent->uk_prop; gcl && gcl->next; gcl = gcl->next);
955 gcl->next = fs_get(sizeof(GEN_ICLINE_S));
956 memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
957 gcl->next->cline = ical_parse_line(&s, ical_buf);
960 } /* end of if(s) */
963 *text = s;
964 return (void *) vevent;
967 void *
968 ical_parse_vtimezone(char **text)
970 char *s, *t;
971 char c;
972 void *v;
973 VTIMEZONE_S *vtz;
975 ical_debug("ical_parse_vtimezone", *text);
976 vtz = fs_get(sizeof(VTIMEZONE_S));
977 memset((void *)vtz, 0, sizeof(VTIMEZONE_S));
979 /* s must always point the the beginning of a line */
980 for(s = *text; s && *s != '\0';){
981 t = s;
982 s = ical_isolate_token(&t, &c);
983 if(s == NULL){
984 if(t != NULL)
985 s = t;
986 continue;
988 *t = c; /* restore separator */
990 if(s){ /* figure out the token */
991 int ukn = 0; /* unknown token */
992 if(!struncmp(s, "BEGIN", 5)){
993 s += 6; /* 6 = strlen("BEGIN:") */
994 if(!struncmp(s, "STANDARD\r\n", 10)){
995 s += 10; /* 10 = strlen("STANDARD\r\n"); */
996 v = ical_parse_timezone(&s);
997 if(vtz->standardc == NULL)
998 vtz->standardc = (ICAL_TZPROP_S *) v;
999 else{
1000 ICAL_TZPROP_S *dl;
1001 for(dl = vtz->standardc; dl && dl->next; dl = dl->next);
1002 dl->next = (ICAL_TZPROP_S *) v;
1004 } else if(!struncmp(s, "DAYLIGHT\r\n", 10)){
1005 s += 10; /* 10 = strlen("DAYLIGHT\r\n"); */
1006 v = ical_parse_timezone(&s);
1007 if(vtz->daylightc == NULL)
1008 vtz->daylightc = (ICAL_TZPROP_S *) v;
1009 else{
1010 ICAL_TZPROP_S *dl;
1011 for(dl = vtz->daylightc; dl && dl->next; dl = dl->next);
1012 dl->next = (ICAL_TZPROP_S *) v;
1014 } else {
1015 ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0);
1016 ical_free_unknown_comp(&uk_comp);
1018 } else if(!struncmp(s, "END", t-s-1)){
1019 s += 4; /* 4 = strlen("END:") */
1020 if(!struncmp(s, "VTIMEZONE\r\n",11)){
1021 *text = s + 11; /* 11 = strlen("VTIMEZONE\r\n") */
1022 return (void *) vtz;
1024 } else{ TZ_comp i;
1025 for(i = 0; i < TZCUnknown; i++)
1026 if(!struncmp(s, tz_comp[i].prop, t-s))
1027 break;
1028 if(tz_comp[i].parse){
1029 if(vtz->prop == NULL){
1030 vtz->prop = fs_get(TZCUnknown*sizeof(void *));
1031 memset((void *)vtz->prop, 0, TZCUnknown*sizeof(void *));
1033 (tz_comp[i].parse)(&vtz->prop[tz_comp[i].pos], &s, tz_comp[i].prop);
1035 else
1036 ukn++;
1039 if(ukn){
1040 if(ical_buf_len < t - s){
1041 fs_resize((void **)&ical_buf, t-s+1);
1042 ical_buf_len = t-s;
1044 *t = '\0';
1045 strcpy(ical_buf, s);
1046 *t = c;
1047 if(vtz->uk_prop == NULL){
1048 vtz->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
1049 memset((void *)vtz->uk_prop, 0, sizeof(GEN_ICLINE_S));
1050 vtz->uk_prop->cline = ical_parse_line(&s, ical_buf);
1052 else{
1053 GEN_ICLINE_S *gcl;
1054 for (gcl = vtz->uk_prop; gcl && gcl->next; gcl = gcl->next);
1055 gcl->next = fs_get(sizeof(GEN_ICLINE_S));
1056 memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
1057 gcl->next->cline = ical_parse_line(&s, ical_buf);
1060 } /* end of if(s) */
1063 *text = s;
1064 return (void *) vtz;
1067 void *
1068 ical_parse_timezone(char **text)
1070 char *s, *t;
1071 char c;
1072 ICAL_TZPROP_S *tzprop;
1074 ical_debug("ical_parse_timezone", *text);
1075 tzprop = fs_get(sizeof(ICAL_TZPROP_S));
1076 memset((void *)tzprop, 0, sizeof(ICAL_TZPROP_S));
1078 /* s must always point the the beginning of a line */
1079 for(s = *text; s && *s != '\0';){
1080 t = s;
1081 s = ical_isolate_token(&t, &c);
1082 if(s == NULL){
1083 if(t != NULL)
1084 s = t;
1085 continue;
1087 *t = c; /* restore separator */
1089 if(s){ /* figure out the token */
1090 int ukn = 0; /* unknown token */
1091 if(!struncmp(s, "BEGIN", 5)){
1092 s += 6; /* 6 = strlen("BEGIN:") */
1093 ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0);
1094 ical_free_unknown_comp(&uk_comp);
1095 } else if(!struncmp(s, "END", t-s-1)){
1096 s += 4; /* 4 = strlen("END:") */
1097 if(!struncmp(s, "STANDARD\r\n", 10)
1098 || !struncmp(s, "DAYLIGHT\r\n", 10)){
1099 *text = s + 10; /* 10 = strlen("STANDARD\r\n") */
1100 return (void *) tzprop;
1102 } else{ TZ_prop i;
1103 for(i = 0; i < TZPUnknown; i++)
1104 if(!struncmp(s, tz_prop[i].prop, t-s))
1105 break;
1106 if(tz_prop[i].parse){
1107 if(tzprop->prop == NULL){
1108 tzprop->prop = fs_get(TZPUnknown*sizeof(void *));
1109 memset((void *)tzprop->prop, 0, TZPUnknown*sizeof(void *));
1111 (tz_prop[i].parse)(&tzprop->prop[tz_prop[i].pos], &s, tz_prop[i].prop);
1113 else
1114 ukn++;
1117 if(ukn){
1118 if(ical_buf_len < t - s){
1119 fs_resize((void **)&ical_buf, t-s+1);
1120 ical_buf_len = t-s;
1122 *t = '\0';
1123 strcpy(ical_buf, s);
1124 *t = c;
1125 if(tzprop->uk_prop == NULL){
1126 tzprop->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
1127 memset((void *)tzprop->uk_prop, 0, sizeof(GEN_ICLINE_S));
1128 tzprop->uk_prop->cline = ical_parse_line(&s, ical_buf);
1130 else{
1131 GEN_ICLINE_S *gcl;
1132 for (gcl = tzprop->uk_prop; gcl && gcl->next; gcl = gcl->next);
1133 gcl->next = fs_get(sizeof(GEN_ICLINE_S));
1134 memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
1135 gcl->next->cline = ical_parse_line(&s, ical_buf);
1138 } /* end of if(s) */
1141 *text = s;
1142 return (void *) tzprop;
1145 void *
1146 ical_parse_valarm(char **text)
1148 char *s, *t;
1149 char c;
1150 VALARM_S *valarm;
1152 ical_debug("ical_parse_valarm", *text);
1153 valarm = fs_get(sizeof(VALARM_S));
1154 memset((void *)valarm, 0, sizeof(VALARM_S));
1156 /* s must always point the the beginning of a line */
1157 for(s = *text; s && *s != '\0';){
1158 t = s;
1159 s = ical_isolate_token(&t, &c);
1160 if(s == NULL){
1161 if(t != NULL)
1162 s = t;
1163 continue;
1165 *t = c; /* restore separator */
1167 if(s){ /* figure out the token */
1168 int ukn = 0; /* unknown token */
1169 if(!struncmp(s, "BEGIN", 5)){
1170 s += 6; /* 6 = strlen("BEGIN:") */
1171 ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0);
1172 ical_free_unknown_comp(&uk_comp);
1173 } else if(!struncmp(s, "END", t-s-1)){
1174 s += 4; /* 4 = strlen("END:") */
1175 if(!struncmp(s, "ALARM\r\n", 7)){
1176 *text = s + 7; /* 7 = strlen("ALARM\r\n") */
1177 return (void *) valarm;
1179 } else{ Alarm_prop i;
1180 for(i = 0; i < AlUnknown; i++)
1181 if(!struncmp(s, alarm_prop[i].prop, t-s))
1182 break;
1183 if(alarm_prop[i].parse){
1184 if(valarm->prop == NULL){
1185 valarm->prop = fs_get(AlUnknown*sizeof(void *));
1186 memset((void *)valarm->prop, 0, AlUnknown*sizeof(void *));
1188 (alarm_prop[i].parse)(&valarm->prop[alarm_prop[i].pos], &s, alarm_prop[i].prop);
1190 else
1191 ukn++;
1194 if(ukn){
1195 if(ical_buf_len < t - s){
1196 fs_resize((void **)&ical_buf, t-s+1);
1197 ical_buf_len = t-s;
1199 *t = '\0';
1200 strcpy(ical_buf, s);
1201 *t = c;
1202 if(valarm->uk_prop == NULL){
1203 valarm->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
1204 memset((void *)valarm->uk_prop, 0, sizeof(GEN_ICLINE_S));
1205 valarm->uk_prop->cline = ical_parse_line(&s, ical_buf);
1207 else{
1208 GEN_ICLINE_S *gcl;
1209 for (gcl = valarm->uk_prop; gcl && gcl->next; gcl = gcl->next);
1210 gcl->next = fs_get(sizeof(GEN_ICLINE_S));
1211 memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
1212 gcl->next->cline = ical_parse_line(&s, ical_buf);
1215 } /* end of if(s) */
1218 *text = s;
1219 return (void *) valarm;
1222 void *
1223 ical_parse_vtodo(char **text)
1225 return NULL;
1228 void *
1229 ical_parse_vjournal(char **text)
1231 return NULL;
1234 void *
1235 ical_parse_vfreebusy(char **text)
1237 return NULL;
1240 ICAL_S *
1241 ical_parse_generic_comp(char **text, int level)
1243 ICAL_S *ical;
1244 char *s, *t;
1245 char *token = NULL;
1246 GEN_ICLINE_S *gcl = NULL;
1247 char c;
1249 ical_debug("ical_parse_generic_comp", *text);
1250 ical = fs_get(sizeof(ICAL_S));
1251 memset((void *)ical, 0, sizeof(ICAL_S));
1253 ical->comp = ical_get_value(text);
1254 token = fs_get(strlen(ical->comp) + 2 + 1);
1255 sprintf(token, "%s\r\n", ical->comp); /* this is allocated memory */
1257 /* s must always point the the beginning of a line */
1258 for(s = *text; s && *s != '\0';){
1259 t = s;
1260 s = ical_isolate_token(&t, &c);
1261 if(s == NULL){
1262 if(t != NULL)
1263 s = t;
1264 continue;
1267 *t = c; /* restore character */
1268 if(s){ /* figure out the token */
1269 int ukn = 0; /* unknown token */
1270 switch(*s){
1271 case 'B':
1272 case 'b': if(!struncmp(s+1, "EGIN", 4)){
1273 s += 6; /* 6 = strlen("BEGIN:") */
1274 if(ical->next == NULL)
1275 ical->next = ical_parse_unknown_comp(&s, level+1);
1276 else{
1277 ICAL_S *b;
1278 int i;
1280 for(i = 0, b = ical; i <= level && b && b->next; b = b->next, i++);
1281 if(b->branch == NULL)
1282 b->branch = ical_parse_unknown_comp(&s, level+1);
1283 else {
1284 for(; b && b->branch; b = b->branch);
1285 b->branch = ical_parse_unknown_comp(&s, level+1);
1288 } else ukn++;
1289 break;
1291 case 'E':
1292 case 'e': if(!struncmp(s+1, "ND", 2)){
1293 *t = c;
1294 s += 4; /* 4 = strlen("END:") */
1295 if(!struncmp(s, token, strlen(token))){
1296 *text = s + strlen(token);
1297 ical->value = (void *) gcl;
1298 return ical;
1300 } else ukn++;
1301 break;
1303 default : ukn++;
1304 break;
1305 } /* end of switch(*s) */
1306 if(ukn){
1307 if(ical_buf_len < t - s){
1308 fs_resize((void **)&ical_buf, t-s+1);
1309 ical_buf_len = t-s;
1311 *t = '\0';
1312 strcpy(ical_buf, s);
1313 *t = c;
1314 if(gcl == NULL){
1315 gcl = fs_get(sizeof(GEN_ICLINE_S));
1316 memset((void *)gcl, 0, sizeof(GEN_ICLINE_S));
1317 gcl->cline = ical_parse_line(&s, ical_buf);
1319 else{
1320 GEN_ICLINE_S *gencl;
1321 for (gencl = gcl; gencl && gencl->next; gencl = gencl->next);
1322 gencl->next = fs_get(sizeof(GEN_ICLINE_S));
1323 memset((void *)gencl->next, 0, sizeof(GEN_ICLINE_S));
1324 gencl->next->cline = ical_parse_line(&s, ical_buf);
1327 } /* end of if(s) */
1330 ical->value = (void *) gcl;
1331 *text = s;
1332 return ical;
1335 ICAL_S *
1336 ical_parse_unknown_comp(char **text, int level)
1338 ICAL_S *ical;
1339 int i;
1341 ical_debug("ical_parse_unknown_comp", *text);
1342 for(i = 0; ical_comp[i].comp
1343 && (struncmp(*text, ical_comp[i].comp, ical_comp[i].len)
1344 || struncmp(*text + ical_comp[i].len, "\r\n", 2)); i++);
1346 if(ical_comp[i].parse){
1347 *text += ical_comp[i].len + 2;
1348 ical = fs_get(sizeof(ICAL_S));
1349 memset((void *)ical, 0, sizeof(ICAL_S));
1350 ical->comp = cpystr(ical_comp[i].comp);
1351 ical->value = (ical_comp[i].parse)(text);
1352 } else
1353 ical = ical_parse_generic_comp(text, level);
1355 return ical;
1358 ICLINE_S *
1359 ical_parse_line(char **text, char *name)
1361 ICLINE_S *ic;
1362 char *s = *text;
1364 ic = fs_get(sizeof(ICLINE_S));
1365 memset((void *)ic, 0, sizeof(ICLINE_S));
1367 ic->token = cpystr(name);
1368 s += strlen(name);
1369 if(*s == ';'){
1370 s++;
1371 ic->param = ical_get_parameter(&s);
1373 if(*s == ':'){
1374 s++;
1375 ic->value = ical_get_value(&s);
1378 *text = s;
1379 return ic;
1382 /***
1383 *** PARSE PROPERTY FUNCTIONS
1384 ***/
1386 void
1387 ical_parse_freq(Freq_value *fval, char *text)
1389 if(fval == NULL) return;
1391 *fval = FUnknown;
1393 if(text == NULL) return;
1395 if(!strucmp(text, "SECONDLY")) *fval = FSecondly;
1396 else if(!strucmp(text, "MINUTELY")) *fval = FMinutely;
1397 else if(!strucmp(text, "HOURLY")) *fval = FHourly;
1398 else if(!strucmp(text, "DAILY")) *fval = FDaily;
1399 else if(!strucmp(text, "WEEKLY")) *fval = FWeekly;
1400 else if(!strucmp(text, "MONTHLY")) *fval = FMonthly;
1401 else if(!strucmp(text, "YEARLY")) *fval = FYearly;
1404 void
1405 ical_parse_weekday_list(BYWKDY_S **bywkdyp, char *wklist)
1407 BYWKDY_S *bywkdy, *w;
1408 char *s, *t, c;
1409 int done;
1410 size_t len;
1412 if(bywkdyp == NULL) return;
1413 *bywkdyp = bywkdy = NULL;
1414 if(wklist == NULL) return;
1416 for(t = s = wklist; done == 0; s++){
1417 if(*s != ',' && *s != '\0')
1418 continue;
1419 c = *s;
1420 if(c == ',')
1421 *s = '\0';
1422 else /* c == '\0' */
1423 done++;
1424 len = strlen(t);
1425 if(len > 1){
1426 for(w = bywkdy; w && w->next; w = w->next);
1427 w = fs_get(sizeof(BYWKDY_S));
1428 memset((void *)w, 0, sizeof(BYWKDY_S));
1429 if(!strucmp(t+len-2, "SU")) w->wd = Sunday;
1430 else if(!strucmp(t+len-2, "MO")) w->wd = Monday;
1431 else if(!strucmp(t+len-2, "TU")) w->wd = Tuesday;
1432 else if(!strucmp(t+len-2, "WE")) w->wd = Wednesday;
1433 else if(!strucmp(t+len-2, "TH")) w->wd = Thursday;
1434 else if(!strucmp(t+len-2, "FR")) w->wd = Friday;
1435 else if(!strucmp(t+len-2, "SA")) w->wd = Saturday;
1436 // t[len - 2] = '\0';
1437 if(*t != '\0')
1438 w->value = strtoul(t, &t, 10);
1439 if(bywkdy == NULL)
1440 bywkdy = w;
1442 *s = c;
1443 if(*s == ',')
1444 t = s + 1;
1447 if(*bywkdyp)
1448 ical_free_weekday_list((void **)&bywkdy);
1449 else
1450 *bywkdyp = bywkdy;
1453 void
1454 ical_parse_number_list(BYWKDY_S **bynop, char *nolist)
1456 BYWKDY_S *byno, *n;
1457 char *s, *t, c;
1458 int done;
1460 if(bynop == NULL) return;
1461 *bynop = byno = NULL;
1462 if(nolist == NULL) return;
1464 for(t = s = nolist; done == 0; s++){
1465 if(*s != ',' && *s != '\0')
1466 continue;
1467 c = *s;
1468 if(c == ',')
1469 *s = '\0';
1470 else /* c == '\0' */
1471 done++;
1473 for(n = byno; n && n->next; n = n->next);
1474 n = fs_get(sizeof(BYWKDY_S));
1475 memset((void *)n, 0, sizeof(BYWKDY_S));
1476 n->value = strtoul(t, &t, 10);
1477 if(byno == NULL)
1478 byno = n;
1479 *s = c;
1480 if(*s == ',')
1481 t = s + 1;
1484 if(*bynop)
1485 ical_free_weekday_list((void **)&byno);
1486 else
1487 *bynop = byno;
1490 void
1491 ical_parse_rrule(RRULE_S **rrulep, char **text)
1493 RRULE_S *rrule;
1494 ICLINE_S *icl;
1495 char *s;
1496 ICAL_PARAMETER_S *param, *p;
1497 int i;
1499 if(rrulep == NULL
1500 || text == NULL || *text == NULL || struncmp(*text, "RRULE", 5)) return;
1501 rrule = fs_get(sizeof(RRULE_S));
1502 memset((void *) rrule, 0, sizeof(RRULE_S));
1504 /* recurring rules are special. First, we parse the icline that contains it */
1505 icl = ical_parse_line(text, "RRULE");
1507 /* now we copy the parameters that it contains */
1508 rrule->param = ical_parameter_cpy(icl->param);
1510 /* then we parse icl->value as if it was a parameter */
1511 s = icl->value;
1512 param = ical_get_parameter(&s);
1514 /* now we check which values were given, and fill the prop array */
1515 rrule->prop = fs_get(RRUnknown*sizeof(void *));
1516 memset((void *) rrule->prop, 0, RRUnknown*sizeof(void *));
1518 for(p = param; p != NULL; p = p->next){
1519 for(i = 0; rrule_prop[i].prop != NULL && strucmp(p->name, rrule_prop[i].prop); i++);
1520 if(rrule_prop[i].parse)
1521 (rrule_prop[i].parse)(&rrule->prop[rrule_prop[i].pos], p->value);
1524 ical_free_param(&param);
1525 ical_free_cline((void **)&icl);
1526 if(*rrulep)
1527 ical_free_rrule((void **)&rrule);
1528 else
1529 *rrulep = rrule;
1532 /*** UTF-8 for ICAL ***/
1535 ical_non_ascii_valid(unsigned char c)
1537 static unsigned char icu[6];
1538 static int utf8_len = 0;
1539 static int utf8_type = 0;
1540 int rv;
1542 if(utf8_len == 0)
1543 utf8_type = (c >= 0xF0 && c <= 0xF4)
1544 ? 4 : (c >= 0xE0 && c <= 0xEF)
1545 ? 3 : (c >= 0xC2 && c <= 0xDF)
1546 ? 2 : 0;
1548 if(utf8_type == 0)
1549 return 0;
1551 icu[utf8_len++] = c; /* count it */
1553 if(utf8_type == 2){
1554 if(utf8_len < 2)
1555 rv = NEED_MORE;
1556 else if(utf8_len == 2){
1557 rv = (icu[0] >= 0xC2 && icu[0] <= 0xDF)
1558 && (icu[1] >= 0x80 && icu[1] <= 0xBF) ? UTF8_COMPLETE : 0;
1559 utf8_len = 0;
1561 } else if (utf8_type == 3){
1562 if(utf8_len < 3)
1563 rv = NEED_MORE;
1564 else{
1565 if(icu[0] == 0xE0)
1566 rv = (icu[1] >= 0xA0 && icu[1] <= 0xBF)
1567 && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0;
1568 else if(icu[0] >= 0xE1 && icu[0] <= 0xEC)
1569 rv = (icu[1] >= 0x80 && icu[1] <= 0xBF)
1570 && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0;
1571 else if(icu[0] == 0xED)
1572 rv = (icu[1] >= 0x80 && icu[1] <= 0x9F)
1573 && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0;
1574 else if(icu[0] >= 0xE1 && icu[0] <= 0xEC)
1575 rv = (icu[1] >= 0x80 && icu[1] <= 0xBF)
1576 && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0;
1577 utf8_len = 0;
1579 } else if (utf8_type == 4){
1580 if(utf8_len < 4)
1581 rv = NEED_MORE;
1582 else{
1583 if(icu[0] == 0xF0)
1584 rv = (icu[1] >= 0x90 && icu[1] <= 0xBF)
1585 && (icu[2] >= 0x80 && icu[2] <= 0xBF)
1586 && (icu[3] >= 0x80 && icu[3] <= 0xBF) ? UTF8_COMPLETE : 0;
1587 else if(icu[0] >= 0xF1 && icu[0] <= 0xF3)
1588 rv = (icu[1] >= 0x80 && icu[1] <= 0xBF)
1589 && (icu[2] >= 0x80 && icu[2] <= 0xBF)
1590 && (icu[3] >= 0x80 && icu[3] <= 0xBF) ? UTF8_COMPLETE : 0;
1591 else if(icu[0] == 0xF4)
1592 rv = (icu[1] >= 0x80 && icu[1] <= 0x8F)
1593 && (icu[2] >= 0x80 && icu[2] <= 0xBF)
1594 && (icu[3] >= 0x80 && icu[3] <= 0xBF) ? UTF8_COMPLETE : 0;
1595 utf8_len = 0;
1598 return rv;
1602 ical_get_number_value(char *value, int beg_pos, int end_pos)
1604 char c, *err;
1605 int rv;
1607 c = value[end_pos];
1608 value[end_pos] = '\0';
1609 rv = strtoul(value + beg_pos, &err, 10);
1610 if(err != NULL && *err != '\0') return -1;
1611 value[end_pos] = c;
1612 return rv;
1615 void
1616 ical_free_duration(ICAL_DURATION_S **ic_d)
1618 if(ic_d == NULL || *ic_d == NULL)
1619 return;
1621 if((*ic_d)->next) ical_free_duration(&(*ic_d)->next);
1622 fs_give((void **)ic_d);
1625 /* returns 0 if no error, -1 if some error */
1627 ical_parse_duration(char *value, ICAL_DURATION_S *ic_d)
1629 int i, j, rv = 0;
1631 if(value == NULL || ic_d == NULL) return -1;
1633 memset((void *)ic_d, 0, sizeof(ICAL_DURATION_S));
1635 if(value[i = 0] == '-'){
1636 i++;
1637 ic_d->sign = 1;
1638 } else if(value[i] == '+')
1639 i++;
1641 if(value[i++] == 'P'){
1642 for(j = i; value[j] != '\0' && value[j] != ','; j++){
1643 if(!isdigit(value[j]))
1644 switch(value[j]){
1645 case 'W': ic_d->weeks = ical_get_number_value(value, i, j-1);
1646 i = ++j;
1647 break;
1648 case 'D': ic_d->days = ical_get_number_value(value, i, j-1);
1649 i = ++j;
1650 break;
1651 case 'H': ic_d->hours = ical_get_number_value(value, i, j-1);
1652 i = ++j;
1653 break;
1654 case 'M': ic_d->minutes = ical_get_number_value(value, i, j-1);
1655 i = ++j;
1656 break;
1657 case 'S': ic_d->seconds = ical_get_number_value(value, i, j-1);
1658 i = ++j;
1659 break;
1660 case 'T': i = j + 1;
1661 break;
1662 default: rv = -1;
1663 break;
1667 else
1668 rv = -1;
1670 if(value[j++] == ','){
1671 ICAL_DURATION_S next;
1672 rv = ical_parse_duration(value+j, &next);
1673 ic_d->next = &next;
1676 return rv;
1679 /* return -1 if any error,
1680 0 if value has the DATE-TIME form
1681 1 if value has the DATE form only
1684 ical_parse_date(char *value, struct tm *t)
1686 int i, rv;
1687 struct tm Tm;
1689 rv = -1;
1690 if(t == NULL) return rv;
1691 memset((void *)&Tm, 0, sizeof(struct tm));
1693 if(value == NULL) return rv;
1695 rv = 0; /* assume DATE-TIME format */
1696 /* a simple check for the format of the string */
1697 for(i = 0; isdigit(value[i]); i++);
1698 if (i == 8 && value[i] == '\0')
1699 rv = 1;
1700 else
1701 if (i != 8 || value[i++] != 'T') return -1;
1702 if(rv == 0) {
1703 for(; isdigit(value[i]); i++);
1704 if(i != 15 || (value[i] != '\0' && (value[i] != 'Z' || value[i+1] != '\0')))
1705 return -1;
1708 Tm.tm_year = ical_get_number_value(value, 0, 4) - 1900;
1709 Tm.tm_mon = ical_get_number_value(value, 4, 6) - 1;
1710 Tm.tm_mday = ical_get_number_value(value, 6, 8);
1711 if(rv == 0){
1712 Tm.tm_hour = ical_get_number_value(value, 9, 11);
1713 Tm.tm_min = ical_get_number_value(value, 11, 13);
1714 Tm.tm_sec = ical_get_number_value(value, 13, 15);
1716 *t = Tm;
1718 return (t->tm_mon > 11 || t->tm_mon < 0
1719 || t->tm_mday > 31 || t->tm_mday < 0
1720 || t->tm_hour > 23 || t->tm_hour < 0
1721 || t->tm_min > 59 || t->tm_min < 0
1722 || t->tm_sec > 60 || t->tm_sec < 0)
1723 ? - 1 : rv;
1726 void
1727 ical_set_date(ICLINE_S *icl, VTIMEZONE_S *vtz)
1729 int date_form; /* date forms from section 3.3.4 in RFC 5545 */
1730 ICAL_PARAMETER_S *param;
1731 char *tz = NULL;
1732 struct tm ic_date;
1733 time_t t;
1735 if(icl == NULL) return;
1737 for(param = icl->param; param != NULL; param = param->next)
1738 if(!strucmp(param->name, "TZID"))
1739 tz = param->value;
1741 if(tz != NULL)
1742 date_form = 3; /* local time with timezone */
1743 else if(icl->value[strlen(icl->value)-1] == 'Z')
1744 date_form = 2; /* utc time */
1745 else date_form = 1; /* local time */
1747 ical_parse_date(icl->value, &ic_date);
1748 ic_date.tm_wday = ical_day_of_week(ic_date); /* find out day of the week */
1750 switch(date_form){
1751 case 1: break;
1752 case 2: ical_adjust_date(&ic_date, vtz);
1753 break;
1754 case 3: break;
1755 default: alpine_panic ("Impossible date_form");
1759 ICAL_TZPROP_S *
1760 ical_std_or_daylight(struct tm *date, VTIMEZONE_S *vtz)
1762 struct tm standard, daylight;
1769 /* adjusts time to given time zone */
1770 void
1771 ical_adjust_date(struct tm *date, VTIMEZONE_S *vtz)
1773 char *tzname = NULL;
1774 ICLINE_S *icl;
1775 ICAL_TZPROP_S *cur_std_day;
1777 if(vtz == NULL)
1778 return;
1780 if(vtz->prop){
1781 if((icl = (ICLINE_S *)vtz->prop[TZCid]) != NULL)
1782 tzname = cpystr(icl->value);
1785 //+++ cur_std_day = ical_std_or_daylight(date, vtz);
1788 void
1789 ical_set_date_vevent(void *veventv, void *vtzv)
1791 VEVENT_S *vevent = (VEVENT_S *) veventv;
1792 VTIMEZONE_S *vtz = (VTIMEZONE_S *) vtzv;
1794 if(vevent){
1795 ical_set_date(vevent->prop[EvDtstamp], vtz);
1796 ical_set_date(vevent->prop[EvDtstart], vtz);
1797 ical_set_date(vevent->prop[EvDtend], vtz);
1801 #define LEAP_YEAR(X) ((((X) % 4 == 0) \
1802 && (((X) % 100 != 0) || ((X) % 400 == 0))) \
1803 || (X) == 1700)
1805 #define CAL_OFFSET(X) (((X) == 1752) ? 5 : (LEAP_YEAR((X)) ? 2 : 1))
1807 /* given a year, after day_zero, return the day
1808 * of the week of the first of january of that year. On error,
1809 * return a negative number.
1810 * Assumption: day_zero is the date of january 1, of some year.
1813 ical_january_first(int year)
1815 int i, january_first;
1817 if(year < day_zero.tm_year) return -1; /* not supported */
1819 year += 1900;
1820 january_first = day_zero.tm_wday;
1821 for(i = 1900 + day_zero.tm_year + 1; i <= year; i++)
1822 january_first += CAL_OFFSET(i-1);
1824 return january_first % 7;
1827 /* given a month, week day, and year, return all days of the month
1828 * that have that day as the week day. For example, return all
1829 * sundays in november 2012.
1831 int *
1832 ical_day_from_week(int month, Weekday day, int year)
1834 int *rv = NULL;
1835 int fday, nday;
1836 Weekday wday;
1837 int i;
1839 fday = ical_first_of_month(month, year);
1840 year += 1900; /* restore year */
1841 if(year == 1752 && month == 8){
1842 fday = 9;
1843 } else {
1844 for(nday = 1, wday = (Weekday) fday; wday != day; wday = (wday+1) % 7, nday++)
1846 rv = fs_get(6*sizeof(int));
1847 memset((void *)&rv, 0, 6*sizeof(int));
1848 for(i = 0; nday <= month_len[month]; i++){
1849 rv[i] = nday;
1850 nday += 7;
1852 if(LEAP_YEAR(year) && month == 1 && nday == 29)
1853 rv[i] = nday;
1856 return rv;
1860 /* given a month and a year, return the weekday of the first of the
1861 * month in that year.
1862 * return value: on error -1, otherwise the day of the week.
1865 ical_first_of_month(int month, int year)
1867 int i, d;
1869 if((d = ical_january_first(year)) < 0)
1870 return -1;
1872 year += 1900;
1873 for(i = 0; i < month; i++)
1874 d += month_len[i];
1876 if(LEAP_YEAR(year) && month >= 2)
1877 d += 1;
1879 if(year == 1752 && month >= 9)
1880 d -= 11;
1882 return d % 7;
1885 /* given a day, month and year, return the weekday of that day
1886 * return value: on error -1, otherwise the day of the week.
1888 int ical_day_of_week(struct tm date)
1890 int d;
1892 if((d = ical_first_of_month(date.tm_mon, date.tm_year)) < 0)
1893 return -1;
1895 d += date.tm_mday - 1;
1897 if(date.tm_year + 1900 == 1752){
1898 if(date.tm_mday > 2 && date.tm_mday < 14)
1899 return -1;
1900 if(date.tm_mday >= 14)
1901 d -= 11;
1903 return d % 7;
1907 /* given an initial date dtstart, and a recurring rule, rrule,
1908 * adjust the date to the first date on the same year, when
1909 * the rule actually starts
1911 struct tm
1912 adjust_date_rrule(struct tm *dtstart, RRULE_S *rrule)
1914 struct tm t;
1916 memset((void *) &t, 0, sizeof(struct tm));
1917 t.tm_year = dtstart->tm_year; /* same year */
1918 if(rrule->prop[RRFreq]){
1920 if(rrule->prop[RRCount]){
1922 else if(rrule->prop[RRInterval]){
1924 if(rrule->prop[RRBysecond]){
1925 BYWKDY_S *sec = (BYWKDY_S *) rrule->prop[RRBysecond], *seco;
1926 for (seco = sec; seco != NULL; seco = seco->next)
1927 if(seco == sec) t.tm_sec = seco->value;
1928 else if (seco->value < t.tm_sec)
1929 t.tm_sec = seco->value;
1931 if (rrule->prop[RRByminute]){
1932 BYWKDY_S *sec = (BYWKDY_S *) rrule->prop[RRByminute], *seco;
1933 for (seco = sec; seco != NULL; seco = seco->next)
1934 if(seco == sec) t.tm_min = seco->value;
1935 else if (seco->value < t.tm_sec)
1936 t.tm_min = seco->value;
1938 if (rrule->prop[RRByhour]){
1939 BYWKDY_S *sec = (BYWKDY_S *) rrule->prop[RRByhour], *seco;
1940 for (seco = sec; seco != NULL; seco = seco->next)
1941 if(seco == sec) t.tm_hour = seco->value;
1942 else if (seco->value < t.tm_sec)
1943 t.tm_hour = seco->value;
1945 if (rrule->prop[RRByday]){
1947 if (rrule->prop[RRByweekno]){
1949 if (rrule->prop[RRBymonthday]){
1951 if (rrule->prop[RRByyearday]){
1953 if (rrule->prop[RRByweekno]){
1955 if (rrule->prop[RRBymonth]){
1956 BYWKDY_S *m = (BYWKDY_S *) rrule->prop[RRBymonth], *mo;
1957 for (mo = m; mo != NULL; mo = mo->next)
1958 if(mo == m) t.tm_mon = mo->value - 1;
1959 else if (mo->value - 1 < t.tm_mon)
1960 t.tm_mon = mo->value - 1;
1962 if (rrule->prop[RRBysetpos]){
1964 if (rrule->prop[RRWkst]){
1968 void
1969 ical_initialize(void)
1971 ical_buf_len = 1024;
1972 ical_buf = fs_get(ical_buf_len+1);
1974 memset((void *) &day_zero, 0, sizeof(struct tm));
1975 day_zero.tm_year = 1601 - 1900;
1976 day_zero.tm_mday = 1;
1977 day_zero.tm_wday = 4;
1980 /* we create a summary of the event, and pass that back as
1981 an ical parameter
1983 VEVENT_SUMMARY_S *
1984 ical_vevent_summary(VCALENDAR_S *vcal)
1986 VEVENT_SUMMARY_S *rv;
1987 ICLINE_S *method;
1988 VEVENT_S *vevent;
1989 GEN_ICLINE_S *gicl;
1990 ICLINE_S *icl;
1991 char *k;
1993 if(vcal == NULL) return NULL;
1995 method = vcal->method;
1996 vevent = (VEVENT_S *) vcal->comp[VEvent];
1998 if(vevent == NULL || vevent->prop == NULL)
1999 return NULL;
2001 rv = fs_get(sizeof(VEVENT_SUMMARY_S));
2002 memset((void *) rv, 0, sizeof(VEVENT_SUMMARY_S));
2004 if(method != NULL && !strucmp(method->value, "CANCEL"))
2005 rv->cancel++;
2007 if((icl = (ICLINE_S *) vevent->prop[EvPriority]) != NULL)
2008 rv->priority = atoi(icl->value);
2010 if((icl = (ICLINE_S *) vevent->prop[EvSummary]) != NULL)
2011 rv->summary = cpystr(icl->value ? icl->value : _("No Summary"));
2013 if((icl = (ICLINE_S *) vevent->prop[EvClass]) != NULL)
2014 rv->class = cpystr(icl->value ? icl->value : _("PUBLIC"));
2015 else
2016 rv->class = cpystr(_("PUBLIC"));
2018 if((icl = (ICLINE_S *) vevent->prop[EvOrganizer]) != NULL){
2019 char *cn, *sender, *address;
2020 ICAL_PARAMETER_S *param;
2022 cn = sender = address = NULL;
2023 for(param = icl->param; param != NULL; param = param->next)
2024 if(!strucmp(param->name, "CN"))
2025 cn = param->value;
2026 else if(!strucmp(param->name, "SENT-BY"))
2027 sender = param->value;
2029 if(sender != NULL){
2030 if(!struncmp(sender, "MAILTO:", 7))
2031 sender += 7;
2032 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "<%s>", sender);
2033 rv->sender = cpystr(tmp_20k_buf);
2036 if((address = icl->value) != NULL){
2037 if(!struncmp(address, "MAILTO:", 7))
2038 address += 7;
2039 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s<%s>",
2040 cn ? cn : "", cn ? " " : "",
2041 address ? address : _("Unknown address"));
2042 rv->organizer = cpystr(tmp_20k_buf);
2044 } /* end of if(organizer) */
2046 if((icl = (ICLINE_S *) vevent->prop[EvLocation]) != NULL)
2047 rv->location = cpystr(icl->value ? icl->value : _("Location undisclosed"));
2049 if((icl = (ICLINE_S *) vevent->prop[EvDtstart]) != NULL){
2050 struct tm ic_date;
2051 char tmp[200];
2052 int icd; /* ical date return value */
2054 memset((void *)&ic_date, 0, sizeof(struct tm));
2055 icd = ical_parse_date(icl->value, &ic_date);
2056 if(icd >= 0){
2057 ic_date.tm_wday = ical_day_of_week(ic_date);
2058 switch(icd){
2059 case 0: /* DATE-TIME */
2060 our_strftime(tmp, sizeof(tmp), "%a %x %I:%M %p", &ic_date);
2061 break;
2062 case 1: /* DATE */
2063 our_strftime(tmp, sizeof(tmp), "%a %x", &ic_date);
2064 break;
2065 default: alpine_panic("Unhandled ical date format");
2066 break;
2069 else{
2070 strncpy(tmp, _("Error while parsing event date"), sizeof(tmp));
2071 tmp[sizeof(tmp) - 1] = '\0';
2073 rv->evstart = cpystr(icl->value ? tmp : _("Unknown Start Date"));
2074 } /* end of if dtstart */
2076 if((icl = (ICLINE_S *) vevent->prop[EvDuration]) != NULL){
2077 int i, done = 0;
2078 ICAL_DURATION_S ic_d, icd2;
2079 if(ical_parse_duration(icl->value, &ic_d) == 0){
2080 char tmp[MAILTMPLEN+1];
2082 for(i = 1, icd2 = ic_d; icd2.next != NULL; icd2 = *icd2.next, i++);
2083 rv->duration = fs_get((i+1)*sizeof(char *));
2084 i = 0;
2086 do {
2087 tmp[0] = '\0';
2089 if(ic_d.weeks > 0)
2090 utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
2091 "%d %s ", ic_d.weeks, ic_d.weeks == 1 ? _("week") : _("weeks"));
2092 if(ic_d.days > 0)
2093 utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
2094 "%d %s ", ic_d.days, ic_d.days == 1 ? _("day") : _("days"));
2095 if(ic_d.hours > 0)
2096 utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
2097 "%d %s ", ic_d.hours, ic_d.hours == 1 ? _("hour") : _("hours"));
2098 if(ic_d.minutes > 0)
2099 utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
2100 "%d %s ", ic_d.minutes, ic_d.minutes == 1 ? _("minute") : _("minutes"));
2101 if(ic_d.seconds > 0)
2102 utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
2103 "%d %s ", ic_d.seconds, ic_d.seconds == 1 ? _("second") : _("seconds"));
2105 tmp[MAILTMPLEN] = '\0';
2106 rv->duration[i++] = cpystr(tmp);
2108 if(ic_d.next != NULL)
2109 ic_d = *ic_d.next;
2110 else
2111 done++;
2112 } while (done == 0);
2113 rv->duration[i] = NULL;
2115 } /* end of DURATION */
2116 else if((icl = (ICLINE_S *) vevent->prop[EvDtend]) != NULL){
2117 struct tm ic_date;
2118 char tmp[200];
2119 int icd;
2121 memset((void *)&ic_date, 0, sizeof(struct tm));
2122 icd = ical_parse_date(icl->value, &ic_date);
2123 if(icd >= 0){
2124 ic_date.tm_wday = ical_day_of_week(ic_date);
2125 switch(icd){
2126 case 0: /* DATE-TIME */
2127 our_strftime(tmp, sizeof(tmp), "%a %x %I:%M %p", &ic_date);
2128 break;
2129 case 1: /* DATE */
2130 our_strftime(tmp, sizeof(tmp), "%a %x", &ic_date);
2131 break;
2132 default: alpine_panic("Unhandled ical date format");
2133 break;
2136 else{
2137 strncpy(tmp, _("Error while parsing event date"), sizeof(tmp));
2138 tmp[sizeof(tmp) - 1] = '\0';
2140 rv->evend = cpystr(icl->value ? tmp : _("Unknown End Date"));
2141 } /* end of if dtend */
2143 if((gicl = (GEN_ICLINE_S *) vevent->prop[EvAttendee]) != NULL){
2144 int nattendees, i;
2146 for(nattendees = 0; gicl != NULL; gicl = gicl->next, nattendees++);
2147 rv->attendee = fs_get((nattendees+1)*sizeof(char *));
2149 gicl = (GEN_ICLINE_S *) vevent->prop[EvAttendee];
2150 for(i = 0; gicl != NULL; gicl = gicl->next, i++){
2151 char *role, *partstat, *rsvp;
2152 char *cn, *mailto;
2153 ICAL_PARAMETER_S *param;
2155 icl = gicl->cline;
2156 role = partstat = rsvp = cn = mailto = NULL;
2157 for(param = icl->param; param != NULL; param = param->next){
2158 if(!strucmp(param->name, "ROLE")){
2159 if(!strucmp(param->value, "REQ-PARTICIPANT"))
2160 role = _("[Required]");
2161 else if(!strucmp(param->value, "OPT-PARTICIPANT"))
2162 role = _("[Optional]");
2163 else if(!strucmp(param->value, "NON-PARTICIPANT"))
2164 role = _("[Informed]");
2165 else if(!strucmp(param->value, "CHAIR"))
2166 role = _("[ Chair ]");
2167 else
2168 role = param->value;
2170 else if(!strucmp(param->name, "PARTSTAT")){
2171 if(!strucmp(param->value, "NEEDS-ACTION"))
2172 partstat = _("[Need-Reply]");
2173 else if(!strucmp(param->value, "ACCEPTED"))
2174 partstat = _("[ Accepted ]");
2175 else if(!strucmp(param->value, "DECLINED"))
2176 partstat = _("[ Declined ]");
2177 else if(!strucmp(param->value, "TENTATIVE"))
2178 partstat = _("[ Tentative]");
2179 else if(!strucmp(param->value, "DELEGATED"))
2180 partstat = _("[ Delegated]");
2181 else
2182 partstat = param->value;
2184 else if(!strucmp(param->name, "RSVP"))
2185 rsvp = param->value;
2186 else if(!strucmp(param->name, "CN"))
2187 cn = param->value;
2189 if(icl->value && !struncmp(icl->value, "MAILTO:", strlen("MAILTO:")))
2190 mailto = icl->value + 7; /* 7 = strlen("MAILTO:") */
2191 if(!strucmp(cn, mailto))
2192 cn = "";
2193 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s%s%s%s<%s>",
2194 role && *role ? role : "",
2195 role && *role ? " " : "",
2196 partstat ? partstat : _("[Unknown Reply]"),
2197 " ",
2198 cn && *cn ? cn : "",
2199 cn && *cn ? " " : "",
2200 mailto ? mailto : _("Unknown address"));
2201 rv->attendee[i] = cpystr(tmp_20k_buf);
2203 rv->attendee[i] = NULL;
2204 } /* end of ATTENDEES */
2206 if((icl = (ICLINE_S *) vevent->prop[EvDescription]) != NULL){
2207 char *s, *t, *u, *v;
2208 int i, escaped;
2210 if(icl->value == NULL){
2211 free_vevent_summary(&rv);
2212 return NULL;
2215 v = cpystr(icl->value); /* process a copy of icl->value */
2217 for(i = 1, escaped = 0, s = v; s && *s; s++){
2218 if(*s == '\\' && escaped == 0){ escaped = 1; continue; }
2219 if(escaped){
2220 if(!(*s == '\\' || *s == ',' || *s == 'n' || *s == 'N' || *s == ';')){
2221 free_vevent_summary(&rv);
2222 fs_give((void **)&v);
2223 return NULL;
2225 escaped = 0;
2226 continue;
2228 if(*s == ',') i++; /* a non-scaped comma is a new value for text */
2231 rv->description = fs_get((i+1)*sizeof(char *));
2232 i = 0;
2233 for (s = t = u = v, escaped = 0; *t != '\0'; t++){
2234 if(*t == '\\' && escaped == 0){ escaped = 1; continue; }
2235 if(escaped){
2236 switch(*t){
2237 case '\\':
2238 case ',':
2239 case ';':
2240 *u++ = *t;
2241 break;
2242 case 'n':
2243 case 'N':
2244 *u++ = '\n';
2245 break;
2246 default: free_vevent_summary(&rv);
2247 fs_give((void **)&v);
2248 return NULL;
2250 escaped = 0;
2251 continue;
2253 if(*t == ','){
2254 *u = '\0';
2255 rv->description[i++] = cpystr(ical_decode(s, vcal->encoding));
2256 s = u = t+1;
2257 } else
2258 *u++ = *t;
2260 *u = '\0';
2261 rv->description[i++] = cpystr(ical_decode(s, vcal->encoding));
2262 rv->description[i] = NULL;
2263 fs_give((void **)&v);
2264 } /* end of if(description) */
2266 return rv;
2269 void
2270 free_vevent_summary(VEVENT_SUMMARY_S **vesy)
2272 int i;
2273 if(vesy == NULL || *vesy == NULL) return;
2275 if((*vesy)->class) fs_give((void **)&(*vesy)->class);
2276 if((*vesy)->summary) fs_give((void **)&(*vesy)->summary);
2277 if((*vesy)->sender) fs_give((void **)&(*vesy)->sender);
2278 if((*vesy)->organizer) fs_give((void **)&(*vesy)->organizer);
2279 if((*vesy)->location) fs_give((void **)&(*vesy)->location);
2280 if((*vesy)->evstart) fs_give((void **)&(*vesy)->evstart);
2281 if((*vesy)->evend) fs_give((void **)&(*vesy)->evend);
2282 if((*vesy)->duration){
2283 for(i = 0; (*vesy)->duration[i] != NULL; i++)
2284 fs_give((void **) &(*vesy)->duration[i]);
2285 fs_give((void **) (*vesy)->duration);
2287 if((*vesy)->attendee){
2288 for(i = 0; (*vesy)->attendee[i] != NULL; i++)
2289 fs_give((void **) &(*vesy)->attendee[i]);
2290 fs_give((void **) (*vesy)->attendee);
2292 if((*vesy)->description){
2293 for(i = 0; (*vesy)->description[i] != NULL; i++)
2294 fs_give((void **) &(*vesy)->description[i]);
2295 fs_give((void **) (*vesy)->description);
2297 fs_give((void **) vesy);
2300 #ifdef STANDALONE
2301 void ical_print_line(ICLINE_S icl)
2303 ICAL_PARAMETER_S *p;
2304 printf("token=%s, ", icl.token);
2305 for(p = icl.param; p != NULL; p = p->next){
2306 if(p->name) printf("param->name=%s, ", p->name);
2307 if(p->value) printf("param->value=%s, ", p->value);
2309 printf("value=%s", icl.value);
2310 printf("%c", '\n');
2313 #define CRLFCOUNT 100
2315 char *lf2crlf(char *text)
2317 char *rv, *s;
2318 int i, len;
2320 if(text == NULL || *text == '\0')
2321 return NULL;
2323 len = strlen(text);
2324 rv = fs_get(len+1);
2326 for(i = 0, s = text; *s;){
2327 if(i == len - 1){
2328 len += CRLFCOUNT;
2329 fs_resize((void *)&rv, len);
2331 if(*s == '\n')
2332 rv[i++] = '\r';
2333 rv[i++] = *s++;
2335 rv[i] = '\0';
2336 fs_give((void **)&text);
2337 text = rv;
2339 return rv;
2342 int main (int argc, char *argv[])
2344 char *text;
2345 VCALENDAR_S *vcal;
2347 if(argc != 2){
2348 fprintf(stderr, "Only give one argument: File name to read\n");
2349 _exit(1);
2352 if(readfile(argv[1], &text, NULL) < 0){
2353 fprintf(stderr, "Can not read %s succesfully\n", argv[1]);
2354 _exit(2);
2357 text = lf2crlf(text);
2358 vcal = ical_parse_text(text);
2360 ical_free_vcalendar((void **)&vcal);
2361 return 0;
2365 #endif /* STANDALONE */