1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 8; -*-
2 ======================================================================
3 FILE: icalparser.c
4 CREATOR: eric 04 August 1999
5
6 $Id: icalparser.c,v 1.53 2008-01-15 23:17:40 dothebart Exp $
7 $Locker: $
8
9 The contents of this file are subject to the Mozilla Public License
10 Version 1.0 (the "License"); you may not use this file except in
11 compliance with the License. You may obtain a copy of the License at
12 http://www.mozilla.org/MPL/
13
14 Software distributed under the License is distributed on an "AS IS"
15 basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
16 the License for the specific language governing rights and
17 limitations under the License.
18
19
20 This program is free software; you can redistribute it and/or modify
21 it under the terms of either:
22
23 The LGPL as published by the Free Software Foundation, version
24 2.1, available at: http://www.fsf.org/copyleft/lesser.html
25
26 Or:
27
28 The Mozilla Public License Version 1.0. You may obtain a copy of
29 the License at http://www.mozilla.org/MPL/
30
31 The Initial Developer of the Original Code is Eric Busboom
32
33 (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org>
34 http://www.softwarestudio.org
35 ======================================================================*/
36
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif
40
41
42 #include "pvl.h"
43 #include "icalerror.h"
44 #include "icalvalue.h"
45 #include "icalderivedparameter.h"
46 #include "icalparameter.h"
47 #include "icalproperty.h"
48 #include "icalcomponent.h"
49
50 #include <string.h> /* For strncpy & size_t */
51 #include <stdio.h> /* For FILE and fgets and snprintf */
52 #include <stdlib.h> /* for free */
53 #include <ctype.h>
54
55 #include "icalmemory.h"
56 #include "icalparser.h"
57
58 #ifdef HAVE_WCTYPE_H
59 # include <wctype.h>
60 /* Some systems have an imcomplete implementation on wctype (FreeBSD,
61 * Darwin). Cope with that. */
62 # ifndef HAVE_ISWSPACE
63 # define iswspace isspace
64 # endif
65 #else
66 # if !defined HAVE_ISWSPACE && !defined WIN32
67 # define iswspace isspace
68 # endif
69 #endif
70
71 #ifdef WIN32
72 #if defined(_MSC_VER) && (_MSC_VER < 1900)
73 #define snprintf _snprintf
74 #endif
75 #define strcasecmp stricmp
76 #endif
77
78 static char* parser_get_next_char(char c, char *str, int qm);
79 static char* parser_get_next_parameter(char* line,char** end);
80 static char* parser_get_next_value(char* line, char **end, icalvalue_kind kind);
81 static char* parser_get_prop_name(char* line, char** end);
82 static char* parser_get_param_name(char* line, char **end, char **buf_value);
83
84 #define TMP_BUF_SIZE 80
85
86 struct icalparser_impl
87 {
88 int buffer_full; /* flag indicates that temp is smaller that
89 data being read into it*/
90 int continuation_line; /* last line read was a continuation line */
91 size_t tmp_buf_size;
92 char temp[TMP_BUF_SIZE];
93 icalcomponent *root_component;
94 int version;
95 int level;
96 int lineno;
97 icalparser_state state;
98 pvl_list components;
99
100 void *line_gen_data;
101
102 };
103
104
105 /*
106 * New version of strstrip() that does not move the pointer.
107 */
strstriplt(char * buf)108 void strstriplt(char *buf)
109 {
110 size_t len;
111 int a;
112
113 if (buf==NULL) {
114 return;
115 }
116 if (buf[0] == 0) {
117 return;
118 }
119 len = strlen(buf);
120 while ((buf[0] != 0) && (isspace(buf[len - 1]))) {
121 buf[--len] = 0;
122 }
123 if (buf[0] == 0) {
124 return;
125 }
126 a = 0;
127 while ((buf[0]!=0) && (isspace(buf[a]))) {
128 a++;
129 }
130 if (a > 0) {
131 memmove(buf, &buf[a], len - a + 1);
132 }
133 }
134
135
136
icalparser_new(void)137 icalparser* icalparser_new(void)
138 {
139 struct icalparser_impl* impl = 0;
140 if ( ( impl = (struct icalparser_impl*)
141 malloc(sizeof(struct icalparser_impl))) == 0) {
142 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
143 return 0;
144 }
145
146 impl->root_component = 0;
147 impl->components = pvl_newlist();
148 impl->level = 0;
149 impl->state = ICALPARSER_SUCCESS;
150 impl->tmp_buf_size = TMP_BUF_SIZE;
151 impl->buffer_full = 0;
152 impl->continuation_line = 0;
153 impl->lineno = 0;
154 impl->continuation_line = 0;
155 memset(impl->temp,0, TMP_BUF_SIZE);
156
157 return (icalparser*)impl;
158 }
159
160
icalparser_free(icalparser * parser)161 void icalparser_free(icalparser* parser)
162 {
163 icalcomponent *c;
164
165 if (parser->root_component != 0){
166 icalcomponent_free(parser->root_component);
167 }
168
169 while( (c=pvl_pop(parser->components)) != 0){
170 icalcomponent_free(c);
171 }
172
173 pvl_free(parser->components);
174
175 free(parser);
176 }
177
icalparser_set_gen_data(icalparser * parser,void * data)178 void icalparser_set_gen_data(icalparser* parser, void* data)
179 {
180 parser->line_gen_data = data;
181 }
182
183
184 icalvalue* icalvalue_new_From_string_with_error(icalvalue_kind kind,
185 char* str,
186 icalproperty **error);
187
188
189 static
parser_get_next_char(char c,char * str,int qm)190 char* parser_get_next_char(char c, char *str, int qm)
191 {
192 int quote_mode = 0;
193 char *p = str;
194 char next_char = *p;
195 char prev_char = 0;
196
197 while (next_char != '\0') {
198 if (prev_char != '\\') {
199 if (qm == 1 && next_char == '"') {
200 /* Encountered a quote, toggle quote mode */
201 quote_mode = !quote_mode;
202 } else if (quote_mode == 0 && next_char == c) {
203 /* Found a matching character out of quote mode, return it */
204 return p;
205 }
206 }
207
208 /* Save the previous character so we can check if it's a backslash in the next iteration */
209 prev_char = next_char;
210 next_char = *(++p);
211 }
212
213 return 0;
214 }
215
216
217 /** make a new tmp buffer out of a substring */
make_segment(char * start,char * end)218 static char* make_segment(char* start, char* end)
219 {
220 char *buf, *tmp;
221 size_t size = (size_t)end - (size_t)start;
222
223 buf = icalmemory_new_buffer(size+1);
224 strncpy(buf,start,size);
225 *(buf+size) = 0;
226
227 tmp = (buf+size);
228 while ((tmp >= buf) &&
229 ((*tmp == '\0') || iswspace(*tmp)))
230 {
231 *tmp = 0;
232 tmp--;
233 }
234
235 return buf;
236 }
237
238 static
parser_get_prop_name(char * line,char ** end)239 char* parser_get_prop_name(char* line, char** end)
240 {
241 char* p;
242 char* v;
243 char *str;
244
245 p = parser_get_next_char(';',line,1);
246 v = parser_get_next_char(':',line,1);
247 if (p== 0 && v == 0) {
248 return 0;
249 }
250
251 /* There is no ';' or, it is after the ';' that marks the beginning of
252 the value */
253 if (v!=0 && ( p == 0 || p > v)){
254 str = make_segment(line,v);
255 *end = v+1;
256 } else {
257 str = make_segment(line,p);
258 *end = p+1;
259 }
260
261 return str;
262 }
263
264 static
parser_get_param_name(char * line,char ** end,char ** buf)265 char* parser_get_param_name(char* line, char **end, char **buf)
266 {
267 char* next;
268 char *str;
269
270 next = parser_get_next_char('=',line,1);
271
272 *buf = 0;
273 if (next == 0) {
274 return 0;
275 }
276
277 str = make_segment(line,next);
278 *end = next+1;
279 if (**end == '"') {
280 *end = *end+1;
281 next = parser_get_next_char('"',*end,0);
282 if (next == 0) {
283 free(str);
284 return 0;
285 }
286
287 *buf = *end = make_segment(*end,next);
288 }
289
290 return str;
291 }
292
293 #if 0
294 static
295 char* parser_get_next_paramvalue(char* line, char **end)
296 {
297 char* next;
298 char *str;
299
300 next = parser_get_next_char(',',line,1);
301
302 if (next == 0){
303 next = (char*)(size_t)line+(size_t)strlen(line);\
304 }
305
306 if (next == line){
307 return 0;
308 } else {
309 str = make_segment(line,next);
310 *end = next+1;
311 return str;
312 }
313 }
314 #endif
315
icalparser_get_value(char * line,char ** end,icalvalue_kind kind)316 char* icalparser_get_value(char* line, char **end, icalvalue_kind kind)
317 {
318 char *str;
319 size_t length = strlen(line);
320
321 if (length == 0){
322 return 0;
323 }
324
325 *end = line+length;
326 str = make_segment(line, *end);
327
328 return str;
329 }
330
331 /**
332 A property may have multiple values, if the values are seperated by
333 commas in the content line. This routine will look for the next
334 comma after line and will set the next place to start searching in
335 end. */
336
337 static
parser_get_next_value(char * line,char ** end,icalvalue_kind kind)338 char* parser_get_next_value(char* line, char **end, icalvalue_kind kind)
339 {
340
341 char* next;
342 char *p;
343 char *str;
344 size_t length = strlen(line);
345
346 p = line;
347 while(1){
348
349 next = parser_get_next_char(',',p,1);
350
351 /* Unforunately, RFC2445 says that for the RECUR value, COMMA
352 can both separate digits in a list, and it can separate
353 multiple recurrence specifications. This is not a friendly
354 part of the spec. This weirdness tries to
355 distinguish the two uses. it is probably a HACK*/
356
357 if( kind == ICAL_RECUR_VALUE ) {
358 if ( next != 0 &&
359 (*end+length) > next+5 &&
360 strncmp(next,"FREQ",4) == 0
361 ) {
362 /* The COMMA was followed by 'FREQ', is it a real separator*/
363 /* Fall through */
364 } else if (next != 0){
365 /* Not real, get the next COMMA */
366 p = next+1;
367 next = 0;
368 continue;
369 }
370 }
371 /* ignore all , for query value. select dtstart, dtend etc ... */
372 else if( kind == ICAL_QUERY_VALUE) {
373 if ( next != 0) {
374 p = next+1;
375 continue;
376 }
377 else
378 break;
379 }
380
381 /* If the comma is preceded by a '\', then it is a literal and
382 not a value separator*/
383
384 if ((next!=0 && *(next-1) == '\\') ||
385 (next!=0 && (next-3) >= line && *(next-3) == '\\' && *(next-2) == '\r' && *(next-1) == '\n'))
386 /* second clause for '\' is on prev line */
387 {
388 p = next+1;
389 } else {
390 break;
391 }
392
393 }
394
395 if (next == 0){
396 next = (char*)(size_t)line+length;
397 *end = next;
398 } else {
399 *end = next+1;
400 }
401
402 if (next == line){
403 return 0;
404 }
405
406
407 str = make_segment(line,next);
408 return str;
409
410 }
411
412 static
parser_get_next_parameter(char * line,char ** end)413 char* parser_get_next_parameter(char* line,char** end)
414 {
415 char *next;
416 char *v;
417 char *str;
418
419 v = parser_get_next_char(':',line,1);
420 next = parser_get_next_char(';', line,1);
421
422 /* There is no ';' or, it is after the ':' that marks the beginning of
423 the value */
424
425 if (next == 0 || next > v) {
426 next = parser_get_next_char(':', line,1);
427 }
428
429 if (next != 0) {
430 str = make_segment(line,next);
431 *end = next+1;
432 return str;
433 } else {
434 *end = line;
435 return 0;
436 }
437 }
438
439
440 /**
441 * Get a single property line, from the property name through the
442 * final new line, and include any continuation lines
443 */
icalparser_get_line(icalparser * parser,char * (* line_gen_func)(char * s,size_t size,void * d))444 char* icalparser_get_line(icalparser *parser,
445 char* (*line_gen_func)(char *s, size_t size, void *d))
446 {
447 char *line;
448 char *line_p;
449 size_t buf_size = parser->tmp_buf_size;
450
451 line_p = line = icalmemory_new_buffer(buf_size);
452 line[0] = '\0';
453
454 /* Read lines by calling line_gen_func and putting the data into
455 parser->temp. If the line is a continuation line ( begins with a
456 space after a newline ) then append the data onto line and read
457 again. Otherwise, exit the loop. */
458
459 while(1) {
460
461 /* The first part of the loop deals with the temp buffer,
462 which was read on he last pass through the loop. The
463 routine is split like this because it has to read lone line
464 ahead to determine if a line is a continuation line. */
465
466
467 /* The tmp buffer is not clear, so transfer the data in it to the
468 output. This may be left over from a previous call */
469 if (parser->temp[0] != '\0' ) {
470
471 /* If the last position in the temp buffer is occupied,
472 mark the buffer as full. The means we will do another
473 read later, because the line is not finished */
474 if (parser->temp[parser->tmp_buf_size-1] == 0 &&
475 parser->temp[parser->tmp_buf_size-2] != '\n'&&
476 parser->temp[parser->tmp_buf_size-2] != 0 ){
477 parser->buffer_full = 1;
478 } else {
479 parser->buffer_full = 0;
480 }
481
482 /* Copy the temp to the output and clear the temp buffer. */
483 if(parser->continuation_line==1){
484 /* back up the pointer to erase the continuation characters */
485 parser->continuation_line = 0;
486 line_p--;
487
488 if ( *(line_p-1) == '\r'){
489 line_p--;
490 }
491
492 /* copy one space up to eliminate the leading space*/
493 icalmemory_append_string(&line,&line_p,&buf_size,
494 parser->temp+1);
495
496 } else {
497 icalmemory_append_string(&line,&line_p,&buf_size,parser->temp);
498 }
499
500 parser->temp[0] = '\0' ;
501 }
502
503 parser->temp[parser->tmp_buf_size-1] = 1; /* Mark end of buffer */
504
505 /****** Here is where the routine gets string data ******************/
506 if ((*line_gen_func)(parser->temp,parser->tmp_buf_size,parser->line_gen_data)
507 ==0){/* Get more data */
508
509 /* If the first position is clear, it means we didn't get
510 any more data from the last call to line_ge_func*/
511 if (parser->temp[0] == '\0'){
512
513 if(line[0] != '\0'){
514 /* There is data in the output, so fall trhough and process it*/
515 break;
516 } else {
517 /* No data in output; return and signal that there
518 is no more input*/
519 free(line);
520 return 0;
521 }
522 }
523 }
524
525
526 /* If the output line ends in a '\n' and the temp buffer
527 begins with a ' ' or tab, then the buffer holds a continuation
528 line, so keep reading. RFC 2445, section 4.1 */
529
530 if ( line_p > line+1 && *(line_p-1) == '\n'
531 && (parser->temp[0] == ' ' || parser->temp[0] == '\t') ) {
532
533 parser->continuation_line = 1;
534
535 } else if ( parser->buffer_full == 1 ) {
536
537 /* The buffer was filled on the last read, so read again */
538
539 } else {
540
541 /* Looks like the end of this content line, so break */
542 break;
543 }
544
545
546 }
547
548 /* Erase the final newline and/or carriage return*/
549 if ( line_p > line+1 && *(line_p-1) == '\n') {
550 *(line_p-1) = '\0';
551 if ( *(line_p-2) == '\r'){
552 *(line_p-2) = '\0';
553 }
554
555 } else {
556 *(line_p) = '\0';
557 }
558
559 while ( (*line_p == '\0' || iswspace(*line_p)) && line_p > line )
560 {
561 *line_p = '\0';
562 line_p--;
563 }
564
565 return line;
566
567 }
568
insert_error(icalcomponent * comp,const char * text,const char * message,icalparameter_xlicerrortype type)569 static void insert_error(icalcomponent* comp, const char* text,
570 const char* message, icalparameter_xlicerrortype type)
571 {
572 char temp[1024];
573
574 if (text == 0){
575 snprintf(temp,1024,"%s:",message);
576 } else {
577 snprintf(temp,1024,"%s: %s",message,text);
578 }
579
580 icalcomponent_add_property
581 (comp,
582 icalproperty_vanew_xlicerror(
583 temp,
584 icalparameter_new_xlicerrortype(type),
585 0));
586 }
587
588
line_is_blank(char * line)589 static int line_is_blank(char* line){
590 int i=0;
591
592 for(i=0; *(line+i)!=0; i++){
593 char c = *(line+i);
594
595 if(c != ' ' && c != '\n' && c != '\t'){
596 return 0;
597 }
598 }
599
600 return 1;
601 }
602
icalparser_parse(icalparser * parser,char * (* line_gen_func)(char * s,size_t size,void * d))603 icalcomponent* icalparser_parse(icalparser *parser,
604 char* (*line_gen_func)(char *s, size_t size,
605 void* d))
606 {
607
608 char* line;
609 icalcomponent *c=0;
610 icalcomponent *root=0;
611 icalerrorstate es = icalerror_get_error_state(ICAL_MALFORMEDDATA_ERROR);
612 int cont;
613
614 icalerror_check_arg_rz((parser !=0),"parser");
615
616 icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,ICAL_ERROR_NONFATAL);
617
618 do{
619 line = icalparser_get_line(parser, line_gen_func);
620
621 if ((c = icalparser_add_line(parser,line)) != 0){
622
623 if(icalcomponent_get_parent(c) !=0){
624 /* This is bad news... assert? */
625 }
626
627 assert(parser->root_component == 0);
628 assert(pvl_count(parser->components) ==0);
629
630 if (root == 0){
631 /* Just one component */
632 root = c;
633 } else if(icalcomponent_isa(root) != ICAL_XROOT_COMPONENT) {
634 /*Got a second component, so move the two components under
635 an XROOT container */
636 icalcomponent *tempc = icalcomponent_new(ICAL_XROOT_COMPONENT);
637 icalcomponent_add_component(tempc, root);
638 icalcomponent_add_component(tempc, c);
639 root = tempc;
640 } else if(icalcomponent_isa(root) == ICAL_XROOT_COMPONENT) {
641 /* Already have an XROOT container, so add the component
642 to it*/
643 icalcomponent_add_component(root, c);
644
645 } else {
646 /* Badness */
647 assert(0);
648 }
649
650 c = 0;
651
652 }
653 cont = 0;
654 if(line != 0){
655 icalmemory_free_buffer(line);
656 cont = 1;
657 }
658 } while ( cont );
659
660 icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,es);
661
662 return root;
663
664 }
665
666
icalparser_add_line(icalparser * parser,char * line)667 icalcomponent* icalparser_add_line(icalparser* parser,
668 char* line)
669 {
670 char *str;
671 char *end;
672 int vcount = 0;
673 icalproperty *prop;
674 icalproperty_kind prop_kind;
675 icalvalue *value;
676 icalvalue_kind value_kind = ICAL_NO_VALUE;
677
678
679 icalerror_check_arg_rz((parser != 0),"parser");
680
681
682 if (line == 0)
683 {
684 parser->state = ICALPARSER_ERROR;
685 return 0;
686 }
687
688 if(line_is_blank(line) == 1){
689 return 0;
690 }
691
692 /* Begin by getting the property name at the start of the line. The
693 property name may end up being "BEGIN" or "END" in which case it
694 is not really a property, but the marker for the start or end of
695 a component */
696
697 end = 0;
698 str = parser_get_prop_name(line, &end);
699
700 if (str == 0 || *str == '\0' ){
701 /* Could not get a property name */
702 icalcomponent *tail = pvl_data(pvl_tail(parser->components));
703
704 if (tail){
705 insert_error(tail,line,
706 "Got a data line, but could not find a property name or component begin tag",
707 ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
708 }
709 tail = 0;
710 parser->state = ICALPARSER_ERROR;
711 icalmemory_free_buffer(str);
712 str = NULL;
713 return 0;
714 }
715
716 /**********************************************************************
717 * Handle begin and end of components
718 **********************************************************************/
719 /* If the property name is BEGIN or END, we are actually
720 starting or ending a new component */
721
722
723 if(strcasecmp(str,"BEGIN") == 0){
724 icalcomponent *c;
725 icalcomponent_kind comp_kind;
726
727 icalmemory_free_buffer(str);
728 str = NULL;
729
730 parser->level++;
731 str = parser_get_next_value(end,&end, value_kind);
732
733 comp_kind = icalenum_string_to_component_kind(str);
734
735 c = icalcomponent_new(comp_kind);
736
737 if (c == 0){
738 c = icalcomponent_new(ICAL_XLICINVALID_COMPONENT);
739 insert_error(c,str,"Parse error in component name",
740 ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
741 }
742
743 pvl_push(parser->components,c);
744
745 parser->state = ICALPARSER_BEGIN_COMP;
746
747 icalmemory_free_buffer(str);
748 str = NULL;
749 return 0;
750
751 } else if (strcasecmp(str,"END") == 0 ) {
752 icalcomponent* tail;
753
754 icalmemory_free_buffer(str);
755 str = NULL;
756 parser->level--;
757
758 str = parser_get_next_value(end,&end, value_kind);
759
760 /* Pop last component off of list and add it to the second-to-last*/
761 parser->root_component = pvl_pop(parser->components);
762
763 tail = pvl_data(pvl_tail(parser->components));
764
765 if(tail != 0){
766 icalcomponent_add_component(tail,parser->root_component);
767 }
768
769 tail = 0;
770 icalmemory_free_buffer(str);
771 str = NULL;
772
773 /* Return the component if we are back to the 0th level */
774 if (parser->level == 0){
775 icalcomponent *rtrn;
776
777 if(pvl_count(parser->components) != 0){
778 /* There are still components on the stack -- this means
779 that one of them did not have a proper "END" */
780 pvl_push(parser->components,parser->root_component);
781 icalparser_clean(parser); /* may reset parser->root_component*/
782 }
783
784 assert(pvl_count(parser->components) == 0);
785
786 parser->state = ICALPARSER_SUCCESS;
787 rtrn = parser->root_component;
788 parser->root_component = 0;
789 return rtrn;
790
791 } else {
792 parser->state = ICALPARSER_END_COMP;
793 return 0;
794 }
795 }
796
797
798 /* There is no point in continuing if we have not seen a
799 component yet */
800
801 if(pvl_data(pvl_tail(parser->components)) == 0){
802 parser->state = ICALPARSER_ERROR;
803 icalmemory_free_buffer(str);
804 str = NULL;
805 return 0;
806 }
807
808
809 /**********************************************************************
810 * Handle property names
811 **********************************************************************/
812
813 /* At this point, the property name really is a property name,
814 (Not a component name) so make a new property and add it to
815 the component */
816
817
818 prop_kind = icalproperty_string_to_kind(str);
819
820 prop = icalproperty_new(prop_kind);
821
822 if (prop != 0){
823 icalcomponent *tail = pvl_data(pvl_tail(parser->components));
824
825 if(prop_kind==ICAL_X_PROPERTY){
826 icalproperty_set_x_name(prop,str);
827 }
828
829 icalcomponent_add_property(tail, prop);
830
831 /* Set the value kind for the default for this type of
832 property. This may be re-set by a VALUE parameter */
833 value_kind = icalproperty_kind_to_value_kind(icalproperty_isa(prop));
834
835 } else {
836 icalcomponent* tail = pvl_data(pvl_tail(parser->components));
837
838 insert_error(tail,str,"Parse error in property name",
839 ICAL_XLICERRORTYPE_PROPERTYPARSEERROR);
840
841 tail = 0;
842 parser->state = ICALPARSER_ERROR;
843 icalmemory_free_buffer(str);
844 str = NULL;
845 return 0;
846 }
847
848 icalmemory_free_buffer(str);
849 str = NULL;
850
851 /**********************************************************************
852 * Handle parameter values
853 **********************************************************************/
854
855 /* Now, add any parameters to the last property */
856
857 while(1) {
858
859 if (*(end-1) == ':'){
860 /* if the last separator was a ":" and the value is a
861 URL, icalparser_get_next_parameter will find the
862 ':' in the URL, so better break now. */
863 break;
864 }
865
866 str = parser_get_next_parameter(end,&end);
867 strstriplt(str);
868 if (str != 0){
869 char* name = 0;
870 char* pvalue = 0;
871 char *buf_value = NULL;
872
873 icalparameter *param = 0;
874 icalparameter_kind kind;
875 icalcomponent *tail = pvl_data(pvl_tail(parser->components));
876
877 name = parser_get_param_name(str,&pvalue,&buf_value);
878
879 if (name == 0){
880 /* 'tail' defined above */
881 insert_error(tail, str, "Cant parse parameter name",
882 ICAL_XLICERRORTYPE_PARAMETERNAMEPARSEERROR);
883 tail = 0;
884 break;
885 }
886
887 kind = icalparameter_string_to_kind(name);
888
889 if(kind == ICAL_X_PARAMETER){
890 param = icalparameter_new(ICAL_X_PARAMETER);
891 if(param != 0){
892 icalparameter_set_xname(param,name);
893 icalparameter_set_xvalue(param,pvalue);
894 }
895 icalmemory_free_buffer(buf_value);
896 buf_value = NULL;
897 } else if (kind == ICAL_IANA_PARAMETER){
898 ical_unknown_token_handling tokHandlingSetting =
899 ical_get_unknown_token_handling_setting();
900 if (tokHandlingSetting == ICAL_DISCARD_TOKEN)
901 continue;
902 param = icalparameter_new(ICAL_IANA_PARAMETER);
903
904 if(param != 0){
905 icalparameter_set_xname(param,name);
906 icalparameter_set_xvalue(param,pvalue);
907 }
908 icalmemory_free_buffer(buf_value);
909 buf_value = NULL;
910
911 } else if (kind != ICAL_NO_PARAMETER){
912 param = icalparameter_new_from_value_string(kind,pvalue);
913
914 icalmemory_free_buffer(buf_value);
915 buf_value = NULL;
916
917 } else {
918 /* Error. Failed to parse the parameter*/
919 /* 'tail' defined above */
920
921 /* Change for mozilla */
922 /* have the option of being flexible towards unsupported parameters */
923 #ifndef ICAL_ERRORS_ARE_FATAL
924 continue;
925 #endif
926
927 insert_error(tail, str, "Cant parse parameter name",
928 ICAL_XLICERRORTYPE_PARAMETERNAMEPARSEERROR);
929 tail = 0;
930 parser->state = ICALPARSER_ERROR;
931 /* if (pvalue) {
932 free(pvalue);
933 pvalue = 0;
934 } */
935 if (name) {
936 free(name);
937 name = 0;
938 }
939 return 0;
940 }
941
942 /* if (pvalue) {
943 free(pvalue);
944 pvalue = 0;
945 } */
946 if (name) {
947 free(name);
948 name = 0;
949 }
950
951 if (param == 0){
952 /* 'tail' defined above */
953 insert_error(tail,str,"Cant parse parameter value",
954 ICAL_XLICERRORTYPE_PARAMETERVALUEPARSEERROR);
955
956 tail = 0;
957 parser->state = ICALPARSER_ERROR;
958
959 icalmemory_free_buffer(buf_value);
960 buf_value = NULL;
961 icalmemory_free_buffer(name);
962 name = NULL;
963 icalmemory_free_buffer(str);
964 str = NULL;
965
966 continue;
967 }
968
969 /* If it is a VALUE parameter, set the kind of value*/
970 if (icalparameter_isa(param)==ICAL_VALUE_PARAMETER){
971 const char unknown_type[] =
972 "Got a VALUE parameter with an unknown type";
973 const char illegal_type[] =
974 "Got a VALUE parameter with an illegal type for property";
975 const char *value_err = NULL;
976
977
978 value_kind = (icalvalue_kind)
979 icalparameter_value_to_value_kind(
980 icalparameter_get_value(param)
981 );
982
983 if (value_kind == ICAL_NO_VALUE){
984
985 /* Ooops, could not parse the value of the
986 parameter ( it was not one of the defined
987 values ), so reset the value_kind */
988
989 value_err = unknown_type;
990 }
991 else if (value_kind !=
992 icalproperty_kind_to_value_kind(icalproperty_isa(prop))) {
993 /* VALUE parameter type does not match default type
994 for this property (check for allowed alternate types) */
995
996 switch (prop_kind) {
997 case ICAL_ATTACH_PROPERTY:
998 // case ICAL_IMAGE_PROPERTY:
999 /* Accept BINARY */
1000 if (value_kind != ICAL_BINARY_VALUE)
1001 value_err = illegal_type;
1002 break;
1003
1004 case ICAL_DTEND_PROPERTY:
1005 case ICAL_DUE_PROPERTY:
1006 case ICAL_DTSTART_PROPERTY:
1007 case ICAL_EXDATE_PROPERTY:
1008 case ICAL_RECURRENCEID_PROPERTY:
1009 /* Accept DATE */
1010 if (value_kind != ICAL_DATE_VALUE)
1011 value_err = illegal_type;
1012 break;
1013
1014 case ICAL_GEO_PROPERTY:
1015 /* Accept FLOAT (but change to GEO) */
1016 if (value_kind != ICAL_FLOAT_VALUE)
1017 value_err = illegal_type;
1018 else value_kind = ICAL_GEO_VALUE;
1019 break;
1020
1021 case ICAL_RDATE_PROPERTY:
1022 /* Accept DATE-TIME, DATE or PERIOD */
1023 if (value_kind != ICAL_DATETIME_VALUE &&
1024 value_kind != ICAL_DATE_VALUE &&
1025 value_kind != ICAL_PERIOD_VALUE)
1026 value_err = illegal_type;
1027 break;
1028
1029 case ICAL_TRIGGER_PROPERTY:
1030 /* Accept DATE-TIME */
1031 if (value_kind != ICAL_DATETIME_VALUE &&
1032 value_kind != ICAL_DURATION_VALUE)
1033 value_err = illegal_type;
1034 break;
1035
1036 case ICAL_X_PROPERTY:
1037 /* Accept ANY value type */
1038 break;
1039
1040 default:
1041 /* ONLY default type is allowed */
1042 value_err = illegal_type;
1043 break;
1044 }
1045 }
1046
1047 if (value_err != NULL) {
1048 /* Ooops, unknown/illegal VALUE parameter,
1049 so reset the value_kind */
1050
1051 insert_error(tail, str, value_err,
1052 ICAL_XLICERRORTYPE_PARAMETERVALUEPARSEERROR);
1053
1054 value_kind =
1055 icalproperty_kind_to_value_kind(
1056 icalproperty_isa(prop));
1057
1058 icalparameter_free(param);
1059 tail = 0;
1060 parser->state = ICALPARSER_ERROR;
1061
1062 icalmemory_free_buffer(name);
1063 name = NULL;
1064 icalmemory_free_buffer(str);
1065 str = NULL;
1066 continue;
1067 }
1068 }
1069 icalmemory_free_buffer(name);
1070 name = NULL;
1071
1072 /* Everything is OK, so add the parameter */
1073 icalproperty_add_parameter(prop,param);
1074 tail = 0;
1075 icalmemory_free_buffer(str);
1076 str = NULL;
1077
1078 } else { /* if ( str != 0) */
1079 /* If we did not get a param string, go on to looking for a value */
1080 if (str != NULL) {
1081 icalmemory_free_buffer(str);
1082 str = NULL;
1083 }
1084 break;
1085 } /* if ( str != 0) */
1086
1087 } /* while(1) */
1088
1089 /**********************************************************************
1090 * Handle values
1091 **********************************************************************/
1092
1093 /* Look for values. If there are ',' characters in the values,
1094 then there are multiple values, so clone the current
1095 parameter and add one part of the value to each clone */
1096
1097 vcount=0;
1098 while(1) {
1099 /* Only some properies can have multiple values. This list was taken
1100 from rfc2445. Also added the x-properties, because the spec actually
1101 says that commas should be escaped. For x-properties, other apps may
1102 depend on that behaviour
1103 */
1104 switch (prop_kind) {
1105 case ICAL_X_PROPERTY:
1106 case ICAL_CATEGORIES_PROPERTY:
1107 case ICAL_RESOURCES_PROPERTY:
1108 /* Referring to RFC 2445, section 4.8.5.3 and section 4.8.5.1:
1109 RDATE and EXDATE can specify a list of dates/date-times/periods.
1110 */
1111 case ICAL_RDATE_PROPERTY:
1112 case ICAL_EXDATE_PROPERTY:
1113 /* Referring to RFC 2445, section 4.8.2.6 Free/Busy Time:
1114 The "FREEBUSY" property can specify more than one value, separated by
1115 the COMMA character (US-ASCII decimal 44).
1116 */
1117 case ICAL_FREEBUSY_PROPERTY:
1118 str = parser_get_next_value(end,&end, value_kind);
1119 strstriplt (str);
1120 break;
1121 default:
1122 str = icalparser_get_value(end, &end, value_kind);
1123 strstriplt (str);
1124 break;
1125 }
1126
1127 if (str != 0){
1128
1129 if (vcount > 0){
1130 /* Actually, only clone after the second value */
1131 icalproperty* clone = icalproperty_new_clone(prop);
1132 icalcomponent* tail = pvl_data(pvl_tail(parser->components));
1133
1134 icalcomponent_add_property(tail, clone);
1135 prop = clone;
1136 tail = 0;
1137 }
1138
1139 value = icalvalue_new_from_string(value_kind, str);
1140
1141 /* Don't add properties without value */
1142 if (value == 0){
1143 icalcomponent* tail = pvl_data(pvl_tail(parser->components));
1144
1145 /* Remove the empty property */
1146 icalcomponent_remove_property(tail,prop);
1147 icalproperty_free(prop);
1148 prop = NULL;
1149 } else {
1150 vcount++;
1151 icalproperty_set_value(prop, value);
1152 }
1153 icalmemory_free_buffer(str);
1154 str = NULL;
1155
1156 } else {
1157 /* Don't replace empty properties with an error.
1158 Set an empty length string (not null) as the value instead */
1159 if (vcount == 0) {
1160 icalproperty_set_value(prop, icalvalue_new(ICAL_NO_VALUE));
1161 }
1162
1163 break;
1164 }
1165 }
1166
1167 /****************************************************************
1168 * End of component parsing.
1169 *****************************************************************/
1170
1171 if (pvl_data(pvl_tail(parser->components)) == 0 &&
1172 parser->level == 0){
1173 /* HACK. Does this clause ever get executed? */
1174 parser->state = ICALPARSER_SUCCESS;
1175 assert(0);
1176 return parser->root_component;
1177 } else {
1178 parser->state = ICALPARSER_IN_PROGRESS;
1179 return 0;
1180 }
1181
1182 }
1183
icalparser_get_state(icalparser * parser)1184 icalparser_state icalparser_get_state(icalparser* parser)
1185 {
1186 return parser->state;
1187
1188 }
1189
icalparser_clean(icalparser * parser)1190 icalcomponent* icalparser_clean(icalparser* parser)
1191 {
1192 icalcomponent *tail;
1193
1194 icalerror_check_arg_rz((parser != 0 ),"parser");
1195
1196 /* We won't get a clean exit if some components did not have an
1197 "END" tag. Clear off any component that may be left in the list */
1198
1199 while((tail=pvl_data(pvl_tail(parser->components))) != 0){
1200
1201 insert_error(tail," ",
1202 "Missing END tag for this component. Closing component at end of input.",
1203 ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
1204
1205
1206 parser->root_component = pvl_pop(parser->components);
1207 tail=pvl_data(pvl_tail(parser->components));
1208
1209 if(tail != 0 && parser->root_component != NULL){
1210 if(icalcomponent_get_parent(parser->root_component)!=0){
1211 icalerror_warn("icalparser_clean is trying to attach a component for the second time");
1212 } else {
1213 icalcomponent_add_component(tail,parser->root_component);
1214 }
1215 }
1216
1217 }
1218
1219 return parser->root_component;
1220
1221 }
1222
1223 struct slg_data {
1224 const char* pos;
1225 const char* str;
1226 };
1227
1228
icalparser_string_line_generator(char * out,size_t buf_size,void * d)1229 char* icalparser_string_line_generator(char *out, size_t buf_size, void *d)
1230 {
1231 char *n;
1232 size_t size;
1233 struct slg_data* data = (struct slg_data*)d;
1234
1235 if(data->pos==0){
1236 data->pos=data->str;
1237 }
1238
1239 /* If the pointer is at the end of the string, we are done */
1240 if (*(data->pos)==0){
1241 return 0;
1242 }
1243
1244 n = strchr(data->pos,'\n');
1245
1246 if (n == 0){
1247 size = strlen(data->pos);
1248 } else {
1249 n++; /* include newline in output */
1250 size = (n-data->pos);
1251 }
1252
1253 if (size > buf_size-1){
1254 size = buf_size-1;
1255 }
1256
1257
1258 strncpy(out,data->pos,size);
1259
1260 *(out+size) = '\0';
1261
1262 data->pos += size;
1263
1264 return out;
1265 }
1266
icalparser_parse_string(const char * str)1267 icalcomponent* icalparser_parse_string(const char* str)
1268 {
1269 icalcomponent *c;
1270 struct slg_data d;
1271 icalparser *p;
1272
1273 icalerrorstate es = icalerror_get_error_state(ICAL_MALFORMEDDATA_ERROR);
1274
1275 d.pos = 0;
1276 d.str = str;
1277
1278 p = icalparser_new();
1279 icalparser_set_gen_data(p,&d);
1280
1281 icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,ICAL_ERROR_NONFATAL);
1282
1283 c = icalparser_parse(p,icalparser_string_line_generator);
1284
1285 icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,es);
1286
1287 icalparser_free(p);
1288
1289 return c;
1290
1291 }
1292