Coverage Report

Created: 2024-02-11 06:26

/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
32.5k
#define TMP_BUF_SIZE 80
27
614k
#define MAXIMUM_ALLOWED_PARAMETERS 100
28
719k
#define MAXIMUM_ALLOWED_MULTIPLE_VALUES 500
29
426k
#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.10M
{
56
1.10M
    size_t len;
57
1.10M
    int a;
58
59
1.10M
    if (buf == NULL) {
60
336k
        return;
61
336k
    }
62
766k
    if (buf[0] == 0) {
63
7.33k
        return;
64
7.33k
    }
65
759k
    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
759k
    while ((buf[0] != 0) && (isspace((unsigned char)buf[len - 1]))) {
69
0
        buf[--len] = 0;
70
0
    }
71
759k
    if (buf[0] == 0) {
72
0
        return;
73
0
    }
74
759k
    a = 0;
75
2.00M
    while ((buf[0] != 0) && (isspace((unsigned char)buf[a]))) {
76
1.24M
        a++;
77
1.24M
    }
78
759k
    if (a > 0) {
79
2.31k
        memmove(buf, &buf[a], len - a + 1);
80
2.31k
    }
81
759k
}
82
83
icalparser *icalparser_new(void)
84
16.2k
{
85
16.2k
    struct icalparser_impl *impl = 0;
86
87
16.2k
    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.2k
    impl->root_component = 0;
93
16.2k
    impl->components = pvl_newlist();
94
16.2k
    impl->level = 0;
95
16.2k
    impl->state = ICALPARSER_SUCCESS;
96
16.2k
    impl->tmp_buf_size = TMP_BUF_SIZE;
97
16.2k
    impl->buffer_full = 0;
98
16.2k
    impl->continuation_line = 0;
99
16.2k
    impl->lineno = 0;
100
16.2k
    impl->error_count = 0;
101
16.2k
    memset(impl->temp, 0, TMP_BUF_SIZE);
102
103
16.2k
    return (icalparser *) impl;
104
16.2k
}
105
106
void icalparser_free(icalparser *parser)
107
16.2k
{
108
16.2k
    icalcomponent *c;
109
110
16.2k
    if (parser->root_component != 0) {
111
1.36k
        icalcomponent_free(parser->root_component);
112
1.36k
    }
113
114
328k
    while ((c = pvl_pop(parser->components)) != 0) {
115
312k
        icalcomponent_free(c);
116
312k
    }
117
118
16.2k
    pvl_free(parser->components);
119
120
16.2k
    icalmemory_free_buffer(parser);
121
16.2k
}
122
123
void icalparser_set_gen_data(icalparser *parser, void *data)
124
16.2k
{
125
16.2k
    parser->line_gen_data = data;
126
16.2k
}
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
7.22M
    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.88M
{
160
#if defined(__GNUC__) && !defined(__clang__)
161
#pragma GCC diagnostic push
162
#pragma GCC diagnostic ignored "-Wstringop-truncation"
163
#endif
164
4.88M
    char *buf, *tmp;
165
4.88M
    ptrdiff_t size = (ptrdiff_t)(end - start);
166
167
4.88M
    buf = icalmemory_new_buffer((size_t)(size + 1));
168
4.88M
    strncpy(buf, start, size);
169
4.88M
    *(buf + size) = 0;
170
171
4.88M
    tmp = (buf + size);
172
9.84M
    while ((tmp >= buf) && ((*tmp == '\0') || iswspace((wint_t)*tmp))) {
173
4.96M
        *tmp = 0;
174
4.96M
        tmp--;
175
4.96M
    }
176
177
4.88M
    return buf;
178
#if defined(__GNUC__) && !defined(__clang__)
179
#pragma GCC diagnostic pop
180
#endif
181
4.88M
}
182
183
static char *parser_get_prop_name(char *line, char **end)
184
3.68M
{
185
3.68M
    char *p;
186
3.68M
    char *v;
187
3.68M
    char *str;
188
189
3.68M
    p = parser_get_next_char(';', line, 1);
190
3.68M
    v = parser_get_next_char(':', line, 1);
191
3.68M
    if (p == 0 && v == 0) {
192
68.8k
        return 0;
193
68.8k
    }
194
195
    /* There is no ';' or, it is after the ';' that marks the beginning of
196
       the value */
197
3.61M
    if (v != 0 && (p == 0 || p > v)) {
198
3.18M
        str = make_segment(line, v);
199
3.18M
        *end = v + 1;
200
3.18M
    } else {
201
425k
        str = make_segment(line, p);
202
425k
        *end = p + 1;
203
425k
    }
204
205
3.61M
    return str;
206
3.68M
}
207
208
/** Decode parameter value per RFC6868 */
209
static void parser_decode_param_value(char *value)
210
308k
{
211
308k
    char *in, *out;
212
213
14.8M
    for (in = out = value; *in; in++, out++) {
214
14.5M
        int found_escaped_char = 0;
215
216
14.5M
        if (*in == '^') {
217
125k
            switch (*(in + 1)) {
218
1.20k
            case 'n':
219
1.20k
                *out = '\n';
220
1.20k
                found_escaped_char = 1;
221
1.20k
                break;
222
104k
            case '^':
223
104k
                *out = '^';
224
104k
                found_escaped_char = 1;
225
104k
                break;
226
227
10.3k
            case '\'':
228
10.3k
                *out = '"';
229
10.3k
                found_escaped_char = 1;
230
10.3k
                break;
231
125k
            }
232
125k
        }
233
234
14.5M
        if (found_escaped_char) {
235
115k
            ++in;
236
14.3M
        } else {
237
14.3M
            *out = *in;
238
14.3M
        }
239
14.5M
    }
240
241
424k
    while (*out) *out++ = '\0';
242
308k
}
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
331k
{
247
331k
    char *next, *end_quote;
248
331k
    size_t requested_name_length, requested_value_length;
249
250
    /* The name is everything up to the equals sign */
251
331k
    next = parser_get_next_char('=', line, 1);
252
253
331k
    if (next == 0) {
254
9.25k
        return 0;
255
9.25k
    }
256
257
322k
    requested_name_length = (ptrdiff_t)(next - line);
258
259
    /* Figure out what range of line contains the value (everything after the equals sign) */
260
322k
    next++;
261
262
322k
    if (next[0] == '"') {
263
        /* Dequote the value */
264
16.1k
        next++;
265
266
16.1k
        end_quote = (*next == '"') ? next : parser_get_next_char('"', next, 0);
267
268
16.1k
        if (end_quote == 0) {
269
13.8k
            return 0;
270
13.8k
        }
271
272
2.21k
        requested_value_length = (ptrdiff_t)(end_quote - next);
273
306k
    } else {
274
306k
        requested_value_length = strlen(next);
275
306k
    }
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
308k
    if (requested_name_length >= name_length - 1 || requested_value_length >= value_length - 1) {
280
4.05k
        return 0;
281
4.05k
    }
282
283
304k
    strncpy(name, line, requested_name_length);
284
304k
    name[requested_name_length] = 0;
285
286
304k
    strncpy(value, next, requested_value_length);
287
304k
    value[requested_value_length] = 0;
288
289
304k
    parser_decode_param_value(value);
290
291
304k
    return 1;
292
308k
}
293
294
static char *parser_get_param_name_heap(char *line, char **end)
295
27.2k
{
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
27.2k
    char *next;
301
27.2k
    char *str;
302
303
27.2k
    next = parser_get_next_char('=', line, 1);
304
305
27.2k
    if (next == 0) {
306
9.25k
        return 0;
307
9.25k
    }
308
309
17.9k
    str = make_segment(line, next);
310
17.9k
    *end = next + 1;
311
17.9k
    if (**end == '"') {
312
14.8k
        *end = *end + 1;
313
14.8k
        next = (**end == '"') ? *end : parser_get_next_char('"', *end, 0);
314
14.8k
        if (next == 0) {
315
13.8k
            icalmemory_free_buffer(str);
316
13.8k
            *end = NULL;
317
13.8k
            return 0;
318
13.8k
        }
319
320
966
        *end = make_segment(*end, next);
321
3.09k
    } else {
322
3.09k
        *end = make_segment(*end, *end + strlen(*end));
323
3.09k
    }
324
325
4.05k
    parser_decode_param_value(*end);
326
327
4.05k
    return str;
328
17.9k
}
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
490k
{
356
490k
    char *str;
357
490k
    size_t length = strlen(line);
358
359
490k
    _unused(kind);
360
361
490k
    if (length == 0) {
362
225k
        return 0;
363
225k
    }
364
365
264k
    *end = line + length;
366
264k
    str = make_segment(line, *end);
367
368
264k
    return str;
369
490k
}
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.39M
{
379
3.39M
    char *next = 0;
380
3.39M
    char *p;
381
3.39M
    char *str;
382
3.39M
    size_t length = strlen(line);
383
3.39M
    int quoted = 0;
384
385
3.39M
    if (line[0] == '\"' && line[length - 1] == '\"') {
386
        /* This line is quoted, don't split into multiple values */
387
614
        quoted = 1;
388
614
    }
389
390
3.39M
    p = line;
391
3.39M
    while (!quoted) {
392
393
3.39M
        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.39M
        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.39M
        else if (kind == ICAL_QUERY_VALUE || kind == ICAL_X_VALUE) {
414
54.4k
            if (next != 0) {
415
2.84k
                p = next + 1;
416
2.84k
                continue;
417
51.6k
            } else {
418
51.6k
                break;
419
51.6k
            }
420
54.4k
        }
421
422
        /* If the comma is preceded by a '\', then it is a literal and
423
           not a value separator */
424
425
3.34M
        if ((next != 0 && *(next - 1) == '\\') || (next != 0 && *(next - 3) == '\\')
426
3.34M
            )
427
            /*second clause for '/' is on prev line. HACK may be out of bounds */
428
516
        {
429
516
            p = next + 1;
430
3.33M
        } else {
431
3.33M
            break;
432
3.33M
        }
433
3.34M
    }
434
435
3.39M
    if (next == 0) {
436
3.23M
        next = (char *)(size_t) line + length;
437
3.23M
        *end = next;
438
3.23M
    } else {
439
158k
        *end = next + 1;
440
158k
    }
441
442
3.39M
    if (next == line) {
443
2.74M
        return 0;
444
2.74M
    }
445
446
651k
    str = make_segment(line, next);
447
651k
    return str;
448
3.39M
}
449
450
static char *parser_get_next_parameter(char *line, char **end)
451
384k
{
452
384k
    char *next;
453
384k
    char *v;
454
384k
    char *str;
455
456
384k
    v = parser_get_next_char(':', line, 1);
457
384k
    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
384k
    if (next == 0 || next > v) {
463
111k
        next = parser_get_next_char(':', line, 1);
464
111k
    }
465
466
384k
    if (next != 0) {
467
311k
        str = make_segment(line, next);
468
311k
        *end = next + 1;
469
311k
        return str;
470
311k
    } else {
471
72.2k
        *end = line;
472
72.2k
        return 0;
473
72.2k
    }
474
384k
}
475
476
char *icalparser_get_line(icalparser *parser,
477
                          icalparser_line_gen_func line_gen_func)
