1 /*-
2  * Copyright (c) 2012 Michihiro NAKAJIMA
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "archive_platform.h"
27 
28 #ifdef HAVE_ERRNO_H
29 #include <errno.h>
30 #endif
31 #ifdef HAVE_STDLIB_H
32 #include <stdlib.h>
33 #endif
34 #ifdef HAVE_STRING_H
35 #include <string.h>
36 #endif
37 
38 #include "archive.h"
39 #include "archive_private.h"
40 #include "archive_string.h"
41 #include "archive_write_private.h"
42 
43 #define LBYTES	57
44 
45 struct private_b64encode {
46 	int			mode;
47 	struct archive_string	name;
48 	struct archive_string	encoded_buff;
49 	size_t			bs;
50 	size_t			hold_len;
51 	unsigned char		hold[LBYTES];
52 };
53 
54 static int archive_filter_b64encode_options(struct archive_write_filter *,
55     const char *, const char *);
56 static int archive_filter_b64encode_open(struct archive_write_filter *);
57 static int archive_filter_b64encode_write(struct archive_write_filter *,
58     const void *, size_t);
59 static int archive_filter_b64encode_close(struct archive_write_filter *);
60 static int archive_filter_b64encode_free(struct archive_write_filter *);
61 static void la_b64_encode(struct archive_string *, const unsigned char *, size_t);
62 static int64_t atol8(const char *, size_t);
63 
64 static const char base64[] = {
65 	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
66 	'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
67 	'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
68 	'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
69 	'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
70 	'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
71 	'w', 'x', 'y', 'z', '0', '1', '2', '3',
72 	'4', '5', '6', '7', '8', '9', '+', '/'
73 };
74 
75 /*
76  * Add a compress filter to this write handle.
77  */
78 int
79 archive_write_add_filter_b64encode(struct archive *_a)
80 {
81 	struct archive_write *a = (struct archive_write *)_a;
82 	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
83 	struct private_b64encode *state;
84 
85 	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
86 	    ARCHIVE_STATE_NEW, "archive_write_add_filter_uu");
87 
88 	state = (struct private_b64encode *)calloc(1, sizeof(*state));
89 	if (state == NULL) {
90 		archive_set_error(f->archive, ENOMEM,
91 		    "Can't allocate data for b64encode filter");
92 		return (ARCHIVE_FATAL);
93 	}
94 	archive_strcpy(&state->name, "-");
95 	state->mode = 0644;
96 
97 	f->data = state;
98 	f->name = "b64encode";
99 	f->code = ARCHIVE_FILTER_UU;
100 	f->open = archive_filter_b64encode_open;
101 	f->options = archive_filter_b64encode_options;
102 	f->write = archive_filter_b64encode_write;
103 	f->close = archive_filter_b64encode_close;
104 	f->free = archive_filter_b64encode_free;
105 
106 	return (ARCHIVE_OK);
107 }
108 
109 /*
110  * Set write options.
111  */
112 static int
113 archive_filter_b64encode_options(struct archive_write_filter *f, const char *key,
114     const char *value)
115 {
116 	struct private_b64encode *state = (struct private_b64encode *)f->data;
117 
118 	if (strcmp(key, "mode") == 0) {
119 		if (value == NULL) {
120 			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
121 			    "mode option requires octal digits");
122 			return (ARCHIVE_FAILED);
123 		}
124 		state->mode = (int)atol8(value, strlen(value)) & 0777;
125 		return (ARCHIVE_OK);
126 	} else if (strcmp(key, "name") == 0) {
127 		if (value == NULL) {
128 			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
129 			    "name option requires a string");
130 			return (ARCHIVE_FAILED);
131 		}
132 		archive_strcpy(&state->name, value);
133 		return (ARCHIVE_OK);
134 	}
135 
136 	/* Note: The "warn" return is just to inform the options
137 	 * supervisor that we didn't handle it.  It will generate
138 	 * a suitable error if no one used this option. */
139 	return (ARCHIVE_WARN);
140 }
141 
142 /*
143  * Setup callback.
144  */
145 static int
146 archive_filter_b64encode_open(struct archive_write_filter *f)
147 {
148 	struct private_b64encode *state = (struct private_b64encode *)f->data;
149 	size_t bs = 65536, bpb;
150 
151 	if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
152 		/* Buffer size should be a multiple number of the of bytes
153 		 * per block for performance. */
154 		bpb = archive_write_get_bytes_per_block(f->archive);
155 		if (bpb > bs)
156 			bs = bpb;
157 		else if (bpb != 0)
158 			bs -= bs % bpb;
159 	}
160 
161 	state->bs = bs;
162 	if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) {
163 		archive_set_error(f->archive, ENOMEM,
164 		    "Can't allocate data for b64encode buffer");
165 		return (ARCHIVE_FATAL);
166 	}
167 
168 	archive_string_sprintf(&state->encoded_buff, "begin-base64 %o %s\n",
169 	    state->mode, state->name.s);
170 
171 	f->data = state;
172 	return (0);
173 }
174 
175 static void
176 la_b64_encode(struct archive_string *as, const unsigned char *p, size_t len)
177 {
178 	int c;
179 
180 	for (; len >= 3; p += 3, len -= 3) {
181 		c = p[0] >> 2;
182 		archive_strappend_char(as, base64[c]);
183 		c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4);
184 		archive_strappend_char(as, base64[c]);
185 		c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6);
186 		archive_strappend_char(as, base64[c]);
187 		c = p[2] & 0x3f;
188 		archive_strappend_char(as, base64[c]);
189 	}
190 	if (len > 0) {
191 		c = p[0] >> 2;
192 		archive_strappend_char(as, base64[c]);
193 		c = (p[0] & 0x03) << 4;
194 		if (len == 1) {
195 			archive_strappend_char(as, base64[c]);
196 			archive_strappend_char(as, '=');
197 			archive_strappend_char(as, '=');
198 		} else {
199 			c |= (p[1] & 0xf0) >> 4;
200 			archive_strappend_char(as, base64[c]);
201 			c = (p[1] & 0x0f) << 2;
202 			archive_strappend_char(as, base64[c]);
203 			archive_strappend_char(as, '=');
204 		}
205 	}
206 	archive_strappend_char(as, '\n');
207 }
208 
209 /*
210  * Write data to the encoded stream.
211  */
212 static int
213 archive_filter_b64encode_write(struct archive_write_filter *f, const void *buff,
214     size_t length)
215 {
216 	struct private_b64encode *state = (struct private_b64encode *)f->data;
217 	const unsigned char *p = buff;
218 	int ret = ARCHIVE_OK;
219 
220 	if (length == 0)
221 		return (ret);
222 
223 	if (state->hold_len) {
224 		while (state->hold_len < LBYTES && length > 0) {
225 			state->hold[state->hold_len++] = *p++;
226 			length--;
227 		}
228 		if (state->hold_len < LBYTES)
229 			return (ret);
230 		la_b64_encode(&state->encoded_buff, state->hold, LBYTES);
231 		state->hold_len = 0;
232 	}
233 
234 	for (; length >= LBYTES; length -= LBYTES, p += LBYTES)
235 		la_b64_encode(&state->encoded_buff, p, LBYTES);
236 
237 	/* Save remaining bytes. */
238 	if (length > 0) {
239 		memcpy(state->hold, p, length);
240 		state->hold_len = length;
241 	}
242 	while (archive_strlen(&state->encoded_buff) >= state->bs) {
243 		ret = __archive_write_filter(f->next_filter,
244 		    state->encoded_buff.s, state->bs);
245 		memmove(state->encoded_buff.s,
246 		    state->encoded_buff.s + state->bs,
247 		    state->encoded_buff.length - state->bs);
248 		state->encoded_buff.length -= state->bs;
249 	}
250 
251 	return (ret);
252 }
253 
254 
255 /*
256  * Finish the compression...
257  */
258 static int
259 archive_filter_b64encode_close(struct archive_write_filter *f)
260 {
261 	struct private_b64encode *state = (struct private_b64encode *)f->data;
262 
263 	/* Flush remaining bytes. */
264 	if (state->hold_len != 0)
265 		la_b64_encode(&state->encoded_buff, state->hold, state->hold_len);
266 	archive_string_sprintf(&state->encoded_buff, "====\n");
267 	/* Write the last block */
268 	archive_write_set_bytes_in_last_block(f->archive, 1);
269 	return __archive_write_filter(f->next_filter,
270 	    state->encoded_buff.s, archive_strlen(&state->encoded_buff));
271 }
272 
273 static int
274 archive_filter_b64encode_free(struct archive_write_filter *f)
275 {
276 	struct private_b64encode *state = (struct private_b64encode *)f->data;
277 
278 	archive_string_free(&state->name);
279 	archive_string_free(&state->encoded_buff);
280 	free(state);
281 	return (ARCHIVE_OK);
282 }
283 
284 static int64_t
285 atol8(const char *p, size_t char_cnt)
286 {
287 	int64_t l;
288 	int digit;
289 
290 	l = 0;
291 	while (char_cnt-- > 0) {
292 		if (*p >= '0' && *p <= '7')
293 			digit = *p - '0';
294 		else
295 			break;
296 		p++;
297 		l <<= 3;
298 		l |= digit;
299 	}
300 	return (l);
301 }
302 
303