blob: 03e708fd55aaced4acf69b9e5ed27d944ecfaa8f [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 */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013const char bb_uuenc_tbl_base64[65 + 2] 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 */,
23 '\n', '\0' /* needed for uudecode.c */
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/*
76 * Decode base64 encoded stream.
77 * Can stop on EOF, specified char, or on uuencode-style "====" line:
78 * flags argument controls it.
79 */
80void FAST_FUNC read_base64(FILE *src_stream, FILE *dst_stream, int flags)
81{
82/* Note that EOF _can_ be passed as exit_char too */
83#define exit_char ((int)(signed char)flags)
84#define uu_style_end (flags & BASE64_FLAG_UU_STOP)
85
86 int term_count = 0;
87
88 while (1) {
89 unsigned char translated[4];
90 int count = 0;
91
92 /* Process one group of 4 chars */
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 * 012345678901234567890123456789012345678901234567890123456789012345
101 * "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n"
102 */
103 do {
104 ch = fgetc(src_stream);
105 if (ch == exit_char && count == 0)
106 return;
107 if (ch == EOF)
108 bb_error_msg_and_die("truncated base64 input");
109 table_ptr = strchr(bb_uuenc_tbl_base64, ch);
110//TODO: add BASE64_FLAG_foo to die on bad char?
111//Note that then we may need to still allow '\r' (for mail processing)
112 } while (!table_ptr);
113
114 /* Convert encoded character to decimal */
115 ch = table_ptr - bb_uuenc_tbl_base64;
116
117 if (ch == 65 /* '\n' */) {
118 /* Terminating "====" line? */
119 if (uu_style_end && term_count == 4)
120 return; /* yes */
121 term_count = 0;
122 continue;
123 }
124 /* ch is 64 if char was '=', otherwise 0..63 */
125 translated[count] = ch & 63; /* 64 -> 0 */
126 if (ch == 64) {
127 term_count++;
128 break;
129 }
130 count++;
131 term_count = 0;
132 }
133
134 /* Merge 6 bit chars to 8 bit.
135 * count can be < 4 when we decode the tail:
136 * "eQ==" -> "y", not "y NUL NUL"
137 */
138 if (count > 1)
139 fputc(translated[0] << 2 | translated[1] >> 4, dst_stream);
140 if (count > 2)
141 fputc(translated[1] << 4 | translated[2] >> 2, dst_stream);
142 if (count > 3)
143 fputc(translated[2] << 6 | translated[3], dst_stream);
144 } /* while (1) */
145}