478
3.76M
{
479
3.76M
    char *line;
480
3.76M
    char *line_p;
481
3.76M
    size_t buf_size = parser->tmp_buf_size;
482
483
3.76M
    line_p = line = icalmemory_new_buffer(buf_size);
484
3.76M
    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.23M
    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.23M
        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.20M
            if (parser->temp[parser->tmp_buf_size - 1] == 0 &&
506
4.20M
                parser->temp[parser->tmp_buf_size - 2] != '\n' &&
507
4.20M
                parser->temp[parser->tmp_buf_size - 2] != 0) {
508
415k
                parser->buffer_full = 1;
509
3.78M
            } else {
510
3.78M
                parser->buffer_full = 0;
511
3.78M
            }
512
513
            /* Copy the temp to the output and clear the temp buffer. */
514
4.20M
            if (parser->continuation_line == 1) {
515
                /* back up the pointer to erase the continuation characters */
516
50.2k
                parser->continuation_line = 0;
517
50.2k
                line_p--;
518
519
50.2k
                if (*(line_p - 1) == '\r') {
520
3.43k
                    line_p--;
521
3.43k
                }
522
523
                /* copy one space up to eliminate the leading space */
524
50.2k
                icalmemory_append_string(&line, &line_p, &buf_size, parser->temp + 1);
525
526
4.15M
            } else {
527
4.15M
                icalmemory_append_string(&line, &line_p, &buf_size, parser->temp);
528
4.15M
            }
529
530
4.20M
            parser->temp[0] = '\0';
531
4.20M
        }
