static void managesieve_parser_save_arg(struct managesieve_parser *parser, const unsigned char *data, size_t size) { struct managesieve_arg *arg; char *str; arg = managesieve_arg_create(parser); switch (parser->cur_type) { case ARG_PARSE_ATOM: /* simply save the string */ arg->type = MANAGESIEVE_ARG_ATOM; arg->_data.str = p_strndup(parser->pool, data, size); arg->str_len = size; break; case ARG_PARSE_STRING: /* data is quoted and may contain escapes. */ if ((parser->flags & MANAGESIEVE_PARSE_FLAG_STRING_STREAM) != 0) { arg->type = MANAGESIEVE_ARG_STRING_STREAM; arg->_data.str_stream = parser->str_stream; } else { i_assert(size > 0); arg->type = MANAGESIEVE_ARG_STRING; str = p_strndup(parser->pool, data+1, size-1); /* remove the escapes */ if (parser->str_first_escape >= 0 && (parser->flags & MANAGESIEVE_PARSE_FLAG_NO_UNESCAPE) == 0) { /* -1 because we skipped the '"' prefix */ str_unescape(str + parser->str_first_escape-1); } arg->_data.str = str; arg->str_len = strlen(str); } break; case ARG_PARSE_LITERAL_DATA: if ((parser->flags & MANAGESIEVE_PARSE_FLAG_STRING_STREAM) != 0) { arg->type = MANAGESIEVE_ARG_STRING_STREAM; arg->_data.str_stream = parser->str_stream; } else if ((parser->flags & MANAGESIEVE_PARSE_FLAG_LITERAL_TYPE) != 0) { arg->type = MANAGESIEVE_ARG_LITERAL; arg->_data.str = p_strndup(parser->pool, data, size); arg->str_len = size; } else { arg->type = MANAGESIEVE_ARG_STRING; arg->_data.str = p_strndup(parser->pool, data, size); arg->str_len = size; } break; default: i_unreached(); } parser->cur_type = ARG_PARSE_NONE; }
int rfc822_parse_content_param(struct rfc822_parser_context *ctx, const char **key_r, const char **value_r) { string_t *tmp; size_t value_pos; int ret; /* .. := *(";" parameter) parameter := attribute "=" value attribute := token value := token / quoted-string */ *key_r = NULL; *value_r = NULL; if (ctx->data == ctx->end) return 0; if (*ctx->data != ';') return -1; ctx->data++; if (rfc822_skip_lwsp(ctx) <= 0) return -1; tmp = t_str_new(64); if (rfc822_parse_mime_token(ctx, tmp) <= 0) return -1; str_append_c(tmp, '\0'); value_pos = str_len(tmp); if (*ctx->data != '=') return -1; ctx->data++; if ((ret = rfc822_skip_lwsp(ctx)) <= 0) { /* broken / no value */ } else if (*ctx->data == '"') { ret = rfc822_parse_quoted_string(ctx, tmp); (void)str_unescape(str_c_modifiable(tmp) + value_pos); } else if (ctx->data != ctx->end && *ctx->data == '=') { /* workaround for broken input: name==?utf-8?b?...?= */ while (ctx->data != ctx->end && *ctx->data != ';' && *ctx->data != ' ' && *ctx->data != '\t' && *ctx->data != '\r' && *ctx->data != '\n') { str_append_c(tmp, *ctx->data); ctx->data++; } } else { ret = rfc822_parse_mime_token(ctx, tmp); } *key_r = str_c(tmp); *value_r = *key_r + value_pos; return ret < 0 ? -1 : 1; }
ADTList list_from_str(char *str, void *(*from_str)(char *)) { ADTList list = list_new(); int len = (int) strlen(str); for (int i = 0, escape = 0, start = 0; i <= len; i++) { if ((str[i] == ',' && !escape) || (i == len && len > 0)) { char *substr = str_substr(str, start, i - start); char *unescaped = str_unescape(substr); list_append(list, from_str(unescaped)); start = i + 1; free(unescaped); free(substr); } if (str[i] == '\\' && !escape) escape = 1; else escape = 0; } return list; }
int str_unescape_next(const char **str, const char **unescaped_r) { const char *p; char *escaped; bool esc_found = FALSE; for (p = *str; *p != '\0'; p++) { if (*p == '"') break; else if (*p == '\\') { if (p[1] == '\0') return -1; esc_found = TRUE; p++; } } if (*p != '"') return -1; escaped = p_strdup_until(unsafe_data_stack_pool, *str, p); *str = p+1; *unescaped_r = !esc_found ? escaped : str_unescape(escaped); return 0; }
static bool lsb_distro_get(const char *path, const char **name_r) { const char *data, *const *p, *str, *end; if (!readfile(path, &data)) return FALSE; for (p = t_strsplit(data, "\n"); *p != NULL; p++) { if (strncmp(*p, "DISTRIB_DESCRIPTION=", 20) == 0) break; } if (*p == NULL) return FALSE; str = t_strcut(*p + 20, '\n'); if (*str != '"') *name_r = str; else { end = strrchr(++str, '"'); *name_r = str_unescape(p_strdup_until(unsafe_data_stack_pool, str, end)); } return TRUE; }
static void imap_parser_save_arg(struct imap_parser *parser, const unsigned char *data, size_t size) { struct imap_arg *arg; char *str; arg = imap_arg_create(parser); switch (parser->cur_type) { case ARG_PARSE_ATOM: case ARG_PARSE_TEXT: if (size == 3 && i_memcasecmp(data, "NIL", 3) == 0) { /* NIL argument. it might be an actual NIL, but if we're reading astring, it's an atom and we can't lose its case. */ arg->type = IMAP_ARG_NIL; } else { /* simply save the string */ arg->type = IMAP_ARG_ATOM; } arg->_data.str = imap_parser_strdup(parser, data, size); arg->str_len = size; break; case ARG_PARSE_STRING: /* data is quoted and may contain escapes. */ i_assert(size > 0); arg->type = IMAP_ARG_STRING; str = p_strndup(parser->pool, data+1, size-1); /* remove the escapes */ if (parser->str_first_escape >= 0 && (parser->flags & IMAP_PARSE_FLAG_NO_UNESCAPE) == 0) { /* -1 because we skipped the '"' prefix */ (void)str_unescape(str + parser->str_first_escape-1); } arg->_data.str = str; arg->str_len = strlen(str); break; case ARG_PARSE_LITERAL_DATA: if ((parser->flags & IMAP_PARSE_FLAG_LITERAL_SIZE) != 0) { /* save literal size */ arg->type = parser->literal_nonsync ? IMAP_ARG_LITERAL_SIZE_NONSYNC : IMAP_ARG_LITERAL_SIZE; arg->_data.literal_size = parser->literal_size; arg->literal8 = parser->literal8; break; } /* fall through */ case ARG_PARSE_LITERAL_DATA_FORCED: if ((parser->flags & IMAP_PARSE_FLAG_LITERAL_TYPE) != 0) arg->type = IMAP_ARG_LITERAL; else arg->type = IMAP_ARG_STRING; arg->_data.str = imap_parser_strdup(parser, data, size); arg->literal8 = parser->literal8; arg->str_len = size; break; default: i_unreached(); } parser->cur_type = ARG_PARSE_NONE; }
bool settings_read_i(const char *path, const char *section, settings_callback_t *callback, settings_section_callback_t *sect_callback, void *context, const char **error_r) { /* pretty horrible code, but v2.0 will have this rewritten anyway.. */ struct input_stack root, *input; const char *errormsg, *next_section, *name, *last_section_path = NULL; char *line, *key, *p, quote; string_t *full_line; size_t len; int fd, last_section_line = 0, skip, sections, root_section; fd = open(path, O_RDONLY); if (fd < 0) { *error_r = t_strdup_printf( "Can't open configuration file %s: %m", path); return FALSE; } if (section == NULL) { skip = 0; next_section = NULL; } else { skip = 1; next_section = t_strcut(section, '/'); } memset(&root, 0, sizeof(root)); root.path = path; input = &root; full_line = t_str_new(512); sections = 0; root_section = 0; errormsg = NULL; input->input = i_stream_create_fd_autoclose(&fd, (size_t)-1); i_stream_set_return_partial_line(input->input, TRUE); prevfile: while ((line = i_stream_read_next_line(input->input)) != NULL) { input->linenum++; /* @UNSAFE: line is modified */ /* skip whitespace */ while (IS_WHITE(*line)) line++; /* ignore comments or empty lines */ if (*line == '#' || *line == '\0') continue; /* strip away comments. pretty kludgy way really.. */ for (p = line; *p != '\0'; p++) { if (*p == '\'' || *p == '"') { quote = *p; for (p++; *p != quote && *p != '\0'; p++) { if (*p == '\\' && p[1] != '\0') p++; } if (*p == '\0') break; } else if (*p == '#') { if (!IS_WHITE(p[-1])) { i_warning("Configuration file %s line %u: " "Ambiguous '#' character in line, treating it as comment. " "Add a space before it to remove this warning.", input->path, input->linenum); } *p = '\0'; break; } } /* remove whitespace from end of line */ len = strlen(line); while (IS_WHITE(line[len-1])) len--; line[len] = '\0'; if (len > 0 && line[len-1] == '\\') { /* continues in next line */ len--; while (IS_WHITE(line[len-1])) len--; str_append_n(full_line, line, len); str_append_c(full_line, ' '); continue; } if (str_len(full_line) > 0) { str_append(full_line, line); line = str_c_modifiable(full_line); } /* a) key = value b) section_type [section_name] { c) } */ key = line; while (!IS_WHITE(*line) && *line != '\0' && *line != '=') line++; if (IS_WHITE(*line)) { *line++ = '\0'; while (IS_WHITE(*line)) line++; } if (strcmp(key, "!include_try") == 0 || strcmp(key, "!include") == 0) { if (settings_include(fix_relative_path(line, input), &input, strcmp(key, "!include_try") == 0, &errormsg) == 0) goto prevfile; } else if (*line == '=') { /* a) */ *line++ = '\0'; while (IS_WHITE(*line)) line++; len = strlen(line); if (len > 0 && ((*line == '"' && line[len-1] == '"') || (*line == '\'' && line[len-1] == '\''))) { line[len-1] = '\0'; line = str_unescape(line+1); } errormsg = skip ? NULL : callback(key, line, context); } else if (strcmp(key, "}") != 0 || *line != '\0') { /* b) + errors */ line[-1] = '\0'; if (*line == '{') name = ""; else { name = line; while (!IS_WHITE(*line) && *line != '\0') line++; if (*line != '\0') { *line++ = '\0'; while (IS_WHITE(*line)) line++; } } if (*line != '{') errormsg = "Expecting '='"; else { sections++; if (next_section != NULL && strcmp(next_section, name) == 0) { section += strlen(next_section); if (*section == '\0') { skip = 0; next_section = NULL; root_section = sections; } else { i_assert(*section == '/'); section++; next_section = t_strcut(section, '/'); } } if (skip > 0) skip++; else { skip = sect_callback == NULL ? 1 : !sect_callback(key, name, context, &errormsg); if (errormsg != NULL && last_section_line != 0) { errormsg = t_strdup_printf( SECTION_ERRORMSG, errormsg, last_section_path, last_section_line); } } last_section_path = input->path; last_section_line = input->linenum; } } else { /* c) */ if (sections == 0) errormsg = "Unexpected '}'"; else { if (skip > 0) skip--; else { i_assert(sect_callback != NULL); sect_callback(NULL, NULL, context, &errormsg); if (root_section == sections && errormsg == NULL) { /* we found the section, now quit */ break; } } last_section_path = input->path; last_section_line = input->linenum; sections--; } } if (errormsg != NULL) { *error_r = t_strdup_printf( "Error in configuration file %s line %d: %s", input->path, input->linenum, errormsg); break; } str_truncate(full_line, 0); } i_stream_destroy(&input->input); input = input->prev; if (line == NULL && input != NULL) goto prevfile; return errormsg == NULL; }
void test_strescape(void) { static struct strinput unesc[] = { { "foo", "foo" }, { "\\\\\\\\\\\"\\\"\\\'\\\'", "\\\\\"\"\'\'" }, { "\\a\\n\\r\\", "anr" } }; static struct strinput tabesc[] = { { "foo", "foo" }, { "\001", "\0011" }, { "\t", "\001t" }, { "\r", "\001r" }, { "\n", "\001n" }, { "\001\001\t\t\r\r\n\n", "\0011\0011\001t\001t\001r\001r\001n\001n" } }; unsigned char buf[1 << CHAR_BIT]; const char *escaped, *tabstr; string_t *str; unsigned int i; test_begin("str_escape"); for (i = 1; i < sizeof(buf); i++) buf[i-1] = i; buf[i-1] = '\0'; escaped = str_escape((char *)buf); test_assert(strlen(escaped) == (1 << CHAR_BIT) - 1 + 3); test_assert(escaped['\"'-1] == '\\'); /* 34 */ test_assert(escaped['\"'] == '\"'); test_assert(escaped['\''+1-1] == '\\'); /* 39 */ test_assert(escaped['\''+1] == '\''); test_assert(escaped['\\'+2-1] == '\\'); /* 92 */ test_assert(escaped['\\'+2] == '\\'); test_assert(strcmp(str_escape("\\\\\"\"\'\'"), "\\\\\\\\\\\"\\\"\\\'\\\'") == 0); test_end(); str = t_str_new(256); test_begin("str_unescape"); for (i = 0; i < N_ELEMENTS(unesc); i++) { test_assert(strcmp(str_unescape(t_strdup_noconst(unesc[i].input)), unesc[i].output) == 0); str_truncate(str, 0); str_append_unescaped(str, unesc[i].input, strlen(unesc[i].input)); test_assert(strcmp(str_c(str), unesc[i].output) == 0); } test_end(); test_begin("str_tabescape"); for (i = 0; i < N_ELEMENTS(tabesc); i++) { test_assert(strcmp(str_tabunescape(t_strdup_noconst(tabesc[i].output)), tabesc[i].input) == 0); test_assert(strcmp(str_tabescape(tabesc[i].input), tabesc[i].output) == 0); str_truncate(str, 0); str_append_tabunescaped(str, tabesc[i].output, strlen(tabesc[i].output)); test_assert(strcmp(str_c(str), tabesc[i].input) == 0); } str_truncate(str, 0); tabstr = "\0012\001l\001"; str_append_tabunescaped(str, tabstr, strlen(tabstr)); test_assert(strcmp(str_c(str), "2l") == 0); test_assert(strcmp(str_c(str), str_tabunescape(t_strdup_noconst(tabstr))) == 0); test_end(); }