1 /*
2  * Heirloom mailx - a mail user agent derived from Berkeley Mail.
3  *
4  * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5  */
6 /*
7  * These base64 routines are derived from the metamail-2.7 sources which
8  * state the following copyright notice:
9  *
10  * Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)
11  *
12  * Permission to use, copy, modify, and distribute this material
13  * for any purpose and without fee is hereby granted, provided
14  * that the above copyright notice and this permission notice
15  * appear in all copies, and that the name of Bellcore not be
16  * used in advertising or publicity pertaining to this
17  * material without the specific, prior written permission
18  * of an authorized representative of Bellcore.  BELLCORE
19  * MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY
20  * OF THIS MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED "AS IS",
21  * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
22  */
23 
24 #ifndef lint
25 #ifdef	DOSCCS
26 static char sccsid[] = "@(#)base64.c	2.14 (gritter) 4/21/06";
27 #endif
28 #endif /* not lint */
29 
30 /*
31  * Mail -- a mail program
32  *
33  * base64 functions
34  */
35 
36 #include "rcv.h"
37 #include "extern.h"
38 
39 static const char b64table[] =
40 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
41 static const signed char b64index[] = {
42 	-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
43 	-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
44 	-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
45 	52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
46 	-1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
47 	15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
48 	-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
49 	41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
50 };
51 
52 #define char64(c)  ((c) < 0 ? -1 : b64index[(int)(c)])
53 
54 static signed char *ctob64(unsigned char *p, int pad);
55 
56 /*
57  * Convert three characters to base64.
58  */
59 static signed char *
ctob64(unsigned char * p,int pad)60 ctob64(unsigned char *p, int pad)
61 {
62 	static signed char b64[4];
63 
64 	b64[0] = b64table[p[0] >> 2];
65 	b64[1] = b64table[((p[0] & 0x3) << 4) | ((p[1] & 0xF0) >> 4)];
66 	if (pad == 2) {
67 		b64[1] = b64table[(p[0] & 0x3) << 4];
68 		b64[2] = b64[3] = '=';
69 	} else if (pad == 1) {
70 		b64[2] = b64table[((p[1] & 0xF) << 2)];
71 		b64[3] = '=';
72 	} else {
73 		b64[2] = b64table[((p[1] & 0xF) << 2) | ((p[2] & 0xC0) >> 6)];
74 		b64[3] = b64table[p[2] & 0x3F];
75 	}
76 	return b64;
77 }
78 
79 char *
strtob64(const char * p)80 strtob64(const char *p)
81 {
82 	return memtob64(p, strlen(p));
83 }
84 
85 char *
memtob64(const void * vp,size_t isz)86 memtob64(const void *vp, size_t isz)
87 {
88 	char	q[3];
89 	const char	*p = vp;
90 	signed char	*h;
91 	size_t	c = 0;
92 	int	i, l = 0, sz = 0, pads;
93 	char	*rs = NULL;
94 
95 	if (isz == 0) {
96 		rs = smalloc(1);
97 		*rs = '\0';
98 		return rs;
99 	}
100 	do {
101 		for (pads = 2, i = 0; i <= 2; i++, pads--) {
102 			q[i] = p[c++];
103 			if (c == isz)
104 				break;
105 		}
106 		h = ctob64((unsigned char *)q, pads);
107 		if (l + 5 >= sz)
108 			rs = srealloc(rs, sz = l + 100);
109 		for (i = 0; i < 4; i++)
110 			rs[l++] = h[i];
111 	} while (c < isz);
112 	rs[l] = '\0';
113 	return rs;
114 }
115 
116 /*
117  * Write to a file converting to base64. The input must be aligned
118  * e.g. at 972 character bounds.
119  */
120 size_t
mime_write_tob64(struct str * in,FILE * fo,int is_header)121 mime_write_tob64(struct str *in, FILE *fo, int is_header)
122 {
123 	char *p, *upper, q[3];
124 	signed char *h;
125 	int i, l, pads;
126 	size_t sz;
127 
128 	sz = 0;
129 	upper = in->s + in->l;
130 	for (p = in->s, l = 0; p < upper; ) {
131 		for (pads = 2, i = 0; i <= 2; i++, pads--) {
132 			q[i] = *p++;
133 			if (p == upper)
134 				break;
135 		}
136 		h = ctob64((unsigned char *)q, pads);
137 		fwrite(h, sizeof(char), 4, fo);
138 		sz += 4, l += 4;
139 		if (l >= 71) {
140 			putc('\n', fo), sz++;
141 			l = 0;
142 		}
143 	}
144 	if (l != 0 && !is_header) {
145 		putc('\n', fo), sz++;
146 	}
147 	return sz;
148 }
149 
150 /*
151  * Decode from base64.
152  */
153 void
mime_fromb64(struct str * in,struct str * out,int is_text)154 mime_fromb64(struct str *in, struct str *out, int is_text)
155 {
156 	char *p, *q, *upper;
157 	signed char c, d, e, f, g;
158 	int done = 0, newline = 0;
159 
160 	out->s = smalloc(in->l * 3 / 4 + 2);
161 	out->l = 0;
162 	upper = in->s + in->l;
163 	for (p = in->s, q = out->s; p < upper; ) {
164 		while (c = *p++, whitechar(c));
165 		if (p >= upper) break;
166 		if (done) continue;
167 		while (d = *p++, whitechar(d));
168 		if (p >= upper) break;
169 		while (e = *p++, whitechar(e));
170 		if (p >= upper) break;
171 		while (f = *p++, whitechar(f));
172 		if (c == '=' || d == '=') {
173 			done = 1;
174 			continue;
175 		}
176 		c = char64(c);
177 		d = char64(d);
178 		g = ((c << 2) | ((d & 0x30) >> 4));
179 		if (is_text) {
180 			if (g == '\r') {
181 				newline = 1;
182 			} else if (g == '\n' && newline) {
183 				q--;
184 				out->l--;
185 				newline = 0;
186 			} else {
187 				newline = 0;
188 			}
189 		}
190 		*q++ = g;
191 		out->l++;
192 		if (e == '=') {
193 			done = 1;
194 		} else {
195 			e = char64(e);
196 			g = (((d & 0xF) << 4) | ((e & 0x3C) >> 2));
197 			if (is_text) {
198 				if (g == '\r') {
199 					newline = 1;
200 				} else if (g == '\n' && newline) {
201 					q--;
202 					out->l--;
203 					newline = 0;
204 				} else {
205 					newline = 0;
206 				}
207 			}
208 			*q++ = g;
209 			out->l++;
210 			if (f == '=') {
211 				done = 1;
212 			} else {
213 				f = char64(f);
214 				g = (((e & 0x03) << 6) | f);
215 				if (is_text) {
216 					if (g == '\r') {
217 						newline = 1;
218 					} else if (g == '\n' && newline) {
219 						q--;
220 						out->l--;
221 						newline = 0;
222 					} else {
223 						newline = 0;
224 					}
225 				}
226 				*q++ = g;
227 				out->l++;
228 			}
229 		}
230 	}
231 	return;
232 }
233 
234 /*
235  * Buffer the base64 input so mime_fromb64 gets always multiples of
236  * 4 characters.
237  * As we have only one buffer, this function is not reentrant.
238  */
239 void
mime_fromb64_b(struct str * in,struct str * out,int is_text,FILE * f)240 mime_fromb64_b(struct str *in, struct str *out, int is_text, FILE *f)
241 {
242 	static signed char b[4];
243 	static int n;
244 	static FILE *f_b = (FILE *)-1;
245 	signed char c;
246 	int i;
247 	struct str nin;
248 
249 	nin.s = smalloc(in->l + n);
250 	if (n != 0 && f_b == f) {
251 		for (nin.l = 0; nin.l < n; nin.l++)
252 			nin.s[nin.l] = b[nin.l];
253 	} else {
254 		nin.l = 0;
255 		n = 0;
256 	}
257 
258 	for (i = 0; i <= in->l; i++) {
259 		c = in->s[i];
260 		if (char64(c) == -1 && c != '=')
261 			continue;
262 		b[n] = nin.s[nin.l++] = c;
263 		if (n >= 3)
264 			n = 0;
265 		else
266 			n++;
267 	}
268 	nin.l -= n;
269 	mime_fromb64(&nin, out, is_text);
270 	free(nin.s);
271 	f_b = f;
272 }
273