532
533
4.23M
        parser->temp[parser->tmp_buf_size - 1] = 1;     /* Mark end of buffer */
534
535
        /****** Here is where the routine gets string data ******************/
536
4.23M
        if ((*line_gen_func) (parser->temp, parser->tmp_buf_size, parser->line_gen_data)
537
4.23M
            == 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
32.5k
            if (parser->temp[0] == '\0') {
542
543
32.5k
                if (line[0] != '\0') {
544
                    /* There is data in the output, so fall through and process it */
545
16.2k
                    break;
546
16.2k
                } else {
547
                    /* No data in output; return and signal that there
548
                       is no more input */
549
16.2k
                    icalmemory_free_buffer(line);
550
16.2k
                    return 0;
551
16.2k
                }
552
32.5k
            }
553
32.5k
        }
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.20M
        if (line_p > line + 1 && *(line_p - 1) == '\n'
560
4.20M
            && (parser->temp[0] == ' ' || parser->temp[0] == '\t')) {
561
562
50.2k
            parser->continuation_line = 1;
563
564
4.15M
        } else if (parser->buffer_full == 1) {
565
566
            /* The buffer was filled on the last read, so read again */
567
568
3.73M
        } else {
569
570
            /* Looks like the end of this content line, so break */
571
3.73M
            break;
572
3.73M
        }
573
4.20M
    }
574
575
    /* Erase the final newline and/or carriage return */
576
3.75M
    if (line_p > line + 1 && *(line_p - 1) == '\n') {
577
3.66M
        *(line_p - 1) = '\0';
578
3.66M
        if (*(line_p - 2) == '\r') {
579
2.32k
            *(line_p - 2) = '\0';
580
2.32k
        }
581
582
3.66M
    } else {
583
85.4k
        *(line_p) = '\0';
584
85.4k
    }
585
586
11.1M
    while ((*line_p == '\0' || iswspace((wint_t) * line_p)) && line_p > line) {
587
7.41M
        *line_p = '\0';
588
7.41M
        line_p--;
589
7.41M
    }
