blob: f32a3da7d848014a6640784f6441a9c814849640 [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 */
Denis Vlasenko21afc7d2006-09-03 15:49:40 +00009#include "libbb.h"
10
11/* Conversion table. for base 64 */
Denys Vlasenkoe6094d92011-10-28 16:15:00 +020012const char bb_uuenc_tbl_base64[65 + 1] ALIGN1 = {
Denis Vlasenko21afc7d2006-09-03 15:49:40 +000013 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
14 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
15 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
16 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
17 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
18 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
19 'w', 'x', 'y', 'z', '0', '1', '2', '3',
20 '4', '5', '6', '7', '8', '9', '+', '/',
Denis Vlasenkoaa198dd2007-06-12 07:24:11 +000021 '=' /* termination character */,
Denys Vlasenkoe6094d92011-10-28 16:15:00 +020022 '\0' /* needed for uudecode.c only */
Denis Vlasenko21afc7d2006-09-03 15:49:40 +000023};
24
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000025const char bb_uuenc_tbl_std[65] ALIGN1 = {
Denis Vlasenko21afc7d2006-09-03 15:49:40 +000026 '`', '!', '"', '#', '$', '%', '&', '\'',
27 '(', ')', '*', '+', ',', '-', '.', '/',
28 '0', '1', '2', '3', '4', '5', '6', '7',
29 '8', '9', ':', ';', '<', '=', '>', '?',
30 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
31 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
32 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
33 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
34 '`' /* termination character */
35};
36
37/*
Denis Vlasenkoe8240f12007-06-26 15:59:37 +000038 * Encode bytes at S of length LENGTH to uuencode or base64 format and place it
Denis Vlasenko21afc7d2006-09-03 15:49:40 +000039 * to STORE. STORE will be 0-terminated, and must point to a writable
40 * buffer of at least 1+BASE64_LENGTH(length) bytes.
41 * where BASE64_LENGTH(len) = (4 * ((LENGTH + 2) / 3))
42 */
Denis Vlasenkodefc1ea2008-06-27 02:52:20 +000043void FAST_FUNC bb_uuencode(char *p, const void *src, int length, const char *tbl)
Denis Vlasenko21afc7d2006-09-03 15:49:40 +000044{
Denis Vlasenkoe8240f12007-06-26 15:59:37 +000045 const unsigned char *s = src;
Denis Vlasenko21afc7d2006-09-03 15:49:40 +000046
Denis Vlasenko46611172007-08-06 15:43:17 +000047 /* Transform the 3x8 bits to 4x6 bits */
48 while (length > 0) {
49 unsigned s1, s2;
50
51 /* Are s[1], s[2] valid or should be assumed 0? */
52 s1 = s2 = 0;
53 length -= 3; /* can be >=0, -1, -2 */
Denis Vlasenkoe5dbba22007-08-06 15:49:12 +000054 if (length >= -1) {
Denis Vlasenko46611172007-08-06 15:43:17 +000055 s1 = s[1];
Denis Vlasenkoe5dbba22007-08-06 15:49:12 +000056 if (length >= 0)
Denis Vlasenko46611172007-08-06 15:43:17 +000057 s2 = s[2];
58 }
Denis Vlasenko21afc7d2006-09-03 15:49:40 +000059 *p++ = tbl[s[0] >> 2];
Denis Vlasenko46611172007-08-06 15:43:17 +000060 *p++ = tbl[((s[0] & 3) << 4) + (s1 >> 4)];
61 *p++ = tbl[((s1 & 0xf) << 2) + (s2 >> 6)];
62 *p++ = tbl[s2 & 0x3f];
Denis Vlasenko21afc7d2006-09-03 15:49:40 +000063 s += 3;
64 }
Denis Vlasenko46611172007-08-06 15:43:17 +000065 /* Zero-terminate */
Denis Vlasenko21afc7d2006-09-03 15:49:40 +000066 *p = '\0';
Denis Vlasenko46611172007-08-06 15:43:17 +000067 /* If length is -2 or -1, pad last char or two */
68 while (length) {
69 *--p = tbl[64];
70 length++;
71 }
Denis Vlasenko21afc7d2006-09-03 15:49:40 +000072}
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +020073
74/*
Leonid Lisovskiy328f27f2011-10-28 13:59:04 +020075 * Decode base64 encoded string. Stops on '\0'.
76 *
77 * Returns: pointer to the undecoded part of source.
78 * If points to '\0', then the source was fully decoded.
Denys Vlasenkoe6094d92011-10-28 16:15:00 +020079 * (*pp_dst): advanced past the last written byte.
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +020080 */
Leonid Lisovskiy328f27f2011-10-28 13:59:04 +020081const char* FAST_FUNC decode_base64(char **pp_dst, const char *src)
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +020082{
Leonid Lisovskiy328f27f2011-10-28 13:59:04 +020083 char *dst = *pp_dst;
84 const char *src_tail;
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +020085
86 while (1) {
Denys Vlasenkoe6094d92011-10-28 16:15:00 +020087 unsigned char six_bit[4];
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +020088 int count = 0;
89
Denys Vlasenkoe6094d92011-10-28 16:15:00 +020090 /* Fetch up to four 6-bit values */
Leonid Lisovskiy328f27f2011-10-28 13:59:04 +020091 src_tail = src;
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +020092 while (count < 4) {
93 char *table_ptr;
94 int ch;
95
96 /* Get next _valid_ character.
97 * bb_uuenc_tbl_base64[] contains this string:
98 * 0 1 2 3 4 5 6
Denys Vlasenkoe6094d92011-10-28 16:15:00 +020099 * 01234567890123456789012345678901234567890123456789012345678901234
100 * "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200101 */
102 do {
Leonid Lisovskiy328f27f2011-10-28 13:59:04 +0200103 ch = *src;
104 if (ch == '\0') {
105 if (count == 0) {
106 /* Example:
107 * If we decode "QUJD <NUL>", we want
108 * to return ptr to NUL, not to ' ',
109 * because we did fully decode
110 * the string (to "ABC").
111 */
112 src_tail = src;
113 }
114 goto ret;
115 }
116 src++;
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200117 table_ptr = strchr(bb_uuenc_tbl_base64, ch);
118//TODO: add BASE64_FLAG_foo to die on bad char?
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200119 } while (!table_ptr);
120
121 /* Convert encoded character to decimal */
122 ch = table_ptr - bb_uuenc_tbl_base64;
123
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200124 /* ch is 64 if char was '=', otherwise 0..63 */
Denys Vlasenkoe6094d92011-10-28 16:15:00 +0200125 if (ch == 64)
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200126 break;
Denys Vlasenkoe6094d92011-10-28 16:15:00 +0200127 six_bit[count] = ch;
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200128 count++;
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200129 }
130
Denys Vlasenkoe6094d92011-10-28 16:15:00 +0200131 /* Transform 6-bit values to 8-bit ones.
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200132 * count can be < 4 when we decode the tail:
Denys Vlasenkoe6094d92011-10-28 16:15:00 +0200133 * "eQ==" -> "y", not "y NUL NUL".
134 * Note that (count > 1) is always true,
135 * "x===" encoding is not valid:
136 * even a single zero byte encodes as "AA==".
137 * However, with current logic we come here with count == 1
138 * when we decode "==" tail.
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200139 */
140 if (count > 1)
Denys Vlasenkoe6094d92011-10-28 16:15:00 +0200141 *dst++ = six_bit[0] << 2 | six_bit[1] >> 4;
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200142 if (count > 2)
Denys Vlasenkoe6094d92011-10-28 16:15:00 +0200143 *dst++ = six_bit[1] << 4 | six_bit[2] >> 2;
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200144 if (count > 3)
Denys Vlasenkoe6094d92011-10-28 16:15:00 +0200145 *dst++ = six_bit[2] << 6 | six_bit[3];
146 /* Note that if we decode "AA==" and ate first '=',
147 * we just decoded one char (count == 2) and now we'll
148 * do the loop once more to decode second '='.
149 */
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200150 } /* while (1) */
Leonid Lisovskiy328f27f2011-10-28 13:59:04 +0200151 ret:
152 *pp_dst = dst;
153 return src_tail;
154}
155
156/*
157 * Decode base64 encoded stream.
158 * Can stop on EOF, specified char, or on uuencode-style "====" line:
159 * flags argument controls it.
160 */
161void FAST_FUNC read_base64(FILE *src_stream, FILE *dst_stream, int flags)
162{
163/* Note that EOF _can_ be passed as exit_char too */
164#define exit_char ((int)(signed char)flags)
165#define uu_style_end (flags & BASE64_FLAG_UU_STOP)
166
167 /* uuencoded files have 61 byte lines. Use 64 byte buffer
168 * to process line at a time.
169 */
170 enum { BUFFER_SIZE = 64 };
171
172 char in_buf[BUFFER_SIZE + 2];
173 char out_buf[BUFFER_SIZE / 4 * 3 + 2];
174 char *out_tail;
175 const char *in_tail;
176 int term_seen = 0;
177 int in_count = 0;
178
179 while (1) {
180 while (in_count < BUFFER_SIZE) {
181 int ch = fgetc(src_stream);
182 if (ch == exit_char) {
183 if (in_count == 0)
184 return;
185 term_seen = 1;
186 break;
187 }
188 if (ch == EOF) {
189 term_seen = 1;
190 break;
191 }
192 /* Prevent "====" line to be split: stop if we see '\n'.
193 * We can also skip other whitespace and skirt the problem
194 * of files with NULs by stopping on any control char or space:
195 */
196 if (ch <= ' ')
197 break;
198 in_buf[in_count++] = ch;
199 }
200 in_buf[in_count] = '\0';
201
202 /* Did we encounter "====" line? */
203 if (uu_style_end && strcmp(in_buf, "====") == 0)
204 return;
205
206 out_tail = out_buf;
207 in_tail = decode_base64(&out_tail, in_buf);
208
Denys Vlasenko73d249e2011-10-28 14:07:44 +0200209 fwrite(out_buf, (out_tail - out_buf), 1, dst_stream);
Leonid Lisovskiy328f27f2011-10-28 13:59:04 +0200210
211 if (term_seen) {
212 /* Did we consume ALL characters? */
213 if (*in_tail == '\0')
214 return;
215 /* No */
216 bb_error_msg_and_die("truncated base64 input");
217 }
218
219 /* It was partial decode */
220 in_count = strlen(in_tail);
221 memmove(in_buf, in_tail, in_count);
222 }
Denys Vlasenkoc8f9a8d2010-09-16 18:10:04 +0200223}