blob: f7b2484928d87d3ba06ea667398a5af494d15722 [file] [log] [blame]
Denis Vlasenko21afc7d2006-09-03 15:49:40 +00001/* vi: set sw=4 ts=4: */
2/*
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +02003 * Copyright 2003, Glenn McGrath
4 * Copyright 2006, Rob Landley <rob@landley.net>
5 * Copyright 2010, Denys Vlasenko
Denis Vlasenko21afc7d2006-09-03 15:49:40 +00006 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +02007 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Denis Vlasenko21afc7d2006-09-03 15:49:40 +00008 */
9
10#include "libbb.h"
11
12/* Conversion table. for base 64 */
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +020013const char bb_uuenc_tbl_base64[65 + 1] ALIGN1 = {
Denis Vlasenko21afc7d2006-09-03 15:49:40 +000014 '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', 'a', 'b', 'c', 'd', 'e', 'f',
18 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
19 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
20 'w', 'x', 'y', 'z', '0', '1', '2', '3',
21 '4', '5', '6', '7', '8', '9', '+', '/',
Denis Vlasenkoaa198dd2007-06-12 07:24:11 +000022 '=' /* termination character */,
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +020023 '\0' /* needed for uudecode.c only */
Denis Vlasenko21afc7d2006-09-03 15:49:40 +000024};
25
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000026const char bb_uuenc_tbl_std[65] ALIGN1 = {
Denis Vlasenko21afc7d2006-09-03 15:49:40 +000027 '`', '!', '"', '#', '$', '%', '&', '\'',
28 '(', ')', '*', '+', ',', '-', '.', '/',
29 '0', '1', '2', '3', '4', '5', '6', '7',
30 '8', '9', ':', ';', '<', '=', '>', '?',
31 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
32 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
33 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
34 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
35 '`' /* termination character */
36};
37
38/*
Denis Vlasenkoe8240f12007-06-26 15:59:37 +000039 * Encode bytes at S of length LENGTH to uuencode or base64 format and place it
Denis Vlasenko21afc7d2006-09-03 15:49:40 +000040 * to STORE. STORE will be 0-terminated, and must point to a writable
41 * buffer of at least 1+BASE64_LENGTH(length) bytes.
42 * where BASE64_LENGTH(len) = (4 * ((LENGTH + 2) / 3))
43 */
Denis Vlasenkodefc1ea2008-06-27 02:52:20 +000044void FAST_FUNC bb_uuencode(char *p, const void *src, int length, const char *tbl)
Denis Vlasenko21afc7d2006-09-03 15:49:40 +000045{
Denis Vlasenkoe8240f12007-06-26 15:59:37 +000046 const unsigned char *s = src;
Denis Vlasenko21afc7d2006-09-03 15:49:40 +000047
Denis Vlasenko46611172007-08-06 15:43:17 +000048 /* Transform the 3x8 bits to 4x6 bits */
49 while (length > 0) {
50 unsigned s1, s2;
51
52 /* Are s[1], s[2] valid or should be assumed 0? */
53 s1 = s2 = 0;
54 length -= 3; /* can be >=0, -1, -2 */
Denis Vlasenkoe5dbba22007-08-06 15:49:12 +000055 if (length >= -1) {
Denis Vlasenko46611172007-08-06 15:43:17 +000056 s1 = s[1];
Denis Vlasenkoe5dbba22007-08-06 15:49:12 +000057 if (length >= 0)
Denis Vlasenko46611172007-08-06 15:43:17 +000058 s2 = s[2];
59 }
Denis Vlasenko21afc7d2006-09-03 15:49:40 +000060 *p++ = tbl[s[0] >> 2];
Denis Vlasenko46611172007-08-06 15:43:17 +000061 *p++ = tbl[((s[0] & 3) << 4) + (s1 >> 4)];
62 *p++ = tbl[((s1 & 0xf) << 2) + (s2 >> 6)];
63 *p++ = tbl[s2 & 0x3f];
Denis Vlasenko21afc7d2006-09-03 15:49:40 +000064 s += 3;
65 }
Denis Vlasenko46611172007-08-06 15:43:17 +000066 /* Zero-terminate */
Denis Vlasenko21afc7d2006-09-03 15:49:40 +000067 *p = '\0';
Denis Vlasenko46611172007-08-06 15:43:17 +000068 /* If length is -2 or -1, pad last char or two */
69 while (length) {
70 *--p = tbl[64];
71 length++;
72 }
Denis Vlasenko21afc7d2006-09-03 15:49:40 +000073}
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +020074
75/*
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +020076 * Decode base64 encoded string. Stops on '\0'.
77 *
78 * Returns: pointer to the undecoded part of source.
79 * If points to '\0', then the source was fully decoded.
80 * (*pp_dst): advanced past the last written byte.
81 */
82const char* FAST_FUNC decode_base64(char **pp_dst, const char *src)
83{
84 char *dst = *pp_dst;
85 const char *src_tail;
86
87 while (1) {
88 unsigned char six_bit[4];
89 int count = 0;
90
91 /* Fetch up to four 6-bit values */
92 src_tail = src;
93 while (count < 4) {
94 char *table_ptr;
95 int ch;
96
97 /* Get next _valid_ character.
98 * bb_uuenc_tbl_base64[] contains this string:
99 * 0 1 2 3 4 5 6
100 * 01234567890123456789012345678901234567890123456789012345678901234
101 * "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
102 */
103 do {
104 ch = *src;
105 if (ch == '\0') {
106 if (count == 0) {
107 /* Example:
108 * If we decode "QUJD <NUL>", we want
109 * to return ptr to NUL, not to ' ',
110 * because we did fully decode
111 * the string (to "ABC").
112 */
113 src_tail = src;
114 }
115 goto ret;
116 }
117 src++;
118 table_ptr = strchr(bb_uuenc_tbl_base64, ch);
119//TODO: add BASE64_FLAG_foo to die on bad char?
120 } while (!table_ptr);
121
122 /* Convert encoded character to decimal */
123 ch = table_ptr - bb_uuenc_tbl_base64;
124
125 /* ch is 64 if char was '=', otherwise 0..63 */
126 if (ch == 64)
127 break;
128 six_bit[count] = ch;
129 count++;
130 }
131
132 /* Transform 6-bit values to 8-bit ones.
133 * count can be < 4 when we decode the tail:
134 * "eQ==" -> "y", not "y NUL NUL".
135 * Note that (count > 1) is always true,
136 * "x===" encoding is not valid:
137 * even a single zero byte encodes as "AA==".
138 * However, with current logic we come here with count == 1
139 * when we decode "==" tail.
140 */
141 if (count > 1)
142 *dst++ = six_bit[0] << 2 | six_bit[1] >> 4;
143 if (count > 2)
144 *dst++ = six_bit[1] << 4 | six_bit[2] >> 2;
145 if (count > 3)
146 *dst++ = six_bit[2] << 6 | six_bit[3];
147 /* Note that if we decode "AA==" and ate first '=',
148 * we just decoded one char (count == 2) and now we'll
149 * do the loop once more to decode second '='.
150 */
151 } /* while (1) */
152 ret:
153 *pp_dst = dst;
154 return src_tail;
155}
156
157/*
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200158 * Decode base64 encoded stream.
159 * Can stop on EOF, specified char, or on uuencode-style "====" line:
160 * flags argument controls it.
161 */
162void FAST_FUNC read_base64(FILE *src_stream, FILE *dst_stream, int flags)
163{
164/* Note that EOF _can_ be passed as exit_char too */
165#define exit_char ((int)(signed char)flags)
166#define uu_style_end (flags & BASE64_FLAG_UU_STOP)
167
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +0200168 /* uuencoded files have 61 byte lines. Use 64 byte buffer
169 * to process line at a time.
170 */
171 enum { BUFFER_SIZE = 64 };
172
173 char in_buf[BUFFER_SIZE + 2];
174 char out_buf[BUFFER_SIZE / 4 * 3 + 2];
175 char *out_tail;
176 const char *in_tail;
177 int term_seen = 0;
178 int in_count = 0;
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200179
180 while (1) {
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +0200181 while (in_count < BUFFER_SIZE) {
182 int ch = fgetc(src_stream);
183 if (ch == exit_char) {
184 if (in_count == 0)
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200185 return;
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +0200186 term_seen = 1;
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200187 break;
188 }
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +0200189 if (ch == EOF) {
190 term_seen = 1;
191 break;
192 }
193 /* Prevent "====" line to be split: stop if we see '\n'.
194 * We can also skip other whitespace and skirt the problem
195 * of files with NULs by stopping on any control char or space:
196 */
197 if (ch <= ' ')
198 break;
199 in_buf[in_count++] = ch;
200 }
201 in_buf[in_count] = '\0';
202
203 /* Did we encounter "====" line? */
204 if (uu_style_end && strcmp(in_buf, "====") == 0)
205 return;
206
207 out_tail = out_buf;
208 in_tail = decode_base64(&out_tail, in_buf);
209
210 fwrite(out_buf, (out_tail - out_buf), 1, dst_stream);
211
212 if (term_seen) {
213 /* Did we consume ALL characters? */
214 if (*in_tail == '\0')
215 return;
216 /* No */
217 bb_error_msg_and_die("truncated base64 input");
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200218 }
219
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +0200220 /* It was partial decode */
221 in_count = strlen(in_tail);
222 memmove(in_buf, in_tail, in_count);
223 }
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200224}