From: Ken Hornstein Date: Thu, 7 Nov 2013 21:02:14 +0000 (-0500) Subject: Getting closer to getting base64 header encoding working. X-Git-Url: https://diplodocus.org/git/nmh/commitdiff_plain/ee9fb87fde55c25f9ab1df2cbcc6e9d21d53b235?ds=sidebyside;hp=b6fff86c5a551eb0723ff48ca56c653b74dbedf1 Getting closer to getting base64 header encoding working. --- diff --git a/h/prototypes.h b/h/prototypes.h index e94bee36..57e51d7b 100644 --- a/h/prototypes.h +++ b/h/prototypes.h @@ -250,6 +250,7 @@ int what_now (char *, int, int, char *, char *, int WhatNow(int, char **); int writeBase64aux(FILE *, FILE *); int writeBase64 (unsigned char *, size_t, unsigned char *); +int writeBase64raw (unsigned char *, size_t, unsigned char *); /* * credentials management diff --git a/sbr/base64.c b/sbr/base64.c index c3045ab7..5ff9f53c 100644 --- a/sbr/base64.c +++ b/sbr/base64.c @@ -114,3 +114,49 @@ writeBase64 (unsigned char *in, size_t length, unsigned char *out) return OK; } + +/* + * Essentially a duplicate of writeBase64, but without line wrapping or + * newline termination (note: string IS NUL terminated) + */ + +int +writeBase64raw (unsigned char *in, size_t length, unsigned char *out) +{ + while (1) { + unsigned long bits; + unsigned char *bp; + unsigned int cc; + for (cc = 0, bp = in; length > 0 && cc < 3; ++cc, ++bp, --length) + /* empty */ ; + + if (cc == 0) { + break; + } else { + bits = (in[0] & 0xff) << 16; + if (cc > 1) { + bits |= (in[1] & 0xff) << 8; + if (cc > 2) { + bits |= in[2] & 0xff; + } + } + } + + for (bp = out + 4; bp > out; bits >>= 6) + *--bp = nib2b64[bits & 0x3f]; + if (cc < 3) { + out[3] = '='; + if (cc < 2) + out[2] = '='; + out += 4; + break; + } + + in += 3; + out += 4; + } + + *out = '\0'; + + return OK; +} diff --git a/sbr/encode_rfc2047.c b/sbr/encode_rfc2047.c index 2929db1c..fcb1c104 100644 --- a/sbr/encode_rfc2047.c +++ b/sbr/encode_rfc2047.c @@ -37,8 +37,15 @@ static char *address_headers[] = { #define is_fws(c) (c == '\t' || c == ' ' || c == '\n') +#define qphrasevalid(c) ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || \ + (c >= 'a' && c <= 'z') || \ + c == '!' || c == '*' || c == '+' || c == '-' || \ + c == '/' || c == '=' || c == '_') #define qpspecial(c) (c < ' ' || c == '=' || c == '?' || c == '_') +#define base64len(n) (((n + 2) / 3 ) * 4) /* String len to base64 len */ +#define strbase64(n) (n * 3 / 4) /* Chars that fit in base64 */ + #define ENCODELINELIMIT 76 static void unfold_header(char **, int); @@ -287,8 +294,72 @@ field_encode_quoted(const char *name, char **value, const char *charset, */ static int -field_encode_base64(const char *name, char **value, const char *encoding) +field_encode_base64(const char *name, char **value, const char *charset) { + int prefixlen = name ? strlen(name) + 2 : 0, charsetlen = strlen(charset); + int outlen = 0, numencode; + char *output = NULL, *p = *value, *q, *linestart; + + /* + * If we had a zero-length prefix, then just encode the whole field + * as-is, without line wrapping. Note that in addition to the encoding + * + * The added length we need is =? + charset + ?B? ... ?= + * + * That's 7 + strlen(charset) + 2 (for \n NUL). + */ + + while (prefixlen && ((base64len(strlen(p)) + 7 + charsetlen + + prefixlen) > ENCODELINELIMIT)) { + + /* + * Our very first time, don't pad the line in the front + */ + + + if (! output) { + outlen += ENCODELINELIMIT - prefixlen + 1; + output = q = mh_xmalloc(outlen); + linestart = q - prefixlen; /* Yes, this is intentional */ + } else { + int curlen = q - output; + + outlen += ENCODELINELIMIT + 1; + output = mh_xrealloc(output, outlen); + linestart = q = output + curlen; + q += snprintf(q, outlen - (q - output), "%*s", prefixlen, ""); + } + + /* + * We should have enough space now, so prepend the encoding markers + * and character set information + */ + + q += snprintf(q, outlen - (q - output), "=?%s?B?", charset); + + /* + * Find out how much room we have left on the line and see how many + * characters we can stuff in. The start of our line is marked + * by "linestart", so use that to figure out how many characters + * are left out of ENCODELINELIMIT. Reserve 2 characters for the + * end markers, and calculate how many characters we can fit into + * that space given the base64 encoding expansion. + */ + + numencode = strbase64(ENCODELINELIMIT - (q - linestart) - 2); + + if (numencode <= 0) { + advise(NULL, "Internal error: tried to encode %d characters " + "in base64", numencode); + return 1; + } + + /* + * RFC 2047 prohibits spanning multibyte characters across tokens. + * Right now we only check for UTF-8. + */ + } + return 0; }