590
591
3.75M
    return line;
592
3.76M
}
593
594
static void insert_error(icalparser *parser, icalcomponent *comp, const char *text,
595
                         const char *message, icalparameter_xlicerrortype type)
596
426k
{
597
426k
    char temp[1024];
598
599
426k
    if (parser->error_count > MAXIMUM_ALLOWED_ERRORS) {
600
337k
        return;
601
337k
    }
602
603
89.4k
    if (text == 0) {
604
8.07k
        snprintf(temp, 1024, "%s:", message);
605
81.3k
    } else {
606
81.3k
        snprintf(temp, 1024, "%s: %s", message, text);
607
81.3k
    }
608
609
89.4k
    icalcomponent_add_property(
610
89.4k
        comp,
611
89.4k
        icalproperty_vanew_xlicerror(temp, icalparameter_new_xlicerrortype(type), (void *)0));
612
613
89.4k
    parser->error_count++;
614
89.4k
}
615
616
static int line_is_blank(char *line)
617
3.75M
{
618
3.75M
    int i = 0;
619
620
3.80M
    for (i = 0; *(line + i) != 0; i++) {
621
3.73M
        char c = *(line + i);
622
623
3.73M
        if (c != ' ' && c != '\n' && c != '\t') {
624
3.68M
            return 0;
625
3.68M
        }
626
3.73M
    }
627
628
70.5k
    return 1;
629
3.75M
}
630
631
icalcomponent *icalparser_parse(icalparser *parser,
632
                                icalparser_line_gen_func line_gen_func)
633
16.2k
{
634
16.2k
    char *line;
635
16.2k
    icalcomponent *c = 0;
636
16.2k
    icalcomponent *root = 0;
637
16.2k
    icalerrorstate es = icalerror_get_error_state(ICAL_MALFORMEDDATA_ERROR);
638
16.2k
    int cont;
639
640
16.2k
    icalerror_check_arg_rz((parser != 0), "parser");
641
642
16.2k
    icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR, ICAL_ERROR_NONFATAL);
643
644
3.76M
    do {
645
3.76M
        line = icalparser_get_line(parser, line_gen_func);
646
647
3.76M
        if ((c = icalparser_add_line(parser, line)) != 0) {
648
649
192k
            if (icalcomponent_get_parent(c) != 0) {
650
                /* This is bad news... assert? */
651
0
            }
652
653
192k
            icalassert(parser->root_component == 0);
654
192k
            icalassert(pvl_count(parser->components) == 0);
655
656
192k
            if (root == 0) {
657
                /* Just one component */
658
3.36k
                root = c;
659
188k
            } 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.09k
                icalcomponent *tempc = icalcomponent_new(ICAL_XROOT_COMPONENT);
663
664
1.09k
                icalcomponent_add_component(tempc, root);
665
1.09k
                icalcomponent_add_component(tempc, c);
666
1.09k
                root = tempc;
667
187k
            } else if (icalcomponent_isa(root) == ICAL_XROOT_COMPONENT) {
668
                /* Already have an XROOT container, so add the component
669
                   to it */
670
187k
                icalcomponent_add_component(root, c);
671
672
187k
            } else {
673
                /* Badness */
674
0
                icalassert(0);
675
0
            }
676
677
192k
            c = 0;
678
192k
        }
679
3.76M
        cont = 0;
680
3.76M
        if (line != 0) {
681
3.75M
            icalmemory_free_buffer(line);
682
3.75M
            cont = 1;
683
3.75M
        }
684
3.76M
    } while (cont);
685
686
16.2k
    icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR, es);
687
688
16.2k
    return root;
