aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@highsecure.ru>2016-12-12 18:07:13 +0000
committerVsevolod Stakhov <vsevolod@highsecure.ru>2016-12-12 18:07:13 +0000
commitee6527b21ca0027e58a409b3ee6a4847b4f57b4e (patch)
treee05b6b6996ca6ab5990e84ddcf7d46e774b593a0
parent45e9ab083468a4f91f52f800894978779a15d817 (diff)
downloadrspamd-ee6527b21ca0027e58a409b3ee6a4847b4f57b4e.tar.gz
rspamd-ee6527b21ca0027e58a409b3ee6a4847b4f57b4e.zip
[Feature] Add rfc2047 variant for QP decoding
-rw-r--r--src/libutil/str_util.c112
-rw-r--r--src/libutil/str_util.h11
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