Skip to content

Commit

Permalink
charset.c: implement base64 jmapid encoding
Browse files Browse the repository at this point in the history
  • Loading branch information
ksmurchison committed Dec 20, 2024
1 parent c4ddf2e commit 5599d95
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 5 deletions.
36 changes: 36 additions & 0 deletions cunit/charset.testc
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#include <stdlib.h>
#include <time.h>

#include "cunit/cyrunit.h"
#include "charset.h"
#include "byteorder.h"

extern int charset_debug;

Expand Down Expand Up @@ -1332,6 +1334,14 @@ static void test_charset_decode(void)
TESTCASE("vu_A3g", 6, "\xbe\xef\xc0\xde", 4, ENCODING_BASE64URL);
TESTCASE("vu_A3g==", 6, "\xbe\xef\xc0\xde", 4, ENCODING_BASE64URL);

/* Base64 JMAPID encoding */
TESTCASE("-----------", 11,
"\x00\x00\x00\x00\x00\x00\x00\x00", 8, ENCODING_BASE64JMAPID);
TESTCASE("zzzzzzzzzzw", 11,
"\xff\xff\xff\xff\xff\xff\xff\xff", 8, ENCODING_BASE64JMAPID);
TESTCASE("012-abc_TUV", 11,
"\x04\x20\xc0\x9a\x7a\x25\x79\xf8", 8, ENCODING_BASE64JMAPID);

#undef TESTCASE

struct buf buf = BUF_INITIALIZER;
Expand All @@ -1354,6 +1364,11 @@ static void test_charset_decode(void)
CU_ASSERT_EQUAL(-1, r);
buf_reset(&buf);

/* Base64jmapid with invalid characters */
r = charset_decode(&buf, "Zm9v@@@YmFy", 11, ENCODING_BASE64JMAPID);
CU_ASSERT_EQUAL(-1, r);
buf_reset(&buf);

buf_free(&buf);
}

Expand Down Expand Up @@ -1645,4 +1660,25 @@ static void test_unicode_casemap(void)
#undef TESTCASE
}

static void test_jmap_email_id(void)
{
struct timespec now;
uint64_t u64;
struct buf id = BUF_INITIALIZER;
struct buf buf = BUF_INITIALIZER;

clock_gettime(CLOCK_REALTIME, &now);
u64 = htonll(UINT64_MAX - (now.tv_sec * 1000000000 + now.tv_nsec));
charset_encode(&id, (const char *) &u64, sizeof(u64), ENCODING_BASE64JMAPID);

charset_decode(&buf, buf_cstring(&id), buf_len(&id), ENCODING_BASE64JMAPID);
u64 = ntohll(UINT64_MAX - *(uint64_t *) buf_base(&buf));

CU_ASSERT_EQUAL(u64 / 1000000000, now.tv_sec);
CU_ASSERT_EQUAL(u64 % 1000000000, now.tv_nsec);

buf_free(&id);
buf_free(&buf);
}

/* vim: set ft=c: */
65 changes: 60 additions & 5 deletions lib/charset.c
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,28 @@ static const char index_64url[256] = {
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
};

/*
* Table for decoding base64jmapid
*/
static const char index_64jmapid[256] = {
XX,XX,XX,XX, XX,XX,XX,XX, XX,XS,XS,XX, XX,XS,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XS,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX, 0,XX,XX,
1, 2, 3, 4, 5, 6, 7, 9, 9,10,XX,XX, XX,64,XX,XX,
XX,11,12,13, 14,15,16,17, 18,19,20,21, 22,23,24,25,
26,27,28,29, 30,31,32,33, 34,35,36,XX, XX,XX,XX,37,
XX,38,39,40, 41,42,43,44, 45,46,47,48, 49,50,51,52,
53,54,55,56, 57,58,59,60, 61,62,63,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
};
#define CHAR64(c, index) (index[(unsigned char)(c)])