689
16.2k
}
690
691
icalcomponent *icalparser_add_line(icalparser *parser, char *line)
692
3.76M
{
693
3.76M
    char *str;
694
3.76M
    char *end;
695
3.76M
    int pcount = 0;
696
3.76M
    int vcount = 0;
697
3.76M
    icalproperty *prop;
698
3.76M
    icalproperty_kind prop_kind;
699
3.76M
    icalvalue *value;
700
3.76M
    icalvalue_kind value_kind = ICAL_NO_VALUE;
701
702
3.76M
    icalerror_check_arg_rz((parser != 0), "parser");
703
704
3.76M
    if (line == 0) {
705
16.2k
        parser->state = ICALPARSER_ERROR;
706
16.2k
        return 0;
707
16.2k
    }
708
709
3.75M
    if (line_is_blank(line) == 1) {
710
70.5k
        return 0;
711
70.5k
    }
712
713
3.68M
    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.68M
    end = 0;
760
3.68M
    str = parser_get_prop_name(line, &end);
761
762
3.68M
    if (str == 0 || *str == '\0') {
763
        /* Could not get a property name */
764
69.6k
        icalcomponent *tail = pvl_data(pvl_tail(parser->components));
765
766
69.6k
        if (tail) {
767
63.3k
            insert_error(
768
63.3k
                parser, tail, line,
769
63.3k
                "Got a data line, but could not find a property name or component begin tag",
770
63.3k
                ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
771
63.3k
        }
772
69.6k
        tail = 0;
773
69.6k
        parser->state = ICALPARSER_ERROR;
774
69.6k
        icalmemory_free_buffer(str);
775
69.6k
        str = NULL;
776
69.6k
        return 0;
777
69.6k
    }
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.61M
    if (strcasecmp(str, "BEGIN") == 0) {
786
1.73M
        icalcomponent *c;
787
1.73M
        icalcomponent_kind comp_kind;
788
789
1.73M
        parser->level++;
790
1.73M
        icalmemory_free_buffer(str);
791
1.73M
        str = parser_get_next_value(end, &end, value_kind);
792
793
1.73M
        comp_kind = icalenum_string_to_component_kind(str);
794
795
1.73M
        if (comp_kind == ICAL_X_COMPONENT) {
796
995
            c = icalcomponent_new_x(str);
797
1.73M
        } else {
798
1.73M
            c = icalcomponent_new(comp_kind);
799
1.73M
        }
800
801
1.73M
        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.73M
        pvl_push(parser->components, c);
808
809
1.73M
        parser->state = ICALPARSER_BEGIN_COMP;
810
811
1.73M
        icalmemory_free_buffer(str);
812
1.73M
        str = NULL;
813
1.73M
        return 0;
814
815
1.88M
    } else if (strcasecmp(str, "END") == 0) {
816
1.43M
        icalcomponent *tail;
817
818
1.43M
        parser->level--;
819
1.43M
        icalmemory_free_buffer(str);
820
1.43M
        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.43M
        parser->root_component = pvl_pop(parser->components);
824
825
1.43M
        tail = pvl_data(pvl_tail(parser->components));
826
827
1.43M
        if (tail != 0) {
828
1.22M
            icalcomponent_add_component(tail, parser->root_component);
829
1.22M
        }
830
831
1.43M
        tail = 0;
832
1.43M
        icalmemory_free_buffer(str);
833
1.43M
        str = NULL;
834
835
1.43M
        if (parser->level < 0) {
836
            // Encountered an END before any BEGIN, this must be invalid data
837
12.1k
            icalerror_warn("Encountered END before BEGIN");
838
839
12.1k
            parser->state = ICALPARSER_ERROR;
840
12.1k
            parser->level = 0;
841
12.1k
            return 0;
842
1.41M
        } else if (parser->level == 0) {
843
            /* Return the component if we are back to the 0th level */
844
192k
            icalcomponent *rtrn;
845
846
192k
            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
192k
            icalassert(pvl_count(parser->components) == 0);
854
855
192k
            parser->state = ICALPARSER_SUCCESS;
856
192k
            rtrn = parser->root_component;
857
192k
            parser->root_component = 0;
858
192k
            return rtrn;
859
860
1.22M
        } else {
861
1.22M
            parser->state = ICALPARSER_END_COMP;
862
1.22M
            return 0;
863
1.22M
        }
864
1.43M
    }
865
866
    /* There is no point in continuing if we have not seen a
867
       component yet */
868
869
448k
    if (pvl_data(pvl_tail(parser->components)) == 0) {
870
13.0k
        parser->state = ICALPARSER_ERROR;
871
13.0k
        icalmemory_free_buffer(str);
872
13.0k
        str = NULL;
873
13.0k
        return 0;
874
13.0k
    }
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
435k
    prop_kind = icalproperty_string_to_kind(str);
885
886
435k
    prop = icalproperty_new(prop_kind);
887
888
435k
    if (prop != 0) {
889
311k
        icalcomponent *tail = pvl_data(pvl_tail(parser->components));
890
891
311k
        if (prop_kind == ICAL_X_PROPERTY) {
892
34.9k
            icalproperty_set_x_name(prop, str);
893
34.9k
        }
894
895
311k
        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
311k
        value_kind = icalproperty_kind_to_value_kind(icalproperty_isa(prop));
900
901
311k
    } else {
902
123k
        icalcomponent *tail = pvl_data(pvl_tail(parser->components));
903
904
123k
        insert_error(parser, tail, str, "Parse error in property name",
905
123k
                     ICAL_XLICERRORTYPE_PROPERTYPARSEERROR);
906
907
123k
        tail = 0;
908
123k
        parser->state = ICALPARSER_ERROR;
909
123k
        icalmemory_free_buffer(str);
910
123k
        str = NULL;
911
123k
        return 0;
912
123k
    }
913
914
311k
    icalmemory_free_buffer(str);
915
311k
    str = NULL;
916
917
    /**********************************************************************
918
     * Handle parameter values
919
     **********************************************************************/
920
921
    /* Now, add any parameters to the last property */
922
923
614k
    while (pcount < MAXIMUM_ALLOWED_PARAMETERS) {
924
614k
        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
230k
            break;
929
230k
        }
930
931
384k
        icalmemory_free_buffer(str);
932
384k
        str = parser_get_next_parameter(end, &end);
933
384k
        strstriplt(str);
934
384k
        if (str != 0) {
935
311k
            char *name_heap = 0;
936
311k
            char *pvalue_heap = 0;
937
311k
            char name_stack[TMP_BUF_SIZE];
938
311k
            char pvalue_stack[TMP_BUF_SIZE];
939
311k
            char *name = name_stack;
940
311k
            char *pvalue = pvalue_stack;
941
942
311k
            icalparameter *param = 0;
943
311k
            icalparameter_kind kind;
944
311k
            icalcomponent *tail = pvl_data(pvl_tail(parser->components));
945
946
311k
            if (!parser_get_param_name_stack(str, name_stack, sizeof(name_stack),
947
311k
                                             pvalue_stack, sizeof(pvalue_stack))) {
948
9.89k
                name_heap = parser_get_param_name_heap(str, &pvalue_heap);
949
950
9.89k
                name = name_heap;
951
9.89k
                pvalue = pvalue_heap;
952
953
9.89k
                if (name_heap == 0) {
954
                    /* 'tail' defined above */
955
8.64k
                    insert_error(parser, tail, str, "Can't parse parameter name",
956
8.64k
                                 ICAL_XLICERRORTYPE_PARAMETERNAMEPARSEERROR);
957
8.64k
                    tail = 0;
958
8.64k
                    break;
959
8.64k
                }
960
9.89k
            }
961
962
303k
            kind = icalparameter_string_to_kind(name);
963
964
303k
            if (kind == ICAL_X_PARAMETER) {
965
32.8k
                param = icalparameter_new(ICAL_X_PARAMETER);
966
32.8k
                if (param != 0) {
967
32.8k
                    icalparameter_set_xname(param, name);
968
32.8k
                    icalparameter_set_xvalue(param, pvalue);
969
32.8k
                }
970
270k
            } else if (kind == ICAL_IANA_PARAMETER) {
971
433
                ical_unknown_token_handling tokHandlingSetting =
972
433
                    ical_get_unknown_token_handling_setting();
973
433
                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
433
                param = icalparameter_new(ICAL_IANA_PARAMETER);
986
987
433
                if (param != 0) {
988
433
                    icalparameter_set_xname(param, name);
989
433
                    icalparameter_set_xvalue(param, pvalue);
990
433
                }
991
269k
            } 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.2k
                    lastColon = nextSemicolon;
1025
17.2k
                }
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.8k
                    char *strStart = line + strlen(name) + 2;
1034
1035
17.8k
                    end = lastColon + 1;
1036
1037
17.8k
                    icalmemory_free_buffer(str);
1038
17.8k
                    str = make_segment(strStart, end - 1);
1039
17.8k
                }
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.3k
                    if (pvalue_heap) {
1045
528
                        icalmemory_free_buffer(pvalue_heap);
1046
528
                        pvalue_heap = 0;
1047
528
                        pvalue = 0;
1048
528
                    }
1049
17.3k
                    if (name_heap) {
1050
528
                        icalmemory_free_buffer(name_heap);
1051
528
                        name = 0;
1052
528
                    }
1053
17.3k
                    name_heap = parser_get_param_name_heap(str, &pvalue_heap);
1054
17.3k
                    pvalue = pvalue_heap;
1055
17.3k
                }
