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