diff options
Diffstat (limited to 'src/lib/strescape.c')
-rw-r--r-- | src/lib/strescape.c | 358 |
1 files changed, 358 insertions, 0 deletions
diff --git a/src/lib/strescape.c b/src/lib/strescape.c new file mode 100644 index 0000000..bfa4473 --- /dev/null +++ b/src/lib/strescape.c @@ -0,0 +1,358 @@ +/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "str.h" +#include "strescape.h" + +const char *str_nescape(const void *str, size_t len) +{ + string_t *dest = t_str_new(len*2); + str_append_escaped(dest, str, len); + return str_c(dest); +} + +void str_append_escaped(string_t *dest, const void *src, size_t src_size) +{ + const unsigned char *pstart = src, *p = src, *pend = pstart + src_size; + /* see if we need to quote it */ + for (; p < pend; p++) { + if (IS_ESCAPED_CHAR(*p)) + break; + } + + /* quote */ + str_append_data(dest, pstart, (size_t)(p - pstart)); + + for (; p < pend; p++) { + if (IS_ESCAPED_CHAR(*p)) + str_append_c(dest, '\\'); + str_append_data(dest, p, 1); + } +} + +void str_append_unescaped(string_t *dest, const void *src, size_t src_size) +{ + const unsigned char *src_c = src; + size_t start = 0, i = 0; + + while (i < src_size) { + for (; i < src_size; i++) { + if (src_c[i] == '\\') + break; + } + + str_append_data(dest, src_c + start, i-start); + + if (i < src_size) { + if (++i == src_size) + break; + str_append_c(dest, src_c[i++]); + } + start = i; + } +} + +char *str_unescape(char *str) +{ + /* @UNSAFE */ + char *dest, *start = str; + + while (*str != '\\') { + if (*str == '\0') + return start; + str++; + } + + for (dest = str; *str != '\0'; str++) { + if (*str == '\\') { + str++; + if (*str == '\0') + break; + } + + *dest++ = *str; + } + + *dest = '\0'; + return start; +} + +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; +} + +void str_append_tabescaped_n(string_t *dest, const unsigned char *src, size_t src_size) +{ + size_t prev_pos = 0; + char esc[2] = { '\001', '\0' }; + + for (size_t i = 0; i < src_size; i++) { + switch (src[i]) { + case '\000': + esc[1] = '0'; + break; + case '\001': + esc[1] = '1'; + break; + case '\t': + esc[1] = 't'; + break; + case '\r': + esc[1] = 'r'; + break; + case '\n': + esc[1] = 'n'; + break; + default: + continue; + } + str_append_data(dest, src + prev_pos, i - prev_pos); + str_append_data(dest, esc, 2); + prev_pos = i + 1; + } + str_append_data(dest, src + prev_pos, src_size - prev_pos); +} + +void str_append_tabescaped(string_t *dest, const char *src) +{ + size_t pos, prev_pos = 0; + char esc[2] = { '\001', '\0' }; + + for (;;) { + pos = prev_pos + strcspn(src + prev_pos, "\001\t\r\n"); + str_append_data(dest, src + prev_pos, pos - prev_pos); + prev_pos = pos + 1; + + switch (src[pos]) { + case '\000': + /* end of src string reached */ + return; + case '\001': + esc[1] = '1'; + break; + case '\t': + esc[1] = 't'; + break; + case '\r': + esc[1] = 'r'; + break; + case '\n': + esc[1] = 'n'; + break; + default: + i_unreached(); + } + str_append_data(dest, esc, 2); + } +} + + +const char *str_tabescape(const char *str) +{ + string_t *tmp; + const char *p; + + if ((p = strpbrk(str, "\001\t\r\n")) != NULL) { + tmp = t_str_new(128); + str_append_data(tmp, str, p-str); + str_append_tabescaped(tmp, p); + return str_c(tmp); + } + return str; +} + +void str_append_tabunescaped(string_t *dest, const void *src, size_t src_size) +{ + const unsigned char *src_c = src; + size_t start = 0, i = 0; + + while (i < src_size) { + for (; i < src_size; i++) { + if (src_c[i] == '\001') + break; + } + + str_append_data(dest, src_c + start, i-start); + + if (i < src_size) { + i++; + if (i < src_size) { + switch (src_c[i]) { + case '0': + str_append_c(dest, '\000'); + break; + case '1': + str_append_c(dest, '\001'); + break; + case 't': + str_append_c(dest, '\t'); + break; + case 'r': + str_append_c(dest, '\r'); + break; + case 'n': + str_append_c(dest, '\n'); + break; + default: + str_append_c(dest, src_c[i]); + break; + } + i++; + } + } + start = i; + } +} + +static char *str_tabunescape_from(char *str, char *src) +{ + /* @UNSAFE */ + char *dest, *p; + + dest = src; + for (;;) { + switch (src[1]) { + case '\0': + /* truncated input */ + *dest = '\0'; + return str; + case '0': + *dest++ = '\000'; + break; + case '1': + *dest++ = '\001'; + break; + case 't': + *dest++ = '\t'; + break; + case 'r': + *dest++ = '\r'; + break; + case 'n': + *dest++ = '\n'; + break; + default: + *dest++ = src[1]; + break; + } + src += 2; + + p = strchr(src, '\001'); + if (p == NULL) { + memmove(dest, src, strlen(src)+1); + break; + } + + size_t copy_len = p - src; + memmove(dest, src, copy_len); + dest += copy_len; + src = p; + } + return str; +} + +char *str_tabunescape(char *str) +{ + char *src = strchr(str, '\001'); + if (src == NULL) { + /* no unescaping needed */ + return str; + } + return str_tabunescape_from(str, src); +} + +const char *t_str_tabunescape(const char *str) +{ + const char *p; + + p = strchr(str, '\001'); + if (p == NULL) + return str; + + char *dest = t_strdup_noconst(str); + return str_tabunescape_from(dest, dest + (p - str)); +} + +static char **p_strsplit_tabescaped_inplace(pool_t pool, char *data) +{ + /* @UNSAFE */ + char **array; + unsigned int count, new_alloc_count, alloc_count; + + if (*data == '\0') + return p_new(pool, char *, 1); + + alloc_count = 32; + array = pool == unsafe_data_stack_pool ? + t_malloc_no0(sizeof(char *) * alloc_count) : + p_malloc(pool, sizeof(char *) * alloc_count); + + array[0] = data; count = 1; + char *need_unescape = NULL; + while ((data = strpbrk(data, "\t\001")) != NULL) { + /* separator or escape char found */ + if (*data == '\001') { + if (need_unescape == NULL) + need_unescape = data; + data++; + continue; + } + if (count+1 >= alloc_count) { + new_alloc_count = nearest_power(alloc_count+1); + array = p_realloc(pool, array, + sizeof(char *) * alloc_count, + sizeof(char *) * + new_alloc_count); + alloc_count = new_alloc_count; + } + *data++ = '\0'; + if (need_unescape != NULL) { + str_tabunescape_from(array[count-1], need_unescape); + need_unescape = NULL; + } + array[count++] = data; + } + if (need_unescape != NULL) + str_tabunescape_from(array[count-1], need_unescape); + i_assert(count < alloc_count); + array[count] = NULL; + + return array; +} + +const char *const *t_strsplit_tabescaped_inplace(char *data) +{ + char *const *escaped = + p_strsplit_tabescaped_inplace(unsafe_data_stack_pool, data); + return (const char *const *)escaped; +} + +char **p_strsplit_tabescaped(pool_t pool, const char *str) +{ + return p_strsplit_tabescaped_inplace(pool, p_strdup(pool, str)); +} + +const char *const *t_strsplit_tabescaped(const char *str) +{ + return t_strsplit_tabescaped_inplace(t_strdup_noconst(str)); +} |