Coverage Report

Created: 2023-11-19 06:49

/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
}