1056
19.9k
                param = icalparameter_new_from_value_string(kind, pvalue);
1057
249k
            } else if (kind != ICAL_NO_PARAMETER) {
1058
166k
                param = icalparameter_new_from_value_string(kind, pvalue);
1059
166k
            } 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
83.1k
                if (name_heap) {
1083
667
                    icalmemory_free_buffer(name_heap);
1084
667
                    name_heap = 0;
1085
667
                }
1086
83.1k
                if (pvalue_heap) {
1087
667
                    icalmemory_free_buffer(pvalue_heap);
1088
667
                    pvalue_heap = 0;
1089
667
                }
1090
83.1k
                icalmemory_free_buffer(str);
1091
83.1k
                str = NULL;
1092
83.1k
                continue;
1093
83.1k
#endif
1094
83.1k
            }
1095
1096
220k
            if (pvalue_heap) {
1097
2.86k
                icalmemory_free_buffer(pvalue_heap);
1098
2.86k
                pvalue_heap = 0;
1099
2.86k
            }
1100
1101
220k
            if (name_heap) {
1102
2.86k
                icalmemory_free_buffer(name_heap);
1103
2.86k
                name_heap = 0;
1104
2.86k
            }
1105
1106
220k
            if (param == 0) {
1107
                /* 'tail' defined above */
1108
14.5k
                insert_error(parser, tail, str, "Can't parse parameter value",
1109
14.5k
                             ICAL_XLICERRORTYPE_PARAMETERVALUEPARSEERROR);
1110
1111
14.5k
                tail = 0;
1112
14.5k
                parser->state = ICALPARSER_ERROR;
1113
1114
14.5k
                icalmemory_free_buffer(str);
1115
14.5k
                str = NULL;
1116
1117
14.5k
                continue;
1118
14.5k
            }
