1 /* vi: set sw=4 ts=4: */
2 /*
3  * Copyright 2003, Glenn McGrath
4  * Copyright 2006, Rob Landley <rob@landley.net>
5  * Copyright 2010, Denys Vlasenko
6  *
7  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8  */
9 #include "libbb.h"
10 
11 /* Conversion tables */
12 #if ENABLE_BASE32
13 const char bb_uuenc_tbl_base32[] ALIGN1 = {
14 	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
15 	'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
16 	'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
17 	'Y', 'Z', '2', '3', '4', '5', '6', '7',
18 	/* unused: '=', */
19 };
20 #endif
21 /* for base 64 */
22 const char bb_uuenc_tbl_base64[] ALIGN1 = {
23 	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
24 	'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
25 	'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
26 	'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
27 	'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
28 	'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
29 	'w', 'x', 'y', 'z', '0', '1', '2', '3',
30 	'4', '5', '6', '7', '8', '9', '+', '/',
31 	'=' /* termination character */
32 };
33 const char bb_uuenc_tbl_std[] ALIGN1 = {
34 	'`', '!', '"', '#', '$', '%', '&', '\'',
35 	'(', ')', '*', '+', ',', '-', '.', '/',
36 	'0', '1', '2', '3', '4', '5', '6', '7',
37 	'8', '9', ':', ';', '<', '=', '>', '?',
38 	'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
39 	'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
40 	'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
41 	'X', 'Y', 'Z', '[', '\\',']', '^', '_',
42 	'`' /* termination character */
43 };
44 
45 /*
46  * Encode bytes at S of length LENGTH to uuencode or base64 format and place it
47  * to STORE.  STORE will be 0-terminated, and must point to a writable
48  * buffer of at least 1+BASE64_LENGTH(length) bytes.
49  * where BASE64_LENGTH(len) = (4 * ((LENGTH + 2) / 3))
50  */
bb_uuencode(char * p,const void * src,int length,const char * tbl)51 void FAST_FUNC bb_uuencode(char *p, const void *src, int length, const char *tbl)
52 {
53 	const unsigned char *s = src;
54 
55 	/* Transform the 3x8 bits to 4x6 bits */
56 	while (length > 0) {
57 		unsigned s1, s2;
58 
59 		/* Are s[1], s[2] valid or should be assumed 0? */
60 		s1 = s2 = 0;
61 		length -= 3; /* can be >=0, -1, -2 */
62 		if (length >= -1) {
63 			s1 = s[1];
64 			if (length >= 0)
65 				s2 = s[2];
66 		}
67 		*p++ = tbl[s[0] >> 2];
68 		*p++ = tbl[((s[0] & 3) << 4) + (s1 >> 4)];
69 		*p++ = tbl[((s1 & 0xf) << 2) + (s2 >> 6)];
70 		*p++ = tbl[s2 & 0x3f];
71 		s += 3;
72 	}
73 	/* Zero-terminate */
74 	*p = '\0';
75 	/* If length is -2 or -1, pad last char or two */
76 	while (length) {
77 		*--p = tbl[64];
78 		length++;
79 	}
80 }
81 
82 /*
83  * Decode base64 encoded string.
84  *
85  * Returns: pointer past the last written output byte,
86  * the result is not NUL-terminated.
87  * (*pp_src) is advanced past the last read byte.
88  * If points to '\0', then the source was fully decoded.
89  */
decode_base64(char * dst,const char ** pp_src)90 char* FAST_FUNC decode_base64(char *dst, const char **pp_src)
91 {
92 	const char *src = pp_src ? *pp_src : dst; /* for httpd.c, support NULL 2nd param */
93 	unsigned ch = 0;
94 	unsigned t;
95 	int i = 0;
96 
97 	while ((t = (unsigned char)*src) != '\0') {
98 		src++;
99 
100 		/* "if" forest is faster than strchr(bb_uuenc_tbl_base64, t) */
101 		if (t >= '0' && t <= '9')
102 			t = t - '0' + 52;
103 		else if (t >= 'A' && t <= 'Z')
104 			t = t - 'A';
105 		else if (t >= 'a' && t <= 'z')
106 			t = t - 'a' + 26;
107 		else if (t == '+')
108 			t = 62;
109 		else if (t == '/')
110 			t = 63;
111 		else if (t == '=' && (i == 3 || (i == 2 && *src == '=')))
112 			/* the above disallows "==AA", "A===", "AA=A" etc */
113 			t = 0x1000000;
114 		else
115 //TODO: add BASE64_FLAG_foo to die on bad char?
116 			continue;
117 
118 		ch = (ch << 6) | t;
119 		i = (i + 1) & 3;
120 		if (i == 0) {
121 			*dst++ = (char) (ch >> 16);
122 			*dst++ = (char) (ch >> 8);
123 			*dst++ = (char) ch;
124 			if (ch & 0x1000000) { /* was last input char '='? */
125 				dst--;
126 				if (ch & (0x1000000 << 6)) /* was it "=="? */
127 					dst--;
128 				break;
129 			}
130 			ch = 0;
131 		}
132 	}
133 	/* i is zero here if full 4-char block was decoded */
134 	if (pp_src)
135 		*pp_src = src - i; /* -i signals truncation: e.g. "MQ" and "MQ=" (correct encoding is "MQ==" -> "1") */
136 	return dst;
137 }
138 
139 #if ENABLE_BASE32
decode_base32(char * dst,const char ** pp_src)140 char* FAST_FUNC decode_base32(char *dst, const char **pp_src)
141 {
142 	const char *src = *pp_src;
143 	uint64_t ch = 0;
144 	unsigned t;
145 	int i = 0;
146 
147 	while ((t = (unsigned char)*src) != '\0') {
148 		src++;
149 
150 		/* "if" forest is faster than strchr(bb_uuenc_tbl_base32, t) */
151 		if (t >= '2' && t <= '7')
152 			t = t - '2' + 26;
153 		else if (t == '=' && i > 1)
154 			t = 0;
155 		else {
156 			t = (t | 0x20) - 'a';
157 			if (t > 25)
158 //TODO: add BASE64_FLAG_foo to die on bad char?
159 				continue;
160 		}
161 
162 		ch = (ch << 5) | t;
163 		i = (i + 1) & 7;
164 		if (i == 0) {
165 			*dst++ = (char) (ch >> 32);
166 			if (src[-1] == '=') /* was last input char '='? */
167 				goto tail;
168 			*dst++ = (char) (ch >> 24);
169 			*dst++ = (char) (ch >> 16);
170 			*dst++ = (char) (ch >> 8);
171 			*dst++ = (char) ch;
172 		}
173 	}
174 	/* i is zero here if full 8-char block was decoded */
175 	*pp_src = src - i;
176 	return dst;
177  tail:
178 	{
179 		const char *s = src;
180 		while (*--s == '=')
181 			i++;
182 		/* Why duplicate the below code? Testcase:
183 		 * echo ' 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18' | base32 | base32 -d
184 		 * IOW, decoding of
185 		 * EAYSAMRAGMQDIIBVEA3CANZAHAQDSIBRGAQDCMJAGEZCAMJTEAYTIIBRGUQDCNRAGE3SAMJYBI==
186 		 * ====
187 		 * must correctly stitch together the tail, must not overwrite
188 		 * the tail before it is analyzed! (we can be decoding in-place)
189 		 * Else testcase fails, prints trailing extra NUL bytes.
190 		 */
191 		*dst++ = (char) (ch >> 24);
192 		*dst++ = (char) (ch >> 16);
193 		*dst++ = (char) (ch >> 8);
194 		*dst++ = (char) ch;
195 		dst -= (i+1) * 2 / 3; /* discard last 1, 2, 3 or 4 bytes */
196 	}
197 	*pp_src = src;
198 	return dst;
199 }
200 #endif
201 
202 /*
203  * Decode base64 encoded stream.
204  * Can stop on EOF, specified char, or on uuencode-style "====" line:
205  * flags argument controls it.
206  */
read_base64(FILE * src_stream,FILE * dst_stream,int flags)207 void FAST_FUNC read_base64(FILE *src_stream, FILE *dst_stream, int flags)
208 {
209 /* Note that EOF _can_ be passed as exit_char too */
210 #define exit_char    ((int)(signed char)flags)
211 #define uu_style_end (flags & BASE64_FLAG_UU_STOP)
212 #define base32       (flags & BASE64_32)
213 
214 	/* uuencoded files have 61 byte lines.
215 	 * base32/64 have 76 byte lines by default.
216 	 * Use 80 byte buffer to process one line at a time.
217 	 */
218 	enum { BUFFER_SIZE = 80 };
219 	/* decoded data is shorter than input, can use single buffer for both */
220 	char buf[BUFFER_SIZE + 2];
221 	int term_seen = 0;
222 	int in_count = 0;
223 
224 	while (1) {
225 		char *out_tail;
226 		const char *in_tail;
227 
228 		while (in_count < BUFFER_SIZE) {
229 			int ch = fgetc(src_stream);
230 			if (ch == exit_char) {
231 				if (in_count == 0)
232 					return;
233 				term_seen = 1;
234 				break;
235 			}
236 			if (ch == EOF) {
237 				term_seen = 1;
238 				break;
239 			}
240 			/* Prevent "====" line to be split: stop if we see '\n'.
241 			 * We can also skip other whitespace and skirt the problem
242 			 * of files with NULs by stopping on any control char or space:
243 			 */
244 			if (ch <= ' ')
245 				break;
246 			buf[in_count++] = ch;
247 		}
248 		buf[in_count] = '\0';
249 
250 		/* Did we encounter "====" line? */
251 		if (uu_style_end && strcmp(buf, "====") == 0)
252 			return;
253 
254 		in_tail = buf;
255 #if ENABLE_BASE32
256 		if (base32)
257 			out_tail = decode_base32(buf, &in_tail);
258 		else
259 #endif
260 			out_tail = decode_base64(buf, &in_tail);
261 
262 		fwrite(buf, (out_tail - buf), 1, dst_stream);
263 
264 		if (term_seen) {
265 			/* Did we consume ALL characters? */
266 			if (*in_tail == '\0')
267 				return;
268 			/* No */
269 			bb_simple_error_msg_and_die("truncated input");
270 		}
271 
272 		/* It was partial decode */
273 		in_count = strlen(in_tail);
274 		memmove(buf, in_tail, in_count);
275 	}
276 }
277