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