1119
1120
            /* If it is a VALUE parameter, set the kind of value */
1121
205k
            if (icalparameter_isa(param) == ICAL_VALUE_PARAMETER) {
1122
162k
                value_kind =
1123
162k
                    (icalvalue_kind)icalparameter_value_to_value_kind(
1124
162k
                        icalparameter_get_value(param));
1125
1126
162k
                if (!icalproperty_value_kind_is_valid(prop_kind, value_kind)) {
1127
1128
                    /* Ooops, invalid VALUE parameter, so reset the value_kind */
1129
1130
155k
                    const char *err_str = "Invalid VALUE type for property";
1131
155k
                    const char *prop_str = icalproperty_kind_to_string(prop_kind);
1132
155k
                    size_t tmp_buf_len = strlen(err_str) + strlen(prop_str) + 2;
1133
155k
                    char *tmp_buf = icalmemory_tmp_buffer(tmp_buf_len);
1134
155k
                    snprintf(tmp_buf, tmp_buf_len, "%s %s", err_str, prop_str);
1135
1136
155k
                    insert_error(parser, tail, str, tmp_buf,
1137
155k
                                 ICAL_XLICERRORTYPE_PARAMETERVALUEPARSEERROR);
1138
1139
155k
                    value_kind = icalproperty_kind_to_value_kind(prop_kind);
1140
1141
155k
                    icalparameter_free(param);
1142
155k
                    tail = 0;
1143
155k
                    parser->state = ICALPARSER_ERROR;
1144
1145
155k
                    icalmemory_free_buffer(str);
1146
155k
                    str = NULL;
1147
155k
                    pcount++;
1148
155k
                    continue;
1149
155k
                }
1150
162k
            }
1151
1152
            /* Everything is OK, so add the parameter */
1153
50.5k
            icalproperty_add_parameter(prop, param);
1154
50.5k
            tail = 0;
1155
50.5k
            icalmemory_free_buffer(str);
1156
50.5k
            str = NULL;
1157
50.5k
            pcount++;
1158
1159
72.2k
        } else {
1160
            /* str is NULL */
1161
72.2k
            break;
1162
72.2k
        }
1163
1164
384k
    }   /* 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
311k
    vcount = 0;
1175
719k
    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
719k
        icalmemory_free_buffer(str);
1182
719k
        str = NULL;
1183
1184
719k
        if (icalproperty_value_kind_is_multivalued(prop_kind, &value_kind)) {
1185
229k
            str = parser_get_next_value(end, &end, value_kind);
1186
490k
        } else {
1187
490k
            str = icalparser_get_value(end, &end, value_kind);
1188
490k
        }
1189
719k
        strstriplt(str);
1190
1191
719k
        if (str != 0) {
1192
1193
454k
            if (vcount > 0) {
1194
                /* Actually, only clone after the second value */
1195
157k
                icalproperty *clone = icalproperty_clone(prop);
1196
157k
                icalcomponent *tail = pvl_data(pvl_tail(parser->components));
1197
1198
157k
                icalcomponent_add_property(tail, clone);
1199
157k
                prop = clone;
1200
157k
                tail = 0;
1201
157k
            }
1202
1203
454k
            value = icalvalue_new_from_string(value_kind, str);
1204
1205
            /* Don't add properties without value */
1206
454k
            if (value == 0) {
1207
46.8k
                char temp[200]; /* HACK */
1208
1209
46.8k
                icalproperty_kind prop_kind = icalproperty_isa(prop);
1210
46.8k
                icalcomponent *tail = pvl_data(pvl_tail(parser->components));
1211
1212
46.8k
                snprintf(temp, sizeof(temp),
1213
46.8k
                         "Can't parse as %s value in %s property. Removing entire property",
1214
46.8k
                         icalvalue_kind_to_string(value_kind),
1215
46.8k
                         icalproperty_kind_to_string(prop_kind));
1216
1217
46.8k
                insert_error(parser, tail, str, temp, ICAL_XLICERRORTYPE_VALUEPARSEERROR);
1218
1219
                /* Remove the troublesome property */
1220
46.8k
                icalcomponent_remove_property(tail, prop);
1221
46.8k
                icalproperty_free(prop);
1222
46.8k
                prop = 0;
1223
46.8k
                tail = 0;
1224
46.8k
                parser->state = ICALPARSER_ERROR;
1225
1226
46.8k
                icalmemory_free_buffer(str);
1227
46.8k
                str = NULL;
1228
46.8k
                return 0;
1229
1230
407k
            } else {
1231
407k
                vcount++;
1232
407k
                icalproperty_set_value(prop, value);
1233
407k
            }
1234
407k
            icalmemory_free_buffer(str);
1235
407k
            str = NULL;
1236
1237
407k
        } 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