EXPORTED int encoding_lookupname(const char *s)
Expand Down Expand Up @@ -374,6 +396,8 @@ EXPORTED int encoding_lookupname(const char *s)
return ENCODING_BASE64;
if (!strcasecmp(s, "BASE64URL"))
return ENCODING_BASE64URL;
if (!strcasecmp(s, "BASE64JMAPID"))
return ENCODING_BASE64JMAPID;
if (!strcasecmp(s, "BINARY"))
return ENCODING_NONE;
break;
Expand Down Expand Up @@ -405,6 +429,7 @@ EXPORTED const char *encoding_name(int encoding)
case ENCODING_QP: return "QUOTED-PRINTABLE";
case ENCODING_BASE64: return "BASE64";
case ENCODING_BASE64URL: return "BASE64URL";
case ENCODING_BASE64JMAPID: return "BASE64JMAPID";
case ENCODING_UNKNOWN: return "UNKNOWN";
default: return "WTF";
}
Expand Down Expand Up @@ -574,7 +599,7 @@ static int b64_flush(struct convert_rock *rock)
{
struct b64_state *s = (struct b64_state *)rock->state;
if (s->invalid) {
if (s->index == index_64url)
if (s->index != index_64)
return -1;
else
xsyslog(LOG_WARNING, "ignoring invalid base64 characters", NULL);
Expand Down Expand Up @@ -2348,7 +2373,12 @@ static struct convert_rock *b64_init(struct convert_rock *next, int enc)
{
struct convert_rock *rock = xzmalloc(sizeof(struct convert_rock));
struct b64_state *state = xzmalloc(sizeof(struct b64_state));
state->index = enc == ENCODING_BASE64URL ? index_64url : index_64;
if (enc == ENCODING_BASE64JMAPID)
state->index = index_64jmapid;
else if (enc == ENCODING_BASE64URL)
state->index = index_64url;
else
state->index = index_64;
rock->state = state;
rock->f = b64_2byte;
rock->flush = b64_flush;
Expand Down Expand Up @@ -2819,6 +2849,7 @@ EXPORTED char *charset_to_imaputf7(const char *msg_base, size_t len, charset_t c

case ENCODING_BASE64:
case ENCODING_BASE64URL:
case ENCODING_BASE64JMAPID:
input = b64_init(input, encoding);
/* XXX have to have nl-mapping base64 in order to
* properly count \n as 2 raw characters
Expand Down Expand Up @@ -2978,6 +3009,7 @@ EXPORTED int charset_to_utf8(struct buf *dst, const char *src, size_t len, chars

case ENCODING_BASE64:
case ENCODING_BASE64URL:
case ENCODING_BASE64JMAPID:
input = b64_init(input, encoding);
/* XXX have to have nl-mapping base64 in order to
* properly count \n as 2 raw characters
Expand Down Expand Up @@ -3040,6 +3072,7 @@ EXPORTED int charset_decode(struct buf *dst, const char *src, size_t len, int en

case ENCODING_BASE64:
case ENCODING_BASE64URL:
case ENCODING_BASE64JMAPID:
input = b64_init(input, encoding);
/* XXX have to have nl-mapping base64 in order to
* properly count \n as 2 raw characters
Expand All @@ -3063,8 +3096,25 @@ static void encode_b64(struct buf *dst, const char *src, size_t len, int encodin
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const char b64url[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
const char *b64 = encoding == ENCODING_BASE64URL ? b64url : b64std;
char pad = encoding == ENCODING_BASE64URL ? '\0' : '=';
static const char b64jmapid[] =
"-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
const char *b64;
char pad;

switch (encoding) {
case ENCODING_BASE64JMAPID:
b64 = b64jmapid;
pad = '\0';
break;
case ENCODING_BASE64URL:
b64 = b64url;
pad = '\0';
break;
default:
b64 = b64std;
pad = '=';
break;
}

const uint8_t *s = (uint8_t*)src;
size_t r = len;
Expand Down Expand Up @@ -3098,7 +3148,8 @@ EXPORTED int charset_encode(struct buf *dst, const char *src, size_t len, int en
buf_setmap(dst, src, len);
return 0;
}
else if (encoding == ENCODING_BASE64 || encoding == ENCODING_BASE64URL) {
else if (encoding == ENCODING_BASE64 ||
encoding == ENCODING_BASE64URL || ENCODING_BASE64JMAPID) {
encode_b64(dst, src, len, encoding);
return 0;
}
Expand Down Expand Up @@ -3140,6 +3191,7 @@ EXPORTED int charset_decode_sha1(uint8_t dest[SHA1_DIGEST_LENGTH], size_t *decod

case ENCODING_BASE64:
case ENCODING_BASE64URL:
case ENCODING_BASE64JMAPID:
input = b64_init(input, encoding);
/* XXX have to have nl-mapping base64 in order to
* properly count \n as 2 raw characters
Expand Down Expand Up @@ -3606,6 +3658,7 @@ EXPORTED int charset_searchfile(const char *substr, comp_pat *pat,

case ENCODING_BASE64:
case ENCODING_BASE64URL:
case ENCODING_BASE64JMAPID:
input = b64_init(input, encoding);
/* XXX have to have nl-mapping base64 in order to
* properly count \n as 2 raw characters
Expand Down Expand Up @@ -3684,6 +3737,7 @@ EXPORTED int charset_extract(int (*cb)(const struct buf *, void *),

case ENCODING_BASE64:
case ENCODING_BASE64URL:
case ENCODING_BASE64JMAPID:
input = b64_init(input, encoding);
/* XXX have to have nl-mapping base64 in order to
* properly count \n as 2 raw characters
Expand Down Expand Up @@ -3754,6 +3808,7 @@ EXPORTED const char *charset_decode_mimebody(const char *msg_base, size_t len, i

case ENCODING_BASE64:
case ENCODING_BASE64URL:
case ENCODING_BASE64JMAPID:
tobuffer = buffer_init(len);
input = b64_init(tobuffer, encoding);
break;
Expand Down
1 change: 1 addition & 0 deletions lib/charset.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#define ENCODING_QP 1
#define ENCODING_BASE64 2
#define ENCODING_BASE64URL 3
#define ENCODING_BASE64JMAPID 4
#define ENCODING_UNKNOWN 255

#define CHARSET_SKIPDIACRIT (1<<0)
Expand Down

0 comments on commit 5599d95

Please sign in to comment.