* Replace body.c by body.obj in pith/makefile.wnt to fix
[alpine.git] / pith / ical.c
blob7cf742f59ccb745f2b15cca98bb3966a431fabc3
1 #include "../pith/headers.h"
2 #include "../pith/mailpart.h"
3 #include "../pith/store.h"
4 #include "../pith/ical.h"
6 typedef struct ical_iana_comp_s {
7 char *comp; /* component name */
8 size_t len; /* size of component name (strlen(x->comp)) */
9 int pos; /* position of this component in comp array */
10 void *(*parse)(char **); /* parser */
11 void (*give)(void **); /* free memory */
12 } ICAL_COMP_S;
14 typedef struct ical_iana_prop_s {
15 char *prop; /* component PROPerty name */
16 size_t len; /* size of component name (strlen(x->prop)) */
17 int pos; /* location of this component in the prop array */
18 void *(*parse)(); /* parser */
19 void (*give)(void **); /* free memory */
20 } ICAL_PROP_S;
22 int ical_january_first(int); /* args: year */
23 void ical_adjust_date(struct tm *, VTIMEZONE_S *);
25 void ical_initialize(void);
27 int ical_non_ascii_valid(unsigned char);
28 char *ical_unfold_line(char *);
29 ICLINE_S *ical_parse_line(char **, char *);
31 ICLINE_S *ical_cline_cpy(ICLINE_S *);
32 ICAL_PARAMETER_S *ical_parameter_cpy(ICAL_PARAMETER_S *param);
34 char *ical_get_value(char **);
35 unsigned char *ical_decode(char *, unsigned short);
37 /* pase component */
38 void *ical_parse_vcalendar(char **);
39 void *ical_parse_vevent(char **);
40 void *ical_parse_vtodo(char **);
41 void *ical_parse_vjournal(char **);
42 void *ical_parse_vfreebusy(char **);
43 void *ical_parse_vtimezone(char **);
44 void *ical_parse_valarm(char **);
45 void *ical_parse_timezone(char **);
46 ICAL_S *ical_parse_unknown_comp(char **, int);
47 ICAL_S *ical_parse_generic_comp(char **, int);
49 /* free components */
50 void ical_free_vevent(void **);
51 void ical_free_vtodo(void **);
52 void ical_free_vjournal(void **);
53 void ical_free_vfreebusy(void **);
54 void ical_free_vtimezone(void **);
55 void ical_free_timezone(void **);
56 void ical_free_valarm(void **);
57 void ical_free_unknown_comp(ICAL_S **);
59 /* parse properties */
60 void *ical_cline_from_token(void *, char **, char *);
61 void *ical_gencline_from_token(void *, char **, char *);
63 void *ical_parse_rrule(void *, char **, char *);
64 void *ical_parse_time(void *, char **, char *);
65 void *ical_parse_offset(void *, char **, char *);
67 void *ical_parse_freq(void *, char *);
68 void *ical_parse_until(void *, char *);
69 void *ical_parse_count(void *, char *);
70 void *ical_parse_interval(void *, char *);
71 void *ical_parse_weekday_list(void *, char *);
72 void *ical_parse_number_list(void *, char *);
74 int ical_get_number_value(char *, int, int);
75 void ical_set_date(ICLINE_S *, VTIMEZONE_S *);
76 void ical_set_date_vevent(void *, void *);
78 /* free properties */
79 void ical_free_prop(void **, ICAL_PROP_S *);
80 void ical_free_null(void **);
81 void ical_free_cline(void **);
82 void ical_free_param(ICAL_PARAMETER_S **);
83 void ical_free_gencline(void **);
84 void ical_free_rrule(void **);
85 void ical_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_free_null},
119 {"UNTIL", 5, RRUntil, ical_parse_until, ical_free_null},
120 {"COUNT", 5, RRCount, ical_parse_count, ical_free_null},
121 {"INTERVAL", 8, RRInterval, ical_parse_interval, ical_free_null},
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_free_null},
179 {"TZOFFSETTO", 10, TZPOffsetto, ical_parse_offset, ical_free_null},
180 {"TZOFFSETFROM", 12, TZPOffsetfrom, ical_parse_offset, ical_free_null},
181 {"RRULE", 5, TZPRrule, ical_parse_rrule, ical_free_rrule},
182 {"COMMENT", 7, TZPComment, ical_gencline_from_token, ical_free_gencline},
183 {"RDATE", 5, TZPRdate, ical_gencline_from_token, ical_free_gencline},
184 {"TZNAME", 6, TZPTzname, ical_gencline_from_token, ical_free_gencline},
185 {NULL, 0, TZPUnknown, 0, 0}
188 ICAL_PROP_S alarm_prop[] = {
189 {"ACTION", 6, AlAction, ical_cline_from_token, ical_free_cline},
190 {"TRIGGER", 7, AlTrigger, ical_cline_from_token, ical_free_cline},
191 {"DURATION", 8, AlDuration, ical_cline_from_token, ical_free_cline},
192 {"REPEAT", 6, AlRepeat, ical_cline_from_token, ical_free_cline},
193 {"DESCRIPTION", 11, AlDescription, ical_cline_from_token, ical_free_cline},
194 {"SUMMARY", 7, AlSummary, ical_cline_from_token, ical_free_cline},
195 {"ATTACH", 6, AlAttach, ical_gencline_from_token, ical_free_gencline},
196 {"ATTENDEE", 8, AlAttendee, ical_gencline_from_token, ical_free_gencline},
197 {NULL, 0, AlUnknown, 0, 0}
200 /* some useful macros for character analysis */
202 #define ical_wspace(X) \
203 ((X) == ' ' || (X) == '\t')
205 #define ical_name_allowed_char(X) \
206 (((X) >= 'A' && (X) <= 'Z') || \
207 ((X) >= 'a' && (X) <= 'z') || \
208 (X) == '-' )
210 #define ical_control(X) \
211 (((X) >= 0x00 && (X) <= 0x08) || \
212 ((X) >= 0x0A && (X) <= 0x1F) || \
213 (X) == 0x7F)
215 #define ical_safe_char(X) \
216 (ical_non_ascii_valid(X) \
217 || ical_wspace(X) \
218 || (X) == 0x21 \
219 || ((X) >= 0x23 && (X) <= 0x2B) \
220 || ((X) >= 0x2D && (X) <= 0x39) \
221 || ((X) >= 0x3C && (X) <= 0x7E))
223 #define ical_qsafe_char(X) \
224 (ical_non_ascii_valid((X)) \
225 || ical_wspace(X) \
226 || (X) == 0x21 \
227 || ((X) >= 0x23 && (X) <= 0x7E))
229 #define ical_value_char(X) \
230 (ical_non_ascii_valid(X) \
231 || ical_wspace(X) \
232 || ((X) >= 0x21 && (X) <= 0x7E))
234 /* Finally, here begins the code. */
236 unsigned char *
237 ical_decode(char *text, unsigned short encoding)
239 unsigned char *t;
240 unsigned long callen;
241 if(encoding == ENCQUOTEDPRINTABLE){
242 t = rfc822_qprint ((unsigned char *) text,strlen(text),&callen);
243 if(t != NULL){
244 strncpy(text, t, strlen(t));
245 fs_give((void **) &t);
248 return (unsigned char *) text;
252 /* Return code:
253 0 - if no errors
254 -1 - if an error occured
255 Args: a pointer to the text. If there is an error, the text is not modified.
258 ical_remove_escapes(char **textp)
260 char *text, *s, *t;
261 int rv = 0;
262 int escaped;
264 if(textp == NULL) return 0;
266 t = cpystr(*textp); /* work on a copy of the text */
267 /* the variable text below points to the beginning of the filtered text */
268 for (text = s = t, escaped = 0; rv == 0 && *s != '\0'; s++){
269 if(*s == '\\' && escaped == 0){
270 escaped = 1;
271 continue;
273 if(escaped){
274 switch(*s){
275 case '\\':
276 case ',':
277 case ';':
278 *t++ = *s;
279 break;
281 case 'n':
282 case 'N':
283 *t++ = '\n';
284 break;
285 default: rv = -1;
286 break;
288 escaped = 0;
290 else *t++ = *s;
292 *t = '\0'; /* tie off filtered text */
293 t = text; /* reset t to the beginning */
294 if(rv == -1)
295 fs_give((void **) &t);
296 else{
297 strncpy(*textp, t, strlen(t)); /* overwrite given text with filtered text */
298 (*textp)[strlen(t)] = '\0';
300 return rv;
303 void ical_debug(char *fcn, char *text)
305 char piece[50];
306 strncpy(piece, text, 49);
307 dprint((9, "%s: %s", fcn, piece));
310 /***
311 *** FREE MEMORY FUNCTIONS
312 ***/
314 void
315 ical_free_param(ICAL_PARAMETER_S **param)
317 if(param == NULL || *param == NULL)
318 return;
320 if((*param)->name) fs_give((void **) &(*param)->name);
321 if((*param)->value) fs_give((void **) &(*param)->value);
322 if((*param)->next) ical_free_param(&(*param)->next);
323 fs_give((void **)param);
326 void
327 ical_free_null(void **n)
329 if(n != NULL) *n = NULL;
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);
357 void
358 ical_free_vcalendar(void **vcalpv)
360 VCALENDAR_S **vcalp = (VCALENDAR_S **)vcalpv;
362 if(vcalp == NULL || *vcalp == NULL) return;
364 if((*vcalp)->prodid) ical_free_cline((void **) &(*vcalp)->prodid);
365 if((*vcalp)->version) ical_free_cline((void **) &(*vcalp)->version);
366 if((*vcalp)->calscale) ical_free_cline((void **) &(*vcalp)->calscale);
367 if((*vcalp)->method) ical_free_cline((void **) &(*vcalp)->method);
368 if((*vcalp)->uk_prop) ical_free_gencline((void **) &(*vcalp)->uk_prop);
369 if((*vcalp)->comp){
370 Cal_comp i;
371 for(i = 0; i < VUnknown; i++)
372 if((*vcalp)->comp[i]) (ical_comp[i].give)(&(*vcalp)->comp[i]);
373 fs_give((*vcalp)->comp);
375 if((*vcalp)->uk_comp) ical_free_unknown_comp(&(*vcalp)->uk_comp);
376 fs_give(vcalpv);
379 void
380 ical_free_vevent(void **veventpv)
382 VEVENT_S **veventp = (VEVENT_S **) veventpv;
384 if(veventp == NULL || *veventp == NULL) return;
386 ical_free_prop((*veventp)->prop, event_prop);
387 if((*veventp)->uk_prop) ical_free_gencline((void **) &(*veventp)->uk_prop);
388 if((*veventp)->valarm) ical_free_valarm((void **) &(*veventp)->valarm);
389 fs_give(veventpv);
392 void
393 ical_free_rrule(void **rrulepv)
395 RRULE_S **rrulep = (RRULE_S **) rrulepv;
397 if(rrulep && *rrulep){
398 ical_free_prop((*rrulep)->prop, rrule_prop);
399 ical_free_param(&(*rrulep)->param);
400 fs_give(rrulepv);
404 void
405 ical_free_weekday_list(void **wkdylv)
407 BYWKDY_S **wkdyl = (BYWKDY_S **) wkdylv;
409 if(wkdyl == NULL) return;
411 if((*wkdyl)->next)
412 ical_free_weekday_list((void **) &(*wkdyl)->next);
414 fs_give(wkdylv);
418 void
419 ical_free_vtodo(void **vtodopv)
423 void
424 ical_free_vjournal(void **vjournalpv)
428 void
429 ical_free_vfreebusy(void **vfbpv)
433 void
434 ical_free_prop(void **propv, ICAL_PROP_S *aux_comp)
436 int i, j;
438 if(propv == NULL) return;
440 for(i = 0; aux_comp[i].prop != NULL; i++)
441 if(propv[i]){
442 for(j = 0; aux_comp[j].prop != NULL && aux_comp[j].pos != i; j++);
443 if(aux_comp[j].give) (aux_comp[j].give)(&propv[i]);
445 fs_give(propv);
449 void
450 ical_free_vtimezone(void **vtzpv)
452 VTIMEZONE_S **vtzp = (VTIMEZONE_S **) vtzpv;
453 TZ_comp i,j;
455 if(vtzp == NULL || *vtzp == NULL) return;
457 ical_free_prop((*vtzp)->prop, tz_comp);
459 if((*vtzp)->uk_prop) ical_free_gencline((void **) &(*vtzp)->uk_prop);
460 if((*vtzp)->standardc) ical_free_timezone((void **) &(*vtzp)->standardc);
461 if((*vtzp)->daylightc) ical_free_timezone((void **) &(*vtzp)->daylightc);
462 fs_give(vtzpv);
465 void
466 ical_free_timezone(void **tzpv)
468 ICAL_TZPROP_S **tzp = (ICAL_TZPROP_S **) tzpv;
470 if(tzp == NULL || *tzp == NULL) return;
472 ical_free_prop((*tzp)->prop, tz_prop);
473 if((*tzp)->uk_prop) ical_free_gencline((void **) &(*tzp)->uk_prop);
474 if((*tzp)->next) ical_free_timezone((void **) &(*tzp)->next);
475 fs_give(tzpv);
478 void ical_free_valarm(void **valarmpv)
482 void
483 ical_free_unknown_comp(ICAL_S **icalp)
485 int i;
486 if(icalp == NULL || *icalp == NULL) return;
487 for(i = 0; ical_comp[i].comp && strucmp((*icalp)->comp,ical_comp[i].comp); i++);
488 if(ical_comp[i].give)
489 (ical_comp[i].give)(&(*icalp)->value);
490 else
491 ical_free_gencline((void **) &(*icalp)->value);
492 fs_give((void **)&(*icalp)->comp);
493 ical_free_unknown_comp(&(*icalp)->next);
494 ical_free_unknown_comp(&(*icalp)->branch);
495 fs_give((void **)icalp);
498 char *ical_unfold_line(char *line)
500 int i, j;
502 if(line == NULL)
503 return NULL;
505 for(i = 0, j = 0; line[j] != '\0';)
506 switch(line[j]){
507 case '\r': if(line[j+1] == '\n' && ical_wspace(line[j+2])){
508 j += 3; /* get past white space */
509 continue;
511 default : line[i++] = line[j++];
513 line[i] = '\0';
514 return line;
517 ICAL_PARAMETER_S *
518 ical_get_parameter(char **line)
520 ICAL_PARAMETER_S *param = NULL;
521 char *s;
523 if(line == NULL || *line == NULL)
524 return NULL;
526 for(s = *line; s && *s && ical_name_allowed_char(*s) ; s++);
528 if(*s == '='){
529 int quoted;
530 char c;
532 param = fs_get(sizeof(ICAL_PARAMETER_S));
533 memset((void *)param, 0, sizeof(ICAL_PARAMETER_S));
534 *s = '\0';
535 param->name = cpystr(*line);
536 *s = '=';
537 *line = s+1; /* step over '=' */
538 quoted = **line == '"' ? 1 : 0;
539 if(quoted != 0){
540 for(s = ++*line; s && *s && ical_qsafe_char((unsigned char) *s); s++);
541 if(*s != '"'){ /* error, do not parse this line */
542 ical_free_param(&param);
543 *line = strchr(s, ':'); /* reset line to closest ':' */
544 return NULL;
547 else
548 for(s = *line; s && *s && (ical_safe_char((unsigned char) *s)); s++);
549 c = *s;
550 *s = '\0';
551 param->value = cpystr(*line);
552 *s = c; /* restore character */
553 *line = quoted ? s + 1 : s;
555 if(**line == ';'){
556 ++*line;
557 param->next = ical_get_parameter(line);
560 return param;
563 char *ical_get_value(char **line)
565 char *s, *t;
567 if(line == NULL || *line == NULL)
568 return NULL;
570 for (s = *line; *s && ical_value_char((unsigned char) *s); s++);
571 if(*s == '\r'){
572 *s = '\0';
573 t = cpystr(*line);
574 *s = '\r';
575 *line = s+2;
577 else{
578 t = NULL;
579 s = strchr(*line, '\r');
580 if(s != NULL)
581 *line = s + 2;
583 return t;
586 ICAL_PARAMETER_S *
587 ical_parameter_cpy(ICAL_PARAMETER_S *param)
589 ICAL_PARAMETER_S *rv;
591 if(param == NULL) return NULL;
593 rv = fs_get(sizeof(ICAL_PARAMETER_S));
594 memset((void *)rv, 0, sizeof(ICAL_PARAMETER_S));
596 if(param->name) rv->name = cpystr(param->name);
597 if(param->value) rv->value = cpystr(param->value);
598 if(param->next) rv->next = ical_parameter_cpy(param->next);
600 return rv;
603 ICLINE_S *
604 ical_cline_cpy(ICLINE_S *icl)
606 ICLINE_S *rv;
608 if(icl == NULL)
609 return NULL;
611 rv = fs_get(sizeof(ICLINE_S));
612 memset((void *)rv, 0, sizeof(ICLINE_S));
614 if(icl->token) rv->token = cpystr(icl->token);
615 if(icl->param) rv->param = ical_parameter_cpy(icl->param);
616 if(icl->value) rv->value = cpystr(icl->value);
618 return rv;
621 /* Given a \r\n-ending line (called *text), isolate the ocurrence
622 * of the token in that line.
623 * Return the token, and modify the pointer to *text to point to the
624 * end of the token. Modify sep to contain the character following
625 * the token
626 * ical-line = token ':'/';' rest of the line\r\n
627 * on error return null, and set *text to the next line, if possible.
629 char *
630 ical_isolate_token(char **text, char *sep)
632 char *s, *t;
634 for(t = s = *text; *t && ical_name_allowed_char(*s); s++);
635 /* only followed by parameter or value */
636 if(*s == ':' || *s == ';'){
637 *sep = *s;
638 *s = '\0'; /* isolate token at pointer s */
639 *text = s;
641 else{ /* bad data - bail out of here */
642 t = NULL;
643 if(*s == '\0' || (s = strstr(s, "\r\n")) == NULL)
644 *text = NULL;
645 else /* move to next line */
646 *text = s + 2;
648 return t;
652 VCALENDAR_S *
653 ical_parse_text(char *text)
655 char *s;
656 VCALENDAR_S *vcal = NULL;
658 ical_debug("ical_parse_text", text);
659 ical_initialize();
661 text = ical_unfold_line(text);
662 for(s = text; s && *s != '\0'; s++){
663 if(*s != 'B' && *s != 'b')
664 continue;
665 if(!struncmp(s+1, "EGIN:VCALENDAR\r\n", 16)){
666 s += 17; /* 17 = strlen("BEGIN:VCALENDAR\r\n") */
667 vcal = (VCALENDAR_S *) ical_parse_vcalendar(&s);
668 break;
671 return vcal;
674 void *
675 ical_parse_time(void *ic_datep, char **text, char *token)
677 struct tm ic_date;
678 ICLINE_S *icl;
680 icl = ical_parse_line(text, token);
681 ical_parse_date(icl->value, &ic_date);
682 ical_free_cline((void **) &icl);
683 ic_datep = (void *) &ic_date;
685 return ic_datep;
688 void *
689 ical_parse_interval(void *longvp, char *value)
692 unsigned long longv;
694 longv = atoi(value);
695 longvp = (void *) &longv;
697 return longvp;
701 void *
702 ical_parse_offset(void *offsetv, char **text, char *token)
704 ICLINE_S *icl;
705 char *value;
706 int h, m, offset;
708 icl = ical_parse_line(text, token);
710 if(*icl->value == '+' || *icl->value == '-')
711 value = icl->value + 1;
712 else
713 value = icl->value;
715 h = ical_get_number_value(value, 0, 2);
716 m = ical_get_number_value(value, 2, 4);
718 offset = 60*(60*h + m);
719 if(*icl->value == '-')
720 offset *= -1;
722 ical_free_cline((void **) &icl);
723 offsetv = (void *) &offset;
725 return offsetv;
728 /* This function processes the information in *text, and returns
729 * a pointer to the information in iclp, but only if iclp is NULL
730 * otherwise, it simply returns the current value and advances the
731 * pointer to *text.
732 * Call this function as follows
733 * rv = (cast here *) ical_cline_from_token((void *)rv, &text, token);
735 void *
736 ical_cline_from_token(void *iclp, char **text, char *token)
738 ICLINE_S *icl;
740 icl = ical_parse_line(text, token);
742 if(iclp != NULL)
743 ical_free_cline((void **)&icl);
744 else
745 iclp = (void *) icl;
747 return iclp;
750 void *
751 ical_gencline_from_token(void *giclv, char **text, char *token)
753 GEN_ICLINE_S *gicl= NULL;
755 if(!struncmp(*text, token, strlen(token))){
756 gicl = fs_get(sizeof(GEN_ICLINE_S));
757 memset((void *) gicl, 0, sizeof(GEN_ICLINE_S));
758 gicl->cline = ical_parse_line(text, token);
759 // gicl->line = (ICLINE_S *) ical_cline_from_token((void *) gicl->cline, text, token);
760 gicl->next = (GEN_ICLINE_S *) ical_gencline_from_token((void *) gicl->next, text, token);
763 if(giclv != NULL)
764 ical_free_gencline((void **) &gicl);
765 else
766 giclv = (void *) gicl;
768 return giclv;
771 /***
772 *** PARSE COMPONENT FUNCTIONS
773 ***/
775 void *
776 ical_parse_vcalendar(char **text)
778 char *s, *t;
779 char c;
780 VCALENDAR_S *vcal;
781 void *v;
783 dprint((9, "ical_parse_vcalendar:\n"));
784 ical_debug("ical_parse_vcalendar", *text);
786 vcal = fs_get(sizeof(VCALENDAR_S));
787 memset((void *) vcal, 0, sizeof(VCALENDAR_S));
789 /* s must always point the the beginning of a line */
790 for(s = *text; s && *s != '\0';){
791 t = s;
792 s = ical_isolate_token(&t, &c);
793 if(s == NULL){
794 if(t != NULL)
795 s = t;
796 continue;
799 *t = c; /* restore character */
800 if(s){ /* figure out the token */
801 int ukn = 0; /* unknown token */
802 int i;
803 switch(*s){
804 case 'B':
805 case 'b': if(!struncmp(s+1, "EGIN", 4)){
806 s += 6; /* 6 = strlen("BEGIN:") */
807 for(i = 0; ical_comp[i].comp
808 && (struncmp(s, ical_comp[i].comp, ical_comp[i].len)
809 || struncmp(s + ical_comp[i].len, "\r\n", 2)); i++);
811 if(ical_comp[i].parse){
812 s += ical_comp[i].len + 2;
813 v = (ical_comp[i].parse)(&s);
814 if(vcal->comp == NULL){
815 vcal->comp = fs_get(VUnknown*sizeof(void *));
816 memset((void *) vcal->comp, 0, VUnknown*sizeof(void *));
819 if(vcal->comp[ical_comp[i].pos] == NULL)
820 vcal->comp[ical_comp[i].pos] = v;
821 else
822 (ical_comp[i].give)(&v);
823 } else {
824 v = (void *) ical_parse_unknown_comp(&s, 0);
825 if(vcal->uk_comp == NULL)
826 vcal->uk_comp = (ICAL_S *) v;
827 else{
828 ICAL_S *ic;
829 for(ic = vcal->uk_comp; ic && ic->branch; ic = ic->branch);
830 ic->branch = (ICAL_S *) v;
833 } else ukn++;
834 break;
836 case 'C':
837 case 'c': if(!struncmp(s+1, "ALSCALE", 7)){
838 v = (void *) vcal->calscale;
839 v = ical_cline_from_token(v, &s, "CALSCALE");
840 vcal->calscale = (ICLINE_S *) v;
842 else ukn++;
843 break;
845 case 'E':
846 case 'e': if(!struncmp(s+1, "ND", 2)){
847 *t = c;
848 s += 4; /* 4 = strlen("END:") */
849 if(!struncmp(s, "VCALENDAR\r\n", 11)){
850 *text = s + 11; /* 11 = strlen("VCALENDAR\r\n") */
851 return (void *) vcal;
853 // else ukn++; FIX THIS, this is not quite right
854 } else ukn++;
855 break;
857 case 'M':
858 case 'm': if(!struncmp(s+1, "ETHOD", 5)){
859 v = (void *) vcal->method;
860 v = ical_cline_from_token(v, &s, "METHOD");
861 vcal->method = (ICLINE_S *) v;
863 else ukn++;
864 break;
866 case 'P':
867 case 'p': if(!struncmp(s+1, "RODID", 5)){
868 v = (void *) vcal->prodid;
869 v = ical_cline_from_token(v, &s, "PRODID");
870 vcal->prodid = (ICLINE_S *) v;
872 else ukn++;
873 break;
875 case 'V':
876 case 'v': if(!struncmp(s+1, "ERSION", 6)){
877 v = (void *) vcal->version;
878 v = ical_cline_from_token(v, &s, "VERSION");
879 vcal->version = (ICLINE_S *) v;
880 } else ukn++;
881 break;
883 default : ukn++;
884 break;
885 } /* end of switch(*s) */
886 if(ukn){
887 if(ical_buf_len < t - s){
888 fs_resize((void **)&ical_buf, t-s+1);
889 ical_buf_len = t-s;
891 *t = '\0';
892 strcpy(ical_buf, s);
893 *t = c;
894 if(vcal->uk_prop == NULL){
895 vcal->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
896 memset((void *)vcal->uk_prop, 0, sizeof(GEN_ICLINE_S));
897 vcal->uk_prop->cline = ical_parse_line(&s, ical_buf);
899 else{
900 GEN_ICLINE_S *gcl;
901 for (gcl = vcal->uk_prop; gcl && gcl->next; gcl = gcl->next);
902 gcl->next = fs_get(sizeof(GEN_ICLINE_S));
903 memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
904 gcl->next->cline = ical_parse_line(&s, ical_buf);
907 } /* end of if(s) */
910 *text = s;
912 /* ok, we have parsed the vcalendar, now parse some special properties */
913 /* start by parsing dates */
914 ical_set_date_vevent(vcal->comp[VEvent], vcal->comp[VTimeZone]);
915 return (void *) vcal;
918 void *
919 ical_parse_vevent(char **text)
921 char *s, *t;
922 char c;
923 VEVENT_S *vevent;
925 ical_debug("ical_parse_vevent", *text);
926 vevent = fs_get(sizeof(VEVENT_S));
927 memset((void *)vevent, 0, sizeof(VEVENT_S));
929 /* s must always point the the beginning of a line */
930 for(s = *text; s && *s != '\0';){
931 t = s;
932 s = ical_isolate_token(&t, &c);
933 if(s == NULL){
934 if(t != NULL)
935 s = t;
936 continue;
938 *t = c; /* restore separator */
940 if(s){ /* figure out the token */
941 int ukn = 0; /* unknown token */
942 if(!struncmp(s, "BEGIN", 5)){
943 s += 6; /* 6 = strlen("BEGIN:") */
944 if(!struncmp(s, "VALARM\r\n", 8)){
945 s += 8; /* 8 = strlen("VALARM\r\n"); */
946 if(vevent->valarm == NULL)
947 vevent->valarm = ical_parse_valarm(&s);
948 else{
949 VALARM_S *valrm;
950 for(valrm = vevent->valarm; valrm && valrm->next;
951 valrm = valrm->next);
952 valrm->next = ical_parse_valarm(&s);
954 } else {
955 ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0);
956 ical_free_unknown_comp(&uk_comp);
958 } else if(!struncmp(s, "END", t-s-1)){
959 s += 4; /* 4 = strlen("END:") */
960 if(!struncmp(s, "VEVENT\r\n",8)){
961 *text = s + 8; /* 8 = strlen("VCALENDAR\r\n") */
962 return (void *) vevent;
964 } else{ Event_prop i;
965 for(i = 0; i < EvUnknown; i++)
966 if(!struncmp(s, event_prop[i].prop, t-s))
967 break;
968 if(event_prop[i].parse){
969 void *v;
970 if(vevent->prop == NULL){
971 vevent->prop = fs_get(EvUnknown*sizeof(void *));
972 memset((void *)vevent->prop, 0, EvUnknown*sizeof(void *));
974 v = vevent->prop[event_prop[i].pos];
975 v = (event_prop[i].parse)(v , &s, event_prop[i].prop);
976 vevent->prop[event_prop[i].pos] = v;
978 else
979 ukn++;
982 if(ukn){
983 if(ical_buf_len < t - s){
984 fs_resize((void **)&ical_buf, t-s+1);
985 ical_buf_len = t-s;
987 *t = '\0';
988 strcpy(ical_buf, s);
989 *t = c;
990 if(vevent->uk_prop == NULL){
991 vevent->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
992 memset((void *)vevent->uk_prop, 0, sizeof(GEN_ICLINE_S));
993 vevent->uk_prop->cline = ical_parse_line(&s, ical_buf);
995 else{
996 GEN_ICLINE_S *gcl;
997 for (gcl = vevent->uk_prop; gcl && gcl->next; gcl = gcl->next);
998 gcl->next = fs_get(sizeof(GEN_ICLINE_S));
999 memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
1000 gcl->next->cline = ical_parse_line(&s, ical_buf);
1003 } /* end of if(s) */
1006 *text = s;
1007 return (void *) vevent;
1010 void *
1011 ical_parse_vtimezone(char **text)
1013 char *s, *t;
1014 char c;
1015 void *v;
1016 VTIMEZONE_S *vtz;
1018 ical_debug("ical_parse_vtimezone", *text);
1019 vtz = fs_get(sizeof(VTIMEZONE_S));
1020 memset((void *)vtz, 0, sizeof(VTIMEZONE_S));
1022 /* s must always point the the beginning of a line */
1023 for(s = *text; s && *s != '\0';){
1024 t = s;
1025 s = ical_isolate_token(&t, &c);
1026 if(s == NULL){
1027 if(t != NULL)
1028 s = t;
1029 continue;
1031 *t = c; /* restore separator */
1033 if(s){ /* figure out the token */
1034 int ukn = 0; /* unknown token */
1035 if(!struncmp(s, "BEGIN", 5)){
1036 s += 6; /* 6 = strlen("BEGIN:") */
1037 if(!struncmp(s, "STANDARD\r\n", 10)){
1038 s += 10; /* 10 = strlen("STANDARD\r\n"); */
1039 v = ical_parse_timezone(&s);
1040 if(vtz->standardc == NULL)
1041 vtz->standardc = (ICAL_TZPROP_S *) v;
1042 else{
1043 ICAL_TZPROP_S *dl;
1044 for(dl = vtz->standardc; dl && dl->next; dl = dl->next);
1045 dl->next = (ICAL_TZPROP_S *) v;
1047 } else if(!struncmp(s, "DAYLIGHT\r\n", 10)){
1048 s += 10; /* 10 = strlen("DAYLIGHT\r\n"); */
1049 v = ical_parse_timezone(&s);
1050 if(vtz->daylightc == NULL)
1051 vtz->daylightc = (ICAL_TZPROP_S *) v;
1052 else{
1053 ICAL_TZPROP_S *dl;
1054 for(dl = vtz->daylightc; dl && dl->next; dl = dl->next);
1055 dl->next = (ICAL_TZPROP_S *) v;
1057 } else {
1058 ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0);
1059 ical_free_unknown_comp(&uk_comp);
1061 } else if(!struncmp(s, "END", t-s-1)){
1062 s += 4; /* 4 = strlen("END:") */
1063 if(!struncmp(s, "VTIMEZONE\r\n",11)){
1064 *text = s + 11; /* 11 = strlen("VTIMEZONE\r\n") */
1065 return (void *) vtz;
1067 } else{ TZ_comp i;
1068 for(i = 0; i < TZCUnknown; i++)
1069 if(!struncmp(s, tz_comp[i].prop, t-s))
1070 break;
1071 if(tz_comp[i].parse){
1072 void *v;
1073 if(vtz->prop == NULL){
1074 vtz->prop = fs_get(TZCUnknown*sizeof(void *));
1075 memset((void *)vtz->prop, 0, TZCUnknown*sizeof(void *));
1077 v = vtz->prop[tz_comp[i].pos];
1078 v = (tz_comp[i].parse)(v, &s, tz_comp[i].prop);
1079 vtz->prop[tz_comp[i].pos] = v;
1081 else
1082 ukn++;
1085 if(ukn){
1086 if(ical_buf_len < t - s){
1087 fs_resize((void **)&ical_buf, t-s+1);
1088 ical_buf_len = t-s;
1090 *t = '\0';
1091 strcpy(ical_buf, s);
1092 *t = c;
1093 if(vtz->uk_prop == NULL){
1094 vtz->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
1095 memset((void *)vtz->uk_prop, 0, sizeof(GEN_ICLINE_S));
1096 vtz->uk_prop->cline = ical_parse_line(&s, ical_buf);
1098 else{
1099 GEN_ICLINE_S *gcl;
1100 for (gcl = vtz->uk_prop; gcl && gcl->next; gcl = gcl->next);
1101 gcl->next = fs_get(sizeof(GEN_ICLINE_S));
1102 memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
1103 gcl->next->cline = ical_parse_line(&s, ical_buf);
1106 } /* end of if(s) */
1109 *text = s;
1110 return (void *) vtz;
1113 void *
1114 ical_parse_timezone(char **text)
1116 char *s, *t;
1117 char c;
1118 ICAL_TZPROP_S *tzprop;
1120 ical_debug("ical_parse_timezone", *text);
1121 tzprop = fs_get(sizeof(ICAL_TZPROP_S));
1122 memset((void *)tzprop, 0, sizeof(ICAL_TZPROP_S));
1124 /* s must always point the the beginning of a line */
1125 for(s = *text; s && *s != '\0';){
1126 t = s;
1127 s = ical_isolate_token(&t, &c);
1128 if(s == NULL){
1129 if(t != NULL)
1130 s = t;
1131 continue;
1133 *t = c; /* restore separator */
1135 if(s){ /* figure out the token */
1136 int ukn = 0; /* unknown token */
1137 if(!struncmp(s, "BEGIN", 5)){
1138 s += 6; /* 6 = strlen("BEGIN:") */
1139 ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0);
1140 ical_free_unknown_comp(&uk_comp);
1141 } else if(!struncmp(s, "END", t-s-1)){
1142 s += 4; /* 4 = strlen("END:") */
1143 if(!struncmp(s, "STANDARD\r\n", 10)
1144 || !struncmp(s, "DAYLIGHT\r\n", 10)){
1145 *text = s + 10; /* 10 = strlen("STANDARD\r\n") */
1146 return (void *) tzprop;
1148 } else{ TZ_prop i;
1149 for(i = 0; i < TZPUnknown; i++)
1150 if(!struncmp(s, tz_prop[i].prop, t-s))
1151 break;
1152 if(tz_prop[i].parse){
1153 void *v;
1154 if(tzprop->prop == NULL){
1155 tzprop->prop = fs_get(TZPUnknown*sizeof(void *));
1156 memset((void *)tzprop->prop, 0, TZPUnknown*sizeof(void *));
1158 v = tzprop->prop[tz_prop[i].pos];
1159 v = (tz_prop[i].parse)(v, &s, tz_prop[i].prop);
1160 tzprop->prop[tz_prop[i].pos] = v;
1162 else
1163 ukn++;
1166 if(ukn){
1167 if(ical_buf_len < t - s){
1168 fs_resize((void **)&ical_buf, t-s+1);
1169 ical_buf_len = t-s;
1171 *t = '\0';
1172 strcpy(ical_buf, s);
1173 *t = c;
1174 if(tzprop->uk_prop == NULL){
1175 tzprop->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
1176 memset((void *)tzprop->uk_prop, 0, sizeof(GEN_ICLINE_S));
1177 tzprop->uk_prop->cline = ical_parse_line(&s, ical_buf);
1179 else{
1180 GEN_ICLINE_S *gcl;
1181 for (gcl = tzprop->uk_prop; gcl && gcl->next; gcl = gcl->next);
1182 gcl->next = fs_get(sizeof(GEN_ICLINE_S));
1183 memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
1184 gcl->next->cline = ical_parse_line(&s, ical_buf);
1187 } /* end of if(s) */
1190 *text = s;
1191 return (void *) tzprop;
1194 void *
1195 ical_parse_valarm(char **text)
1197 char *s, *t;
1198 char c;
1199 VALARM_S *valarm;
1201 ical_debug("ical_parse_valarm", *text);
1202 valarm = fs_get(sizeof(VALARM_S));
1203 memset((void *)valarm, 0, sizeof(VALARM_S));
1205 /* s must always point the the beginning of a line */
1206 for(s = *text; s && *s != '\0';){
1207 t = s;
1208 s = ical_isolate_token(&t, &c);
1209 if(s == NULL){
1210 if(t != NULL)
1211 s = t;
1212 continue;
1214 *t = c; /* restore separator */
1216 if(s){ /* figure out the token */
1217 int ukn = 0; /* unknown token */
1218 if(!struncmp(s, "BEGIN", 5)){
1219 s += 6; /* 6 = strlen("BEGIN:") */
1220 ICAL_S *uk_comp = ical_parse_unknown_comp(&s, 0);
1221 ical_free_unknown_comp(&uk_comp);
1222 } else if(!struncmp(s, "END", t-s-1)){
1223 s += 4; /* 4 = strlen("END:") */
1224 if(!struncmp(s, "ALARM\r\n", 7)){
1225 *text = s + 7; /* 7 = strlen("ALARM\r\n") */
1226 return (void *) valarm;
1228 } else{ Alarm_prop i;
1229 for(i = 0; i < AlUnknown; i++)
1230 if(!struncmp(s, alarm_prop[i].prop, t-s))
1231 break;
1232 if(alarm_prop[i].parse){
1233 void *v;
1234 if(valarm->prop == NULL){
1235 valarm->prop = fs_get(AlUnknown*sizeof(void *));
1236 memset((void *)valarm->prop, 0, AlUnknown*sizeof(void *));
1238 v = valarm->prop[alarm_prop[i].pos];
1239 v = (alarm_prop[i].parse)(v, &s, alarm_prop[i].prop);
1240 valarm->prop[alarm_prop[i].pos] = v;
1242 else
1243 ukn++;
1246 if(ukn){
1247 if(ical_buf_len < t - s){
1248 fs_resize((void **)&ical_buf, t-s+1);
1249 ical_buf_len = t-s;
1251 *t = '\0';
1252 strcpy(ical_buf, s);
1253 *t = c;
1254 if(valarm->uk_prop == NULL){
1255 valarm->uk_prop = fs_get(sizeof(GEN_ICLINE_S));
1256 memset((void *)valarm->uk_prop, 0, sizeof(GEN_ICLINE_S));
1257 valarm->uk_prop->cline = ical_parse_line(&s, ical_buf);
1259 else{
1260 GEN_ICLINE_S *gcl;
1261 for (gcl = valarm->uk_prop; gcl && gcl->next; gcl = gcl->next);
1262 gcl->next = fs_get(sizeof(GEN_ICLINE_S));
1263 memset((void *)gcl->next, 0, sizeof(GEN_ICLINE_S));
1264 gcl->next->cline = ical_parse_line(&s, ical_buf);
1267 } /* end of if(s) */
1270 *text = s;
1271 return (void *) valarm;
1274 void *
1275 ical_parse_vtodo(char **text)
1277 return NULL;
1280 void *
1281 ical_parse_vjournal(char **text)
1283 return NULL;
1286 void *
1287 ical_parse_vfreebusy(char **text)
1289 return NULL;
1292 ICAL_S *
1293 ical_parse_generic_comp(char **text, int level)
1295 ICAL_S *ical;
1296 char *s, *t;
1297 char *token = NULL;
1298 GEN_ICLINE_S *gcl = NULL;
1299 char c;
1301 ical_debug("ical_parse_generic_comp", *text);
1302 ical = fs_get(sizeof(ICAL_S));
1303 memset((void *)ical, 0, sizeof(ICAL_S));
1305 ical->comp = ical_get_value(text);
1306 token = fs_get(strlen(ical->comp) + 2 + 1);
1307 sprintf(token, "%s\r\n", ical->comp); /* this is allocated memory */
1309 /* s must always point the the beginning of a line */
1310 for(s = *text; s && *s != '\0';){
1311 t = s;
1312 s = ical_isolate_token(&t, &c);
1313 if(s == NULL){
1314 if(t != NULL)
1315 s = t;
1316 continue;
1319 *t = c; /* restore character */
1320 if(s){ /* figure out the token */
1321 int ukn = 0; /* unknown token */
1322 switch(*s){
1323 case 'B':
1324 case 'b': if(!struncmp(s+1, "EGIN", 4)){
1325 s += 6; /* 6 = strlen("BEGIN:") */
1326 if(ical->next == NULL)
1327 ical->next = ical_parse_unknown_comp(&s, level+1);
1328 else{
1329 ICAL_S *b;
1330 int i;
1332 for(i = 0, b = ical; i <= level && b && b->next; b = b->next, i++);
1333 if(b->branch == NULL)
1334 b->branch = ical_parse_unknown_comp(&s, level+1);
1335 else {
1336 for(; b && b->branch; b = b->branch);
1337 b->branch = ical_parse_unknown_comp(&s, level+1);
1340 } else ukn++;
1341 break;
1343 case 'E':
1344 case 'e': if(!struncmp(s+1, "ND", 2)){
1345 *t = c;
1346 s += 4; /* 4 = strlen("END:") */
1347 if(!struncmp(s, token, strlen(token))){
1348 *text = s + strlen(token);
1349 ical->value = (void *) gcl;
1350 return ical;
1352 } else ukn++;
1353 break;
1355 default : ukn++;
1356 break;
1357 } /* end of switch(*s) */
1358 if(ukn){
1359 if(ical_buf_len < t - s){
1360 fs_resize((void **)&ical_buf, t-s+1);
1361 ical_buf_len = t-s;
1363 *t = '\0';
1364 strcpy(ical_buf, s);
1365 *t = c;
1366 if(gcl == NULL){
1367 gcl = fs_get(sizeof(GEN_ICLINE_S));
1368 memset((void *)gcl, 0, sizeof(GEN_ICLINE_S));
1369 gcl->cline = ical_parse_line(&s, ical_buf);
1371 else{
1372 GEN_ICLINE_S *gencl;
1373 for (gencl = gcl; gencl && gencl->next; gencl = gencl->next);
1374 gencl->next = fs_get(sizeof(GEN_ICLINE_S));
1375 memset((void *)gencl->next, 0, sizeof(GEN_ICLINE_S));
1376 gencl->next->cline = ical_parse_line(&s, ical_buf);
1379 } /* end of if(s) */
1382 ical->value = (void *) gcl;
1383 *text = s;
1384 return ical;
1387 ICAL_S *
1388 ical_parse_unknown_comp(char **text, int level)
1390 ICAL_S *ical;
1391 int i;
1393 ical_debug("ical_parse_unknown_comp", *text);
1394 for(i = 0; ical_comp[i].comp
1395 && (struncmp(*text, ical_comp[i].comp, ical_comp[i].len)
1396 || struncmp(*text + ical_comp[i].len, "\r\n", 2)); i++);
1398 if(ical_comp[i].parse){
1399 *text += ical_comp[i].len + 2;
1400 ical = fs_get(sizeof(ICAL_S));
1401 memset((void *)ical, 0, sizeof(ICAL_S));
1402 ical->comp = cpystr(ical_comp[i].comp);
1403 ical->value = (ical_comp[i].parse)(text);
1404 } else
1405 ical = ical_parse_generic_comp(text, level);
1407 return ical;
1410 ICLINE_S *
1411 ical_parse_line(char **text, char *name)
1413 ICLINE_S *ic;
1414 char *s = *text;
1416 ic = fs_get(sizeof(ICLINE_S));
1417 memset((void *)ic, 0, sizeof(ICLINE_S));
1419 ic->token = cpystr(name);
1420 s += strlen(name);
1421 if(*s == ';'){
1422 s++;
1423 ic->param = ical_get_parameter(&s);
1425 if(*s == ':'){
1426 s++;
1427 ic->value = ical_get_value(&s);
1430 *text = s;
1431 return ic;
1434 /***
1435 *** PARSE PROPERTY FUNCTIONS
1436 ***/
1438 void *
1439 ical_parse_freq(void *fvalp, char *text)
1441 Freq_value fval;
1443 fval = FUnknown;
1445 if(text == NULL) return fvalp;
1447 if(!strucmp(text, "SECONDLY")) fval = FSecondly;
1448 else if(!strucmp(text, "MINUTELY")) fval = FMinutely;
1449 else if(!strucmp(text, "HOURLY")) fval = FHourly;
1450 else if(!strucmp(text, "DAILY")) fval = FDaily;
1451 else if(!strucmp(text, "WEEKLY")) fval = FWeekly;
1452 else if(!strucmp(text, "MONTHLY")) fval = FMonthly;
1453 else if(!strucmp(text, "YEARLY")) fval = FYearly;
1455 fvalp = (void *) &fval;
1457 return fvalp;
1460 void *
1461 ical_parse_until(void *Tmp, char *text)
1463 struct tm Tm;
1465 if(text != NULL){
1466 ical_parse_date(text, &Tm);
1467 Tmp = (void *) &Tm;
1470 return Tmp;
1473 void *
1474 ical_parse_count(void *countp, char *text)
1476 int count;
1478 if(text != NULL){
1479 count = atoi(text);
1480 countp = (void *) &count;
1483 return countp;
1486 void *
1487 ical_parse_weekday_list(void *bywkdyp, char *wklist)
1489 BYWKDY_S *bywkdy, *w;
1490 char *s, *t, c;
1491 int done;
1492 size_t len;
1494 bywkdy = NULL;
1495 bywkdyp = (void *) bywkdy;
1497 if(wklist == NULL) return bywkdyp;
1499 for(t = s = wklist; done == 0; s++){
1500 if(*s != ',' && *s != '\0')
1501 continue;
1502 c = *s;
1503 if(c == ',')
1504 *s = '\0';
1505 else /* c == '\0' */
1506 done++;
1507 len = strlen(t);
1508 if(len > 1){
1509 for(w = bywkdy; w && w->next; w = w->next);
1510 w = fs_get(sizeof(BYWKDY_S));
1511 memset((void *)w, 0, sizeof(BYWKDY_S));
1512 if(!strucmp(t+len-2, "SU")) w->wd = Sunday;
1513 else if(!strucmp(t+len-2, "MO")) w->wd = Monday;
1514 else if(!strucmp(t+len-2, "TU")) w->wd = Tuesday;
1515 else if(!strucmp(t+len-2, "WE")) w->wd = Wednesday;
1516 else if(!strucmp(t+len-2, "TH")) w->wd = Thursday;
1517 else if(!strucmp(t+len-2, "FR")) w->wd = Friday;
1518 else if(!strucmp(t+len-2, "SA")) w->wd = Saturday;
1519 // t[len - 2] = '\0';
1520 if(*t != '\0')
1521 w->value = strtoul(t, &t, 10);
1522 if(bywkdy == NULL)
1523 bywkdy = w;
1525 *s = c;
1526 if(*s == ',')
1527 t = s + 1;
1530 if(bywkdyp)
1531 ical_free_weekday_list((void **)&bywkdy);
1532 else
1533 bywkdyp = (void *) bywkdy;
1535 return bywkdyp;
1538 void *
1539 ical_parse_number_list(void *bynop, char *nolist)
1541 BYWKDY_S *byno, *n;
1542 char *s, *t, c;
1543 int done = 0;
1545 byno = NULL;
1546 bynop = (void *) byno;
1548 if(nolist == NULL) return bynop;
1550 for(t = s = nolist; done == 0; s++){
1551 if(*s != ',' && *s != '\0')
1552 continue;
1553 c = *s;
1554 if(c == ',')
1555 *s = '\0';
1556 else /* c == '\0' */
1557 done++;
1559 for(n = byno; n && n->next; n = n->next);
1560 n = fs_get(sizeof(BYWKDY_S));
1561 memset((void *)n, 0, sizeof(BYWKDY_S));
1562 n->value = strtoul(t, &t, 10);
1563 if(byno == NULL)
1564 byno = n;
1565 *s = c;
1566 if(*s == ',')
1567 t = s + 1;
1570 if(bynop)
1571 ical_free_weekday_list((void **)&byno);
1572 else
1573 bynop = (void *) byno;
1575 return bynop;
1578 void *
1579 ical_parse_rrule(void *rrulep, char **text, char *token)
1581 RRULE_S *rrule;
1582 ICLINE_S *icl;
1583 char *s;
1584 ICAL_PARAMETER_S *param, *p;
1585 int i;
1587 if(text == NULL || *text == NULL || struncmp(*text, "RRULE", 5))
1588 return rrulep;
1590 rrule = fs_get(sizeof(RRULE_S));
1591 memset((void *) rrule, 0, sizeof(RRULE_S));
1593 /* recurring rules are special. First, we parse the icline that contains it */
1594 icl = ical_parse_line(text, token);
1596 /* now we copy the parameters that it contains */
1597 rrule->param = ical_parameter_cpy(icl->param);
1599 /* then we parse icl->value as if it was a parameter */
1600 s = icl->value;
1601 param = ical_get_parameter(&s);
1603 /* now we check which values were given, and fill the prop array */
1604 rrule->prop = fs_get(RRUnknown*sizeof(void *));
1605 memset((void *) rrule->prop, 0, RRUnknown*sizeof(void *));
1607 for(p = param; p != NULL; p = p->next){
1608 for(i = 0; rrule_prop[i].prop != NULL && strucmp(p->name, rrule_prop[i].prop); i++);
1609 if(rrule_prop[i].parse){
1610 void *v = rrule->prop[rrule_prop[i].pos];
1611 v = (rrule_prop[i].parse)(v, p->value);
1612 rrule->prop[rrule_prop[i].pos] = v;
1615 rrule->prop[RRUnknown] = NULL;
1617 ical_free_param(&param);
1618 ical_free_cline((void **)&icl);
1620 if(rrulep)
1621 ical_free_rrule((void **)&rrule);
1622 else
1623 rrulep = (void *) rrule;
1625 return rrulep;
1628 /*** UTF-8 for ICAL ***/
1631 ical_non_ascii_valid(unsigned char c)
1633 static unsigned char icu[6];
1634 static int utf8_len = 0;
1635 static int utf8_type = 0;
1636 int rv;
1638 if(utf8_len == 0)
1639 utf8_type = (c >= 0xF0 && c <= 0xF4)
1640 ? 4 : (c >= 0xE0 && c <= 0xEF)
1641 ? 3 : (c >= 0xC2 && c <= 0xDF)
1642 ? 2 : 0;
1644 if(utf8_type == 0)
1645 return 0;
1647 icu[utf8_len++] = c; /* count it */
1649 if(utf8_type == 2){
1650 if(utf8_len < 2)
1651 rv = NEED_MORE;
1652 else if(utf8_len == 2){
1653 rv = (icu[0] >= 0xC2 && icu[0] <= 0xDF)
1654 && (icu[1] >= 0x80 && icu[1] <= 0xBF) ? UTF8_COMPLETE : 0;
1655 utf8_len = 0;
1657 } else if (utf8_type == 3){
1658 if(utf8_len < 3)
1659 rv = NEED_MORE;
1660 else{
1661 if(icu[0] == 0xE0)
1662 rv = (icu[1] >= 0xA0 && icu[1] <= 0xBF)
1663 && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0;
1664 else if(icu[0] >= 0xE1 && icu[0] <= 0xEC)
1665 rv = (icu[1] >= 0x80 && icu[1] <= 0xBF)
1666 && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0;
1667 else if(icu[0] == 0xED)
1668 rv = (icu[1] >= 0x80 && icu[1] <= 0x9F)
1669 && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0;
1670 else if(icu[0] >= 0xE1 && icu[0] <= 0xEC)
1671 rv = (icu[1] >= 0x80 && icu[1] <= 0xBF)
1672 && (icu[2] >= 0x80 && icu[2] <= 0xBF) ? UTF8_COMPLETE : 0;
1673 utf8_len = 0;
1675 } else if (utf8_type == 4){
1676 if(utf8_len < 4)
1677 rv = NEED_MORE;
1678 else{
1679 if(icu[0] == 0xF0)
1680 rv = (icu[1] >= 0x90 && icu[1] <= 0xBF)
1681 && (icu[2] >= 0x80 && icu[2] <= 0xBF)
1682 && (icu[3] >= 0x80 && icu[3] <= 0xBF) ? UTF8_COMPLETE : 0;
1683 else if(icu[0] >= 0xF1 && icu[0] <= 0xF3)
1684 rv = (icu[1] >= 0x80 && icu[1] <= 0xBF)
1685 && (icu[2] >= 0x80 && icu[2] <= 0xBF)
1686 && (icu[3] >= 0x80 && icu[3] <= 0xBF) ? UTF8_COMPLETE : 0;
1687 else if(icu[0] == 0xF4)
1688 rv = (icu[1] >= 0x80 && icu[1] <= 0x8F)
1689 && (icu[2] >= 0x80 && icu[2] <= 0xBF)
1690 && (icu[3] >= 0x80 && icu[3] <= 0xBF) ? UTF8_COMPLETE : 0;
1691 utf8_len = 0;
1694 return rv;
1698 ical_get_number_value(char *value, int beg_pos, int end_pos)
1700 char c, *err;
1701 int rv;
1703 c = value[end_pos];
1704 value[end_pos] = '\0';
1705 rv = strtoul(value + beg_pos, &err, 10);
1706 if(err != NULL && *err != '\0') return -1;
1707 value[end_pos] = c;
1708 return rv;
1711 void
1712 ical_free_duration(ICAL_DURATION_S **ic_d)
1714 if(ic_d == NULL || *ic_d == NULL)
1715 return;
1717 if((*ic_d)->next) ical_free_duration(&(*ic_d)->next);
1718 fs_give((void **)ic_d);
1721 /* returns 0 if no error, -1 if some error */
1723 ical_parse_duration(char *value, ICAL_DURATION_S *ic_d)
1725 int i, j, rv = 0;
1727 if(value == NULL || ic_d == NULL) return -1;
1729 memset((void *)ic_d, 0, sizeof(ICAL_DURATION_S));
1731 if(value[i = 0] == '-'){
1732 i++;
1733 ic_d->sign = 1;
1734 } else if(value[i] == '+')
1735 i++;
1737 if(value[i++] == 'P'){
1738 for(j = i; value[j] != '\0' && value[j] != ','; j++){
1739 if(!isdigit(value[j]))
1740 switch(value[j]){
1741 case 'W': ic_d->weeks = ical_get_number_value(value, i, j-1);
1742 i = ++j;
1743 break;
1744 case 'D': ic_d->days = ical_get_number_value(value, i, j-1);
1745 i = ++j;
1746 break;
1747 case 'H': ic_d->hours = ical_get_number_value(value, i, j-1);
1748 i = ++j;
1749 break;
1750 case 'M': ic_d->minutes = ical_get_number_value(value, i, j-1);
1751 i = ++j;
1752 break;
1753 case 'S': ic_d->seconds = ical_get_number_value(value, i, j-1);
1754 i = ++j;
1755 break;
1756 case 'T': i = j + 1;
1757 break;
1758 default: rv = -1;
1759 break;
1763 else
1764 rv = -1;
1766 if(value[j++] == ','){
1767 ICAL_DURATION_S next;
1768 rv = ical_parse_duration(value+j, &next);
1769 ic_d->next = &next;
1772 return rv;
1775 /* return -1 if any error,
1776 0 if value has the DATE-TIME form
1777 1 if value has the DATE form only
1778 2 if value has the DATE-TIME form and is in GMT.
1781 ical_parse_date(char *value, struct tm *t)
1783 int i, rv;
1784 struct tm Tm;
1786 rv = -1;
1787 if(t == NULL) return rv;
1788 memset((void *)&Tm, 0, sizeof(struct tm));
1790 if(value == NULL) return rv;
1792 rv = 0; /* assume DATE-TIME format */
1793 /* a simple check for the format of the string */
1794 for(i = 0; isdigit(value[i]); i++);
1795 if (i == 8 && value[i] == '\0')
1796 rv = 1;
1797 else
1798 if (i != 8 || value[i++] != 'T') return -1;
1799 if(rv == 0) {
1800 for(; isdigit(value[i]); i++);
1801 if(i != 15 || (value[i] != '\0' && (value[i] != 'Z' || value[i+1] != '\0')))
1802 return -1;
1803 if(i == 15 && value[i] == 'Z')
1804 rv = 2;
1807 Tm.tm_year = ical_get_number_value(value, 0, 4) - 1900;
1808 Tm.tm_mon = ical_get_number_value(value, 4, 6) - 1;
1809 Tm.tm_mday = ical_get_number_value(value, 6, 8);
1810 if(rv != 1){
1811 Tm.tm_hour = ical_get_number_value(value, 9, 11);
1812 Tm.tm_min = ical_get_number_value(value, 11, 13);
1813 Tm.tm_sec = ical_get_number_value(value, 13, 15);
1814 Tm.tm_isdst = ICAL_DST_UNKNOWN;
1816 else
1817 Tm.tm_isdst = -1;
1818 *t = Tm;
1820 return (t->tm_mon > 11 || t->tm_mon < 0
1821 || t->tm_mday > 31 || t->tm_mday < 0
1822 || t->tm_hour > 23 || t->tm_hour < 0
1823 || t->tm_min > 59 || t->tm_min < 0
1824 || t->tm_sec > 60 || t->tm_sec < 0)
1825 ? - 1 : rv;
1828 void
1829 ical_set_date(ICLINE_S *icl, VTIMEZONE_S *vtz)
1831 int date_form; /* date forms from section 3.3.4 in RFC 5545 */
1832 ICAL_PARAMETER_S *param;
1833 char *tz = NULL;
1834 struct tm ic_date;
1835 time_t t;
1837 if(icl == NULL) return;
1839 for(param = icl->param; param != NULL; param = param->next)
1840 if(!strucmp(param->name, "TZID"))
1841 tz = param->value;
1843 if(tz != NULL)
1844 date_form = 3; /* local time with timezone */
1845 else if(icl->value[strlen(icl->value)-1] == 'Z')
1846 date_form = 2; /* utc time */
1847 else date_form = 1; /* local time */
1849 ical_parse_date(icl->value, &ic_date);
1850 ic_date.tm_wday = ical_day_of_week(ic_date); /* find out day of the week */
1852 switch(date_form){
1853 case 1: break;
1854 case 2: ical_adjust_date(&ic_date, vtz);
1855 break;
1856 case 3: break;
1857 default: alpine_panic ("Impossible date_form");
1861 ICAL_TZPROP_S *
1862 ical_std_or_daylight(struct tm *date, VTIMEZONE_S *vtz)
1864 struct tm standard, daylight;
1865 ICLINE_S *tzid = (ICLINE_S *) vtz->prop[TZCid];
1867 standard = daylight;
1869 return NULL;
1874 /* adjusts time to given time zone */
1875 void
1876 ical_adjust_date(struct tm *date, VTIMEZONE_S *vtz)
1878 char *tzname = NULL;
1879 ICLINE_S *icl;
1880 ICAL_TZPROP_S *cur_std_day;
1882 if(vtz == NULL)
1883 return;
1885 if(vtz->prop){
1886 if((icl = (ICLINE_S *)vtz->prop[TZCid]) != NULL)
1887 tzname = cpystr(icl->value);
1890 cur_std_day = ical_std_or_daylight(date, vtz);
1893 void
1894 ical_set_date_vevent(void *veventv, void *vtzv)
1896 VEVENT_S *vevent = (VEVENT_S *) veventv;
1897 VTIMEZONE_S *vtz = (VTIMEZONE_S *) vtzv;
1899 if(vevent){
1900 ical_set_date(vevent->prop[EvDtstamp], vtz);
1901 ical_set_date(vevent->prop[EvDtstart], vtz);
1902 ical_set_date(vevent->prop[EvDtend], vtz);
1906 #define LEAP_YEAR(X) ((((X) % 4 == 0) \
1907 && (((X) % 100 != 0) || ((X) % 400 == 0))) \
1908 || (X) == 1700)
1910 #define CAL_OFFSET(X) (((X) == 1752) ? 5 : (LEAP_YEAR((X)) ? 2 : 1))
1912 /* given a year, after day_zero, return the day
1913 * of the week of the first of january of that year. On error,
1914 * return a negative number.
1915 * Assumption: day_zero is the date of january 1, of some year.
1918 ical_january_first(int year)
1920 int i, january_first;
1922 if(year < day_zero.tm_year) return -1; /* not supported */
1924 year += 1900;
1925 january_first = day_zero.tm_wday;
1926 for(i = 1900 + day_zero.tm_year + 1; i <= year; i++)
1927 january_first += CAL_OFFSET(i-1);
1929 return january_first % 7;
1932 /* given a month, week day, and year, return all days of the month
1933 * that have that day as the week day. For example, return all
1934 * sundays in november 2012.
1936 int *
1937 ical_day_from_week(int month, Weekday day, int year)
1939 int *rv = NULL;
1940 int fday, nday;
1941 Weekday wday;
1942 int i;
1944 fday = ical_first_of_month(month, year);
1945 year += 1900; /* restore year */
1946 if(year == 1752 && month == 8){
1947 fday = 9;
1948 } else {
1949 for(nday = 1, wday = (Weekday) fday; wday != day; wday = (wday+1) % 7, nday++)
1951 rv = fs_get(6*sizeof(int));
1952 memset((void *) rv, 0, 6*sizeof(int));
1953 for(i = 0; nday <= month_len[month]; i++){
1954 rv[i] = nday;
1955 nday += 7;
1957 if(LEAP_YEAR(year) && month == 1 && nday == 29)
1958 rv[i] = nday;
1961 return rv;
1965 /* given a month and a year, return the weekday of the first of the
1966 * month in that year.
1967 * return value: on error -1, otherwise the day of the week.
1970 ical_first_of_month(int month, int year)
1972 int i, d;
1974 if((d = ical_january_first(year)) < 0)
1975 return -1;
1977 year += 1900;
1978 for(i = 0; i < month; i++)
1979 d += month_len[i];
1981 if(LEAP_YEAR(year) && month >= 2)
1982 d += 1;
1984 if(year == 1752 && month >= 9)
1985 d -= 11;
1987 return d % 7;
1990 /* given a day, month and year, return the weekday of that day
1991 * return value: on error -1, otherwise the day of the week.
1993 int ical_day_of_week(struct tm date)
1995 int d;
1997 if((d = ical_first_of_month(date.tm_mon, date.tm_year)) < 0)
1998 return -1;
2000 d += date.tm_mday - 1;
2002 if(date.tm_year + 1900 == 1752){
2003 if(date.tm_mday > 2 && date.tm_mday < 14)
2004 return -1;
2005 if(date.tm_mday >= 14)
2006 d -= 11;
2008 return d % 7;
2012 /* given an initial date dtstart, and a recurring rule, rrule,
2013 * adjust the date to the first date on the same year, when
2014 * the rule actually starts
2016 struct tm
2017 adjust_date_rrule(struct tm *dtstart, RRULE_S *rrule)
2019 struct tm t;
2021 memset((void *) &t, 0, sizeof(struct tm));
2022 t.tm_year = dtstart->tm_year; /* same year */
2023 if(rrule->prop[RRFreq]){
2025 if(rrule->prop[RRCount]){
2027 else if(rrule->prop[RRInterval]){
2029 if(rrule->prop[RRBysecond]){
2030 BYWKDY_S *sec = (BYWKDY_S *) rrule->prop[RRBysecond], *seco;
2031 for (seco = sec; seco != NULL; seco = seco->next)
2032 if(seco == sec) t.tm_sec = seco->value;
2033 else if (seco->value < t.tm_sec)
2034 t.tm_sec = seco->value;
2036 if (rrule->prop[RRByminute]){
2037 BYWKDY_S *sec = (BYWKDY_S *) rrule->prop[RRByminute], *seco;
2038 for (seco = sec; seco != NULL; seco = seco->next)
2039 if(seco == sec) t.tm_min = seco->value;
2040 else if (seco->value < t.tm_sec)
2041 t.tm_min = seco->value;
2043 if (rrule->prop[RRByhour]){
2044 BYWKDY_S *sec = (BYWKDY_S *) rrule->prop[RRByhour], *seco;
2045 for (seco = sec; seco != NULL; seco = seco->next)
2046 if(seco == sec) t.tm_hour = seco->value;
2047 else if (seco->value < t.tm_sec)
2048 t.tm_hour = seco->value;
2050 if (rrule->prop[RRByday]){
2052 if (rrule->prop[RRByweekno]){
2054 if (rrule->prop[RRBymonthday]){
2056 if (rrule->prop[RRByyearday]){
2058 if (rrule->prop[RRByweekno]){
2060 if (rrule->prop[RRBymonth]){
2061 BYWKDY_S *m = (BYWKDY_S *) rrule->prop[RRBymonth], *mo;
2062 for (mo = m; mo != NULL; mo = mo->next)
2063 if(mo == m) t.tm_mon = mo->value - 1;
2064 else if (mo->value - 1 < t.tm_mon)
2065 t.tm_mon = mo->value - 1;
2067 if (rrule->prop[RRBysetpos]){
2069 if (rrule->prop[RRWkst]){
2073 void
2074 ical_initialize(void)
2076 ical_buf_len = 1024;
2077 ical_buf = fs_get(ical_buf_len+1);
2079 memset((void *) &day_zero, 0, sizeof(struct tm));
2080 day_zero.tm_year = 1601 - 1900;
2081 day_zero.tm_mday = 1;
2082 day_zero.tm_wday = 4;
2085 /* At this time, we are going to print the date in 24 hour format
2086 * if there is no string for AM or PM, but we use AM or PM when available.
2087 * We plan to make this user configurable, but not today...
2089 void
2090 ical_date_time (char *tmp, size_t len, struct tm *ic_datep)
2092 /* test of the AM/PM string is available */
2093 our_strftime(tmp, len, "%p", ic_datep);
2095 if(tmp[0])
2096 our_strftime(tmp, len, "%a %x %I:%M %p", ic_datep);
2097 else
2098 our_strftime(tmp, len, "%a %x %H:%M", ic_datep);
2101 /* If the icline has a TZID parameter, return its value, otherwise, return
2102 * NULL. Returned value freed by caller.
2104 char *
2105 ical_get_tzid(ICAL_PARAMETER_S *param)
2107 char *tzid = NULL;
2109 if(param == NULL)
2110 return tzid;
2112 if(strucmp(param->name, "TZID") == 0)
2113 tzid = cpystr(param->value);
2114 else
2115 tzid = ical_get_tzid(param->next);
2117 return tzid;
2120 /* we create a summary of the event, and pass that back as
2121 an ical parameter
2123 VEVENT_SUMMARY_S *
2124 ical_vevent_summary(VCALENDAR_S *vcal)
2126 VEVENT_SUMMARY_S *rv;
2127 ICLINE_S *method;
2128 VEVENT_S *vevent;
2129 GEN_ICLINE_S *gicl;
2130 ICLINE_S *icl;
2131 char *k;
2133 if(vcal == NULL) return NULL;
2135 method = vcal->method;
2136 vevent = (VEVENT_S *) vcal->comp[VEvent];
2138 if(vevent == NULL || vevent->prop == NULL)
2139 return NULL;
2141 rv = fs_get(sizeof(VEVENT_SUMMARY_S));
2142 memset((void *) rv, 0, sizeof(VEVENT_SUMMARY_S));
2144 if(method != NULL && !strucmp(method->value, "CANCEL"))
2145 rv->cancel++;
2147 if((icl = (ICLINE_S *) vevent->prop[EvPriority]) != NULL)
2148 rv->priority = atoi(icl->value);
2150 if((icl = (ICLINE_S *) vevent->prop[EvSummary]) != NULL)
2151 rv->summary = cpystr(icl->value ? icl->value : _("No Summary"));
2153 if((icl = (ICLINE_S *) vevent->prop[EvClass]) != NULL)
2154 rv->class = cpystr(icl->value ? icl->value : _("PUBLIC"));
2155 else
2156 rv->class = cpystr(_("PUBLIC"));
2158 if((icl = (ICLINE_S *) vevent->prop[EvOrganizer]) != NULL){
2159 char *cn, *sender, *address;
2160 ICAL_PARAMETER_S *param;
2162 cn = sender = address = NULL;
2163 for(param = icl->param; param != NULL; param = param->next)
2164 if(!strucmp(param->name, "CN"))
2165 cn = param->value;
2166 else if(!strucmp(param->name, "SENT-BY"))
2167 sender = param->value;
2169 if(sender != NULL){
2170 if(!struncmp(sender, "MAILTO:", 7))
2171 sender += 7;
2172 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "<%s>", sender);
2173 rv->sender = cpystr(tmp_20k_buf);
2176 if((address = icl->value) != NULL){
2177 if(!struncmp(address, "MAILTO:", 7))
2178 address += 7;
2179 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s<%s>",
2180 cn ? cn : "", cn ? " " : "",
2181 address ? address : _("Unknown address"));
2182 rv->organizer = cpystr(tmp_20k_buf);
2184 } /* end of if(organizer) */
2186 if((icl = (ICLINE_S *) vevent->prop[EvLocation]) != NULL)
2187 rv->location = cpystr(icl->value ? icl->value : _("Location undisclosed"));
2189 if((icl = (ICLINE_S *) vevent->prop[EvDtstart]) != NULL){
2190 struct tm ic_date;
2191 char tmp[200], *tzid;
2192 int icd; /* ical date return value */
2194 memset((void *)&ic_date, 0, sizeof(struct tm));
2195 icd = ical_parse_date(icl->value, &ic_date);
2196 tzid = ical_get_tzid(icl->param);
2197 if(icd >= 0){
2198 ic_date.tm_wday = ical_day_of_week(ic_date);
2199 switch(icd){
2200 case 0: /* DATE-TIME */
2201 ical_date_time(tmp, sizeof(tmp), &ic_date);
2202 break;
2203 case 1: /* DATE */
2204 our_strftime(tmp, sizeof(tmp), "%a %x", &ic_date);
2205 break;
2206 case 2: /* DATE-TIME in GMT, Bug: add adjust to time zone */
2207 our_strftime(tmp, sizeof(tmp), "%a %x %I:%M %p", &ic_date);
2208 break;
2209 default: alpine_panic("Unhandled ical date format");
2210 break;
2213 else{
2214 strncpy(tmp, _("Error while parsing event date"), sizeof(tmp));
2215 tmp[sizeof(tmp) - 1] = '\0';
2218 if(icl->value == NULL)
2219 rv->evstart = cpystr(_("Unknown Start Date"));
2220 else{
2221 size_t len = strlen(tmp) + 1;
2223 if(tzid != NULL)
2224 len += strlen(tzid) + 3; /* 3 = strlen(" ()") */
2226 rv->evstart = fs_get(len*sizeof(char));
2227 snprintf(rv->evstart, len, "%s%s%s%s", tmp,
2228 tzid != NULL ? " (" : "",
2229 tzid != NULL ? tzid : "",
2230 tzid != NULL ? ")" : "");
2231 rv->evstart[len-1] = '\0';
2233 } /* end of if dtstart */
2235 if((icl = (ICLINE_S *) vevent->prop[EvDuration]) != NULL){
2236 int i, done = 0;
2237 ICAL_DURATION_S ic_d, icd2;
2238 if(ical_parse_duration(icl->value, &ic_d) == 0){
2239 char tmp[MAILTMPLEN+1];
2241 for(i = 1, icd2 = ic_d; icd2.next != NULL; icd2 = *icd2.next, i++);
2242 rv->duration = fs_get((i+1)*sizeof(char *));
2243 i = 0;
2245 do {
2246 tmp[0] = '\0';
2248 if(ic_d.weeks > 0)
2249 utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
2250 "%d %s ", ic_d.weeks, ic_d.weeks == 1 ? _("week") : _("weeks"));
2251 if(ic_d.days > 0)
2252 utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
2253 "%d %s ", ic_d.days, ic_d.days == 1 ? _("day") : _("days"));
2254 if(ic_d.hours > 0)
2255 utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
2256 "%d %s ", ic_d.hours, ic_d.hours == 1 ? _("hour") : _("hours"));
2257 if(ic_d.minutes > 0)
2258 utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
2259 "%d %s ", ic_d.minutes, ic_d.minutes == 1 ? _("minute") : _("minutes"));
2260 if(ic_d.seconds > 0)
2261 utf8_snprintf(tmp+strlen(tmp), MAILTMPLEN - strlen(tmp),
2262 "%d %s ", ic_d.seconds, ic_d.seconds == 1 ? _("second") : _("seconds"));
2264 tmp[MAILTMPLEN] = '\0';
2265 rv->duration[i++] = cpystr(tmp);
2267 if(ic_d.next != NULL)
2268 ic_d = *ic_d.next;
2269 else
2270 done++;
2271 } while (done == 0);
2272 rv->duration[i] = NULL;
2274 } /* end of DURATION */
2275 else if((icl = (ICLINE_S *) vevent->prop[EvDtend]) != NULL){
2276 struct tm ic_date;
2277 char tmp[200], *tzid;
2278 int icd;
2280 memset((void *)&ic_date, 0, sizeof(struct tm));
2281 icd = ical_parse_date(icl->value, &ic_date);
2282 tzid = ical_get_tzid(icl->param);
2283 if(icd >= 0){
2284 ic_date.tm_wday = ical_day_of_week(ic_date);
2285 switch(icd){
2286 case 0: /* DATE-TIME */
2287 ical_date_time(tmp, sizeof(tmp), &ic_date);
2288 break;
2289 case 1: /* DATE */
2290 our_strftime(tmp, sizeof(tmp), "%a %x", &ic_date);
2291 break;
2292 case 2: /* DATE-TIME in GMT, Bug: add adjust to time zone */
2293 our_strftime(tmp, sizeof(tmp), "%a %x %I:%M %p", &ic_date);
2294 break;
2295 default: alpine_panic("Unhandled ical date format");
2296 break;
2299 else{
2300 strncpy(tmp, _("Error while parsing event date"), sizeof(tmp));
2301 tmp[sizeof(tmp) - 1] = '\0';
2304 if(icl->value == NULL)
2305 rv->evend = cpystr(_("Unknown End Date"));
2306 else{
2307 size_t len = strlen(tmp) + 1;
2309 if(tzid != NULL)
2310 len += strlen(tzid) + 3; /* 3 = strlen(" ()") */
2312 rv->evend = fs_get(len*sizeof(char));
2313 snprintf(rv->evend, len, "%s%s%s%s", tmp,
2314 tzid != NULL ? " (" : "",
2315 tzid != NULL ? tzid : "",
2316 tzid != NULL ? ")" : "");
2317 rv->evend[len-1] = '\0';
2319 } /* end of if dtend */
2321 if((icl = (ICLINE_S *) vevent->prop[EvDtstamp]) != NULL){
2322 struct tm ic_date;
2323 char tmp[200], *tzid;
2324 int icd;
2326 memset((void *)&ic_date, 0, sizeof(struct tm));
2327 icd = ical_parse_date(icl->value, &ic_date);
2328 tzid = ical_get_tzid(icl->param);
2329 if(icd >= 0){
2330 ic_date.tm_wday = ical_day_of_week(ic_date);
2331 switch(icd){
2332 case 0: /* DATE-TIME */
2333 ical_date_time(tmp, sizeof(tmp), &ic_date);
2334 break;
2335 case 1: /* DATE */
2336 our_strftime(tmp, sizeof(tmp), "%a %x", &ic_date);
2337 break;
2338 case 2: /* DATE-TIME in GMT, Bug: add adjust to time zone */
2339 our_strftime(tmp, sizeof(tmp), "%a %x %I:%M %p", &ic_date);
2340 break;
2341 default: alpine_panic("Unhandled ical date format");
2342 break;
2345 else{
2346 strncpy(tmp, _("Error while parsing event date"), sizeof(tmp));
2347 tmp[sizeof(tmp) - 1] = '\0';
2349 if(icl->value == NULL)
2350 rv->dtstamp = cpystr(_("Unknown when event was scheduled"));
2351 else{
2352 size_t len = strlen(tmp) + 1;
2354 if(tzid != NULL)
2355 len += strlen(tzid) + 3; /* 3 = strlen(" ()") */
2357 rv->dtstamp = fs_get(len*sizeof(char));
2358 snprintf(rv->dtstamp, len, "%s%s%s%s", tmp,
2359 tzid != NULL ? " (" : "",
2360 tzid != NULL ? tzid : "",
2361 tzid != NULL ? ")" : "");
2362 rv->dtstamp[len-1] = '\0';
2364 } /* end of if dtstamp */
2366 if((gicl = (GEN_ICLINE_S *) vevent->prop[EvAttendee]) != NULL){
2367 int nattendees, i;
2369 for(nattendees = 0; gicl != NULL; gicl = gicl->next, nattendees++);
2370 rv->attendee = fs_get((nattendees+1)*sizeof(char *));
2372 gicl = (GEN_ICLINE_S *) vevent->prop[EvAttendee];
2373 for(i = 0; gicl != NULL; gicl = gicl->next, i++){
2374 char *role, *partstat, *rsvp;
2375 char *cn, *mailto;
2376 ICAL_PARAMETER_S *param;
2378 icl = gicl->cline;
2379 role = partstat = rsvp = cn = mailto = NULL;
2380 for(param = icl->param; param != NULL; param = param->next){
2381 if(!strucmp(param->name, "ROLE")){
2382 if(!strucmp(param->value, "REQ-PARTICIPANT"))
2383 role = _("[Required]");
2384 else if(!strucmp(param->value, "OPT-PARTICIPANT"))
2385 role = _("[Optional]");
2386 else if(!strucmp(param->value, "NON-PARTICIPANT"))
2387 role = _("[Informed]");
2388 else if(!strucmp(param->value, "CHAIR"))
2389 role = _("[ Chair ]");
2390 else
2391 role = param->value;
2393 else if(!strucmp(param->name, "PARTSTAT")){
2394 if(!strucmp(param->value, "NEEDS-ACTION"))
2395 partstat = _("[Need-Reply]");
2396 else if(!strucmp(param->value, "ACCEPTED"))
2397 partstat = _("[ Accepted ]");
2398 else if(!strucmp(param->value, "DECLINED"))
2399 partstat = _("[ Declined ]");
2400 else if(!strucmp(param->value, "TENTATIVE"))
2401 partstat = _("[ Tentative]");
2402 else if(!strucmp(param->value, "DELEGATED"))
2403 partstat = _("[ Delegated]");
2404 else
2405 partstat = param->value;
2407 else if(!strucmp(param->name, "RSVP"))
2408 rsvp = param->value;
2409 else if(!strucmp(param->name, "CN"))
2410 cn = param->value;
2412 if(icl->value && !struncmp(icl->value, "MAILTO:", strlen("MAILTO:")))
2413 mailto = icl->value + 7; /* 7 = strlen("MAILTO:") */
2414 if(!strucmp(cn, mailto))
2415 cn = "";
2416 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s%s%s%s<%s>",
2417 role && *role ? role : "",
2418 role && *role ? " " : "",
2419 partstat ? partstat : _("[Unknown Reply]"),
2420 " ",
2421 cn && *cn ? cn : "",
2422 cn && *cn ? " " : "",
2423 mailto ? mailto : _("Unknown address"));
2424 rv->attendee[i] = cpystr(tmp_20k_buf);
2426 rv->attendee[i] = NULL;
2427 } /* end of ATTENDEES */
2429 if((icl = (ICLINE_S *) vevent->prop[EvDescription]) != NULL){
2430 char *s, *t, *u, *v;
2431 int i, escaped;
2433 if(icl->value == NULL){
2434 free_vevent_summary(&rv);
2435 return NULL;
2438 v = cpystr(icl->value); /* process a copy of icl->value */
2440 for(i = 1, escaped = 0, s = v; s && *s; s++){
2441 if(*s == '\\' && escaped == 0){ escaped = 1; continue; }
2442 if(escaped){
2443 if(!(*s == '\\' || *s == ',' || *s == 'n' || *s == 'N' || *s == ';')){
2444 free_vevent_summary(&rv);
2445 fs_give((void **)&v);
2446 return NULL;
2448 escaped = 0;
2449 continue;
2451 if(*s == ',') i++; /* a non-scaped comma is a new value for text */
2454 rv->description = fs_get((i+1)*sizeof(unsigned char *));
2455 i = 0;
2456 for (s = t = u = v, escaped = 0; *t != '\0'; t++){
2457 if(*t == '\\' && escaped == 0){ escaped = 1; continue; }
2458 if(escaped){
2459 switch(*t){
2460 case '\\':
2461 case ',':
2462 case ';':
2463 *u++ = *t;
2464 break;
2465 case 'n':
2466 case 'N':
2467 *u++ = '\n';
2468 break;
2469 default: free_vevent_summary(&rv);
2470 fs_give((void **)&v);
2471 return NULL;
2473 escaped = 0;
2474 continue;
2476 if(*t == ','){
2477 *u = '\0';
2478 rv->description[i++] = cpystr(ical_decode(s, vcal->encoding));
2479 s = u = t+1;
2480 } else
2481 *u++ = *t;
2483 *u = '\0';
2484 rv->description[i++] = cpystr(ical_decode(s, vcal->encoding));
2485 rv->description[i] = NULL;
2486 fs_give((void **)&v);
2487 } /* end of if(description) */
2489 return rv;
2492 void
2493 free_vevent_summary(VEVENT_SUMMARY_S **vesy)
2495 int i;
2496 if(vesy == NULL || *vesy == NULL) return;
2498 if((*vesy)->class) fs_give((void **)&(*vesy)->class);
2499 if((*vesy)->summary) fs_give((void **)&(*vesy)->summary);
2500 if((*vesy)->sender) fs_give((void **)&(*vesy)->sender);
2501 if((*vesy)->organizer) fs_give((void **)&(*vesy)->organizer);
2502 if((*vesy)->location) fs_give((void **)&(*vesy)->location);
2503 if((*vesy)->evstart) fs_give((void **)&(*vesy)->evstart);
2504 if((*vesy)->evend) fs_give((void **)&(*vesy)->evend);
2505 if((*vesy)->dtstamp) fs_give((void **)&(*vesy)->dtstamp);
2506 if((*vesy)->duration){
2507 for(i = 0; (*vesy)->duration[i] != NULL; i++)
2508 fs_give((void **) &(*vesy)->duration[i]);
2509 fs_give((void **) (*vesy)->duration);
2511 if((*vesy)->attendee){
2512 for(i = 0; (*vesy)->attendee[i] != NULL; i++)
2513 fs_give((void **) &(*vesy)->attendee[i]);
2514 fs_give((void **) (*vesy)->attendee);
2516 if((*vesy)->description){
2517 for(i = 0; (*vesy)->description[i] != NULL; i++)
2518 fs_give((void **) &(*vesy)->description[i]);
2519 fs_give((void **) (*vesy)->description);
2521 fs_give((void **) vesy);