264k
            if (vcount == 0) {
1248
14.1k
                char temp[200]; /* HACK */
1249
1250
14.1k
                icalproperty_kind prop_kind = icalproperty_isa(prop);
1251
14.1k
                icalcomponent *tail = pvl_data(pvl_tail(parser->components));
1252
1253
14.1k
                snprintf(temp, sizeof(temp), "No value for %s property. Removing entire property",
1254
14.1k
                         icalproperty_kind_to_string(prop_kind));
1255
1256
14.1k
                insert_error(parser, tail, str, temp, ICAL_XLICERRORTYPE_VALUEPARSEERROR);
1257
1258
                /* Remove the troublesome property */
1259
14.1k
                icalcomponent_remove_property(tail, prop);
1260
14.1k
                icalproperty_free(prop);
1261
14.1k
                prop = 0;
1262
14.1k
                tail = 0;
1263
14.1k
                parser->state = ICALPARSER_ERROR;
1264
14.1k
                return 0;
1265
250k
            } else {
1266
1267
250k
                break;
1268
250k
            }
1269
264k
#endif
1270
264k
        }
1271
719k
    }
1272
1273
    /****************************************************************
1274
     * End of component parsing.
1275
     *****************************************************************/
1276
1277
250k
    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
250k
    } else {
1283
250k
        parser->state = ICALPARSER_IN_PROGRESS;
1284
250k
        return 0;
1285
250k
    }
1286
250k
}
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.23M
{
1332
4.23M
    int replace_cr = 0;
1333
4.23M
    char *n;
1334
4.23M
    size_t size;
1335
4.23M
    struct slg_data *data = (struct slg_data *)d;
1336
1337
4.23M
    if (data->pos == 0) {
1338
16.2k
        data->pos = data->str;
1339
1340
        /* Skip the UTF-8 marker at the beginning of the string */
1341
16.2k
        if (((unsigned char) data->pos[0]) == 0xEF &&
1342
16.2k
            ((unsigned char) data->pos[1]) == 0xBB &&
1343
16.2k
            ((unsigned char) data->pos[2]) == 0xBF) {
1344
2
            data->pos += 3;
1345
2
        }
1346
16.2k
    }
1347
1348
    /* If the pointer is at the end of the string, we are done */
1349
4.23M
    if (*(data->pos) == 0) {
1350
32.5k
        return 0;
1351
32.5k
    }
1352
1353
4.20M
    n = strchr(data->pos, '\n');
1354
1355
4.20M
    if (n == 0) {
1356
257k
        n = strchr(data->pos, '\r'); /* support malformed input with only CR and no LF
1357
                                        (e.g. from Kerio Connect Server) */
1358
257k
        if(n == 0) {
1359
251k
            size = strlen(data->pos);
1360
251k
        } else {
1361
5.84k
            n++; /* include CR in output - will be replaced by LF later on */
1362
5.84k
            replace_cr = 1;
1363
5.84k
            size = (size_t) (ptrdiff_t) (n - data->pos);
1364
5.84k
        }
1365
3.94M
    } else {
1366
3.94M
        n++;    /* include newline in output */
1367
3.94M
        size = (size_t) (ptrdiff_t) (n - data->pos);
1368
3.94M
    }
1369
1370
4.20M
    if (size > buf_size - 1) {
1371
415k
        size = buf_size - 1;
1372
415k
    }
1373
1374
#if defined(__GNUC__) && !defined(__clang__)
1375
#pragma GCC diagnostic push
1376
#pragma GCC diagnostic ignored "-Wstringop-truncation"
1377
#endif
1378
4.20M
    strncpy(out, data->pos, size);
1379
#if defined(__GNUC__) && !defined(__clang__)
1380
#pragma GCC diagnostic pop
1381
#endif
1382
1383
4.20M
    if(replace_cr) {
1384
5.84k
        *(out + size - 1) = '\n';
1385
5.84k
    }
1386
4.20M
    *(out + size) = '\0';
1387
1388
4.20M
    data->pos += size;
1389
1390
4.20M
    return out;
1391
4.23M
}
1392
1393
icalcomponent *icalparser_parse_string(const char *str)
1394
16.2k
{
1395
16.2k
    icalcomponent *c;
1396
16.2k
    struct slg_data d;
1397
16.2k
    icalparser *p;
1398
1399
16.2k
    icalerrorstate es = icalerror_get_error_state(ICAL_MALFORMEDDATA_ERROR);
1400
1401
16.2k
    d.pos = 0;
1402
16.2k
    d.str = str;
1403
1404
16.2k
    p = icalparser_new();
1405
16.2k
    if (!p)
1406
0
        return NULL;
1407
1408
16.2k
    icalparser_set_gen_data(p, &d);
1409
1410
16.2k
    icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR, ICAL_ERROR_NONFATAL);
1411
1412
16.2k
    c = icalparser_parse(p, icalparser_string_line_generator);
1413
1414
16.2k
    icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR, es);
1415
1416
16.2k
    icalparser_free(p);
1417
1418
16.2k
    return c;
1419
16.2k
}
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
}