From ceba19865c063cf6d2fa5ba3ebcf43674a101549 Mon Sep 17 00:00:00 2001 Message-Id: From: Mark Wooding Date: Mon, 17 May 1999 20:35:00 +0000 Subject: [PATCH] Base64 encoding and decoding support. Organization: Straylight/Edgeware From: mdw --- base64.c | 286 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ base64.h | 112 ++++++++++++++++++++++ 2 files changed, 398 insertions(+) create mode 100644 base64.c create mode 100644 base64.h diff --git a/base64.c b/base64.c new file mode 100644 index 0000000..e5cdcfa --- /dev/null +++ b/base64.c @@ -0,0 +1,286 @@ +/* -*-c-*- + * + * $Id: base64.c,v 1.1 1999/05/17 20:35:00 mdw Exp $ + * + * Base64 encoding and decoding. + * + * (c) 1997 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with mLib; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: base64.c,v $ + * Revision 1.1 1999/05/17 20:35:00 mdw + * Base64 encoding and decoding support. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include + +#include "base64.h" +#include "dstr.h" + +/*----- Important tables --------------------------------------------------*/ + +static const char base64_encodeMap[] = { "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/" }; + +static const signed char base64_decodeMap[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 1x */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 2x */ + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 3x */ + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 4x */ + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 5x */ + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36 ,37, 38, 39, 40, /* 6x */ + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 7x */ +}; + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @base64_encode@ --- * + * + * Arguments: @base64_ctx *ctx@ = pointer to a context block + * @const unsigned char *src@ = pointer to a source buffer + * @size_t sz@ = size of the source buffer + * @dstr *d@ = pointer to destination string + * + * Returns: --- + * + * Use: Encodes a binary string in base64. To flush out the final + * few characters (if necessary), pass a null source pointer. + */ + +void base64_encode(base64_ctx *ctx, + const unsigned char *src, size_t sz, + dstr *d) +{ + if (src) { + unsigned long acc = ctx->acc; + unsigned qsz = ctx->qsz; + + while (sz) { + acc = (acc << 8) | *src++; + qsz++; + sz--; + if (qsz == 3) { + dstr_putc(d, base64_encodeMap[(acc >> 18) & 0x3f]); + dstr_putc(d, base64_encodeMap[(acc >> 12) & 0x3f]); + dstr_putc(d, base64_encodeMap[(acc >> 6) & 0x3f]); + dstr_putc(d, base64_encodeMap[(acc >> 0) & 0x3f]); + ctx->lnlen += 4; + if (ctx->maxline && ctx->lnlen >= ctx->maxline) { + dstr_puts(d, ctx->indent); + ctx->lnlen = 0; + } + qsz = 0; + acc = 0; + } + } + + ctx->acc = acc; + ctx->qsz = qsz; + } else { + unsigned long acc = ctx->acc; + unsigned qsz = ctx->qsz; + + switch (qsz) { + case 0: + break; + case 1: + acc <<= 16; + dstr_putc(d, base64_encodeMap[(acc >> 18) & 0x3f]); + dstr_putc(d, base64_encodeMap[(acc >> 12) & 0x3f]); + dstr_putc(d, '='); + dstr_putc(d, '='); + ctx->lnlen += 4; + break; + case 2: + acc <<= 8; + dstr_putc(d, base64_encodeMap[(acc >> 18) & 0x3f]); + dstr_putc(d, base64_encodeMap[(acc >> 12) & 0x3f]); + dstr_putc(d, base64_encodeMap[(acc >> 6) & 0x3f]); + dstr_putc(d, '='); + ctx->lnlen += 4; + break; + } + ctx->qsz = 0; + ctx->acc = 0; + } +} + +/* --- @base64_decode@ --- * + * + * Arguments: @base64_ctx *ctx@ = pointer to a context block + * @const unsigned char *src@ = pointer to a source buffer + * @size_t sz@ = size of the source buffer + * @dstr *d@ = pointer to destination string + * + * Returns: --- + * + * Use: Decodes a binary string in base64. To flush out the final + * few characters (if necessary), pass a null source pointer. + */ + +void base64_decode(base64_ctx *ctx, + const unsigned char *src, size_t sz, + dstr *d) +{ + if (src) { + unsigned long acc = ctx->acc; + unsigned qsz = ctx->qsz; + int ch; + + while (sz) { + + /* --- Get the next character and convert it --- */ + + ch = *src++; + if (ch >= 128 || ch < 0) + ch = -1; + else + ch = base64_decodeMap[ch]; + sz--; + if (ch == -1) + continue; + + /* --- Bung it in the accumulator --- */ + + acc = (acc << 6) | ch; + qsz++; + + /* --- Maybe write out a completed triplet --- */ + + if (qsz == 4) { + dstr_putc(d, (acc >> 16) & 0xff); + dstr_putc(d, (acc >> 8) & 0xff); + dstr_putc(d, (acc >> 0) & 0xff); + acc = 0; + qsz = 0; + } + } + + ctx->acc = acc; + ctx->qsz = qsz; + } else { + + /* --- Notes about the tail-end bits --- * + * + * Ending Base64 decoding is messy. The reference I'm using to define + * the encoding, RFC1521 section 5.2, is a little hazy on exactly what to + * do at the end. It explains that I'm meant to ignore spurious `=' + * characters, and points out that I'm not guaranteed to see anything + * interesting at the end. I'll play safe here, and ignore all `=' + * characters, relying on my client to work out when to stop feeding me + * data. I'll use the queue size to work out how many tail-end bytes + * I ought to write. + */ + + unsigned long acc = ctx->acc; + unsigned qsz = ctx->qsz; + + /* --- Now fiddle with everything else --- */ + + acc <<= 6 * (4 - qsz); + qsz *= 6; + while (qsz > 8) { + dstr_putc(d, (acc >> 16) & 0xff); + acc <<= 8; + qsz -= 8; + } + + /* --- That seems to be good enough --- */ + + ctx->qsz = 0; + ctx->acc = 0; + } +} + +/* --- @base64_init@ --- * + * + * Arguments: @base64_ctx *ctx@ = pointer to context block to initialize + * + * Returns: --- + * + * Use: Initializes a base64 context properly. + */ + +void base64_init(base64_ctx *ctx) +{ + ctx->acc = 0; + ctx->qsz = 0; + ctx->lnlen = 0; + ctx->indent = "\n"; + ctx->maxline = 72; +} + +/*----- Test driver code --------------------------------------------------*/ + +#ifdef TEST_RIG + +int main(int argc, char *argv[]) +{ + unsigned char buf[BUFSIZ]; + dstr d; + base64_ctx ctx; + void (*proc)(base64_ctx *, const unsigned char *, size_t, dstr *); + size_t sz; + + dstr_create(&d); + base64_init(&ctx); + + if (argc > 1 && strcmp(argv[1], "-d") == 0) + proc = base64_decode; + else { + proc = base64_encode; + putchar('\t'); + ctx.indent = "\n\t"; + ctx.maxline = 64; + } + + do { + sz = fread(buf, 1, sizeof(buf), stdin); + if (sz) { + proc(&ctx, buf, sz, &d); + dstr_write(&d, stdout); + dstr_destroy(&d); + } + } while (sz == sizeof(buf)); + + proc(&ctx, 0, 0, &d); + dstr_write(&d, stdout); + + if (proc == base64_encode) + putchar('\n'); + + return (0); +} + +#endif + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/base64.h b/base64.h new file mode 100644 index 0000000..c5943d3 --- /dev/null +++ b/base64.h @@ -0,0 +1,112 @@ +/* -*-c-*- + * + * $Id: base64.h,v 1.1 1999/05/17 20:35:00 mdw Exp $ + * + * Base64 encoding and decoding + * + * (c) 1997 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with mLib; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: base64.h,v $ + * Revision 1.1 1999/05/17 20:35:00 mdw + * Base64 encoding and decoding support. + * + */ + +#ifndef BASE64_H +#define BASE64_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include "dstr.h" + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct base64_ctx { + unsigned long acc; /* Accumulator for output data */ + unsigned qsz; /* Length of data queued */ + unsigned lnlen; /* Length of the current line */ + const char *indent; /* Newline-and-indent string */ + unsigned maxline; /* Maximum permitted line length */ +} base64_ctx; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @base64_encode@ --- * + * + * Arguments: @base64_ctx *ctx@ = pointer to a context block + * @const unsigned char *src@ = pointer to a source buffer + * @size_t sz@ = size of the source buffer + * @dstr *d@ = pointer to destination string + * + * Returns: --- + * + * Use: Encodes a binary string in base64. To flush out the final + * few characters (if necessary), pass a null source pointer. + */ + +extern void base64_encode(base64_ctx */*ctx*/, + const unsigned char */*src*/, size_t /*sz*/, + dstr */*d*/); + +/* --- @base64_decode@ --- * + * + * Arguments: @base64_ctx *ctx@ = pointer to a context block + * @const unsigned char *src@ = pointer to a source buffer + * @size_t sz@ = size of the source buffer + * @dstr *d@ = pointer to destination string + * + * Returns: --- + * + * Use: Decodes a binary string in base64. To flush out the final + * few characters (if necessary), pass a null source pointer. + */ + +extern void base64_decode(base64_ctx */*ctx*/, + const unsigned char */*src*/, size_t /*sz*/, + dstr */*d*/); + +/* --- @base64_init@ --- * + * + * Arguments: @base64_ctx *ctx@ = pointer to context block to initialize + * + * Returns: --- + * + * Use: Initializes a base64 context properly. + */ + +extern void base64_init(base64_ctx */*ctx*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif -- [mdw]