Imported Upstream version 0.48
[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, char **buf_value);
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, char **buf)
265 {
266     char* next; 
267     char *str;
268
269     next = parser_get_next_char('=',line,1);
270
271     *buf = 0;
272     if (next == 0) {
273         return 0;
274     }
275
276     str = make_segment(line,next);
277     *end = next+1;
278     if (**end == '"') {
279         *end = *end+1;
280             next = parser_get_next_char('"',*end,0);
281             if (next == 0) {
282                         free(str);
283                     return 0;
284             }
285
286             *buf = *end = make_segment(*end,next);
287     }
288
289     return str;
290 }
291
292 #if 0
293 static
294 char* parser_get_next_paramvalue(char* line, char **end)
295 {
296     char* next; 
297     char *str;
298
299     next = parser_get_next_char(',',line,1);
300
301     if (next == 0){
302         next = (char*)(size_t)line+(size_t)strlen(line);\
303     }
304
305     if (next == line){
306         return 0;
307     } else {
308         str = make_segment(line,next);
309         *end = next+1;
310         return str;
311     }
312 }
313 #endif
314
315 char* icalparser_get_value(char* line, char **end, icalvalue_kind kind)
316 {
317     char *str;
318     size_t length = strlen(line);
319
320     if (length == 0){
321         return 0;
322     } 
323
324     *end = line+length;
325     str = make_segment(line, *end);
326
327     return str;
328 }
329
330 /**
331    A property may have multiple values, if the values are seperated by
332    commas in the content line. This routine will look for the next
333    comma after line and will set the next place to start searching in
334    end. */
335
336 static 
337 char* parser_get_next_value(char* line, char **end, icalvalue_kind kind)
338 {
339     
340     char* next;
341     char *p;
342     char *str;
343     size_t length = strlen(line);
344
345     p = line;
346     while(1){
347
348         next = parser_get_next_char(',',p,1);
349
350         /* Unforunately, RFC2445 says that for the RECUR value, COMMA
351            can both separate digits in a list, and it can separate
352            multiple recurrence specifications. This is not a friendly
353            part of the spec. This weirdness tries to
354            distinguish the two uses. it is probably a HACK*/
355       
356         if( kind == ICAL_RECUR_VALUE ) {
357             if ( next != 0 &&
358                  (*end+length) > next+5 &&
359                  strncmp(next,"FREQ",4) == 0
360                 ) {
361                 /* The COMMA was followed by 'FREQ', is it a real separator*/
362                 /* Fall through */
363             } else if (next != 0){
364                 /* Not real, get the next COMMA */
365                 p = next+1;
366                 next = 0;
367                 continue;
368             } 
369         }
370         /* ignore all , for query value. select dtstart, dtend etc ... */
371         else if( kind == ICAL_QUERY_VALUE) {
372             if ( next != 0) {
373                 p = next+1;
374                 continue;
375             }
376             else
377                 break;
378         }
379
380         /* If the comma is preceded by a '\', then it is a literal and
381            not a value separator*/  
382       
383         if ( (next!=0 && *(next-1) == '\\') ||
384              (next!=0 && *(next-3) == '\\')
385             ) 
386             /*second clause for '/' is on prev line. HACK may be out of bounds */
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 
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  */
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
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
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
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
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
734         comp_kind = icalenum_string_to_component_kind(str);
735
736
737         if (comp_kind == ICAL_NO_COMPONENT){
738
739
740             c = icalcomponent_new(ICAL_XLICINVALID_COMPONENT);
741             insert_error(c,str,"Parse error in component name",
742                          ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
743         }
744
745         c  =  icalcomponent_new(comp_kind);
746
747         if (c == 0){
748             c = icalcomponent_new(ICAL_XLICINVALID_COMPONENT);
749             insert_error(c,str,"Parse error in component name",
750                          ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
751         }
752             
753         pvl_push(parser->components,c);
754
755         parser->state = ICALPARSER_BEGIN_COMP;
756
757         icalmemory_free_buffer(str);
758         str = NULL;
759         return 0;
760
761     } else if (strcasecmp(str,"END") == 0 ) {
762         icalcomponent* tail;
763
764         icalmemory_free_buffer(str);
765         str = NULL;
766         parser->level--;
767
768         str = parser_get_next_value(end,&end, value_kind);
769
770         /* Pop last component off of list and add it to the second-to-last*/
771         parser->root_component = pvl_pop(parser->components);
772
773         tail = pvl_data(pvl_tail(parser->components));
774
775         if(tail != 0){
776             icalcomponent_add_component(tail,parser->root_component);
777         } 
778
779         tail = 0;
780         icalmemory_free_buffer(str);
781         str = NULL;
782
783         /* Return the component if we are back to the 0th level */
784         if (parser->level == 0){
785             icalcomponent *rtrn; 
786
787             if(pvl_count(parser->components) != 0){
788             /* There are still components on the stack -- this means
789                that one of them did not have a proper "END" */
790                 pvl_push(parser->components,parser->root_component);
791                 icalparser_clean(parser); /* may reset parser->root_component*/
792             }
793
794             assert(pvl_count(parser->components) == 0);
795
796             parser->state = ICALPARSER_SUCCESS;
797             rtrn = parser->root_component;
798             parser->root_component = 0;
799             return rtrn;
800
801         } else {
802             parser->state = ICALPARSER_END_COMP;
803             return 0;
804         }
805     }
806
807
808     /* There is no point in continuing if we have not seen a
809        component yet */
810
811     if(pvl_data(pvl_tail(parser->components)) == 0){
812         parser->state = ICALPARSER_ERROR;
813         icalmemory_free_buffer(str);
814         str = NULL;
815         return 0;
816     }
817
818
819     /**********************************************************************
820      * Handle property names
821      **********************************************************************/
822                                                                        
823     /* At this point, the property name really is a property name,
824        (Not a component name) so make a new property and add it to
825        the component */
826
827     
828     prop_kind = icalproperty_string_to_kind(str);
829
830     prop = icalproperty_new(prop_kind);
831
832     if (prop != 0){
833         icalcomponent *tail = pvl_data(pvl_tail(parser->components));
834
835         if(prop_kind==ICAL_X_PROPERTY){
836             icalproperty_set_x_name(prop,str);
837         }
838
839         icalcomponent_add_property(tail, prop);
840
841         /* Set the value kind for the default for this type of
842            property. This may be re-set by a VALUE parameter */
843         value_kind = icalproperty_kind_to_value_kind(icalproperty_isa(prop));
844
845     } else {
846         icalcomponent* tail = pvl_data(pvl_tail(parser->components));
847
848         insert_error(tail,str,"Parse error in property name",
849                      ICAL_XLICERRORTYPE_PROPERTYPARSEERROR);
850             
851         tail = 0;
852         parser->state = ICALPARSER_ERROR;
853         icalmemory_free_buffer(str);
854         str = NULL;
855         return 0;
856     }
857
858     icalmemory_free_buffer(str);
859     str = NULL;
860
861     /**********************************************************************
862      * Handle parameter values
863      **********************************************************************/                                                                   
864
865     /* Now, add any parameters to the last property */
866
867     while(1) {
868
869         if (*(end-1) == ':'){
870             /* if the last separator was a ":" and the value is a
871                URL, icalparser_get_next_parameter will find the
872                ':' in the URL, so better break now. */
873             break;
874         }
875
876         str = parser_get_next_parameter(end,&end);
877         strstriplt(str);
878         if (str != 0){
879             char* name = 0;
880             char* pvalue = 0;
881             char *buf_value = NULL;
882         
883             icalparameter *param = 0;
884             icalparameter_kind kind;
885             icalcomponent *tail = pvl_data(pvl_tail(parser->components));
886
887             name = parser_get_param_name(str,&pvalue,&buf_value);
888
889             if (name == 0){
890                     /* 'tail' defined above */
891                     insert_error(tail, str, "Cant parse parameter name",
892                                      ICAL_XLICERRORTYPE_PARAMETERNAMEPARSEERROR);
893                         tail = 0;
894                         break;
895             }
896
897             kind = icalparameter_string_to_kind(name);
898
899             if(kind == ICAL_X_PARAMETER){
900                 param = icalparameter_new(ICAL_X_PARAMETER);
901             if(param != 0){
902                 icalparameter_set_xname(param,name);
903                 icalparameter_set_xvalue(param,pvalue);
904             }
905             icalmemory_free_buffer(buf_value);
906             buf_value = NULL;
907             } else if (kind == ICAL_IANA_PARAMETER){
908             ical_unknown_token_handling tokHandlingSetting = 
909                 ical_get_unknown_token_handling_setting();
910             if (tokHandlingSetting == ICAL_DISCARD_TOKEN)
911                 continue;
912             param = icalparameter_new(ICAL_IANA_PARAMETER);
913             
914             if(param != 0){
915                 icalparameter_set_xname(param,name);
916                 icalparameter_set_xvalue(param,pvalue);
917             }
918             icalmemory_free_buffer(buf_value);
919             buf_value = NULL;
920
921             } else if (kind != ICAL_NO_PARAMETER){
922                         param = icalparameter_new_from_value_string(kind,pvalue);
923
924                         icalmemory_free_buffer(buf_value);
925                         buf_value = NULL;
926
927             } else {
928                     /* Error. Failed to parse the parameter*/
929                     /* 'tail' defined above */
930
931                         icalmemory_free_buffer(buf_value);
932                         buf_value = NULL;
933
934             /* Change for mozilla */
935             /* have the option of being flexible towards unsupported parameters */
936 #if ICAL_ERRORS_ARE_FATAL == 1
937                     insert_error(tail, str, "Cant parse parameter name",
938                                      ICAL_XLICERRORTYPE_PARAMETERNAMEPARSEERROR);
939                         tail = 0;
940                         parser->state = ICALPARSER_ERROR;
941                         /* if (pvalue) {
942                                free(pvalue);
943                                pvalue = 0;
944                            }
945                         */
946                     if (name) {
947                             free(name);
948                             name = 0;
949                     }
950                         icalmemory_free_buffer(str);
951                         str = NULL;
952                     return 0;
953 #else
954                     if (name) {
955                             free(name);
956                             name = 0;
957                     }
958                         icalmemory_free_buffer(str);
959                         str = NULL;
960                     continue;
961 #endif
962             }
963
964             /* if (pvalue) {
965                        free(pvalue);
966                        pvalue = 0;
967                }
968                 */
969             if (name) {
970                     free(name);
971                     name = 0;
972             }
973
974             if (param == 0){
975                     /* 'tail' defined above */
976                     insert_error(tail,str,"Cant parse parameter value",
977                                      ICAL_XLICERRORTYPE_PARAMETERVALUEPARSEERROR);
978                     
979                     tail = 0;
980                     parser->state = ICALPARSER_ERROR;
981                 
982                         icalmemory_free_buffer(buf_value);
983                         buf_value = NULL;
984                         icalmemory_free_buffer(name);
985                         name = NULL;
986                         icalmemory_free_buffer(str);
987                         str = NULL;
988                 
989                         continue;
990             }
991
992             /* If it is a VALUE parameter, set the kind of value*/
993             if (icalparameter_isa(param)==ICAL_VALUE_PARAMETER){
994
995                 value_kind = (icalvalue_kind)
996                     icalparameter_value_to_value_kind(
997                                 icalparameter_get_value(param)
998                                 );
999
1000                 if (value_kind == ICAL_NO_VALUE){
1001
1002                     /* Ooops, could not parse the value of the
1003                        parameter ( it was not one of the defined
1004                        values ), so reset the value_kind */
1005                         
1006                     insert_error(
1007                         tail, str, 
1008                         "Got a VALUE parameter with an unknown type",
1009                         ICAL_XLICERRORTYPE_PARAMETERVALUEPARSEERROR);
1010                         
1011                     value_kind = 
1012                         icalproperty_kind_to_value_kind(
1013                             icalproperty_isa(prop));
1014                         
1015                     icalparameter_free(param);
1016                     tail = 0;
1017                     parser->state = ICALPARSER_ERROR;
1018
1019                         icalmemory_free_buffer(name);
1020                         name = NULL;
1021                         icalmemory_free_buffer(str);
1022                         str = NULL;
1023                     continue;
1024                 } 
1025             }
1026                 icalmemory_free_buffer(name);
1027                 name = NULL;
1028
1029             /* Everything is OK, so add the parameter */
1030             icalproperty_add_parameter(prop,param);
1031             tail = 0;
1032             icalmemory_free_buffer(str);
1033             str = NULL;
1034
1035         } else { /* if ( str != 0)  */
1036             /* If we did not get a param string, go on to looking for a value */
1037                 if (str != NULL) {
1038                         icalmemory_free_buffer(str);
1039                         str = NULL;
1040                 }
1041             break;
1042         } /* if ( str != 0)  */
1043
1044     } /* while(1) */        
1045         
1046     /**********************************************************************
1047      * Handle values
1048      **********************************************************************/                                                                   
1049
1050     /* Look for values. If there are ',' characters in the values,
1051        then there are multiple values, so clone the current
1052        parameter and add one part of the value to each clone */
1053
1054     vcount=0;
1055     while(1) {
1056         /* Only some properies can have multiple values. This list was taken
1057            from rfc2445. Also added the x-properties, because the spec actually
1058            says that commas should be escaped. For x-properties, other apps may
1059            depend on that behaviour
1060         */
1061         switch (prop_kind) {
1062             case ICAL_X_PROPERTY:
1063             case ICAL_CATEGORIES_PROPERTY:
1064             case ICAL_RESOURCES_PROPERTY:
1065             /* Referring to RFC 2445, section 4.8.5.3 and section 4.8.5.1:
1066                RDATE and EXDATE can specify a list of dates/date-times/periods.
1067             */
1068             case ICAL_RDATE_PROPERTY:
1069             case ICAL_EXDATE_PROPERTY:
1070             /* Referring to RFC 2445, section 4.8.2.6 Free/Busy Time:
1071                The "FREEBUSY" property can specify more than one value, separated by
1072                the COMMA character (US-ASCII decimal 44).
1073             */
1074             case ICAL_FREEBUSY_PROPERTY:
1075                  str = parser_get_next_value(end,&end, value_kind);
1076                  strstriplt (str);
1077                  break;
1078             default:
1079                  str = icalparser_get_value(end, &end, value_kind);
1080                  strstriplt (str);
1081                  break;
1082         }
1083
1084         if (str != 0){
1085                 
1086             if (vcount > 0){
1087                 /* Actually, only clone after the second value */
1088                 icalproperty* clone = icalproperty_new_clone(prop);
1089                 icalcomponent* tail = pvl_data(pvl_tail(parser->components));
1090                     
1091                 icalcomponent_add_property(tail, clone);
1092                 prop = clone;               
1093                 tail = 0;
1094             }
1095
1096                 /* If this is a URI value for an ATTACH property, then change
1097                    the value to an ATTACH kind as well.
1098                    Now we can support ATTACH;TYPE=URI:http://my.fav.url
1099                 */
1100                 if(value_kind == ICAL_URI_VALUE && prop_kind == ICAL_ATTACH_PROPERTY){
1101                                 value_kind = ICAL_ATTACH_VALUE;
1102                 }
1103             value = icalvalue_new_from_string(value_kind, str);
1104                 
1105             /* Don't add properties without value */
1106             if (value == 0){
1107                 char temp[200]; /* HACK */
1108
1109                 icalproperty_kind prop_kind = icalproperty_isa(prop);
1110                 icalcomponent* tail = pvl_data(pvl_tail(parser->components));
1111
1112                 snprintf(temp,sizeof(temp),"Can't parse as %s value in %s property. Removing entire property",
1113                         icalvalue_kind_to_string(value_kind),
1114                         icalproperty_kind_to_string(prop_kind));
1115
1116                 insert_error(tail, str, temp,
1117                              ICAL_XLICERRORTYPE_VALUEPARSEERROR);
1118
1119                 /* Remove the troublesome property */
1120                 icalcomponent_remove_property(tail,prop);
1121                 icalproperty_free(prop);
1122                 prop = 0;
1123                 tail = 0;
1124                 parser->state = ICALPARSER_ERROR;
1125         
1126                 icalmemory_free_buffer(str);
1127                 str = NULL;
1128                 return 0;
1129                     
1130             } else {
1131                 vcount++;
1132                 icalproperty_set_value(prop, value);
1133             }
1134             icalmemory_free_buffer(str);
1135             str = NULL;
1136
1137         } else {
1138 #if ICAL_ALLOW_EMPTY_PROPERTIES
1139         /* Don't replace empty properties with an error */
1140         break;
1141 #else
1142             if (vcount == 0){
1143                 char temp[200]; /* HACK */
1144                 
1145                 icalproperty_kind prop_kind = icalproperty_isa(prop);
1146                 icalcomponent *tail = pvl_data(pvl_tail(parser->components));
1147                 
1148                 snprintf(temp,sizeof(temp),"No value for %s property. Removing entire property",
1149                         icalproperty_kind_to_string(prop_kind));
1150
1151                 insert_error(tail, str, temp,
1152                              ICAL_XLICERRORTYPE_VALUEPARSEERROR);
1153
1154                 /* Remove the troublesome property */
1155                 icalcomponent_remove_property(tail,prop);
1156                 icalproperty_free(prop);
1157                 prop = 0;
1158                 tail = 0;
1159                 parser->state = ICALPARSER_ERROR;
1160                 return 0;
1161             } else {
1162
1163                 break;
1164             }
1165 #endif
1166         }
1167     }
1168         
1169     /****************************************************************
1170      * End of component parsing. 
1171      *****************************************************************/
1172
1173     if (pvl_data(pvl_tail(parser->components)) == 0 &&
1174         parser->level == 0){
1175         /* HACK. Does this clause ever get executed? */
1176         parser->state = ICALPARSER_SUCCESS;
1177         assert(0);
1178         return parser->root_component;
1179     } else {
1180         parser->state = ICALPARSER_IN_PROGRESS;
1181         return 0;
1182     }
1183
1184 }
1185
1186 icalparser_state icalparser_get_state(icalparser* parser)
1187 {
1188     return parser->state;
1189
1190 }
1191
1192 icalcomponent* icalparser_clean(icalparser* parser)
1193 {
1194     icalcomponent *tail; 
1195
1196     icalerror_check_arg_rz((parser != 0 ),"parser");
1197
1198     /* We won't get a clean exit if some components did not have an
1199        "END" tag. Clear off any component that may be left in the list */
1200     
1201     while((tail=pvl_data(pvl_tail(parser->components))) != 0){
1202
1203         insert_error(tail," ",
1204                      "Missing END tag for this component. Closing component at end of input.",
1205                      ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
1206         
1207
1208         parser->root_component = pvl_pop(parser->components);
1209         tail=pvl_data(pvl_tail(parser->components));
1210
1211         if(tail != 0 && parser->root_component != NULL){
1212             if(icalcomponent_get_parent(parser->root_component)!=0){
1213                 icalerror_warn("icalparser_clean is trying to attach a component for the second time");
1214             } else {
1215                 icalcomponent_add_component(tail,parser->root_component);
1216             }
1217         }
1218             
1219     }
1220
1221     return parser->root_component;
1222
1223 }
1224
1225 struct slg_data {
1226         const char* pos;
1227         const char* str;
1228 };
1229
1230
1231 char* icalparser_string_line_generator(char *out, size_t buf_size, void *d)
1232 {
1233     char *n;
1234     size_t size;
1235     struct slg_data* data = (struct slg_data*)d;
1236
1237     if(data->pos==0){
1238         data->pos=data->str;
1239     }
1240
1241     /* If the pointer is at the end of the string, we are done */
1242     if (*(data->pos)==0){
1243         return 0;
1244     }
1245
1246     n = strchr(data->pos,'\n');
1247     
1248     if (n == 0){
1249         size = strlen(data->pos);
1250     } else {
1251         n++; /* include newline in output */
1252         size = (n-data->pos);   
1253     }
1254
1255     if (size > buf_size-1){
1256         size = buf_size-1;
1257     }
1258     
1259
1260     strncpy(out,data->pos,size);
1261     
1262     *(out+size) = '\0';
1263     
1264     data->pos += size;
1265
1266     return out;    
1267 }
1268
1269 icalcomponent* icalparser_parse_string(const char* str)
1270 {
1271     icalcomponent *c;
1272     struct slg_data d;
1273     icalparser *p;
1274
1275     icalerrorstate es = icalerror_get_error_state(ICAL_MALFORMEDDATA_ERROR);
1276
1277     d.pos = 0;
1278     d.str = str;
1279
1280     p = icalparser_new();
1281     icalparser_set_gen_data(p,&d);
1282
1283     icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,ICAL_ERROR_NONFATAL);
1284
1285     c = icalparser_parse(p,icalparser_string_line_generator);
1286
1287     icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,es);
1288
1289     icalparser_free(p);
1290
1291     return c;
1292
1293 }