Back to index

lightning-sunbird  0.9+nobinonly
icalparser.c
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 4; c-basic-offset: 8; -*-
00002   ======================================================================
00003   FILE: icalparser.c
00004   CREATOR: eric 04 August 1999
00005   
00006   $Id: icalparser.c,v 1.1.1.2 2005/01/24 22:10:58 mvl%exedo.nl Exp $
00007   $Locker:  $
00008     
00009  The contents of this file are subject to the Mozilla Public License
00010  Version 1.0 (the "License"); you may not use this file except in
00011  compliance with the License. You may obtain a copy of the License at
00012  http://www.mozilla.org/MPL/
00013  
00014  Software distributed under the License is distributed on an "AS IS"
00015  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
00016  the License for the specific language governing rights and
00017  limitations under the License.
00018  
00019 
00020  This program is free software; you can redistribute it and/or modify
00021  it under the terms of either: 
00022 
00023     The LGPL as published by the Free Software Foundation, version
00024     2.1, available at: http://www.fsf.org/copyleft/lesser.html
00025 
00026   Or:
00027 
00028     The Mozilla Public License Version 1.0. You may obtain a copy of
00029     the License at http://www.mozilla.org/MPL/
00030 
00031   The Initial Developer of the Original Code is Eric Busboom
00032 
00033  (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org
00034  ======================================================================*/
00035 
00036 #ifdef HAVE_CONFIG_H
00037 #include "config.h"
00038 #endif
00039 
00040 
00041 #include "pvl.h"
00042 #include "icalerror.h"
00043 #include "icalvalue.h"
00044 #include "icalderivedparameter.h"
00045 #include "icalparameter.h"
00046 #include "icalproperty.h"
00047 #include "icalcomponent.h"
00048 
00049 #include <string.h> /* For strncpy & size_t */
00050 #include <stdio.h> /* For FILE and fgets and snprintf */
00051 #include <stdlib.h> /* for free */
00052 
00053 #include "icalmemory.h"
00054 #include "icalparser.h"
00055 
00056 #ifdef WIN32
00057 #define snprintf      _snprintf
00058 #define strcasecmp    stricmp
00059 #endif
00060 
00061 char* icalparser_get_next_char(char c, char *str, int qm);
00062 char* icalparser_get_next_parameter(char* line,char** end);
00063 char* icalparser_get_next_value(char* line, char **end, icalvalue_kind kind);
00064 char* icalparser_get_prop_name(char* line, char** end);
00065 char* icalparser_get_param_name_and_value(char* line, char **value);
00066 
00067 #define TMP_BUF_SIZE 80
00068 
00069 struct icalparser_impl 
00070 {
00071     int buffer_full; /* flag indicates that temp is smaller that 
00072                         data being read into it*/
00073     int continuation_line; /* last line read was a continuation line */
00074     size_t tmp_buf_size; 
00075     char temp[TMP_BUF_SIZE];
00076     icalcomponent *root_component;
00077     int version; 
00078     int level;
00079     int lineno;
00080     icalparser_state state;
00081     pvl_list components;
00082     
00083     void *line_gen_data;
00084 
00085 };
00086 
00087 
00088 icalparser* icalparser_new(void)
00089 {
00090     struct icalparser_impl* impl = 0;
00091     if ( ( impl = (struct icalparser_impl*)
00092           malloc(sizeof(struct icalparser_impl))) == 0) {
00093        icalerror_set_errno(ICAL_NEWFAILED_ERROR);
00094        return 0;
00095     }
00096     
00097     impl->buffer_full = 0;
00098     impl->continuation_line = 0;
00099     impl->tmp_buf_size = sizeof(impl->temp);
00100     memset(impl->temp, 0, sizeof(impl->temp));
00101     impl->root_component = 0;
00102     impl->version = 0;
00103     impl->level = 0;
00104     impl->lineno = 0;
00105     impl->state = ICALPARSER_SUCCESS;
00106     impl->components  = pvl_newlist();  
00107     impl->line_gen_data = 0;
00108 
00109     return (icalparser*)impl;
00110 }
00111 
00112 
00113 void icalparser_free(icalparser* parser)
00114 {
00115     icalcomponent *c;
00116 
00117     if (parser->root_component != 0){
00118        icalcomponent_free(parser->root_component);
00119     }
00120 
00121     while( (c=pvl_pop(parser->components)) != 0){
00122        icalcomponent_free(c);
00123     }
00124     
00125     pvl_free(parser->components);
00126     
00127     free(parser);
00128 }
00129 
00130 void icalparser_set_gen_data(icalparser* parser, void* data)
00131 {
00132               parser->line_gen_data  = data;
00133 }
00134 
00135 
00136 icalvalue* icalvalue_new_From_string_with_error(icalvalue_kind kind, 
00137                                                 char* str, 
00138                                                 icalproperty **error);
00139 
00140 
00141 
00142 char* icalparser_get_next_char(char c, char *str, int qm)
00143 {
00144     int quote_mode = 0;
00145     char* p;
00146 
00147     for(p=str; *p!=0; p++){
00148            if (qm == 1) {
00149                             if ( quote_mode == 0 && *p=='"' && *(p-1) != '\\' ){
00150                                           quote_mode =1;
00151                                           continue;
00152                             }
00153 
00154                             if ( quote_mode == 1 && *p=='"' && *(p-1) != '\\' ){
00155                                           quote_mode =0;
00156                                           continue;
00157                             }
00158            }
00159               
00160               if (quote_mode == 0 &&  *p== c  && *(p-1) != '\\' ){
00161                             return p;
00162               } 
00163 
00164     }
00165 
00166     return 0;
00167 }
00168 
00169 
00171 static char* make_segment(char* start, char* end)
00172 {
00173     char *buf;
00174     size_t size = (size_t)end - (size_t)start;
00175     
00176     buf = icalmemory_new_buffer(size+1);
00177     
00178 
00179     strncpy(buf,start,size);
00180     *(buf+size) = 0;
00181 
00182     return buf;
00183 }
00184 
00185 
00186 char* icalparser_get_prop_name(char* line, char** end)
00187 {
00188     char* p;
00189     char* v;
00190     char *str;
00191 
00192     p = icalparser_get_next_char(';',line,1); 
00193     v = icalparser_get_next_char(':',line,1); 
00194     if (p== 0 && v == 0) {
00195        return 0;
00196     }
00197 
00198     /* There is no ';' or, it is after the ';' that marks the beginning of
00199        the value */
00200     if (v!=0 && ( p == 0 || p > v)){
00201        str = make_segment(line,v);
00202        *end = v+1;
00203     } else {
00204        str = make_segment(line,p);
00205        *end = p+1;
00206     }
00207 
00208     return str;
00209 }
00210 
00211 
00212 char* icalparser_get_param_name_and_value(char* line, char **value)
00213 {
00214     char* next; 
00215     char *str;
00216     char **end = value;
00217 
00218     *value = NULL;
00219     next = icalparser_get_next_char('=',line,1);
00220 
00221     if (next == 0) {
00222        return 0;
00223     }
00224 
00225     str = make_segment(line,next);
00226     *end = next+1;
00227     if (**end == '"') {
00228         *end = *end+1;
00229            next = icalparser_get_next_char('"',*end,0);
00230            if (next == 0) {
00231                   return 0;
00232            }
00233 
00234            *end = make_segment(*end,next);
00235     }
00236     else
00237        *end = make_segment(*end, *end + strlen(*end));
00238 
00239     return str;
00240 }
00241 
00242 
00243 char* icalparser_get_next_paramvalue(char* line, char **end)
00244 {
00245     char* next; 
00246     char *str;
00247 
00248     next = icalparser_get_next_char(',',line,1);
00249 
00250     if (next == 0){
00251        next = (char*)(size_t)line+(size_t)strlen(line);\
00252     }
00253 
00254     if (next == line){
00255        return 0;
00256     } else {
00257        str = make_segment(line,next);
00258        *end = next+1;
00259        return str;
00260     }
00261 }
00262 
00263 char* icalparser_get_value(char* line, char **end, icalvalue_kind kind)
00264 {
00265     char *str;
00266     size_t length = strlen(line);
00267 
00268     if (length == 0){
00269         return 0;
00270     } 
00271 
00272     *end = line+length;
00273     str = make_segment(line, *end);
00274 
00275     return str;
00276 }
00277 
00284 char* icalparser_get_next_value(char* line, char **end, icalvalue_kind kind)
00285 {
00286     
00287     char* next;
00288     char *p;
00289     char *str;
00290     size_t length = strlen(line);
00291 
00292     p = line;
00293     while(1){
00294 
00295        next = icalparser_get_next_char(',',p,1);
00296 
00297        /* Unforunately, RFC2445 says that for the RECUR value, COMMA
00298           can both seperate digits in a list, and it can seperate
00299           multiple recurrence specifications. This is not a friendly
00300           part of the spec. This weirdness tries to
00301           distinguish the two uses. it is probably a HACK*/
00302       
00303        if( kind == ICAL_RECUR_VALUE ) {
00304            if ( next != 0 &&
00305                (*end+length) > next+5 &&
00306                strncmp(next,"FREQ",4) == 0
00307               ) {
00308               /* The COMMA was followed by 'FREQ', is it a real seperator*/
00309               /* Fall through */
00310            } else if (next != 0){
00311               /* Not real, get the next COMMA */
00312               p = next+1;
00313               next = 0;
00314               continue;
00315            } 
00316        }
00317        /* ignore all , for query value. select dtstart, dtend etc ... */
00318        else if( kind == ICAL_QUERY_VALUE) {
00319            if ( next != 0) {
00320               p = next+1;
00321               continue;
00322            }
00323            else
00324               break;
00325        }
00326 
00327        /* If the comma is preceeded by a '\', then it is a literal and
00328           not a value seperator*/  
00329       
00330        if ( (next!=0 && *(next-1) == '\\') ||
00331             (next!=0 && *(next-3) == '\\')
00332            ) 
00333            /*second clause for '/' is on prev line. HACK may be out of bounds */
00334        {
00335            p = next+1;
00336        } else {
00337            break;
00338        }
00339 
00340     }
00341 
00342     if (next == 0){
00343        next = (char*)(size_t)line+length;
00344        *end = next;
00345     } else {
00346        *end = next+1;
00347     }
00348 
00349     if (next == line){
00350        return 0;
00351     } 
00352        
00353 
00354     str = make_segment(line,next);
00355     return str;
00356    
00357 }
00358 
00359 char* icalparser_get_next_parameter(char* line,char** end)
00360 {
00361     char *next;
00362     char *v;
00363     char *str;
00364 
00365     v = icalparser_get_next_char(':',line,1); 
00366     next = icalparser_get_next_char(';', line,1);
00367     
00368     /* There is no ';' or, it is after the ':' that marks the beginning of
00369        the value */
00370 
00371     if (next == 0 || next > v) {
00372        next = icalparser_get_next_char(':', line,1);
00373     }
00374 
00375     if (next != 0) {
00376        str = make_segment(line,next);
00377        *end = next+1;
00378        return str;
00379     } else {
00380        *end = line;
00381        return 0;
00382     }
00383 }
00384 
00385 
00390 char* icalparser_get_line(icalparser *parser,
00391                           char* (*line_gen_func)(char *s, size_t size, void *d))
00392 {
00393     char *line;
00394     char *line_p;
00395     size_t buf_size = parser->tmp_buf_size;
00396     
00397     line_p = line = icalmemory_new_buffer(buf_size);
00398     line[0] = '\0';
00399    
00400     /* Read lines by calling line_gen_func and putting the data into
00401        parser->temp. If the line is a continuation line ( begins with a
00402        space after a newline ) then append the data onto line and read
00403        again. Otherwise, exit the loop. */
00404 
00405     while(1) {
00406 
00407         /* The first part of the loop deals with the temp buffer,
00408            which was read on he last pass through the loop. The
00409            routine is split like this because it has to read lone line
00410            ahead to determine if a line is a continuation line. */
00411 
00412 
00413        /* The tmp buffer is not clear, so transfer the data in it to the
00414           output. This may be left over from a previous call */
00415        if (parser->temp[0] != '\0' ) {
00416 
00417            /* If the last position in the temp buffer is occupied,
00418                mark the buffer as full. The means we will do another
00419                read later, because the line is not finished */
00420            if (parser->temp[parser->tmp_buf_size-1] == 0 &&
00421               parser->temp[parser->tmp_buf_size-2] != '\n'&& 
00422                 parser->temp[parser->tmp_buf_size-2] != 0 ){
00423               parser->buffer_full = 1;
00424            } else {
00425               parser->buffer_full = 0;
00426            }
00427 
00428            /* Copy the temp to the output and clear the temp buffer. */
00429             if(parser->continuation_line==1){
00430                 /* back up the pointer to erase the continuation characters */
00431                 parser->continuation_line = 0;
00432                 line_p--;
00433                 
00434                 if ( *(line_p-1) == '\r'){
00435                     line_p--;
00436                 }
00437                 
00438                 /* copy one space up to eliminate the leading space*/
00439                 icalmemory_append_string(&line,&line_p,&buf_size,
00440                                          parser->temp+1);
00441 
00442             } else {
00443                 icalmemory_append_string(&line,&line_p,&buf_size,parser->temp);
00444             }
00445 
00446             parser->temp[0] = '\0' ;
00447        }
00448        
00449        parser->temp[parser->tmp_buf_size-1] = 1; /* Mark end of buffer */
00450 
00451         /****** Here is where the routine gets string data ******************/
00452        if ((*line_gen_func)(parser->temp,parser->tmp_buf_size,parser->line_gen_data)
00453            ==0){/* Get more data */
00454 
00455            /* If the first position is clear, it means we didn't get
00456                any more data from the last call to line_ge_func*/
00457            if (parser->temp[0] == '\0'){
00458 
00459               if(line[0] != '\0'){
00460                   /* There is data in the output, so fall trhough and process it*/
00461                   break;
00462               } else {
00463                   /* No data in output; return and signal that there
00464                        is no more input*/
00465                   free(line);
00466                   return 0;
00467               }
00468            }
00469        }
00470        
00471  
00472        /* If the output line ends in a '\n' and the temp buffer
00473           begins with a ' ', then the buffer holds a continuation
00474           line, so keep reading.  */
00475 
00476        if ( line_p > line+1 && *(line_p-1) == '\n'
00477          && (parser->temp[0] == ' ' || parser->temp[0] == '\t') ) {
00478 
00479             parser->continuation_line = 1;
00480 
00481        } else if ( parser->buffer_full == 1 ) {
00482            
00483            /* The buffer was filled on the last read, so read again */
00484 
00485        } else {
00486 
00487            /* Looks like the end of this content line, so break */
00488            break;
00489        }
00490 
00491        
00492     }
00493 
00494     /* Erase the final newline and/or carriage return*/
00495     if ( line_p > line+1 && *(line_p-1) == '\n') {      
00496        *(line_p-1) = '\0';
00497        if ( *(line_p-2) == '\r'){
00498            *(line_p-2) = '\0';
00499        }
00500 
00501     } else {
00502        *(line_p) = '\0';
00503     }
00504 
00505     return line;
00506 
00507 }
00508 
00509 static void insert_error(icalcomponent* comp, char* text, 
00510                 char* message, icalparameter_xlicerrortype type)
00511 {
00512     char temp[1024];
00513     
00514     if (text == 0){
00515        snprintf(temp,1024,"%s:",message);
00516     } else {
00517        snprintf(temp,1024,"%s: %s",message,text);
00518     }  
00519     
00520     icalcomponent_add_property
00521        (comp,
00522         icalproperty_vanew_xlicerror(
00523             temp,
00524             icalparameter_new_xlicerrortype(type),
00525             0));   
00526 }
00527 
00528 
00529 static int line_is_blank(char* line){
00530     int i=0;
00531 
00532     for(i=0; *(line+i)!=0; i++){
00533        char c = *(line+i);
00534 
00535        if(c != ' ' && c != '\n' && c != '\t'){
00536            return 0;
00537        }
00538     }
00539     
00540     return 1;
00541 }
00542 
00543 icalcomponent* icalparser_parse(icalparser *parser,
00544                             char* (*line_gen_func)(char *s, size_t size, 
00545                                                  void* d))
00546 {
00547 
00548     char* line; 
00549     icalcomponent *c=0; 
00550     icalcomponent *root=0;
00551     icalerrorstate es = icalerror_get_error_state(ICAL_MALFORMEDDATA_ERROR);
00552        int cont;
00553 
00554     icalerror_check_arg_rz((parser !=0),"parser");
00555 
00556     icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,ICAL_ERROR_NONFATAL);
00557 
00558     do{
00559            line = icalparser_get_line(parser, line_gen_func);
00560 
00561        if ((c = icalparser_add_line(parser,line)) != 0){
00562 
00563            if(icalcomponent_get_parent(c) !=0){
00564               /* This is bad news... assert? */
00565            }      
00566            
00567            assert(parser->root_component == 0);
00568            assert(pvl_count(parser->components) ==0);
00569 
00570            if (root == 0){
00571               /* Just one component */
00572               root = c;
00573            } else if(icalcomponent_isa(root) != ICAL_XROOT_COMPONENT) {
00574               /*Got a second component, so move the two components under
00575                 an XROOT container */
00576               icalcomponent *tempc = icalcomponent_new(ICAL_XROOT_COMPONENT);
00577               icalcomponent_add_component(tempc, root);
00578               icalcomponent_add_component(tempc, c);
00579               root = tempc;
00580            } else if(icalcomponent_isa(root) == ICAL_XROOT_COMPONENT) {
00581               /* Already have an XROOT container, so add the component
00582                  to it*/
00583               icalcomponent_add_component(root, c);
00584               
00585            } else {
00586               /* Badness */
00587               assert(0);
00588            }
00589 
00590            c = 0;
00591 
00592         }
00593        cont = 0;
00594        if(line != 0){
00595            icalmemory_free_buffer(line);
00596               cont = 1;
00597        }
00598     } while ( cont );
00599 
00600     icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,es);
00601 
00602     return root;
00603 
00604 }
00605 
00606 
00607 icalcomponent* icalparser_add_line(icalparser* parser,
00608                                        char* line)
00609 { 
00610     char *str;
00611     char *end;
00612     int vcount = 0;
00613     icalproperty *prop;
00614     icalproperty_kind prop_kind;
00615     icalvalue *value;
00616     icalvalue_kind value_kind = ICAL_NO_VALUE;
00617 
00618 
00619     icalerror_check_arg_rz((parser != 0),"parser");
00620 
00621 
00622     if (line == 0)
00623     {
00624        parser->state = ICALPARSER_ERROR;
00625        return 0;
00626     }
00627 
00628     if(line_is_blank(line) == 1){
00629        return 0;
00630     }
00631 
00632     /* Begin by getting the property name at the start of the line. The
00633        property name may end up being "BEGIN" or "END" in which case it
00634        is not really a property, but the marker for the start or end of
00635        a component */
00636 
00637     end = 0;
00638     str = icalparser_get_prop_name(line, &end);
00639 
00640     if (str == 0 || strlen(str) == 0 ){
00641        /* Could not get a property name */
00642        icalcomponent *tail = pvl_data(pvl_tail(parser->components));
00643 
00644        if (tail){
00645            insert_error(tail,line,
00646                       "Got a data line, but could not find a property name or component begin tag",
00647                       ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
00648        }
00649        tail = 0;
00650        parser->state = ICALPARSER_ERROR;
00651        return 0; 
00652     }
00653 
00654     /**********************************************************************
00655      * Handle begin and end of components
00656      **********************************************************************/                                                         
00657     /* If the property name is BEGIN or END, we are actually
00658        starting or ending a new component */
00659 
00660 
00661     if(strcmp(str,"BEGIN") == 0){
00662        icalcomponent *c;
00663         icalcomponent_kind comp_kind;
00664 
00665        icalmemory_free_buffer(str);
00666        parser->level++;
00667        str = icalparser_get_next_value(end,&end, value_kind);
00668            
00669 
00670         comp_kind = icalenum_string_to_component_kind(str);
00671 
00672 
00673         if (comp_kind == ICAL_NO_COMPONENT){
00674 
00675 
00676            c = icalcomponent_new(ICAL_XLICINVALID_COMPONENT);
00677            insert_error(c,str,"Parse error in component name",
00678                       ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
00679         }
00680 
00681 
00682        if (comp_kind != ICAL_X_COMPONENT) {
00683            c  =  icalcomponent_new(comp_kind);
00684        } else {
00685            c  =  icalcomponent_new_x(str);
00686        }
00687 
00688        if (c == 0){
00689            c = icalcomponent_new(ICAL_XLICINVALID_COMPONENT);
00690            insert_error(c,str,"Parse error in component name",
00691                       ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
00692        }
00693            
00694        pvl_push(parser->components,c);
00695 
00696        parser->state = ICALPARSER_BEGIN_COMP;
00697        if (str)
00698            icalmemory_free_buffer(str);
00699        return 0;
00700 
00701     } else if (strcmp(str,"END") == 0 ) {
00702        icalcomponent* tail;
00703 
00704        icalmemory_free_buffer(str);
00705        parser->level--;
00706        str = icalparser_get_next_value(end,&end, value_kind);
00707        if (str)
00708            icalmemory_free_buffer(str);
00709 
00710        /* Pop last component off of list and add it to the second-to-last*/
00711        parser->root_component = pvl_pop(parser->components);
00712 
00713        tail = pvl_data(pvl_tail(parser->components));
00714 
00715        if(tail != 0){
00716            icalcomponent_add_component(tail,parser->root_component);
00717        } 
00718 
00719        tail = 0;
00720 
00721        /* Return the component if we are back to the 0th level */
00722        if (parser->level == 0){
00723            icalcomponent *rtrn; 
00724 
00725            if(pvl_count(parser->components) != 0){
00726            /* There are still components on the stack -- this means
00727                that one of them did not have a proper "END" */
00728               pvl_push(parser->components,parser->root_component);
00729               icalparser_clean(parser); /* may reset parser->root_component*/
00730            }
00731 
00732            assert(pvl_count(parser->components) == 0);
00733 
00734            parser->state = ICALPARSER_SUCCESS;
00735            rtrn = parser->root_component;
00736            parser->root_component = 0;
00737            return rtrn;
00738 
00739        } else {
00740            parser->state = ICALPARSER_END_COMP;
00741            return 0;
00742        }
00743     }
00744 
00745 
00746     /* There is no point in continuing if we have not seen a
00747        component yet */
00748 
00749     if(pvl_data(pvl_tail(parser->components)) == 0){
00750        parser->state = ICALPARSER_ERROR;
00751        icalmemory_free_buffer(str);
00752        return 0;
00753     }
00754 
00755 
00756     /**********************************************************************
00757      * Handle property names
00758      **********************************************************************/
00759                                                                
00760     /* At this point, the property name really is a property name,
00761        (Not a component name) so make a new property and add it to
00762        the component */
00763 
00764     
00765     prop_kind = icalproperty_string_to_kind(str);
00766 
00767     prop = icalproperty_new(prop_kind);
00768 
00769     if (prop != 0){
00770        icalcomponent *tail = pvl_data(pvl_tail(parser->components));
00771 
00772         if(prop_kind==ICAL_X_PROPERTY){
00773             icalproperty_set_x_name(prop,str);
00774         }
00775        icalmemory_free_buffer(str);
00776 
00777        icalcomponent_add_property(tail, prop);
00778 
00779        /* Set the value kind for the default for this type of
00780           property. This may be re-set by a VALUE parameter */
00781        value_kind = icalproperty_kind_to_value_kind(icalproperty_isa(prop));
00782 
00783     } else {
00784        icalcomponent* tail = pvl_data(pvl_tail(parser->components));
00785 
00786        insert_error(tail,str,"Parse error in property name",
00787                    ICAL_XLICERRORTYPE_PROPERTYPARSEERROR);
00788            
00789        tail = 0;
00790        parser->state = ICALPARSER_ERROR;
00791        icalmemory_free_buffer(str);
00792        return 0;
00793     }
00794 
00795     /**********************************************************************
00796      * Handle parameter values
00797      **********************************************************************/                                                         
00798 
00799     /* Now, add any parameters to the last property */
00800 
00801     while(1) {
00802 
00803        if (*(end-1) == ':'){
00804            /* if the last seperator was a ":" and the value is a
00805               URL, icalparser_get_next_parameter will find the
00806               ':' in the URL, so better break now. */
00807            break;
00808        }
00809 
00810        str = icalparser_get_next_parameter(end,&end);
00811 
00812        if (str != 0){
00813            char* name;
00814            char* pvalue;
00815         
00816            icalparameter *param = 0;
00817            icalparameter_kind kind;
00818            icalcomponent *tail = pvl_data(pvl_tail(parser->components));
00819 
00820            name = icalparser_get_param_name_and_value(str,&pvalue);
00821 
00822            if (name == 0){
00823               /* 'tail' defined above */
00824               insert_error(tail, str, "Cant parse parameter name",
00825                           ICAL_XLICERRORTYPE_PARAMETERNAMEPARSEERROR);
00826               tail = 0;
00827               icalmemory_free_buffer(str);
00828               if (pvalue)
00829                   icalmemory_free_buffer(pvalue);
00830               break;
00831            }
00832 
00833            kind = icalparameter_string_to_kind(name);
00834 
00835            if(kind == ICAL_X_PARAMETER){
00836               param = icalparameter_new(ICAL_X_PARAMETER);
00837               
00838               if(param != 0){
00839                   icalparameter_set_xname(param,name);
00840                   icalparameter_set_xvalue(param,pvalue);
00841               }
00842 
00843 
00844            } else if (kind != ICAL_NO_PARAMETER){
00845               param = icalparameter_new_from_value_string(kind,pvalue);
00846            } else {
00847               /* Error. Failed to parse the parameter*/
00848               /* 'tail' defined above */
00849 
00850                 /* Change for mozilla */
00851                 /* have the option of being flexible towards unsupported parameters */
00852                 #ifndef ICAL_ERRORS_ARE_FATAL
00853                 continue;
00854                 #endif
00855 
00856               insert_error(tail, str, "Cant parse parameter name",
00857                           ICAL_XLICERRORTYPE_PARAMETERNAMEPARSEERROR);
00858               tail = 0;
00859               parser->state = ICALPARSER_ERROR;
00860               icalmemory_free_buffer(str);
00861               icalmemory_free_buffer(name);
00862               if (pvalue)
00863                   icalmemory_free_buffer(pvalue);
00864               return 0;
00865            }
00866            icalmemory_free_buffer(name);
00867            if (pvalue)
00868               icalmemory_free_buffer(pvalue);
00869 
00870 
00871            if (param == 0){
00872               /* 'tail' defined above */
00873               insert_error(tail,str,"Cant parse parameter value",
00874                           ICAL_XLICERRORTYPE_PARAMETERVALUEPARSEERROR);
00875                   
00876               tail = 0;
00877               parser->state = ICALPARSER_ERROR;
00878               icalmemory_free_buffer(str);
00879               continue;
00880            }
00881 
00882            /* If it is a VALUE parameter, set the kind of value*/
00883            if (icalparameter_isa(param)==ICAL_VALUE_PARAMETER){
00884 
00885               value_kind = (icalvalue_kind)
00886                     icalparameter_value_to_value_kind(
00887                                 icalparameter_get_value(param)
00888                                 );
00889 
00890               if (value_kind == ICAL_NO_VALUE){
00891 
00892                   /* Ooops, could not parse the value of the
00893                      parameter ( it was not one of the defined
00894                      values ), so reset the value_kind */
00895                      
00896                   insert_error(
00897                      tail, str, 
00898                      "Got a VALUE parameter with an unknown type",
00899                      ICAL_XLICERRORTYPE_PARAMETERVALUEPARSEERROR);
00900                   icalparameter_free(param);
00901                      
00902                   value_kind = 
00903                      icalproperty_kind_to_value_kind(
00904                          icalproperty_isa(prop));
00905                      
00906                   icalparameter_free(param);
00907                   tail = 0;
00908                   parser->state = ICALPARSER_ERROR;
00909                   icalmemory_free_buffer(str);
00910                   return 0;
00911               } 
00912            }
00913 
00914            /* Everything is OK, so add the parameter */
00915            icalproperty_add_parameter(prop,param);
00916            tail = 0;
00917            icalmemory_free_buffer(str);
00918 
00919        } else { /* if ( str != 0)  */
00920            /* If we did not get a param string, go on to looking
00921               for a value */
00922            break;
00923        } /* if ( str != 0)  */
00924 
00925     } /* while(1) */     
00926        
00927     /**********************************************************************
00928      * Handle values
00929      **********************************************************************/                                                         
00930 
00931     /* Look for values. If there are ',' characters in the values,
00932        then there are multiple values, so clone the current
00933        parameter and add one part of the value to each clone */
00934 
00935     vcount=0;
00936     while(1) {
00937         /* Only some properies can have multiple values. This list was taken
00938            from rfc2445. Also added the x-properties, because the spec actually
00939            says that commas should be escaped. For x-properties, other apps may
00940            depend on that behaviour
00941         */
00942         switch (prop_kind) {
00943             case ICAL_X_PROPERTY:
00944             case ICAL_CATEGORIES_PROPERTY:
00945             case ICAL_RESOURCES_PROPERTY:
00946             /* Referring to RFC 2445, section 4.8.5.3 and section 4.8.5.1:
00947                RDATE and EXDATE can specify a list of dates/date-times/periods.
00948             */
00949             case ICAL_RDATE_PROPERTY:
00950             case ICAL_EXDATE_PROPERTY:
00951             /* Referring to RFC 2445, section 4.8.2.6 Free/Busy Time:
00952                The "FREEBUSY" property can specify more than one value, separated by
00953                the COMMA character (US-ASCII decimal 44).
00954             */
00955             case ICAL_FREEBUSY_PROPERTY:
00956                  str = icalparser_get_next_value(end,&end, value_kind);
00957                  break;
00958             default:
00959                  str = icalparser_get_value(end, &end, value_kind);
00960                  break;
00961         }
00962 
00963        if (str != 0){
00964               
00965            if (vcount > 0){
00966               /* Actually, only clone after the second value */
00967               icalproperty* clone = icalproperty_new_clone(prop);
00968               icalcomponent* tail = pvl_data(pvl_tail(parser->components));
00969                   
00970               icalcomponent_add_property(tail, clone);
00971               prop = clone;            
00972               tail = 0;
00973            }
00974               
00975            value = icalvalue_new_from_string(value_kind, str);
00976               
00977            /* Don't add properties without value */
00978            if (value == 0){
00979               char temp[200]; /* HACK */
00980 
00981               icalproperty_kind prop_kind = icalproperty_isa(prop);
00982               icalcomponent* tail = pvl_data(pvl_tail(parser->components));
00983 
00984               snprintf(temp,sizeof(temp),"Cant parse as %s value in %s property. Removing entire property",
00985                      icalvalue_kind_to_string(value_kind),
00986                      icalproperty_kind_to_string(prop_kind));
00987 
00988               insert_error(tail, str, temp,
00989                           ICAL_XLICERRORTYPE_VALUEPARSEERROR);
00990 
00991               /* Remove the troublesome property */
00992               icalcomponent_remove_property(tail,prop);
00993               icalproperty_free(prop);
00994               prop = 0;
00995               tail = 0;
00996               parser->state = ICALPARSER_ERROR;
00997               icalmemory_free_buffer(str);
00998               return 0;
00999                   
01000            } else {
01001               vcount++;
01002               icalproperty_set_value(prop, value);
01003               icalmemory_free_buffer(str);
01004            }
01005 
01006 
01007        } else {
01008            if (vcount == 0){
01009               char temp[200]; /* HACK */
01010               
01011               icalproperty_kind prop_kind = icalproperty_isa(prop);
01012               icalcomponent *tail = pvl_data(pvl_tail(parser->components));
01013               
01014               snprintf(temp,sizeof(temp),"No value for %s property. Removing entire property",
01015                      icalproperty_kind_to_string(prop_kind));
01016 
01017               insert_error(tail, str, temp,
01018                           ICAL_XLICERRORTYPE_VALUEPARSEERROR);
01019 
01020               /* Remove the troublesome property */
01021               icalcomponent_remove_property(tail,prop);
01022               icalproperty_free(prop);
01023               prop = 0;
01024               tail = 0;
01025               parser->state = ICALPARSER_ERROR;
01026               return 0;
01027            } else {
01028 
01029               break;
01030            }
01031        }
01032     }
01033        
01034     /****************************************************************
01035      * End of component parsing. 
01036      *****************************************************************/
01037 
01038     if (pvl_data(pvl_tail(parser->components)) == 0 &&
01039        parser->level == 0){
01040        /* HACK. Does this clause ever get executed? */
01041        parser->state = ICALPARSER_SUCCESS;
01042        assert(0);
01043        return parser->root_component;
01044     } else {
01045        parser->state = ICALPARSER_IN_PROGRESS;
01046        return 0;
01047     }
01048 
01049 }
01050 
01051 icalparser_state icalparser_get_state(icalparser* parser)
01052 {
01053     return parser->state;
01054 
01055 }
01056 
01057 icalcomponent* icalparser_clean(icalparser* parser)
01058 {
01059     icalcomponent *tail; 
01060 
01061     icalerror_check_arg_rz((parser != 0 ),"parser");
01062 
01063     /* We won't get a clean exit if some components did not have an
01064        "END" tag. Clear off any component that may be left in the list */
01065     
01066     while((tail=pvl_data(pvl_tail(parser->components))) != 0){
01067 
01068        insert_error(tail," ",
01069                    "Missing END tag for this component. Closing component at end of input.",
01070                    ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
01071        
01072 
01073        parser->root_component = pvl_pop(parser->components);
01074        tail=pvl_data(pvl_tail(parser->components));
01075 
01076        if(tail != 0){
01077            if(icalcomponent_get_parent(parser->root_component)!=0){
01078               icalerror_warn("icalparser_clean is trying to attach a component for the second time");
01079            } else {
01080               icalcomponent_add_component(tail,parser->root_component);
01081            }
01082        }
01083            
01084     }
01085 
01086     return parser->root_component;
01087 
01088 }
01089 
01090 struct slg_data {
01091        const char* pos;
01092        const char* str;
01093 };
01094 
01095 
01096 char* icalparser_string_line_generator(char *out, size_t buf_size, void *d)
01097 {
01098     char *n;
01099     size_t size;
01100     struct slg_data* data = (struct slg_data*)d;
01101 
01102     if(data->pos==0){
01103        data->pos=data->str;
01104     }
01105 
01106     /* If the pointer is at the end of the string, we are done */
01107     if (*(data->pos)==0){
01108        return 0;
01109     }
01110 
01111     n = strchr(data->pos,'\n');
01112     
01113     if (n == 0){
01114        size = strlen(data->pos);
01115     } else {
01116        n++; /* include newline in output */
01117        size = (n-data->pos);       
01118     }
01119 
01120     if (size > buf_size-1){
01121        size = buf_size-1;
01122     }
01123     
01124 
01125     strncpy(out,data->pos,size);
01126     
01127     *(out+size) = '\0';
01128     
01129     data->pos += size;
01130 
01131     return out;    
01132 }
01133 
01134 icalcomponent* icalparser_parse_string(const char* str)
01135 {
01136     icalcomponent *c;
01137     struct slg_data d;
01138     icalparser *p;
01139 
01140     icalerrorstate es = icalerror_get_error_state(ICAL_MALFORMEDDATA_ERROR);
01141 
01142     d.pos = 0;
01143     d.str = str;
01144 
01145     p = icalparser_new();
01146     icalparser_set_gen_data(p,&d);
01147 
01148     icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,ICAL_ERROR_NONFATAL);
01149 
01150     c = icalparser_parse(p,icalparser_string_line_generator);
01151 
01152     icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,es);
01153 
01154     icalparser_free(p);
01155 
01156     return c;
01157 
01158 }