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 */
Denys Vlasenkoe6094d92011-10-28 16:15:00 +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 */,
Denys Vlasenkoe6094d92011-10-28 16:15:00 +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/*
Leonid Lisovskiy328f27f2011-10-28 13:59:04 +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.
Denys Vlasenkoe6094d92011-10-28 16:15:00 +020080 * (*pp_dst): advanced past the last written byte.
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +020081 */
Leonid Lisovskiy328f27f2011-10-28 13:59:04 +020082const char* FAST_FUNC decode_base64(char **pp_dst, const char *src)
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +020083{
Leonid Lisovskiy328f27f2011-10-28 13:59:04 +020084 char *dst = *pp_dst;
85 const char *src_tail;
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +020086
87 while (1) {
Denys Vlasenkoe6094d92011-10-28 16:15:00 +020088 unsigned char six_bit[4];
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +020089 int count = 0;
90
Denys Vlasenkoe6094d92011-10-28 16:15:00 +020091 /* Fetch up to four 6-bit values */
Leonid Lisovskiy328f27f2011-10-28 13:59:04 +020092 src_tail = src;
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +020093 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
Denys Vlasenkoe6094d92011-10-28 16:15:00 +0200100 * 01234567890123456789012345678901234567890123456789012345678901234
101 * "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200102 */
103 do {
Leonid Lisovskiy328f27f2011-10-28 13:59:04 +0200104 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++;
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200118 table_ptr = strchr(bb_uuenc_tbl_base64, ch);
119//TODO: add BASE64_FLAG_foo to die on bad char?
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200120 } while (!table_ptr);
121
122 /* Convert encoded character to decimal */
123 ch = table_ptr - bb_uuenc_tbl_base64;
124
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200125 /* ch is 64 if char was '=', otherwise 0..63 */
Denys Vlasenkoe6094d92011-10-28 16:15:00 +0200126 if (ch == 64)
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200127 break;
Denys Vlasenkoe6094d92011-10-28 16:15:00 +0200128 six_bit[count] = ch;
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200129 count++;
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200130 }
131
Denys Vlasenkoe6094d92011-10-28 16:15:00 +0200132 /* Transform 6-bit values to 8-bit ones.
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200133 * count can be < 4 when we decode the tail:
Denys Vlasenkoe6094d92011-10-28 16:15:00 +0200134 * "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.
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200140 */
141 if (count > 1)
Denys Vlasenkoe6094d92011-10-28 16:15:00 +0200142 *dst++ = six_bit[0] << 2 | six_bit[1] >> 4;
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200143 if (count > 2)
Denys Vlasenkoe6094d92011-10-28 16:15:00 +0200144 *dst++ = six_bit[1] << 4 | six_bit[2] >> 2;
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200145 if (count > 3)
Denys Vlasenkoe6094d92011-10-28 16:15:00 +0200146 *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 */
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200151 } /* while (1) */
Leonid Lisovskiy328f27f2011-10-28 13:59:04 +0200152 ret:
153 *pp_dst = dst;
154 return src_tail;
155}
156
157/*
158 * 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
168 /* 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;
179
180 while (1) {
181 while (in_count < BUFFER_SIZE) {
182 int ch = fgetc(src_stream);
183 if (ch == exit_char) {
184 if (in_count == 0)
185 return;
186 term_seen = 1;
187 break;
188 }
189 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
Denys Vlasenko73d249e2011-10-28 14:07:44 +0200210 fwrite(out_buf, (out_tail - out_buf), 1, dst_stream);
Leonid Lisovskiy328f27f2011-10-28 13:59:04 +0200211
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");
218 }
219
220 /* 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}