Coverage Report

Created: 2024-09-30 06:41

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