diff options
Diffstat (limited to 'libedsio/base64.c')
-rwxr-xr-x | libedsio/base64.c | 525 |
1 files changed, 525 insertions, 0 deletions
diff --git a/libedsio/base64.c b/libedsio/base64.c new file mode 100755 index 0000000..c37ddae --- /dev/null +++ b/libedsio/base64.c @@ -0,0 +1,525 @@ +/* -*-Mode: C;-*- + * $Id: base64.c 1.1 Sun, 28 Jan 2007 10:02:26 -0800 jmacd $ + * + * Copyright (C) 1998, 1999, Josh MacDonald. + * All Rights Reserved. + * + * Author: Josh MacDonald <jmacd@CS.Berkeley.EDU> + */ + +#include "edsio.h" + +/* BASE64 Encoding + */ + +static const unsigned char base64_table[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', + 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', + '3', '4', '5', '6', '7', '8', '9', '+', '/' +}; + +static gint16 base64_inverse_table[128]; + +static void +init_inverse_table (void) +{ + static int i = 0; + static int j = 0; + + for (; j < 128; j += 1) + base64_inverse_table[j] = -1; + + for (; i < 64; i += 1) + base64_inverse_table[base64_table[i]] = i; + + base64_inverse_table['='] = 0; +} + +GByteArray* +edsio_base64_encode_region (const guint8* data, guint len) +{ + GByteArray* out = g_byte_array_new (); + guint real_len; + + g_byte_array_set_size (out, (len+2)*4/3); + + real_len = out->len; + + if (! edsio_base64_encode_region_into (data, len, out->data, &real_len)) + { + g_byte_array_free (out, TRUE); + return NULL; + } + + g_byte_array_set_size (out, real_len); + + return out; +} + +gboolean +edsio_base64_encode_region_into (const guint8* data, guint len, guint8* out, guint *out_len) +{ + gint i; + guint32 word = 0, count = 0; + + if ((*out_len) < (len + 2) * 4/3) + { + edsio_generate_void_event (EC_EdsioInvalidBase64Encoding); + return FALSE; + } + + *out_len = 0; + + for (i = 0; i < len; i += 1) + { + word |= data[i] << (8*(2-(count++))); + + if (count == 3) + { + out[(*out_len)++] = base64_table[(word>>18) & 0x3f]; + out[(*out_len)++] = base64_table[(word>>12) & 0x3f]; + out[(*out_len)++] = base64_table[(word>> 6) & 0x3f]; + out[(*out_len)++] = base64_table[(word ) & 0x3f]; + + count = 0; + word = 0; + } + } + + if (count > 0) + { + out[(*out_len)++] = base64_table[(word>>18) & 0x3f]; + out[(*out_len)++] = base64_table[(word>>12) & 0x3f]; + out[(*out_len)++] = (count > 1) ? base64_table[(word>>6) & 0x3f] : '='; + out[(*out_len)++] = '='; + } + + return TRUE; +} + +GByteArray* +edsio_base64_decode_region (const guint8* data, guint data_len) +{ + GByteArray* it = g_byte_array_new (); + guint real_len; + + g_byte_array_set_size (it, data_len*3/4); + + real_len = it->len; + + if (! edsio_base64_decode_region_into (data, data_len, it->data, &real_len)) + { + g_byte_array_free (it, TRUE); + return NULL; + } + + g_byte_array_set_size (it, real_len); + + return it; +} + +gboolean +edsio_base64_decode_region_into (const guint8* data, guint len, guint8* out, guint *out_len) +{ + guint32 pos = 0; + gboolean found_end = FALSE; + gint found_end_at = 0; + + init_inverse_table (); + + if ((*out_len) < (len*3/4)) + { + edsio_generate_void_event (EC_EdsioOutputBufferShort); + return FALSE; + } + + (*out_len) = 0; + + while (pos < len) + { + gint i, x; + gint word = 0, junk = 0; + + if (len - pos < 4) + { + edsio_generate_void_event (EC_EdsioInvalidBase64Encoding); + return FALSE; + } + + for (i = 0; i < 4; i += 1) + { + x = data[pos++]; + + if (x > 127 || base64_inverse_table[x] < 0) + { + edsio_generate_void_event (EC_EdsioInvalidBase64Encoding); + return FALSE; + } + + if (x == '=') + { + if (! found_end) + found_end_at = i; + + found_end = TRUE; + } + else + { + if (found_end) + { + edsio_generate_void_event (EC_EdsioInvalidBase64Encoding); + return FALSE; + } + + word |= base64_inverse_table[x] << (6*(3-i)); + } + } + + if (found_end) + { + if (found_end_at < 2) + { + edsio_generate_void_event (EC_EdsioInvalidBase64Encoding); + return FALSE; + } + + if (found_end_at == 2) + junk = 2; + else if (found_end_at == 3) + junk = 1; + } + else + junk = 0; + + out[(*out_len)++] = (word >> 16) & 0xff; + + if (junk < 2) + out[(*out_len)++] = (word >> 8) & 0xff; + + if (junk < 1) + out[(*out_len)++] = (word >> 0) & 0xff; + } + + return TRUE; +} + +/* Base64 sink + */ +typedef struct _Base64Sink Base64Sink; + +static gboolean base64_sink_close (SerialSink* sink); +static gboolean base64_sink_write (SerialSink* sink, const guint8 *ptr, guint32 len); +static void base64_sink_free (SerialSink* sink); +static gboolean base64_sink_quantum (SerialSink* sink); + +struct _Base64Sink +{ + SerialSink sink; + + SerialSink* out; + + guint32 word; + guint32 count; +}; + +SerialSink* +serializeio_base64_sink (SerialSink* out) +{ + Base64Sink* it = g_new0 (Base64Sink, 1); + SerialSink* sink = (SerialSink*) it; + + serializeio_sink_init (sink, + NULL, + base64_sink_close, + base64_sink_write, + base64_sink_free, + base64_sink_quantum); + + it->out = out; + + return sink; +} + +gboolean +base64_sink_write (SerialSink* fsink, const guint8 *ptr, guint32 len) +{ + guint32 i; + + Base64Sink* sink = (Base64Sink*) fsink; + + for (i = 0; i < len; ) + { + if (sink->count == 3) + { + guint8 out[4]; + + out[0] = base64_table[(sink->word>>18) & 0x3f]; + out[1] = base64_table[(sink->word>>12) & 0x3f]; + out[2] = base64_table[(sink->word>> 6) & 0x3f]; + out[3] = base64_table[(sink->word ) & 0x3f]; + +#if 0 + g_print ("%02x %02x %02x -> %c%c%c%c (3)\n", + (sink->word>>16) & 0xff, + (sink->word>>8) & 0xff, + (sink->word>>0) & 0xff, + out[0], + out[1], + out[2], + out[3]); +#endif + + if (! sink->out->sink_write (sink->out, out, 4)) + return FALSE; + + sink->count = 0; + sink->word = 0; + } + + while (sink->count < 3 && i < len) + sink->word |= ptr[i++] << (8*(2-(sink->count++))); + } + + return TRUE; +} + +gboolean +base64_sink_close (SerialSink* fsink) +{ + Base64Sink* sink = (Base64Sink*) fsink; + + if (sink->count == 3) + { + guint8 out[4]; + + out[0] = base64_table[(sink->word>>18) & 0x3f]; + out[1] = base64_table[(sink->word>>12) & 0x3f]; + out[2] = base64_table[(sink->word>> 6) & 0x3f]; + out[3] = base64_table[(sink->word ) & 0x3f]; + +#if 0 + g_print ("%02x %02x %02x -> %c%c%c%c (3)\n", + (sink->word>>16) & 0xff, + (sink->word>>8) & 0xff, + (sink->word>>0) & 0xff, + out[0], + out[1], + out[2], + out[3]); +#endif + + if (! sink->out->sink_write (sink->out, out, 4)) + return FALSE; + + sink->count = 0; + sink->word = 0; + } + + if (sink->count > 0) + { + guint8 out[4]; + + out[0] = base64_table[(sink->word>>18) & 0x3f]; + out[1] = base64_table[(sink->word>>12) & 0x3f]; + out[2] = (sink->count > 1) ? base64_table[(sink->word>>6) & 0x3f] : '='; + out[3] = '='; + +#if 0 + g_print ("%02x %02x %02x -> %c%c%c%c (%d)\n", + (sink->word>>16) & 0xff, + (sink->word>>8) & 0xff, + (sink->word>>0) & 0xff, + out[0], + out[1], + out[2], + out[3], + sink->count); +#endif + + if (! sink->out->sink_write (sink->out, out, 4)) + return FALSE; + + sink->count = 0; + sink->word = 0; + } + + return sink->out->sink_close (sink->out); +} + + +void +base64_sink_free (SerialSink* fsink) +{ + Base64Sink* sink = (Base64Sink*) fsink; + + sink->out->sink_free (sink->out); + + g_free (sink); +} + +gboolean +base64_sink_quantum (SerialSink* fsink) +{ + Base64Sink* sink = (Base64Sink*) fsink; + + if (sink->out->sink_quantum) + return sink->out->sink_quantum (sink->out); + + return TRUE; +} + +/* Base64 source + */ + +typedef struct _Base64Source Base64Source; + +struct _Base64Source { + SerialSource source; + + SerialSource *in; + + guint32 avail; + guint32 count; + gboolean found_end; + gint found_end_at; + guint8 buf[3]; +}; + +static gboolean base64_source_close (SerialSource* source); +static gboolean base64_source_read (SerialSource* source, guint8 *ptr, guint32 len); +static void base64_source_free (SerialSource* source); + +SerialSource* +serializeio_base64_source (SerialSource* in0) +{ + Base64Source* it = g_new0 (Base64Source, 1); + SerialSource* source = (SerialSource*) it; + + serializeio_source_init (source, + NULL, + base64_source_close, + base64_source_read, + base64_source_free, + NULL, + NULL); + + it->in = in0; + + return source; +} + +gboolean +base64_source_close (SerialSource* fsource) +{ + Base64Source* source = (Base64Source*) fsource; + + if (! source->in->source_close (source->in)) + return FALSE; + + return TRUE; +} + +gboolean +base64_source_read (SerialSource* fsource, guint8 *ptr, guint32 len) +{ + guint32 pos; + Base64Source* source = (Base64Source*) fsource; + + init_inverse_table (); + + for (pos = 0; pos < len; ) + { + if (source->count == 0) + { + guint8 buf[4]; + guint32 i, word = 0, junk; + + if (source->found_end) + { + edsio_generate_void_event (EC_EdsioInvalidBase64Encoding); + return FALSE; + } + + if (! source->in->source_read (source->in, buf, 4)) + return FALSE; + + for (i = 0; i < 4; i += 1) + { + gint x = buf[i]; + + if (x > 127 || base64_inverse_table[x] < 0) + { + edsio_generate_void_event (EC_EdsioInvalidBase64Encoding); + return FALSE; + } + + if (x == '=') + { + if (! source->found_end) + source->found_end_at = i; + + source->found_end = TRUE; + } + else + { + if (source->found_end) + { + edsio_generate_void_event (EC_EdsioInvalidBase64Encoding); + return FALSE; + } + + word |= base64_inverse_table[x] << (6*(3-i)); + } + } + + if (source->found_end) + { + if (source->found_end_at == 2) + junk = 2; + else if (source->found_end_at == 3) + junk = 1; + else + { + edsio_generate_void_event (EC_EdsioInvalidBase64Encoding); + return FALSE; + } + } + else + junk = 0; + + source->avail = source->count = 3 - junk; + + source->buf[0] = (word >> 16) & 0xff; + source->buf[1] = (word >> 8) & 0xff; + source->buf[2] = (word >> 0) & 0xff; + +#if 0 + g_print ("%c%c%c%c -> %02x %02x %02x (%d)\n", + buf[0], + buf[1], + buf[2], + buf[3], + (word>>16) & 0xff, + (word>>8) & 0xff, + (word>>0) & 0xff, + 3-junk); +#endif + } + + ptr[pos++] = source->buf[source->avail-(source->count--)]; + } + + return TRUE; +} + +void +base64_source_free (SerialSource* fsource) +{ + Base64Source* source = (Base64Source*) fsource; + + source->in->source_free (source->in); + + g_free (source); +} |