diff options
author | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2016-12-12 18:07:13 +0000 |
---|---|---|
committer | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2016-12-12 18:07:13 +0000 |
commit | ee6527b21ca0027e58a409b3ee6a4847b4f57b4e (patch) | |
tree | e05b6b6996ca6ab5990e84ddcf7d46e774b593a0 | |
parent | 45e9ab083468a4f91f52f800894978779a15d817 (diff) | |
download | rspamd-ee6527b21ca0027e58a409b3ee6a4847b4f57b4e.tar.gz rspamd-ee6527b21ca0027e58a409b3ee6a4847b4f57b4e.zip |
[Feature] Add rfc2047 variant for QP decoding
-rw-r--r-- | src/libutil/str_util.c | 112 | ||||
-rw-r--r-- | src/libutil/str_util.h | 11 |
2 files changed, 123 insertions, 0 deletions
diff --git a/src/libutil/str_util.c b/src/libutil/str_util.c index 5be41f784..cbad775cb 100644 --- a/src/libutil/str_util.c +++ b/src/libutil/str_util.c @@ -1825,6 +1825,118 @@ decode: return (o - out); } +#define BITOP(a,b,op) \ + ((a)[(gsize)(b)/(8*sizeof *(a))] op (gsize)1<<((gsize)(b)%(8*sizeof *(a)))) +static gsize +rspamd_memcspn (const gchar *s, const gchar *e, gsize len) +{ + gsize byteset[32/sizeof(gsize)]; + const gchar *p = s, *end = s + len; + + memset(byteset, 0, sizeof byteset); + + for (; *e && BITOP (byteset, *(guchar *)e, |=); e++); + for (; p < end && !BITOP (byteset, *(guchar *)p, &); p++); + + return p - s; +} + +gssize +rspamd_decode_qp2047_buf (const gchar *in, gsize inlen, + gchar *out, gsize outlen) +{ + gchar *o, *end, c; + const gchar *p; + guchar ret; + gsize remain, processed; + + p = in; + o = out; + end = out + outlen; + remain = inlen; + + while (remain > 0 && o < end) { + if (*p == '=') { + p ++; + remain --; + + if (remain == 0) { + if (end - o > 0) { + *o++ = *p; + break; + } + } +decode: + /* Decode character after '=' */ + c = *p++; + remain --; + + if (c >= '0' && c <= '9') { ret = c - '0'; } + else if (c >= 'A' && c <= 'F') { ret = c - 'A' + 10; } + else if (c >= 'a' && c <= 'f') { ret = c - 'a' + 10; } + else if (c == '\r' || c == '\n') { + /* Soft line break */ + while (remain > 0 && (*p == '\r' || *p == '\n')) { + remain --; + p ++; + } + + continue; + } + + if (remain > 0) { + c = *p++; + ret *= 16; + + if (c >= '0' && c <= '9') ret += c - '0'; + else if (c >= 'A' && c <= 'F') ret += c - 'A' + 10; + else if (c >= 'a' && c <= 'f') ret += c - 'a' + 10; + + if (end - o > 0) { + *o++ = (gchar)ret; + } + else { + return (-1); + } + + remain --; + } + } + else { + if (end - o >= remain) { + processed = rspamd_memcspn (p, "=_", remain); + memcpy (o, p, processed); + o += remain; + + if (processed == remain) { + break; + } + else { + + remain -= processed; + p += processed; + + if (*p == '=') { + p ++; + /* Skip comparison, as we know that we have found match */ + goto decode; + } + else { + *o++ = ' '; + p ++; + } + } + } + else { + /* Buffer overflow */ + return (-1); + } + } + } + + return (o - out); +} + /* * GString ucl emitting functions diff --git a/src/libutil/str_util.h b/src/libutil/str_util.h index 37bd9fe82..1fdb1a857 100644 --- a/src/libutil/str_util.h +++ b/src/libutil/str_util.h @@ -223,6 +223,17 @@ gsize rspamd_decode_url (gchar *dst, const gchar *src, gsize size); gssize rspamd_decode_qp_buf (const gchar *in, gsize inlen, gchar *out, gsize outlen); +/** + * Decode quoted-printable encoded buffer using rfc2047 format, input and output must not overlap + * @param in input + * @param inlen length of input + * @param out output + * @param outlen length of output + * @return real size of decoded output or (-1) if outlen is not enough + */ +gssize rspamd_decode_qp2047_buf (const gchar *in, gsize inlen, + gchar *out, gsize outlen); + #ifndef g_tolower # define g_tolower(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' + 'a' : (x)) #endif |