chiark / gitweb /
5f5009a9a86854eb71b5040f86fcd0e6269763a1
[mLib] / base64.c
1 /* -*-c-*-
2  *
3  * $Id: base64.c,v 1.2 1999/05/18 21:45:27 mdw Exp $
4  *
5  * Base64 encoding and decoding.
6  *
7  * (c) 1997 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of the mLib utilities library.
13  *
14  * mLib is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU Library General Public License as
16  * published by the Free Software Foundation; either version 2 of the
17  * License, or (at your option) any later version.
18  * 
19  * mLib is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU Library General Public License for more details.
23  * 
24  * You should have received a copy of the GNU Library General Public
25  * License along with mLib; if not, write to the Free
26  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27  * MA 02111-1307, USA.
28  */
29
30 /*----- Revision history --------------------------------------------------*
31  *
32  * $Log: base64.c,v $
33  * Revision 1.2  1999/05/18 21:45:27  mdw
34  * Allow Base64 encode and decode of arbitrary rubbish.
35  *
36  * Revision 1.1  1999/05/17 20:35:00  mdw
37  * Base64 encoding and decoding support.
38  *
39  */
40
41 /*----- Header files ------------------------------------------------------*/
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46
47 #include "base64.h"
48 #include "dstr.h"
49
50 /*----- Important tables --------------------------------------------------*/
51
52 static const char base64_encodeMap[] = { "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
53                                          "abcdefghijklmnopqrstuvwxyz" 
54                                          "0123456789+/" };
55
56 static const signed char base64_decodeMap[] = {
57   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  /* 0x */
58   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  /* 1x */
59   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,  /* 2x */
60   52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,  /* 3x */
61   -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,  /* 4x */
62   15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,  /* 5x */
63   -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36 ,37, 38, 39, 40,  /* 6x */
64   41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1   /* 7x */
65 };  
66
67 /*----- Main code ---------------------------------------------------------*/
68
69 /* --- @base64_encode@ --- *
70  *
71  * Arguments:   @base64_ctx *ctx@ = pointer to a context block
72  *              @const void *p@ = pointer to a source buffer
73  *              @size_t sz@ = size of the source buffer
74  *              @dstr *d@ = pointer to destination string
75  *
76  * Returns:     ---
77  *
78  * Use:         Encodes a binary string in base64.  To flush out the final
79  *              few characters (if necessary), pass a null source pointer.
80  */
81
82 void base64_encode(base64_ctx *ctx,
83                    const void *p, size_t sz,
84                    dstr *d)
85 {
86   if (p) {
87     unsigned long acc = ctx->acc;
88     unsigned qsz = ctx->qsz;
89     const unsigned char *src = p;
90
91     while (sz) {
92       acc = (acc << 8) | *src++;
93       qsz++;
94       sz--;
95       if (qsz == 3) {
96         DPUTC(d, base64_encodeMap[(acc >> 18) & 0x3f]);
97         DPUTC(d, base64_encodeMap[(acc >> 12) & 0x3f]);
98         DPUTC(d, base64_encodeMap[(acc >>  6) & 0x3f]);
99         DPUTC(d, base64_encodeMap[(acc >>  0) & 0x3f]);
100         ctx->lnlen += 4;
101         if (ctx->maxline && ctx->lnlen >= ctx->maxline) {
102           dstr_puts(d, ctx->indent);
103           ctx->lnlen = 0;
104         }
105         qsz = 0;
106         acc = 0;
107       }
108     }
109
110     ctx->acc = acc;
111     ctx->qsz = qsz;
112   } else {
113     unsigned long acc = ctx->acc;
114     unsigned qsz = ctx->qsz;
115
116     switch (qsz) {
117       case 0:
118         break;
119       case 1:
120         acc <<= 16;
121         DPUTC(d, base64_encodeMap[(acc >> 18) & 0x3f]);
122         DPUTC(d, base64_encodeMap[(acc >> 12) & 0x3f]);
123         DPUTC(d, '=');
124         DPUTC(d, '=');
125         ctx->lnlen += 4;
126         break;
127       case 2:
128         acc <<= 8;
129         DPUTC(d, base64_encodeMap[(acc >> 18) & 0x3f]);
130         DPUTC(d, base64_encodeMap[(acc >> 12) & 0x3f]);
131         DPUTC(d, base64_encodeMap[(acc >>  6) & 0x3f]);
132         DPUTC(d, '=');
133         ctx->lnlen += 4;
134         break;
135     }
136     ctx->qsz = 0;
137     ctx->acc = 0;
138   }
139 }
140
141 /* --- @base64_decode@ --- *
142  *
143  * Arguments:   @base64_ctx *ctx@ = pointer to a context block
144  *              @const void *p@ = pointer to a source buffer
145  *              @size_t sz@ = size of the source buffer
146  *              @dstr *d@ = pointer to destination string
147  *
148  * Returns:     ---
149  *
150  * Use:         Decodes a binary string in base64.  To flush out the final
151  *              few characters (if necessary), pass a null source pointer.
152  */
153
154 void base64_decode(base64_ctx *ctx,
155                    const void *p, size_t sz,
156                    dstr *d)
157 {
158   if (p) {
159     unsigned long acc = ctx->acc;
160     unsigned qsz = ctx->qsz;
161     const char *src = p;
162     int ch;
163
164     while (sz) {
165
166       /* --- Get the next character and convert it --- */
167
168       ch = *src++;
169       if (ch >= 128 || ch < 0)
170         ch = -1;
171       else
172         ch = base64_decodeMap[ch];
173       sz--;
174       if (ch == -1)
175         continue;
176
177       /* --- Bung it in the accumulator --- */
178
179       acc = (acc << 6) | ch;
180       qsz++;
181
182       /* --- Maybe write out a completed triplet --- */
183
184       if (qsz == 4) {
185         DPUTC(d, (acc >> 16) & 0xff);
186         DPUTC(d, (acc >>  8) & 0xff);
187         DPUTC(d, (acc >>  0) & 0xff);
188         acc = 0;
189         qsz = 0;
190       }
191     }
192
193     ctx->acc = acc;
194     ctx->qsz = qsz;
195   } else {
196
197     /* --- Notes about the tail-end bits --- *
198      *
199      * Ending Base64 decoding is messy.  The reference I'm using to define
200      * the encoding, RFC1521 section 5.2, is a little hazy on exactly what to
201      * do at the end.  It explains that I'm meant to ignore spurious `='
202      * characters, and points out that I'm not guaranteed to see anything
203      * interesting at the end.  I'll play safe here, and ignore all `='
204      * characters, relying on my client to work out when to stop feeding me
205      * data.  I'll use the queue size to work out how many tail-end bytes
206      * I ought to write.
207      */
208
209     unsigned long acc = ctx->acc;
210     unsigned qsz = ctx->qsz;
211
212     /* --- Now fiddle with everything else --- */
213
214     acc <<= 6 * (4 - qsz);
215     qsz *= 6;
216     while (qsz > 8) {
217       DPUTC(d, (acc >> 16) & 0xff);
218       acc <<= 8;
219       qsz -= 8;
220     }
221
222     /* --- That seems to be good enough --- */
223
224     ctx->qsz = 0;
225     ctx->acc = 0;
226   }
227 }
228
229 /* --- @base64_init@ --- *
230  *
231  * Arguments:   @base64_ctx *ctx@ = pointer to context block to initialize
232  *
233  * Returns:     ---
234  *
235  * Use:         Initializes a base64 context properly.
236  */
237
238 void base64_init(base64_ctx *ctx)
239 {
240   ctx->acc = 0;
241   ctx->qsz = 0;
242   ctx->lnlen = 0;
243   ctx->indent = "\n";
244   ctx->maxline = 72;
245 }
246
247 /*----- Test driver code --------------------------------------------------*/
248
249 #ifdef TEST_RIG
250
251 int main(int argc, char *argv[])
252 {
253   unsigned char buf[BUFSIZ];
254   dstr d;
255   base64_ctx ctx;
256   void (*proc)(base64_ctx *, const unsigned char *, size_t, dstr *);
257   size_t sz;
258
259   dstr_create(&d);
260   base64_init(&ctx);
261
262   if (argc > 1 && strcmp(argv[1], "-d") == 0)
263     proc = base64_decode;
264   else {
265     proc = base64_encode;
266     putchar('\t');
267     ctx.indent = "\n\t";
268     ctx.maxline = 64;
269   }
270
271   do {
272     sz = fread(buf, 1, sizeof(buf), stdin);
273     if (sz) {
274       proc(&ctx, buf, sz, &d);
275       dstr_write(&d, stdout);
276       dstr_destroy(&d);
277     }
278   } while (sz == sizeof(buf));
279
280   proc(&ctx, 0, 0, &d);
281   dstr_write(&d, stdout);
282
283   if (proc == base64_encode)
284     putchar('\n');
285
286   return (0);
287 }
288
289 #endif
290
291 /*----- That's all, folks -------------------------------------------------*/