$darkmode
Libical API Documentation 4.0 STABLE VERSION Visit the v3.0 documentation
icalparser.c
Go to the documentation of this file.
1 /*======================================================================
2  FILE: icalparser.c
3  CREATOR: eric 04 August 1999
4 
5  SPDX-FileCopyrightText: 2000, Eric Busboom <eric@civicknowledge.com>
6  SPDX-License-Identifier: LGPL-2.1-only OR MPL-2.0
7 
8  The Initial Developer of the Original Code is Eric Busboom
9  ======================================================================*/
10 
16 #ifdef HAVE_CONFIG_H
17 #include <config.h>
18 #endif
19 
20 #include "icalparser.h"
21 #include "icalerror_p.h"
22 #include "icalerror.h"
23 #include "icallimits.h"
24 #include "icalmemory.h"
25 #include "icalvalue.h"
26 #include "icalparameter.h"
27 #include "icalproperty_p.h"
28 #include "icalpvl_p.h"
29 
30 #include <ctype.h>
31 #include <stddef.h> /* for ptrdiff_t */
32 #include <stdlib.h>
33 #include <string.h>
34 
36 #define TMP_BUF_SIZE 80
37 
39 static enum icalparser_ctrl icalparser_ctrl_g = ICALPARSER_CTRL_KEEP;
40 
41 struct icalparser_impl {
42  int buffer_full; /* flag indicates that temp is smaller that
43  data being read into it */
44  int continuation_line; /* last line read was a continuation line */
45  size_t tmp_buf_size;
46  char temp[TMP_BUF_SIZE];
47  icalcomponent *root_component;
48  int version;
49  int level;
50  int lineno;
51  size_t error_count;
52  icalparser_state state;
53  icalpvl_list components;
54 
55  void *line_gen_data;
56 };
57 
58 /*
59  * New version of strstrip() that does not move the pointer.
60  */
61 static void strstriplt(char *buf)
62 {
63  size_t len, a;
64 
65  if (buf == NULL) {
66  return;
67  }
68  if (buf[0] == 0) {
69  return;
70  }
71  len = strlen(buf);
72  /* the casts to unsigned char below are to work around isspace asserts
73  on Windows due to non-ascii characters becoming negative */
74  while ((buf[0] != 0) && (isspace((unsigned char)buf[len - 1]))) {
75  buf[--len] = 0;
76  }
77  if (buf[0] == 0) {
78  return;
79  }
80  a = 0;
81  while (isspace((unsigned char)buf[a])) {
82  a++;
83  }
84  if (a > 0) {
85  memmove(buf, &buf[a], len - a + 1);
86  }
87 }
88 
89 icalparser *icalparser_new(void)
90 {
91  struct icalparser_impl *impl = 0;
92 
93  if ((impl = (struct icalparser_impl *)icalmemory_new_buffer(sizeof(struct icalparser_impl))) == 0) {
95  return 0;
96  }
97 
98  impl->root_component = 0;
99  impl->components = icalpvl_newlist();
100  impl->level = 0;
101  impl->state = ICALPARSER_SUCCESS;
102  impl->tmp_buf_size = TMP_BUF_SIZE;
103  impl->buffer_full = 0;
104  impl->continuation_line = 0;
105  impl->lineno = 0;
106  impl->error_count = 0;
107  memset(impl->temp, 0, TMP_BUF_SIZE);
108 
109  return (icalparser *)impl;
110 }
111 
112 void icalparser_free(icalparser *parser)
113 {
114  icalcomponent *c;
115 
116  if (parser->root_component) {
117  icalcomponent_free(parser->root_component);
118  }
119 
120  while ((c = icalpvl_pop(parser->components)) != 0) {
122  }
123 
124  icalpvl_free(parser->components);
125 
126  icalmemory_free_buffer(parser);
127 }
128 
129 void icalparser_set_gen_data(icalparser *parser, void *data)
130 {
131  parser->line_gen_data = data;
132 }
133 
134 static char *parser_get_next_char(char c, char *str, int qm)
135 {
136  int quote_mode = 0;
137  char *p = str;
138  char next_char = *p;
139  char prev_char = 0;
140 
141  size_t charCount = 0;
142  const size_t max_search_chars = icallimit_get(ICAL_LIMIT_PARSE_SEARCH);
143  while (next_char != '\0' && charCount++ < max_search_chars) {
144  if ((prev_char != '\0') && (prev_char != '\\')) {
145  if (qm == 1 && next_char == '"') {
146  /* Encountered a quote, toggle quote mode */
147  quote_mode = !quote_mode;
148  } else if (quote_mode == 0 && next_char == c) {
149  /* Found a matching character out of quote mode, return it */
150  return p;
151  }
152  }
153 
154  /* Save the previous character so we can check if it's a backslash in the next iteration */
155  prev_char = next_char;
156  next_char = *(++p);
157  }
158 
159  return 0;
160 }
161 
163 static char *make_segment(const char *start, const char *end)
164 {
165 #if defined(__GNUC__) && !defined(__clang__)
166 #pragma GCC diagnostic push
167 #pragma GCC diagnostic ignored "-Wstringop-truncation"
168 #pragma GCC diagnostic ignored "-Wstringop-overflow"
169 #endif
170  char *buf, *tmp;
171  size_t size = (size_t)(ptrdiff_t)(end - start);
172 
173  buf = icalmemory_new_buffer(size + 1);
174  strncpy(buf, start, size);
175  *(buf + size) = 0;
176 
177  tmp = (buf + size);
178  while ((tmp >= buf) && ((*tmp == '\0') || iswspace((wint_t)*tmp))) {
179  *tmp = 0;
180  tmp--;
181  }
182 
183  return buf;
184 #if defined(__GNUC__) && !defined(__clang__)
185 #pragma GCC diagnostic pop
186 #endif
187 }
188 
189 static char *parser_get_prop_name(char *line, char **end)
190 {
191  char *p;
192  char *v;
193  char *str;
194 
195  p = parser_get_next_char(';', line, 1);
196  v = parser_get_next_char(':', line, 1);
197  if (p == 0 && v == 0) {
198  return 0;
199  }
200 
201  /* There is no ';' or, it is after the ';' that marks the beginning of
202  the value */
203  if (v != 0 && (p == 0 || p > v)) {
204  str = make_segment(line, v);
205  *end = v + 1;
206  } else {
207  str = make_segment(line, p);
208  *end = p + 1;
209  }
210 
211  return str;
212 }
213 
214 static bool parser_get_param_name_stack(char *line, char *name, size_t name_length,
215  char *value, size_t value_length)
216 {
217  char *next;
218  size_t requested_name_length, requested_value_length;
219 
220  /* The name is everything up to the equals sign */
221  next = parser_get_next_char('=', line, 1);
222 
223  if (next == 0) {
224  return false;
225  }
226 
227  requested_name_length = (size_t)(ptrdiff_t)(next - line);
228 
229  /* There's not enough room in the name input, we need to fall back
230  to parser_get_param_name_heap and use heap-allocated strings */
231  if (requested_name_length >= name_length - 1) {
232  return false;
233  }
234 
235  strncpy(name, line, requested_name_length);
236  name[requested_name_length] = 0;
237 
238  icalparameter_kind kind = icalparameter_string_to_kind(name);
239  int is_multivalued = 0;
240  icalparameter_kind_value_kind(kind, &is_multivalued);
241 
242  /* Figure out what range of line contains the value (everything after the equals sign) */
243  next++;
244 
245  if (next[0] == '"' && !is_multivalued) {
246  /* Dequote the value if it is a single quoted-string */
247  next++;
248 
249  const char *end_quote = (*next == '"') ? next : parser_get_next_char('"', next, 0);
250  if (end_quote == 0) {
251  return false;
252  }
253 
254  requested_value_length = (size_t)(ptrdiff_t)(end_quote - next);
255  } else {
256  requested_value_length = strlen(next);
257  }
258 
259  /* There's not enough room in the value input, we need to fall back
260  to parser_get_param_name_heap and use heap-allocated strings */
261  if (requested_value_length >= value_length - 1) {
262  return false;
263  }
264 
265  memcpy(value, next, requested_value_length);
266  value[requested_value_length] = 0;
267 
268  if (!is_multivalued) {
270  }
271 
272  return true;
273 }
274 
275 static char *parser_get_param_name_heap(char *line, char **end)
276 {
277  /* This is similar to parser_get_param_name_stack except it returns heap
278  objects in the return value and the end parameter. This is used in case
279  the name or value is longer than the stack-allocated string.
280  */
281  char *next;
282  char *str;
283 
284  next = parser_get_next_char('=', line, 1);
285 
286  if (next == 0) {
287  return 0;
288  }
289 
290  str = make_segment(line, next);
291 
292  icalparameter_kind kind = icalparameter_string_to_kind(str);
293  int is_multivalued = 0;
294  icalparameter_kind_value_kind(kind, &is_multivalued);
295 
296  *end = next + 1;
297  if (**end == '"' && !is_multivalued) {
298  *end = *end + 1;
299  next = (**end == '"') ? *end : parser_get_next_char('"', *end, 0);
300  if (next == 0) {
302  *end = NULL;
303  return 0;
304  } else {
305  *end = make_segment(*end, next);
306  }
307  } else {
308  *end = make_segment(*end, *end + strlen(*end));
309  }
310 
311  if (!is_multivalued) {
313  }
314 
315  return str;
316 }
317 
318 static char *icalparser_get_value(char *line, char **end, icalvalue_kind kind)
319 {
320  char *str;
321  size_t length = strlen(line);
322 
323  _unused(kind);
324 
325  if (length == 0) {
326  return 0;
327  }
328 
329  *end = line + length;
330  str = make_segment(line, *end);
331 
332  return str;
333 }
334 
341 static char *parser_get_next_value(char *line, char **end, icalvalue_kind kind)
342 {
343  char *next = 0;
344  char *p;
345  char *str;
346  size_t length = strlen(line);
347  int quoted = 0;
348 
349  if (line[0] == '\"' && line[length - 1] == '\"') {
350  /* This line is quoted, don't split into multiple values */
351  quoted = 1;
352  }
353 
354  p = line;
355  while (!quoted) {
356  next = parser_get_next_char(',', p, 1);
357 
358  /* Unfortunately, RFC2445 allowed that for the RECUR value, COMMA
359  could both separate digits in a list, and it could separate
360  multiple recurrence specifications. This is not a friendly
361  part of the spec and was deprecated in RFC5545. The following
362  weirdness tries to distinguish the two uses. It is probably a HACK */
363 
364  if (kind == ICAL_RECUR_VALUE) {
365  if (next != 0 && (*end + length) > next + 5 && strncmp(next, "FREQ", 4) == 0) {
366  /* The COMMA was followed by 'FREQ', is it a real separator */
367  /* Fall through */
368  } else if (next != 0) {
369  /* Not real, get the next COMMA */
370  p = next + 1;
371  next = 0;
372  continue;
373  }
374  }
375  /* ignore all commas for query and x values. select dtstart, dtend etc ... */
376  else if (kind == ICAL_QUERY_VALUE || kind == ICAL_X_VALUE) {
377  if (next != 0) {
378  p = next + 1;
379  continue;
380  } else {
381  break;
382  }
383  }
384 
385  /* If the comma is preceded by a '\', then it is a literal and
386  not a value separator */
387 
388  if ((next != 0 && *(next - 1) == '\\') || (next != 0 && *(next - 3) == '\\'))
389  /*second clause for '/' is on prev line. HACK may be out of bounds */
390  {
391  p = next + 1;
392  } else {
393  break;
394  }
395  }
396 
397  if (next == 0) {
398  next = (char *)(size_t)line + length;
399  *end = next;
400  } else {
401  *end = next + 1;
402  }
403 
404  if (next == line) {
405  return 0;
406  }
407 
408  str = make_segment(line, next);
409  return str;
410 }
411 
412 static char *parser_get_next_parameter(char *line, char **end)
413 {
414  char *next;
415  const char *v;
416 
417  v = parser_get_next_char(':', line, 1);
418  next = parser_get_next_char(';', line, 1);
419 
420  /* There is no ';' or, it is after the ':' that marks the beginning of
421  the value */
422 
423  if (next == 0 || next > v) {
424  next = parser_get_next_char(':', line, 1);
425  }
426 
427  if (next != 0) {
428  char *str = make_segment(line, next);
429  *end = next + 1;
430  return str;
431  } else {
432  *end = line;
433  return 0;
434  }
435 }
436 
437 char *icalparser_get_line(icalparser *parser,
438  icalparser_line_gen_func line_gen_func)
439 {
440  char *line;
441  char *line_p;
442  size_t buf_size = parser->tmp_buf_size;
443 
444  line = icalmemory_new_buffer(buf_size);
445  if (!line) {
446  return NULL;
447  }
448  line_p = line;
449  line[0] = '\0';
450 
451  /* Read lines by calling line_gen_func and putting the data into
452  parser->temp. If the line is a continuation line ( begins with a
453  space after a newline ) then append the data onto line and read
454  again. Otherwise, exit the loop. */
455 
456  while (1) {
457  /* The first part of the loop deals with the temp buffer,
458  which was read on he last pass through the loop. The
459  routine is split like this because it has to read lone line
460  ahead to determine if a line is a continuation line. */
461 
462  /* The tmp buffer is not clear, so transfer the data in it to the
463  output. This may be left over from a previous call */
464  if (parser->temp[0] != '\0') {
465  /* If the last position in the temp buffer is occupied,
466  mark the buffer as full. The means we will do another
467  read later, because the line is not finished */
468  if (parser->temp[parser->tmp_buf_size - 1] == 0 &&
469  parser->temp[parser->tmp_buf_size - 2] != '\n' &&
470  parser->temp[parser->tmp_buf_size - 2] != 0) {
471  parser->buffer_full = 1;
472  } else {
473  parser->buffer_full = 0;
474  }
475 
476  /* Copy the temp to the output and clear the temp buffer. */
477  if (parser->continuation_line == 1) {
478  /* back up the pointer to erase the continuation characters */
479  parser->continuation_line = 0;
480  line_p--;
481 
482  if (*(line_p - 1) == '\r') {
483  line_p--;
484  }
485 
486  /* copy one space up to eliminate the leading space */
487  icalmemory_append_string(&line, &line_p, &buf_size, parser->temp + 1);
488 
489  } else {
490  icalmemory_append_string(&line, &line_p, &buf_size, parser->temp);
491  }
492 
493  parser->temp[0] = '\0';
494  }
495 
496  parser->temp[parser->tmp_buf_size - 1] = 1; /* Mark end of buffer */
497 
498  /****** Here is where the routine gets string data ******************/
499  if ((*line_gen_func)(parser->temp, parser->tmp_buf_size, parser->line_gen_data) == 0) { /* Get more data */
500 
501  /* If the first position is clear, it means we didn't get
502  any more data from the last call to line_ge_func */
503  if (parser->temp[0] == '\0') {
504  if (line[0] != '\0') {
505  /* There is data in the output, so fall through and process it */
506  break;
507  } else {
508  /* No data in output; return and signal that there
509  is no more input */
511  return 0;
512  }
513  }
514  }
515 
516  /* If the output line ends in a '\n' and the temp buffer
517  begins with a ' ' or tab, then the buffer holds a continuation
518  line, so keep reading. RFC 5545, section 3.1 */
519 
520  if (line_p > line + 1 && *(line_p - 1) == '\n' && (parser->temp[0] == ' ' || parser->temp[0] == '\t')) {
521  parser->continuation_line = 1;
522 
523  } else if (parser->buffer_full == 1) {
524  /* The buffer was filled on the last read, so read again */
525 
526  } else {
527  /* Looks like the end of this content line, so break */
528  break;
529  }
530  }
531 
532  /* Erase the final newline and/or carriage return */
533  if (line_p > line + 1 && *(line_p - 1) == '\n') {
534  *(line_p - 1) = '\0';
535  if (*(line_p - 2) == '\r') {
536  *(line_p - 2) = '\0';
537  }
538 
539  } else {
540  *(line_p) = '\0';
541  }
542 
543  while ((*line_p == '\0' || iswspace((wint_t)*line_p)) && line_p > line) {
544  *line_p = '\0';
545  line_p--;
546  }
547 
548  return line;
549 }
550 
551 static void insert_error(icalparser *parser, icalcomponent *comp, const char *text,
552  const char *message, icalparameter_xlicerrortype type)
553 {
554  char temp[1024];
555 
556  if (parser->error_count > icallimit_get(ICAL_LIMIT_PARSE_FAILURE_ERROR_MESSAGES)) {
557  return;
558  }
559 
560  if (text == 0) {
561  snprintf(temp, 1024, "%s:", message);
562  } else {
563  snprintf(temp, 1024, "%s: %s", message, text);
564  }
565 
566  /* coverity[resource_leak] */
567  icalproperty *errProp = icalproperty_vanew_xlicerror(temp, icalparameter_new_xlicerrortype(type), (void *)0);
568  icalcomponent_add_property(comp, errProp);
569 
570  parser->error_count++;
571 }
572 
573 static bool line_is_blank(const char *line)
574 {
575  int i = 0;
576 
577  for (i = 0; *(line + i) != 0; i++) {
578  char c = *(line + i);
579 
580  if (c != ' ' && c != '\n' && c != '\t') {
581  return false;
582  }
583  }
584 
585  return true;
586 }
587 
588 icalcomponent *icalparser_parse(icalparser *parser,
589  icalparser_line_gen_func line_gen_func)
590 {
591  char *line;
592  icalcomponent *c;
593  icalcomponent *root = 0;
595  bool cont = false;
596 
597  icalerror_check_arg_rz((parser != 0), "parser");
598 
600 
601  /* Maximum number of bad parsed lines allowed */
602  const size_t max_parse_failures = icallimit_get(ICAL_LIMIT_PARSE_FAILURES);
603  size_t parse_failures = 0;
604  do {
605  line = icalparser_get_line(parser, line_gen_func);
606 
607  if ((c = icalparser_add_line(parser, line)) != 0) {
608  if (icalcomponent_get_parent(c) != 0) {
609  /* This is bad news... assert? */
610  }
611 
612  icalassert(parser->root_component == 0);
613  icalassert(icalpvl_count(parser->components) == 0);
614 
615  if (root == 0) {
616  /* Just one component */
617  root = c;
618  } else if (icalcomponent_isa(root) != ICAL_XROOT_COMPONENT) {
619  /*Got a second component, so move the two components under
620  an XROOT container */
621  icalcomponent *tempc = icalcomponent_new(ICAL_XROOT_COMPONENT);
622 
623  icalcomponent_add_component(tempc, root);
624  icalcomponent_add_component(tempc, c);
625  root = tempc;
626  } else if (icalcomponent_isa(root) == ICAL_XROOT_COMPONENT) {
627  /* Already have an XROOT container, so add the component
628  to it */
630 
631  } else {
632  /* Badness */
633  icalassert(0);
634  }
635  } else if (parser->state == ICALPARSER_ERROR) {
636  parse_failures++; // track the number of un-parsable data lines
637  }
638  cont = false;
639  if (line != 0) {
641  cont = true;
642  }
643  } while (cont && parse_failures < max_parse_failures); // limit the number of un-parsable data lines
644 
646 
647  return root;
648 }
649 
650 icalcomponent *icalparser_add_line(icalparser *parser, char *line)
651 {
652  char *str;
653  char *end;
654  size_t pcount = 0;
655  size_t vcount = 0;
656  icalproperty *prop;
657  icalproperty_kind prop_kind;
658  icalvalue *value;
659  icalvalue_kind value_kind = ICAL_NO_VALUE;
660 
661  icalerror_check_arg_rz((parser != 0), "parser");
662 
663  if (line == 0) {
664  parser->state = ICALPARSER_ERROR;
665  return 0;
666  }
667 
668  if (line_is_blank(line) == 1) {
669  return 0;
670  }
671 
672  if (icalparser_ctrl_g != ICALPARSER_CTRL_KEEP) {
673  static const unsigned char is_icalctrl[256] = {
674  1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1,
675  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
676  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
677  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
678  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
679  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
680  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
681  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
682  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
683  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
684  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
685  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
686  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
687  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
688  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
689  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
690 
691  char *c, *d;
692  for (c = d = line; *c; c++) {
693  if (!is_icalctrl[(unsigned char)*c]) {
694  *d++ = *c;
695  } else if (icalparser_ctrl_g == ICALPARSER_CTRL_OMIT) {
696  // omit CTRL character
697  } else {
698  icalcomponent *tail = icalpvl_data(icalpvl_tail(parser->components));
699  if (tail) {
700  insert_error(
701  parser, tail, line,
702  "Content line contains invalid CONTROL characters",
703  ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
704  }
705  parser->state = ICALPARSER_ERROR;
706  return 0;
707  }
708  }
709  *d = '\0';
710  }
711 
712  /* Begin by getting the property name at the start of the line. The
713  property name may end up being "BEGIN" or "END" in which case it
714  is not really a property, but the marker for the start or end of
715  a component */
716 
717  end = 0;
718  str = parser_get_prop_name(line, &end);
719 
720  if (str == 0 || *str == '\0') {
721  /* Could not get a property name */
722  icalcomponent *tail = icalpvl_data(icalpvl_tail(parser->components));
723 
724  if (tail) {
725  insert_error(
726  parser, tail, line,
727  "Got a data line, but could not find a property name or component begin tag",
728  ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
729  }
730  parser->state = ICALPARSER_ERROR;
732  str = NULL;
733  return 0;
734  }
735 
736  /**********************************************************************
737  * Handle begin and end of components
738  **********************************************************************/
739  /* If the property name is BEGIN or END, we are actually
740  starting or ending a new component */
741 
742  if (strcasecmp(str, "BEGIN") == 0) {
743  icalcomponent *c = NULL;
744  icalcomponent_kind comp_kind;
745 
746  parser->level++;
748  str = parser_get_next_value(end, &end, value_kind);
749 
750  comp_kind = icalcomponent_string_to_kind(str);
751 
752  if (comp_kind == ICAL_X_COMPONENT) {
753  c = icalcomponent_new_x(str);
754  } else if (comp_kind == ICAL_IANA_COMPONENT) {
755  ical_unknown_token_handling tokHandlingSetting =
757  if (tokHandlingSetting == ICAL_ASSUME_IANA_TOKEN) {
758  c = icalcomponent_new_iana(str);
759  }
760  /* ICAL_DISCARD_TOKEN / ICAL_TREAT_AS_ERROR: treat as error */
761  } else {
762  c = icalcomponent_new(comp_kind);
763  }
764 
765  if (c == 0) {
767  insert_error(parser, c, str, "Parse error in component name",
768  ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
769  }
770 
771  icalpvl_push(parser->components, c);
772 
773  parser->state = ICALPARSER_BEGIN_COMP;
774 
776  str = NULL;
777  return 0;
778 
779  } else if (strcasecmp(str, "END") == 0) {
780  icalcomponent *tail;
781 
782  parser->level--;
784  str = parser_get_next_value(end, &end, value_kind);
785 
786  /* Pop last component off of list and add it to the second-to-last */
787  parser->root_component = icalpvl_pop(parser->components);
788 
789  tail = icalpvl_data(icalpvl_tail(parser->components));
790 
791  if (tail != 0) {
792  icalcomponent_add_component(tail, parser->root_component);
793  }
794 
796  str = NULL;
797 
798  if (parser->level < 0) {
799  // Encountered an END before any BEGIN, this must be invalid data
800  icalerror_warn("Encountered END before BEGIN");
801  parser->state = ICALPARSER_ERROR;
802  parser->level = 0;
803  return 0;
804  } else if (parser->level == 0) {
805  /* Return the component if we are back to the 0th level */
806  icalcomponent *rtrn;
807 
808  if (icalpvl_count(parser->components) != 0) {
809  /* There are still components on the stack -- this means
810  that one of them did not have a proper "END" */
811  icalpvl_push(parser->components, parser->root_component);
812  (void)icalparser_clean(parser); /* may reset parser->root_component */
813  }
814 
815  icalassert(icalpvl_count(parser->components) == 0);
816 
817  parser->state = ICALPARSER_SUCCESS;
818  rtrn = parser->root_component;
819  parser->root_component = 0;
820  return rtrn;
821 
822  } else {
823  parser->state = ICALPARSER_END_COMP;
824  return 0;
825  }
826  }
827 
828  /* There is no point in continuing if we have not seen a
829  component yet */
830 
831  if (icalpvl_data(icalpvl_tail(parser->components)) == 0) {
832  parser->state = ICALPARSER_ERROR;
834  str = NULL;
835  return 0;
836  }
837 
838  /**********************************************************************
839  * Handle property names
840  **********************************************************************/
841 
842  /* At this point, the property name really is a property name,
843  (Not a component name) so make a new property and add it to
844  the component */
845 
846  prop_kind = icalproperty_string_to_kind(str);
847 
848  if (prop_kind == ICAL_IANA_PROPERTY &&
850  prop_kind = ICAL_NO_PROPERTY;
851  }
852 
853  prop = icalproperty_new(prop_kind);
854 
855  if (prop != 0) {
856  icalcomponent *tail = icalpvl_data(icalpvl_tail(parser->components));
857 
858  if (prop_kind == ICAL_X_PROPERTY) {
859  icalproperty_set_x_name(prop, str);
860  } else if (prop_kind == ICAL_IANA_PROPERTY) {
861  icalproperty_set_iana_name(prop, str);
862  }
863 
864  icalcomponent_add_property(tail, prop);
865 
866  /* Set the value kind for the default for this type of
867  property. This may be re-set by a VALUE parameter */
869 
870  } else {
871  icalcomponent *tail = icalpvl_data(icalpvl_tail(parser->components));
872 
873  insert_error(parser, tail, str, "Parse error in property name",
874  ICAL_XLICERRORTYPE_PROPERTYPARSEERROR);
875 
876  parser->state = ICALPARSER_ERROR;
878  str = NULL;
879  return 0;
880  }
881 
883  str = NULL;
884 
885  /**********************************************************************
886  * Handle parameter values
887  **********************************************************************/
888 
889  /* Now, add any parameters to the last property */
890  const size_t maximum_allowed_parameters = icallimit_get(ICAL_LIMIT_PARAMETERS);
891  while (pcount < maximum_allowed_parameters) {
892  if (*(end - 1) == ':') {
893  /* if the last separator was a ":" and the value is a
894  URL, icalparser_get_next_parameter will find the
895  ':' in the URL, so better break now. */
896  break;
897  }
898 
900  str = parser_get_next_parameter(end, &end);
901  strstriplt(str);
902  if (str != 0) {
903  char *name_heap = 0;
904  char *pvalue_heap = 0;
905  char name_stack[TMP_BUF_SIZE];
906  char pvalue_stack[TMP_BUF_SIZE];
907  char *name = name_stack;
908  char *pvalue = pvalue_stack;
909 
910  icalparameter *param = 0;
911  icalparameter_kind kind;
912  icalcomponent *tail = icalpvl_data(icalpvl_tail(parser->components));
913 
914  if (!parser_get_param_name_stack(str, name_stack, sizeof(name_stack),
915  pvalue_stack, sizeof(pvalue_stack))) {
916  name_heap = parser_get_param_name_heap(str, &pvalue_heap);
917 
918  name = name_heap;
919  pvalue = pvalue_heap;
920 
921  if (name_heap == 0) {
922  /* 'tail' defined above */
923  insert_error(parser, tail, str, "Can't parse parameter name",
924  ICAL_XLICERRORTYPE_PARAMETERNAMEPARSEERROR);
925  break;
926  }
927  }
928 
929  kind = icalparameter_string_to_kind(name);
930 
931  if (kind == ICAL_X_PARAMETER) {
932  param = icalparameter_new(ICAL_X_PARAMETER);
933  if (param != 0) {
934  icalparameter_set_xname(param, name);
935  icalparameter_set_xvalue(param, pvalue);
936  }
937  } else if (kind == ICAL_IANA_PARAMETER) {
938  ical_unknown_token_handling tokHandlingSetting =
940  if (tokHandlingSetting == ICAL_DISCARD_TOKEN) {
941  icalmemory_free_buffer(name_heap);
942  name_heap = 0;
943 
944  icalmemory_free_buffer(pvalue_heap);
945  pvalue_heap = 0;
946 
947  continue;
948  }
949 
950  param = icalparameter_new(ICAL_IANA_PARAMETER);
951 
952  if (param != 0) {
953  icalparameter_set_xname(param, name);
954  icalparameter_set_xvalue(param, pvalue);
955  }
956  } else if (kind == ICAL_TZID_PARAMETER && *(end - 1) != ';') {
957  /*
958  Special case handling for TZID to work around invalid incoming data.
959  For example, Google Calendar will send back stuff like this:
960  DTSTART;TZID=GMT+05:30:20120904T020000
961 
962  In this case we read to the next semicolon or the last colon rather
963  than the first colon. This way the TZID will become GMT+05:30 rather
964  than trying to parse the date-time as 30:20120904T020000.
965 
966  This also handles properties that look like this:
967  DTSTART;TZID=GMT+05:30;VALUE=DATE-TIME:20120904T020000
968  */
969  char *lastColon = 0;
970  char *nextColon = end;
971  char *nextSemicolon = parser_get_next_char(';', end, 1);
972 
973  /* Find the last colon in the line */
974  do {
975  nextColon = parser_get_next_char(':', nextColon, 1);
976 
977  if (nextColon) {
978  lastColon = nextColon;
979  }
980  } while (nextColon);
981 
982  if (lastColon && nextSemicolon && nextSemicolon < lastColon) {
983  /*
984  Ensures that we don't read past a semicolon
985 
986  Handles the following line:
987  DTSTART;TZID=GMT+05:30;VALUE=DATE-TIME:20120904T020000
988  */
989  lastColon = nextSemicolon;
990  }
991 
992  /*
993  Rebuild str so that it includes everything up to the next semicolon
994  or the last colon. So given the above example, str will go from
995  "TZID=GMT+05" to "TZID=GMT+05:30"
996  */
997  if (lastColon && *(lastColon + 1) != 0) {
998  const char *strStart = line + strlen(name) + 2;
999 
1000  end = lastColon + 1;
1001 
1003  str = make_segment(strStart, end - 1);
1004  }
1005 
1006  /* Reparse the parameter name and value with the new segment */
1007  if (!parser_get_param_name_stack(str, name_stack, sizeof(name_stack),
1008  pvalue_stack, sizeof(pvalue_stack))) {
1009  icalmemory_free_buffer(pvalue_heap);
1010  pvalue_heap = 0;
1011 
1012  icalmemory_free_buffer(name_heap);
1013  name = 0;
1014  name_heap = parser_get_param_name_heap(str, &pvalue_heap);
1015  pvalue = pvalue_heap;
1016  }
1017  param = icalparameter_new_from_value_string(kind, pvalue);
1018  } else if (kind != ICAL_NO_PARAMETER) {
1019  param = icalparameter_new_from_value_string(kind, pvalue);
1020  } else {
1021  /* Error. Failed to parse the parameter */
1022  /* 'tail' defined above */
1023 
1024  /* Change for mozilla */
1025  /* have the option of being flexible towards unsupported parameters */
1027  insert_error(parser, tail, str, "Can't parse parameter name",
1028  ICAL_XLICERRORTYPE_PARAMETERNAMEPARSEERROR);
1029  parser->state = ICALPARSER_ERROR;
1030 
1031  icalmemory_free_buffer(pvalue_heap);
1032  icalmemory_free_buffer(name_heap);
1034  str = NULL;
1035  return 0;
1036  } else {
1037  icalmemory_free_buffer(name_heap);
1038  name_heap = 0;
1039  icalmemory_free_buffer(pvalue_heap);
1040  pvalue_heap = 0;
1042  str = NULL;
1043  continue;
1044  }
1045  }
1046 
1047  icalmemory_free_buffer(pvalue_heap);
1048  pvalue_heap = 0;
1049 
1050  icalmemory_free_buffer(name_heap);
1051  name_heap = 0;
1052 
1053  if (param == 0) {
1054  /* 'tail' defined above */
1055  insert_error(parser, tail, str, "Can't parse parameter value",
1056  ICAL_XLICERRORTYPE_PARAMETERVALUEPARSEERROR);
1057 
1058  tail = 0;
1059  parser->state = ICALPARSER_ERROR;
1060 
1062  str = NULL;
1063 
1064  continue;
1065  }
1066 
1067  /* If it is a VALUE parameter, set the kind of value */
1068  if (icalparameter_isa(param) == ICAL_VALUE_PARAMETER) {
1069  value_kind =
1070  (icalvalue_kind)icalparameter_value_to_value_kind(
1071  icalparameter_get_value(param));
1072 
1073  if (!icalproperty_value_kind_is_valid(prop_kind, value_kind)) {
1074  /* Ooops, invalid VALUE parameter, so reset the value_kind */
1075 
1076  const char *err_str = "Invalid VALUE type for property";
1077  const char *prop_str = icalproperty_kind_to_string(prop_kind);
1078  size_t tmp_buf_len = strlen(err_str) + strlen(prop_str) + 2;
1079  char *tmp_buf = icalmemory_tmp_buffer(tmp_buf_len);
1080  snprintf(tmp_buf, tmp_buf_len, "%s %s", err_str, prop_str);
1081 
1082  insert_error(parser, tail, str, tmp_buf,
1083  ICAL_XLICERRORTYPE_PARAMETERVALUEPARSEERROR);
1084 
1085  value_kind = icalproperty_kind_to_value_kind(prop_kind);
1086 
1087  icalparameter_free(param);
1088  tail = 0;
1089  parser->state = ICALPARSER_ERROR;
1090 
1092  str = NULL;
1093  pcount++;
1094  continue;
1095  }
1096  }
1097 
1098  /* Everything is OK, so add the parameter */
1099  icalproperty_add_parameter(prop, param);
1101  str = NULL;
1102  pcount++;
1103 
1104  } else {
1105  /* str is NULL */
1106  break;
1107  }
1108 
1109  } /* while(1) */
1110 
1111  /**********************************************************************
1112  * Handle values
1113  **********************************************************************/
1114 
1115  /* Look for values. If there are ',' characters in the values,
1116  then there are multiple values, so clone the current
1117  parameter and add one part of the value to each clone */
1118 
1119  vcount = 0;
1120  const size_t maximum_property_values = icallimit_get(ICAL_LIMIT_PROPERTY_VALUES);
1121  while (vcount < maximum_property_values) {
1122  /* Only some properties can have multiple values. This list was taken
1123  from rfc5545. Also added the x-properties, because the spec actually
1124  says that commas should be escaped. For x-properties, other apps may
1125  depend on that behaviour
1126  */
1128  str = NULL;
1129 
1130  if (icalproperty_value_kind_is_multivalued(prop_kind, &value_kind)) {
1131  str = parser_get_next_value(end, &end, value_kind);
1132  } else {
1133  str = icalparser_get_value(end, &end, value_kind);
1134  }
1135  strstriplt(str);
1136 
1137  if (str != 0) {
1138  if (vcount > 0) {
1139  /* Actually, only clone after the second value */
1140  icalproperty *clone = icalproperty_clone(prop);
1141  icalcomponent *tail = icalpvl_data(icalpvl_tail(parser->components));
1142 
1143  icalcomponent_add_property(tail, clone);
1144  prop = clone;
1145  }
1146 
1147  value = icalvalue_new_from_string(value_kind, str);
1148 
1149  /* Don't add properties without value */
1150  if (value == 0) {
1151  char temp[200]; /* HACK */
1152 
1153  icalproperty_kind isa_prop_kind = icalproperty_isa(prop);
1154  icalcomponent *tail = icalpvl_data(icalpvl_tail(parser->components));
1155 
1156  snprintf(temp, sizeof(temp),
1157  "Can't parse as %s value in %s property. Removing entire property",
1158  icalvalue_kind_to_string(value_kind),
1159  icalproperty_kind_to_string(isa_prop_kind));
1160 
1161  insert_error(parser, tail, str, temp, ICAL_XLICERRORTYPE_VALUEPARSEERROR);
1162 
1163  /* Remove the troublesome property */
1164  icalcomponent_remove_property(tail, prop);
1165  icalproperty_free(prop);
1166  parser->state = ICALPARSER_ERROR;
1167 
1169  str = NULL;
1170  return 0;
1171 
1172  } else {
1173  vcount++;
1174  icalproperty_set_value(prop, value);
1175  }
1177  str = NULL;
1178 
1179  } else {
1181  /* Don't replace empty properties with an error.
1182  Set an empty length string (not null) as the value instead */
1183  if (vcount == 0) {
1184  icalproperty_set_value(prop, icalvalue_new(ICAL_NO_VALUE));
1185  }
1186  break;
1187  } else {
1188  if (vcount == 0) {
1189  char temp[200]; /* HACK */
1190 
1191  icalproperty_kind isa_prop_kind = icalproperty_isa(prop);
1192  icalcomponent *tail = icalpvl_data(icalpvl_tail(parser->components));
1193 
1194  snprintf(temp, sizeof(temp), "No value for %s property. Removing entire property",
1195  icalproperty_kind_to_string(isa_prop_kind));
1196 
1197  insert_error(parser, tail, str, temp, ICAL_XLICERRORTYPE_VALUEPARSEERROR);
1198 
1199  /* Remove the troublesome property */
1200  icalcomponent_remove_property(tail, prop);
1201  icalproperty_free(prop);
1202  parser->state = ICALPARSER_ERROR;
1203  return 0;
1204  } else {
1205  break;
1206  }
1207  }
1208  }
1209  }
1210 
1211  /****************************************************************
1212  * End of component parsing.
1213  *****************************************************************/
1214 
1215  if (icalpvl_data(icalpvl_tail(parser->components)) == 0 && parser->level == 0) {
1216  /* HACK. Does this clause ever get executed? */
1217  parser->state = ICALPARSER_SUCCESS;
1218  icalassert(0);
1219  return parser->root_component;
1220  } else {
1221  parser->state = ICALPARSER_IN_PROGRESS;
1222  return 0;
1223  }
1224 }
1225 
1226 icalparser_state icalparser_get_state(const icalparser *parser)
1227 {
1228  return parser->state;
1229 }
1230 
1231 icalcomponent *icalparser_clean(icalparser *parser)
1232 {
1233  icalcomponent *tail;
1234 
1235  icalerror_check_arg_rz((parser != 0), "parser");
1236 
1237  /* We won't get a clean exit if some components did not have an
1238  "END" tag. Clear off any component that may be left in the list */
1239 
1240  while ((tail = icalpvl_data(icalpvl_tail(parser->components))) != 0) {
1241  insert_error(parser, tail, " ",
1242  "Missing END tag for this component. Closing component at end of input.",
1243  ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
1244 
1245  parser->root_component = icalpvl_pop(parser->components);
1246  tail = icalpvl_data(icalpvl_tail(parser->components));
1247 
1248  if (tail != 0 && parser->root_component != NULL) {
1249  if (icalcomponent_get_parent(parser->root_component) != 0) {
1250  icalerror_warn(
1251  "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  return parser->root_component;
1259 }
1260 
1261 struct slg_data {
1262  const char *pos;
1263  const char *str;
1264 };
1265 
1266 char *icalparser_string_line_generator(char *out, size_t buf_size, void *d)
1267 {
1268  int replace_cr = 0;
1269  const char *n;
1270  size_t size;
1271  struct slg_data *data = (struct slg_data *)d;
1272 
1273  if (data->pos == 0) {
1274  data->pos = data->str;
1275  if (data->pos && strlen(data->pos) > 2) {
1276  /* Skip the UTF-8 marker at the beginning of the string */
1277  if (((unsigned char)data->pos[0]) == 0xEF &&
1278  ((unsigned char)data->pos[1]) == 0xBB &&
1279  ((unsigned char)data->pos[2]) == 0xBF) {
1280  data->pos += 3;
1281  }
1282  }
1283  }
1284 
1285  /* If the pointer is at the end of the string, we are done */
1286  if (!data->pos || *(data->pos) == 0) {
1287  return 0;
1288  }
1289 
1290  n = strchr(data->pos, '\n');
1291 
1292  if (n == 0) {
1293  n = strchr(data->pos, '\r'); /* support malformed input with only CR and no LF
1294  (e.g. from Kerio Connect Server) */
1295  if (n == 0) {
1296  size = strlen(data->pos);
1297  } else {
1298  n++; /* include CR in output - will be replaced by LF later on */
1299  replace_cr = 1;
1300  size = (size_t)(ptrdiff_t)(n - data->pos);
1301  }
1302  } else {
1303  n++; /* include newline in output */
1304  size = (size_t)(ptrdiff_t)(n - data->pos);
1305  }
1306 
1307  if (size > buf_size - 1) {
1308  size = buf_size - 1;
1309  }
1310 
1311 #if defined(__GNUC__) && !defined(__clang__)
1312 #pragma GCC diagnostic push
1313 #pragma GCC diagnostic ignored "-Wstringop-truncation"
1314 #pragma GCC diagnostic ignored "-Wstringop-overflow"
1315 #endif
1316  strncpy(out, data->pos, size);
1317 #if defined(__GNUC__) && !defined(__clang__)
1318 #pragma GCC diagnostic pop
1319 #endif
1320 
1321  if (replace_cr) {
1322  *(out + size - 1) = '\n';
1323  }
1324  *(out + size) = '\0';
1325 
1326  data->pos += size;
1327 
1328  return out;
1329 }
1330 
1331 icalcomponent *icalparser_parse_string(const char *str)
1332 {
1333  icalcomponent *c;
1334  struct slg_data d;
1335  icalparser *p;
1336 
1338 
1339  d.pos = 0;
1340  d.str = str;
1341 
1342  p = icalparser_new();
1343  if (!p) {
1344  return NULL;
1345  }
1346 
1347  icalparser_set_gen_data(p, &d);
1348 
1350 
1352 
1354 
1355  icalparser_free(p);
1356 
1357  return c;
1358 }
1359 
1361 {
1362  return icalparser_ctrl_g;
1363 }
1364 
1366 {
1367  icalparser_ctrl_g = ctrl;
1368 }
bool icalerror_get_errors_are_fatal(void)
Determine if errors are fatal.
Definition: icalerror.c:36
icalcomponent_kind icalcomponent_string_to_kind(const char *string)
const char * icalproperty_kind_to_string(icalproperty_kind kind)
ical_unknown_token_handling ical_get_unknown_token_handling_setting(void)
Definition: icaltypes.c:188
void icalparameter_decode_value(char *value)
icalcomponent * icalparser_parse_string(const char *str)
Parses a string and returns the parsed icalcomponent.
Definition: icalparser.c:1331
icalparameter_kind icalparameter_isa(const icalparameter *parameter)
Line-oriented parsing.
void icalparameter_free(icalparameter *param)
Frees an icalparameter object.
Definition: icalparameter.c:58
void icalcomponent_remove_property(icalcomponent *component, icalproperty *property)
icalvalue_kind icalparameter_kind_value_kind(const icalparameter_kind kind, int *is_multivalued)
Common memory management routines.
icalproperty_kind icalproperty_isa(const icalproperty *p)
Definition: icalproperty.c:466
icalparser_state
Represents the current state of the parser.
Definition: icalparser.h:39
Defines the data structure representing iCalendar parameter values.
icalproperty_kind icalproperty_string_to_kind(const char *string)
void * icalmemory_tmp_buffer(size_t size)
Creates a new temporary buffer on the ring and returns it.
Definition: icalmemory.c:182
char * icalparser_string_line_generator(char *out, size_t buf_size, void *d)
Definition: icalparser.c:1266
void icalcomponent_add_component(icalcomponent *parent, icalcomponent *child)
void icalmemory_free_buffer(void *buf)
Releases a buffer.
Definition: icalmemory.c:355
void icalparser_set_gen_data(icalparser *parser, void *data)
Sets the data that icalparser_parse will give to the line_gen_func as the parameter 'd'...
Definition: icalparser.c:129
icalerrorstate
Determine if an error is fatal or non-fatal.
Definition: icalerror.h:88
enum icalparser_ctrl icalparser_get_ctrl(void)
Get the current parser setting how to handle CONTROL characters.
Definition: icalparser.c:1360
size_t icallimit_get(icallimits_kind kind)
Definition: icallimits.c:29
void icalerror_set_errno(icalerrorenum x)
Sets the icalerrno to a given error.
Definition: icalerror.c:90
icalproperty * icalproperty_new(icalproperty_kind kind)
Definition: icalproperty.c:91
void icalproperty_add_parameter(icalproperty *p, icalparameter *parameter)
Definition: icalproperty.c:483
icalerrorstate icalerror_get_error_state(icalerrorenum error)
Gets the error state (severity) for a given error.
void icalproperty_set_x_name(icalproperty *prop, const char *name)
Definition: icalproperty.c:844
void icalparser_free(icalparser *parser)
Frees an icalparser object.
Definition: icalparser.c:112
Error handling for libical.
void icalproperty_free(icalproperty *p)
Definition: icalproperty.c:184
void icalparameter_set_xvalue(icalparameter *param, const char *v)
Sets the X-value of param to v.
void icalproperty_set_value(icalproperty *p, icalvalue *value)
Definition: icalproperty.c:746
char * icalparser_get_line(icalparser *parser, icalparser_line_gen_func line_gen_func)
Given a line generator function, returns a single iCal content line.
Definition: icalparser.c:437
icalparser_state icalparser_get_state(const icalparser *parser)
Returns current state of the icalparser.
Definition: icalparser.c:1226
icalvalue_kind icalproperty_kind_to_value_kind(icalproperty_kind kind)
void icalparser_set_ctrl(enum icalparser_ctrl ctrl)
Set the parser setting how to handle CONTROL characters.
Definition: icalparser.c:1365
void * icalmemory_new_buffer(size_t size)
Creates new buffer with the specified size.
Definition: icalmemory.c:315
void icalmemory_append_string(char **buf, char **pos, size_t *buf_size, const char *string)
Appends a string to a buffer.
Definition: icalmemory.c:365
Defines the interface for getting/setting internal library limits.
icalcomponent * icalcomponent_new_iana(const char *iana_name)
Defines the data structure representing iCalendar parameters.
icalcomponent * icalcomponent_new_x(const char *x_name)
icalcomponent * icalcomponent_new(icalcomponent_kind kind)
icalvalue_kind icalparameter_value_to_value_kind(icalparameter_value value)
void icalproperty_set_iana_name(icalproperty *prop, const char *name)
Definition: icalproperty.c:864
const char * icalvalue_kind_to_string(const icalvalue_kind kind)
icalproperty * icalproperty_clone(const icalproperty *old)
Definition: icalproperty.c:100
icalcomponent_kind
Definition: icalenums.h:28
icalcomponent_kind icalcomponent_isa(const icalcomponent *component)
ical_unknown_token_handling
Definition: icaltypes.h:154
void icalparameter_set_xname(icalparameter *param, const char *v)
Sets the X-name of param to v.
icalparameter * icalparameter_new_from_value_string(icalparameter_kind kind, const char *value)
Creates new icalparameter of a given kind with a given value.
icalvalue * icalvalue_new(icalvalue_kind kind)
Definition: icalvalue.c:60
icalparser * icalparser_new(void)
Creates a new icalparser.
Definition: icalparser.c:89
icalparameter_kind icalparameter_string_to_kind(const char *string)
Returns the icalparameter_kind for a given string.
void icalerror_set_error_state(icalerrorenum error, icalerrorstate state)
Sets the icalerrorstate for a given icalerrorenum error.
bool icalproperty_get_allow_empty_properties(void)
Definition: icalproperty.c:72
icalparser_ctrl
Defines how to handle invalid CONTROL characters in content lines.
Definition: icalparser.h:305
icalparameter * icalparameter_new(icalparameter_kind kind)
Creates new icalparameter object.
Definition: icalparameter.c:51
void icalcomponent_free(icalcomponent *c)
void icalcomponent_add_property(icalcomponent *component, icalproperty *property)
icalcomponent * icalparser_add_line(icalparser *parser, char *line)
Adds a single line to be parsed by the icalparser.
Definition: icalparser.c:650
icalcomponent * icalparser_parse(icalparser *parser, icalparser_line_gen_func line_gen_func)
Message oriented parsing.
Definition: icalparser.c:588
icalvalue * icalvalue_new_from_string(icalvalue_kind kind, const char *str)
Definition: icalvalue.c:791
icalcomponent * icalparser_clean(icalparser *parser)
Cleans out an icalparser and returns whatever it has parsed so far.
Definition: icalparser.c:1231