21 #include "icalerror_p.h"
27 #include "icalproperty_p.h"
28 #include "icalpvl_p.h"
36 #define TMP_BUF_SIZE 80
41 struct icalparser_impl {
44 int continuation_line;
46 char temp[TMP_BUF_SIZE];
47 icalcomponent *root_component;
53 icalpvl_list components;
61 static void strstriplt(
char *buf)
74 while ((buf[0] != 0) && (isspace((
unsigned char)buf[len - 1]))) {
81 while (isspace((
unsigned char)buf[a])) {
85 memmove(buf, &buf[a], len - a + 1);
91 struct icalparser_impl *impl = 0;
98 impl->root_component = 0;
99 impl->components = icalpvl_newlist();
102 impl->tmp_buf_size = TMP_BUF_SIZE;
103 impl->buffer_full = 0;
104 impl->continuation_line = 0;
106 impl->error_count = 0;
107 memset(impl->temp, 0, TMP_BUF_SIZE);
109 return (icalparser *)impl;
116 if (parser->root_component) {
120 while ((c = icalpvl_pop(parser->components)) != 0) {
124 icalpvl_free(parser->components);
131 parser->line_gen_data = data;
134 static char *parser_get_next_char(
char c,
char *str,
int qm)
141 size_t charCount = 0;
143 while (next_char !=
'\0' && charCount++ < max_search_chars) {
144 if ((prev_char !=
'\0') && (prev_char !=
'\\')) {
145 if (qm == 1 && next_char ==
'"') {
147 quote_mode = !quote_mode;
148 }
else if (quote_mode == 0 && next_char == c) {
155 prev_char = next_char;
163 static char *make_segment(
const char *start,
const char *end)
165 #if defined(__GNUC__) && !defined(__clang__)
166 #pragma GCC diagnostic push
167 #pragma GCC diagnostic ignored "-Wstringop-truncation"
168 #pragma GCC diagnostic ignored "-Wstringop-overflow"
171 size_t size = (size_t)(ptrdiff_t)(end - start);
174 strncpy(buf, start, size);
178 while ((tmp >= buf) && ((*tmp ==
'\0') || iswspace((wint_t)*tmp))) {
184 #if defined(__GNUC__) && !defined(__clang__)
185 #pragma GCC diagnostic pop
189 static char *parser_get_prop_name(
char *line,
char **end)
195 p = parser_get_next_char(
';', line, 1);
196 v = parser_get_next_char(
':', line, 1);
197 if (p == 0 && v == 0) {
203 if (v != 0 && (p == 0 || p > v)) {
204 str = make_segment(line, v);
207 str = make_segment(line, p);
214 static bool parser_get_param_name_stack(
char *line,
char *name,
size_t name_length,
215 char *value,
size_t value_length)
218 size_t requested_name_length, requested_value_length;
221 next = parser_get_next_char(
'=', line, 1);
227 requested_name_length = (size_t)(ptrdiff_t)(next - line);
231 if (requested_name_length >= name_length - 1) {
235 strncpy(name, line, requested_name_length);
236 name[requested_name_length] = 0;
239 int is_multivalued = 0;
245 if (next[0] ==
'"' && !is_multivalued) {
249 const char *end_quote = (*next ==
'"') ? next : parser_get_next_char(
'"', next, 0);
250 if (end_quote == 0) {
254 requested_value_length = (size_t)(ptrdiff_t)(end_quote - next);
256 requested_value_length = strlen(next);
261 if (requested_value_length >= value_length - 1) {
265 memcpy(value, next, requested_value_length);
266 value[requested_value_length] = 0;
268 if (!is_multivalued) {
275 static char *parser_get_param_name_heap(
char *line,
char **end)
284 next = parser_get_next_char(
'=', line, 1);
290 str = make_segment(line, next);
293 int is_multivalued = 0;
297 if (**end ==
'"' && !is_multivalued) {
299 next = (**end ==
'"') ? *end : parser_get_next_char(
'"', *end, 0);
305 *end = make_segment(*end, next);
308 *end = make_segment(*end, *end + strlen(*end));
311 if (!is_multivalued) {
318 static char *icalparser_get_value(
char *line,
char **end, icalvalue_kind kind)
321 size_t length = strlen(line);
329 *end = line + length;
330 str = make_segment(line, *end);
341 static char *parser_get_next_value(
char *line,
char **end, icalvalue_kind kind)
346 size_t length = strlen(line);
349 if (line[0] ==
'\"' && line[length - 1] ==
'\"') {
356 next = parser_get_next_char(
',', p, 1);
364 if (kind == ICAL_RECUR_VALUE) {
365 if (next != 0 && (*end + length) > next + 5 && strncmp(next,
"FREQ", 4) == 0) {
368 }
else if (next != 0) {
376 else if (kind == ICAL_QUERY_VALUE || kind == ICAL_X_VALUE) {
388 if ((next != 0 && *(next - 1) ==
'\\') || (next != 0 && *(next - 3) ==
'\\'))
398 next = (
char *)(
size_t)line + length;
408 str = make_segment(line, next);
412 static char *parser_get_next_parameter(
char *line,
char **end)
417 v = parser_get_next_char(
':', line, 1);
418 next = parser_get_next_char(
';', line, 1);
423 if (next == 0 || next > v) {
424 next = parser_get_next_char(
':', line, 1);
428 char *str = make_segment(line, next);
438 icalparser_line_gen_func line_gen_func)
442 size_t buf_size = parser->tmp_buf_size;
464 if (parser->temp[0] !=
'\0') {
468 if (parser->temp[parser->tmp_buf_size - 1] == 0 &&
469 parser->temp[parser->tmp_buf_size - 2] !=
'\n' &&
470 parser->temp[parser->tmp_buf_size - 2] != 0) {
471 parser->buffer_full = 1;
473 parser->buffer_full = 0;
477 if (parser->continuation_line == 1) {
479 parser->continuation_line = 0;
482 if (*(line_p - 1) ==
'\r') {
493 parser->temp[0] =
'\0';
496 parser->temp[parser->tmp_buf_size - 1] = 1;
499 if ((*line_gen_func)(parser->temp, parser->tmp_buf_size, parser->line_gen_data) == 0) {
503 if (parser->temp[0] ==
'\0') {
504 if (line[0] !=
'\0') {
520 if (line_p > line + 1 && *(line_p - 1) ==
'\n' && (parser->temp[0] ==
' ' || parser->temp[0] ==
'\t')) {
521 parser->continuation_line = 1;
523 }
else if (parser->buffer_full == 1) {
533 if (line_p > line + 1 && *(line_p - 1) ==
'\n') {
534 *(line_p - 1) =
'\0';
535 if (*(line_p - 2) ==
'\r') {
536 *(line_p - 2) =
'\0';
543 while ((*line_p ==
'\0' || iswspace((wint_t)*line_p)) && line_p > line) {
551 static void insert_error(icalparser *parser, icalcomponent *comp,
const char *text,
552 const char *message, icalparameter_xlicerrortype type)
561 snprintf(temp, 1024,
"%s:", message);
563 snprintf(temp, 1024,
"%s: %s", message, text);
567 icalproperty *errProp = icalproperty_vanew_xlicerror(temp, icalparameter_new_xlicerrortype(type), (
void *)0);
570 parser->error_count++;
573 static bool line_is_blank(
const char *line)
577 for (i = 0; *(line + i) != 0; i++) {
578 char c = *(line + i);
580 if (c !=
' ' && c !=
'\n' && c !=
'\t') {
589 icalparser_line_gen_func line_gen_func)
593 icalcomponent *root = 0;
597 icalerror_check_arg_rz((parser != 0),
"parser");
603 size_t parse_failures = 0;
608 if (icalcomponent_get_parent(c) != 0) {
612 icalassert(parser->root_component == 0);
613 icalassert(icalpvl_count(parser->components) == 0);
643 }
while (cont && parse_failures < max_parse_failures);
657 icalproperty_kind prop_kind;
659 icalvalue_kind value_kind = ICAL_NO_VALUE;
661 icalerror_check_arg_rz((parser != 0),
"parser");
668 if (line_is_blank(line) == 1) {
673 static const unsigned char is_icalctrl[256] = {
674 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1,
675 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
676 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
677 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
678 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
679 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
680 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
681 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
682 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
683 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
684 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
685 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
686 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
687 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
688 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
689 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
692 for (c = d = line; *c; c++) {
693 if (!is_icalctrl[(
unsigned char)*c]) {
698 icalcomponent *tail = icalpvl_data(icalpvl_tail(parser->components));
702 "Content line contains invalid CONTROL characters",
703 ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
718 str = parser_get_prop_name(line, &end);
720 if (str == 0 || *str ==
'\0') {
722 icalcomponent *tail = icalpvl_data(icalpvl_tail(parser->components));
727 "Got a data line, but could not find a property name or component begin tag",
728 ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
742 if (strcasecmp(str,
"BEGIN") == 0) {
743 icalcomponent *c = NULL;
748 str = parser_get_next_value(end, &end, value_kind);
767 insert_error(parser, c, str,
"Parse error in component name",
768 ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
771 icalpvl_push(parser->components, c);
779 }
else if (strcasecmp(str,
"END") == 0) {
784 str = parser_get_next_value(end, &end, value_kind);
787 parser->root_component = icalpvl_pop(parser->components);
789 tail = icalpvl_data(icalpvl_tail(parser->components));
798 if (parser->level < 0) {
800 icalerror_warn(
"Encountered END before BEGIN");
804 }
else if (parser->level == 0) {
808 if (icalpvl_count(parser->components) != 0) {
811 icalpvl_push(parser->components, parser->root_component);
815 icalassert(icalpvl_count(parser->components) == 0);
818 rtrn = parser->root_component;
819 parser->root_component = 0;
831 if (icalpvl_data(icalpvl_tail(parser->components)) == 0) {
848 if (prop_kind == ICAL_IANA_PROPERTY &&
850 prop_kind = ICAL_NO_PROPERTY;
856 icalcomponent *tail = icalpvl_data(icalpvl_tail(parser->components));
858 if (prop_kind == ICAL_X_PROPERTY) {
860 }
else if (prop_kind == ICAL_IANA_PROPERTY) {
871 icalcomponent *tail = icalpvl_data(icalpvl_tail(parser->components));
873 insert_error(parser, tail, str,
"Parse error in property name",
874 ICAL_XLICERRORTYPE_PROPERTYPARSEERROR);
891 while (pcount < maximum_allowed_parameters) {
892 if (*(end - 1) ==
':') {
900 str = parser_get_next_parameter(end, &end);
904 char *pvalue_heap = 0;
905 char name_stack[TMP_BUF_SIZE];
906 char pvalue_stack[TMP_BUF_SIZE];
907 char *name = name_stack;
908 char *pvalue = pvalue_stack;
910 icalparameter *param = 0;
911 icalparameter_kind kind;
912 icalcomponent *tail = icalpvl_data(icalpvl_tail(parser->components));
914 if (!parser_get_param_name_stack(str, name_stack,
sizeof(name_stack),
915 pvalue_stack,
sizeof(pvalue_stack))) {
916 name_heap = parser_get_param_name_heap(str, &pvalue_heap);
919 pvalue = pvalue_heap;
921 if (name_heap == 0) {
923 insert_error(parser, tail, str,
"Can't parse parameter name",
924 ICAL_XLICERRORTYPE_PARAMETERNAMEPARSEERROR);
931 if (kind == ICAL_X_PARAMETER) {
937 }
else if (kind == ICAL_IANA_PARAMETER) {
956 }
else if (kind == ICAL_TZID_PARAMETER && *(end - 1) !=
';') {
970 char *nextColon = end;
971 char *nextSemicolon = parser_get_next_char(
';', end, 1);
975 nextColon = parser_get_next_char(
':', nextColon, 1);
978 lastColon = nextColon;
982 if (lastColon && nextSemicolon && nextSemicolon < lastColon) {
989 lastColon = nextSemicolon;
997 if (lastColon && *(lastColon + 1) != 0) {
998 const char *strStart = line + strlen(name) + 2;
1000 end = lastColon + 1;
1003 str = make_segment(strStart, end - 1);
1007 if (!parser_get_param_name_stack(str, name_stack,
sizeof(name_stack),
1008 pvalue_stack,
sizeof(pvalue_stack))) {
1014 name_heap = parser_get_param_name_heap(str, &pvalue_heap);
1015 pvalue = pvalue_heap;
1018 }
else if (kind != ICAL_NO_PARAMETER) {
1027 insert_error(parser, tail, str,
"Can't parse parameter name",
1028 ICAL_XLICERRORTYPE_PARAMETERNAMEPARSEERROR);
1055 insert_error(parser, tail, str,
"Can't parse parameter value",
1056 ICAL_XLICERRORTYPE_PARAMETERVALUEPARSEERROR);
1071 icalparameter_get_value(param));
1073 if (!icalproperty_value_kind_is_valid(prop_kind, value_kind)) {
1076 const char *err_str =
"Invalid VALUE type for property";
1078 size_t tmp_buf_len = strlen(err_str) + strlen(prop_str) + 2;
1080 snprintf(tmp_buf, tmp_buf_len,
"%s %s", err_str, prop_str);
1082 insert_error(parser, tail, str, tmp_buf,
1083 ICAL_XLICERRORTYPE_PARAMETERVALUEPARSEERROR);
1121 while (vcount < maximum_property_values) {
1130 if (icalproperty_value_kind_is_multivalued(prop_kind, &value_kind)) {
1131 str = parser_get_next_value(end, &end, value_kind);
1133 str = icalparser_get_value(end, &end, value_kind);
1141 icalcomponent *tail = icalpvl_data(icalpvl_tail(parser->components));
1154 icalcomponent *tail = icalpvl_data(icalpvl_tail(parser->components));
1156 snprintf(temp,
sizeof(temp),
1157 "Can't parse as %s value in %s property. Removing entire property",
1161 insert_error(parser, tail, str, temp, ICAL_XLICERRORTYPE_VALUEPARSEERROR);
1192 icalcomponent *tail = icalpvl_data(icalpvl_tail(parser->components));
1194 snprintf(temp,
sizeof(temp),
"No value for %s property. Removing entire property",
1197 insert_error(parser, tail, str, temp, ICAL_XLICERRORTYPE_VALUEPARSEERROR);
1215 if (icalpvl_data(icalpvl_tail(parser->components)) == 0 && parser->level == 0) {
1219 return parser->root_component;
1228 return parser->state;
1233 icalcomponent *tail;
1235 icalerror_check_arg_rz((parser != 0),
"parser");
1240 while ((tail = icalpvl_data(icalpvl_tail(parser->components))) != 0) {
1241 insert_error(parser, tail,
" ",
1242 "Missing END tag for this component. Closing component at end of input.",
1243 ICAL_XLICERRORTYPE_COMPONENTPARSEERROR);
1245 parser->root_component = icalpvl_pop(parser->components);
1246 tail = icalpvl_data(icalpvl_tail(parser->components));
1248 if (tail != 0 && parser->root_component != NULL) {
1249 if (icalcomponent_get_parent(parser->root_component) != 0) {
1251 "icalparser_clean is trying to attach a component for the second time");
1258 return parser->root_component;
1271 struct slg_data *data = (
struct slg_data *)d;
1273 if (data->pos == 0) {
1274 data->pos = data->str;
1275 if (data->pos && strlen(data->pos) > 2) {
1277 if (((
unsigned char)data->pos[0]) == 0xEF &&
1278 ((
unsigned char)data->pos[1]) == 0xBB &&
1279 ((
unsigned char)data->pos[2]) == 0xBF) {
1286 if (!data->pos || *(data->pos) == 0) {
1290 n = strchr(data->pos,
'\n');
1293 n = strchr(data->pos,
'\r');
1296 size = strlen(data->pos);
1300 size = (size_t)(ptrdiff_t)(n - data->pos);
1304 size = (size_t)(ptrdiff_t)(n - data->pos);
1307 if (size > buf_size - 1) {
1308 size = buf_size - 1;
1311 #if defined(__GNUC__) && !defined(__clang__)
1312 #pragma GCC diagnostic push
1313 #pragma GCC diagnostic ignored "-Wstringop-truncation"
1314 #pragma GCC diagnostic ignored "-Wstringop-overflow"
1316 strncpy(out, data->pos, size);
1317 #if defined(__GNUC__) && !defined(__clang__)
1318 #pragma GCC diagnostic pop
1322 *(out + size - 1) =
'\n';
1324 *(out + size) =
'\0';
1362 return icalparser_ctrl_g;
1367 icalparser_ctrl_g = ctrl;
bool icalerror_get_errors_are_fatal(void)
Determine if errors are fatal.
icalcomponent_kind icalcomponent_string_to_kind(const char *string)
const char * icalproperty_kind_to_string(icalproperty_kind kind)
ical_unknown_token_handling ical_get_unknown_token_handling_setting(void)
void icalparameter_decode_value(char *value)
icalcomponent * icalparser_parse_string(const char *str)
Parses a string and returns the parsed icalcomponent.
icalparameter_kind icalparameter_isa(const icalparameter *parameter)
void icalparameter_free(icalparameter *param)
Frees an icalparameter object.
void icalcomponent_remove_property(icalcomponent *component, icalproperty *property)
icalvalue_kind icalparameter_kind_value_kind(const icalparameter_kind kind, int *is_multivalued)
Common memory management routines.
icalproperty_kind icalproperty_isa(const icalproperty *p)
icalparser_state
Represents the current state of the parser.
Defines the data structure representing iCalendar parameter values.
icalproperty_kind icalproperty_string_to_kind(const char *string)
void * icalmemory_tmp_buffer(size_t size)
Creates a new temporary buffer on the ring and returns it.
char * icalparser_string_line_generator(char *out, size_t buf_size, void *d)
void icalcomponent_add_component(icalcomponent *parent, icalcomponent *child)
void icalmemory_free_buffer(void *buf)
Releases a buffer.
void icalparser_set_gen_data(icalparser *parser, void *data)
Sets the data that icalparser_parse will give to the line_gen_func as the parameter 'd'...
icalerrorstate
Determine if an error is fatal or non-fatal.
enum icalparser_ctrl icalparser_get_ctrl(void)
Get the current parser setting how to handle CONTROL characters.
size_t icallimit_get(icallimits_kind kind)
void icalerror_set_errno(icalerrorenum x)
Sets the icalerrno to a given error.
icalproperty * icalproperty_new(icalproperty_kind kind)
void icalproperty_add_parameter(icalproperty *p, icalparameter *parameter)
icalerrorstate icalerror_get_error_state(icalerrorenum error)
Gets the error state (severity) for a given error.
void icalproperty_set_x_name(icalproperty *prop, const char *name)
void icalparser_free(icalparser *parser)
Frees an icalparser object.
Error handling for libical.
void icalproperty_free(icalproperty *p)
void icalparameter_set_xvalue(icalparameter *param, const char *v)
Sets the X-value of param to v.
void icalproperty_set_value(icalproperty *p, icalvalue *value)
char * icalparser_get_line(icalparser *parser, icalparser_line_gen_func line_gen_func)
Given a line generator function, returns a single iCal content line.
icalparser_state icalparser_get_state(const icalparser *parser)
Returns current state of the icalparser.
icalvalue_kind icalproperty_kind_to_value_kind(icalproperty_kind kind)
void icalparser_set_ctrl(enum icalparser_ctrl ctrl)
Set the parser setting how to handle CONTROL characters.
void * icalmemory_new_buffer(size_t size)
Creates new buffer with the specified size.
void icalmemory_append_string(char **buf, char **pos, size_t *buf_size, const char *string)
Appends a string to a buffer.
Defines the interface for getting/setting internal library limits.
icalcomponent * icalcomponent_new_iana(const char *iana_name)
Defines the data structure representing iCalendar parameters.
icalcomponent * icalcomponent_new_x(const char *x_name)
icalcomponent * icalcomponent_new(icalcomponent_kind kind)
icalvalue_kind icalparameter_value_to_value_kind(icalparameter_value value)
void icalproperty_set_iana_name(icalproperty *prop, const char *name)
const char * icalvalue_kind_to_string(const icalvalue_kind kind)
icalproperty * icalproperty_clone(const icalproperty *old)
icalcomponent_kind icalcomponent_isa(const icalcomponent *component)
ical_unknown_token_handling
void icalparameter_set_xname(icalparameter *param, const char *v)
Sets the X-name of param to v.
icalparameter * icalparameter_new_from_value_string(icalparameter_kind kind, const char *value)
Creates new icalparameter of a given kind with a given value.
icalvalue * icalvalue_new(icalvalue_kind kind)
icalparser * icalparser_new(void)
Creates a new icalparser.
icalparameter_kind icalparameter_string_to_kind(const char *string)
Returns the icalparameter_kind for a given string.
void icalerror_set_error_state(icalerrorenum error, icalerrorstate state)
Sets the icalerrorstate for a given icalerrorenum error.
bool icalproperty_get_allow_empty_properties(void)
icalparser_ctrl
Defines how to handle invalid CONTROL characters in content lines.
icalparameter * icalparameter_new(icalparameter_kind kind)
Creates new icalparameter object.
void icalcomponent_free(icalcomponent *c)
void icalcomponent_add_property(icalcomponent *component, icalproperty *property)
icalcomponent * icalparser_add_line(icalparser *parser, char *line)
Adds a single line to be parsed by the icalparser.
icalcomponent * icalparser_parse(icalparser *parser, icalparser_line_gen_func line_gen_func)
Message oriented parsing.
icalvalue * icalvalue_new_from_string(icalvalue_kind kind, const char *str)
icalcomponent * icalparser_clean(icalparser *parser)
Cleans out an icalparser and returns whatever it has parsed so far.