mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-25 12:04:47 +00:00
192 lines
6.1 KiB
C
192 lines
6.1 KiB
C
|
|
#ifndef EXCLUDE_COMMON
|
|
|
|
#include "common/cs_base64.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include "common/cs_dbg.h"
|
|
|
|
/* ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ */
|
|
|
|
#define NUM_UPPERCASES ('Z' - 'A' + 1)
|
|
#define NUM_LETTERS (NUM_UPPERCASES * 2)
|
|
#define NUM_DIGITS ('9' - '0' + 1)
|
|
|
|
/*
|
|
* Emit a base64 code char.
|
|
*
|
|
* Doesn't use memory, thus it's safe to use to safely dump memory in crashdumps
|
|
*/
|
|
static void cs_base64_emit_code(struct cs_base64_ctx *ctx, int v) {
|
|
if (v < NUM_UPPERCASES) {
|
|
ctx->b64_putc(v + 'A', ctx->user_data);
|
|
} else if (v < (NUM_LETTERS)) {
|
|
ctx->b64_putc(v - NUM_UPPERCASES + 'a', ctx->user_data);
|
|
} else if (v < (NUM_LETTERS + NUM_DIGITS)) {
|
|
ctx->b64_putc(v - NUM_LETTERS + '0', ctx->user_data);
|
|
} else {
|
|
ctx->b64_putc(v - NUM_LETTERS - NUM_DIGITS == 0 ? '+' : '/',
|
|
ctx->user_data);
|
|
}
|
|
}
|
|
|
|
static void cs_base64_emit_chunk(struct cs_base64_ctx *ctx) {
|
|
int a, b, c;
|
|
|
|
a = ctx->chunk[0];
|
|
b = ctx->chunk[1];
|
|
c = ctx->chunk[2];
|
|
|
|
cs_base64_emit_code(ctx, a >> 2);
|
|
cs_base64_emit_code(ctx, ((a & 3) << 4) | (b >> 4));
|
|
if (ctx->chunk_size > 1) {
|
|
cs_base64_emit_code(ctx, (b & 15) << 2 | (c >> 6));
|
|
}
|
|
if (ctx->chunk_size > 2) {
|
|
cs_base64_emit_code(ctx, c & 63);
|
|
}
|
|
}
|
|
|
|
void cs_base64_init(struct cs_base64_ctx *ctx, cs_base64_putc_t b64_putc,
|
|
void *user_data) {
|
|
ctx->chunk_size = 0;
|
|
ctx->b64_putc = b64_putc;
|
|
ctx->user_data = user_data;
|
|
}
|
|
|
|
void cs_base64_update(struct cs_base64_ctx *ctx, const char *str, size_t len) {
|
|
const unsigned char *src = (const unsigned char *) str;
|
|
size_t i;
|
|
for (i = 0; i < len; i++) {
|
|
ctx->chunk[ctx->chunk_size++] = src[i];
|
|
if (ctx->chunk_size == 3) {
|
|
cs_base64_emit_chunk(ctx);
|
|
ctx->chunk_size = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void cs_base64_finish(struct cs_base64_ctx *ctx) {
|
|
if (ctx->chunk_size > 0) {
|
|
int i;
|
|
memset(&ctx->chunk[ctx->chunk_size], 0, 3 - ctx->chunk_size);
|
|
cs_base64_emit_chunk(ctx);
|
|
for (i = 0; i < (3 - ctx->chunk_size); i++) {
|
|
ctx->b64_putc('=', ctx->user_data);
|
|
}
|
|
}
|
|
}
|
|
|
|
#define BASE64_ENCODE_BODY \
|
|
static const char *b64 = \
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; \
|
|
int i, j, a, b, c; \
|
|
\
|
|
for (i = j = 0; i < src_len; i += 3) { \
|
|
a = src[i]; \
|
|
b = i + 1 >= src_len ? 0 : src[i + 1]; \
|
|
c = i + 2 >= src_len ? 0 : src[i + 2]; \
|
|
\
|
|
BASE64_OUT(b64[a >> 2]); \
|
|
BASE64_OUT(b64[((a & 3) << 4) | (b >> 4)]); \
|
|
if (i + 1 < src_len) { \
|
|
BASE64_OUT(b64[(b & 15) << 2 | (c >> 6)]); \
|
|
} \
|
|
if (i + 2 < src_len) { \
|
|
BASE64_OUT(b64[c & 63]); \
|
|
} \
|
|
} \
|
|
\
|
|
while (j % 4 != 0) { \
|
|
BASE64_OUT('='); \
|
|
} \
|
|
|
|
#define BASE64_OUT(ch) \
|
|
do { \
|
|
dst[j++] = (ch); \
|
|
} while (0)
|
|
|
|
void cs_base64_encode(const unsigned char *src, int src_len, char *dst, int *enc_len) {
|
|
BASE64_ENCODE_BODY;
|
|
if (enc_len != NULL) *enc_len = j;
|
|
}
|
|
|
|
#undef BASE64_OUT
|
|
|
|
#if CS_ENABLE_STDIO
|
|
#define BASE64_OUT(ch) \
|
|
do { \
|
|
fprintf(f, "%c", (ch)); \
|
|
j++; \
|
|
} while (0)
|
|
|
|
void cs_fprint_base64(FILE *f, const unsigned char *src, int src_len) {
|
|
BASE64_ENCODE_BODY;
|
|
}
|
|
|
|
#undef BASE64_OUT
|
|
#endif /* CS_ENABLE_STDIO */
|
|
|
|
/* Convert one byte of encoded base64 input stream to 6-bit chunk */
|
|
static unsigned char from_b64(unsigned char ch) {
|
|
/* Inverse lookup map */
|
|
static const unsigned char tab[128] = {
|
|
255, 255, 255, 255,
|
|
255, 255, 255, 255, /* 0 */
|
|
255, 255, 255, 255,
|
|
255, 255, 255, 255, /* 8 */
|
|
255, 255, 255, 255,
|
|
255, 255, 255, 255, /* 16 */
|
|
255, 255, 255, 255,
|
|
255, 255, 255, 255, /* 24 */
|
|
255, 255, 255, 255,
|
|
255, 255, 255, 255, /* 32 */
|
|
255, 255, 255, 62,
|
|
255, 255, 255, 63, /* 40 */
|
|
52, 53, 54, 55,
|
|
56, 57, 58, 59, /* 48 */
|
|
60, 61, 255, 255,
|
|
255, 200, 255, 255, /* 56 '=' is 200, on index 61 */
|
|
255, 0, 1, 2,
|
|
3, 4, 5, 6, /* 64 */
|
|
7, 8, 9, 10,
|
|
11, 12, 13, 14, /* 72 */
|
|
15, 16, 17, 18,
|
|
19, 20, 21, 22, /* 80 */
|
|
23, 24, 25, 255,
|
|
255, 255, 255, 255, /* 88 */
|
|
255, 26, 27, 28,
|
|
29, 30, 31, 32, /* 96 */
|
|
33, 34, 35, 36,
|
|
37, 38, 39, 40, /* 104 */
|
|
41, 42, 43, 44,
|
|
45, 46, 47, 48, /* 112 */
|
|
49, 50, 51, 255,
|
|
255, 255, 255, 255, /* 120 */
|
|
};
|
|
return tab[ch & 127];
|
|
}
|
|
|
|
int cs_base64_decode(const unsigned char *s, int len, char *dst, int *dec_len) {
|
|
unsigned char a, b, c, d;
|
|
int orig_len = len;
|
|
char *orig_dst = dst;
|
|
while (len >= 4 && (a = from_b64(s[0])) != 255 &&
|
|
(b = from_b64(s[1])) != 255 && (c = from_b64(s[2])) != 255 &&
|
|
(d = from_b64(s[3])) != 255) {
|
|
s += 4;
|
|
len -= 4;
|
|
if (a == 200 || b == 200) break; /* '=' can't be there */
|
|
*dst++ = a << 2 | b >> 4;
|
|
if (c == 200) break;
|
|
*dst++ = b << 4 | c >> 2;
|
|
if (d == 200) break;
|
|
*dst++ = c << 6 | d;
|
|
}
|
|
if (dec_len != NULL) *dec_len = (dst - orig_dst);
|
|
return orig_len - len;
|
|
}
|
|
|
|
#endif /* EXCLUDE_COMMON */
|