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