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