strescape.c revision 51aceed49d7edcf1ce385d6d97f0acb7067a6608
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen/* Copyright (c) 2003-2016 Dovecot authors, see the included COPYING file */
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen#include "lib.h"
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen#include "str.h"
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen#include "strescape.h"
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainenconst char *str_escape(const char *str)
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen{
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen const char *p;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen string_t *ret;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen /* see if we need to quote it */
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen for (p = str; *p != '\0'; p++) {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (IS_ESCAPED_CHAR(*p))
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen break;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen }
660b4d36110c44b1e4b4b45a78c22d1569ccdb54Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (*p == '\0')
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen return str;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen /* quote */
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen ret = t_str_new((size_t) (p - str) + 128);
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen str_append_n(ret, str, (size_t) (p - str));
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen for (; *p != '\0'; p++) {
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen if (IS_ESCAPED_CHAR(*p))
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen str_append_c(ret, '\\');
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen str_append_c(ret, *p);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen }
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen return str_c(ret);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen}
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenvoid str_append_unescaped(string_t *dest, const void *src, size_t src_size)
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen{
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen const unsigned char *src_c = src;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen size_t start = 0, i = 0;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen while (i < src_size) {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen for (; i < src_size; i++) {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (src_c[i] == '\\')
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen break;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen }
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen str_append_n(dest, src_c + start, i-start);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (i < src_size) {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (++i == src_size)
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen break;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen str_append_c(dest, src_c[i++]);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen }
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen start = i;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen }
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen}
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainenchar *str_unescape(char *str)
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen{
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen /* @UNSAFE */
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen char *dest, *start = str;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen while (*str != '\\') {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (*str == '\0')
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen return start;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen str++;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen }
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen for (dest = str; *str != '\0'; str++) {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (*str == '\\') {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen str++;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (*str == '\0')
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen break;
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen }
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen *dest++ = *str;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen }
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen *dest = '\0';
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen return start;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen}
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainenint str_unescape_next(const char **str, const char **unescaped_r)
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen{
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen const char *p;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen char *escaped;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen bool esc_found = FALSE;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen for (p = *str; *p != '\0'; p++) {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (*p == '"')
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen break;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen else if (*p == '\\') {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (p[1] == '\0')
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen return -1;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen esc_found = TRUE;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen p++;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen }
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen }
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (*p != '"')
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen return -1;
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen escaped = p_strdup_until(unsafe_data_stack_pool, *str, p);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen *str = p+1;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen *unescaped_r = !esc_found ? escaped : str_unescape(escaped);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen return 0;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen}
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainenvoid str_append_tabescaped_n(string_t *dest, const unsigned char *src, size_t src_size)
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen{
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainen for (size_t i = 0; i < src_size; i++) {
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainen switch (src[i]) {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen case '\000':
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen str_append_c(dest, '\001');
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen str_append_c(dest, '0');
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen break;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen case '\001':
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen str_append_c(dest, '\001');
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainen str_append_c(dest, '1');
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen break;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen case '\t':
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen str_append_c(dest, '\001');
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen str_append_c(dest, 't');
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainen break;
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainen case '\r':
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainen str_append_c(dest, '\001');
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainen str_append_c(dest, 'r');
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainen break;
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen case '\n':
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen str_append_c(dest, '\001');
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen str_append_c(dest, 'n');
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen break;
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen default:
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen str_append_c(dest, src[i]);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen break;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen }
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen }
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen}
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainenvoid str_append_tabescaped(string_t *dest, const char *src) {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen str_append_tabescaped_n(dest, (const unsigned char*)src, strlen(src));
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen}
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainen
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainenconst char *str_tabescape(const char *str)
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen{
283ccfe110ed62e48f36e0d84e47da8cae5106beTimo Sirainen string_t *tmp;
2a4e8f370c566ffd360922227fc73d0ee36abee7Timo Sirainen const char *p;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen for (p = str; *p != '\0'; p++) {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (*p <= '\r') {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen tmp = t_str_new(128);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen str_append_n(tmp, str, p-str);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen str_append_tabescaped(tmp, p);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen return str_c(tmp);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen }
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen }
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen return str;
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen}
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainenvoid str_append_tabunescaped(string_t *dest, const void *src, size_t src_size)
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainen{
d6500661eb699ff335ac570c8646b6e067e1aac6Timo Sirainen const unsigned char *src_c = src;
01c43cb586726efc06342114c0545db4b1733d2cTimo Sirainen size_t start = 0, i = 0;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen while (i < src_size) {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen for (; i < src_size; i++) {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen if (src_c[i] == '\001')
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen break;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen }
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen str_append_n(dest, src_c + start, i-start);
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen if (i < src_size) {
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen i++;
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen if (i < src_size) {
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen switch (src_c[i]) {
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen case '0':
6246b93fb37890dcb2f4df9896438f3f376ab284Timo Sirainen str_append_c(dest, '\000');
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen break;
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen case '1':
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen str_append_c(dest, '\001');
9d7451b57769988f7e3e41cd8790e65429ffc5c7Timo Sirainen 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;
}
}
char *str_tabunescape(char *str)
{
/* @UNSAFE */
char *dest, *start = str;
while (*str != '\001') {
if (*str == '\0')
return start;
str++;
}
for (dest = str; *str != '\0'; str++) {
if (*str != '\001')
*dest++ = *str;
else {
str++;
if (*str == '\0')
break;
switch (*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++ = *str;
break;
}
}
}
*dest = '\0';
return start;
}
const char *t_str_tabunescape(const char *str)
{
if (strchr(str, '\001') == NULL)
return str;
else
return str_tabunescape(t_strdup_noconst(str));
}
char **p_strsplit_tabescaped(pool_t pool, const char *str)
{
char **args;
unsigned int i;
args = p_strsplit(pool, str, "\t");
for (i = 0; args[i] != NULL; i++)
args[i] = str_tabunescape(args[i]);
return args;
}
const char *const *t_strsplit_tabescaped(const char *str)
{
return (void *)p_strsplit_tabescaped(pool_datastack_create(), str);
}