archive_write_add_filter_b64encode.c revision 358090
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__FBSDID("$FreeBSD$");
29
30#ifdef HAVE_ERRNO_H
31#include <errno.h>
32#endif
33#ifdef HAVE_STDLIB_H
34#include <stdlib.h>
35#endif
36#ifdef HAVE_STRING_H
37#include <string.h>
38#endif
39
40#include "archive.h"
41#include "archive_private.h"
42#include "archive_string.h"
43#include "archive_write_private.h"
44
45#define LBYTES	57
46
47struct private_b64encode {
48	int			mode;
49	struct archive_string	name;
50	struct archive_string	encoded_buff;
51	size_t			bs;
52	size_t			hold_len;
53	unsigned char		hold[LBYTES];
54};
55
56static int archive_filter_b64encode_options(struct archive_write_filter *,
57    const char *, const char *);
58static int archive_filter_b64encode_open(struct archive_write_filter *);
59static int archive_filter_b64encode_write(struct archive_write_filter *,
60    const void *, size_t);
61static int archive_filter_b64encode_close(struct archive_write_filter *);
62static int archive_filter_b64encode_free(struct archive_write_filter *);
63static void la_b64_encode(struct archive_string *, const unsigned char *, size_t);
64static int64_t atol8(const char *, size_t);
65
66static const char base64[] = {
67	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
68	'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
69	'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
70	'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
71	'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
72	'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
73	'w', 'x', 'y', 'z', '0', '1', '2', '3',
74	'4', '5', '6', '7', '8', '9', '+', '/'
75};
76
77/*
78 * Add a compress filter to this write handle.
79 */
80int
81archive_write_add_filter_b64encode(struct archive *_a)
82{
83	struct archive_write *a = (struct archive_write *)_a;
84	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
85	struct private_b64encode *state;
86
87	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
88	    ARCHIVE_STATE_NEW, "archive_write_add_filter_uu");
89
90	state = (struct private_b64encode *)calloc(1, sizeof(*state));
91	if (state == NULL) {
92		archive_set_error(f->archive, ENOMEM,
93		    "Can't allocate data for b64encode filter");
94		return (ARCHIVE_FATAL);
95	}
96	archive_strcpy(&state->name, "-");
97	state->mode = 0644;
98
99	f->data = state;
100	f->name = "b64encode";
101	f->code = ARCHIVE_FILTER_UU;
102	f->open = archive_filter_b64encode_open;
103	f->options = archive_filter_b64encode_options;
104	f->write = archive_filter_b64encode_write;
105	f->close = archive_filter_b64encode_close;
106	f->free = archive_filter_b64encode_free;
107
108	return (ARCHIVE_OK);
109}
110
111/*
112 * Set write options.
113 */
114static int
115archive_filter_b64encode_options(struct archive_write_filter *f, const char *key,
116    const char *value)
117{
118	struct private_b64encode *state = (struct private_b64encode *)f->data;
119
120	if (strcmp(key, "mode") == 0) {
121		if (value == NULL) {
122			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
123			    "mode option requires octal digits");
124			return (ARCHIVE_FAILED);
125		}
126		state->mode = (int)atol8(value, strlen(value)) & 0777;
127		return (ARCHIVE_OK);
128	} else if (strcmp(key, "name") == 0) {
129		if (value == NULL) {
130			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
131			    "name option requires a string");
132			return (ARCHIVE_FAILED);
133		}
134		archive_strcpy(&state->name, value);
135		return (ARCHIVE_OK);
136	}
137
138	/* Note: The "warn" return is just to inform the options
139	 * supervisor that we didn't handle it.  It will generate
140	 * a suitable error if no one used this option. */
141	return (ARCHIVE_WARN);
142}
143
144/*
145 * Setup callback.
146 */
147static int
148archive_filter_b64encode_open(struct archive_write_filter *f)
149{
150	struct private_b64encode *state = (struct private_b64encode *)f->data;
151	size_t bs = 65536, bpb;
152
153	if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
154		/* Buffer size should be a multiple number of the of bytes
155		 * per block for performance. */
156		bpb = archive_write_get_bytes_per_block(f->archive);
157		if (bpb > bs)
158			bs = bpb;
159		else if (bpb != 0)
160			bs -= bs % bpb;
161	}
162
163	state->bs = bs;
164	if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) {
165		archive_set_error(f->archive, ENOMEM,
166		    "Can't allocate data for b64encode buffer");
167		return (ARCHIVE_FATAL);
168	}
169
170	archive_string_sprintf(&state->encoded_buff, "begin-base64 %o %s\n",
171	    state->mode, state->name.s);
172
173	f->data = state;
174	return (0);
175}
176
177static void
178la_b64_encode(struct archive_string *as, const unsigned char *p, size_t len)
179{
180	int c;
181
182	for (; len >= 3; p += 3, len -= 3) {
183		c = p[0] >> 2;
184		archive_strappend_char(as, base64[c]);
185		c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4);
186		archive_strappend_char(as, base64[c]);
187		c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6);
188		archive_strappend_char(as, base64[c]);
189		c = p[2] & 0x3f;
190		archive_strappend_char(as, base64[c]);
191	}
192	if (len > 0) {
193		c = p[0] >> 2;
194		archive_strappend_char(as, base64[c]);
195		c = (p[0] & 0x03) << 4;
196		if (len == 1) {
197			archive_strappend_char(as, base64[c]);
198			archive_strappend_char(as, '=');
199			archive_strappend_char(as, '=');
200		} else {
201			c |= (p[1] & 0xf0) >> 4;
202			archive_strappend_char(as, base64[c]);
203			c = (p[1] & 0x0f) << 2;
204			archive_strappend_char(as, base64[c]);
205			archive_strappend_char(as, '=');
206		}
207	}
208	archive_strappend_char(as, '\n');
209}
210
211/*
212 * Write data to the encoded stream.
213 */
214static int
215archive_filter_b64encode_write(struct archive_write_filter *f, const void *buff,
216    size_t length)
217{
218	struct private_b64encode *state = (struct private_b64encode *)f->data;
219	const unsigned char *p = buff;
220	int ret = ARCHIVE_OK;
221
222	if (length == 0)
223		return (ret);
224
225	if (state->hold_len) {
226		while (state->hold_len < LBYTES && length > 0) {
227			state->hold[state->hold_len++] = *p++;
228			length--;
229		}
230		if (state->hold_len < LBYTES)
231			return (ret);
232		la_b64_encode(&state->encoded_buff, state->hold, LBYTES);
233		state->hold_len = 0;
234	}
235
236	for (; length >= LBYTES; length -= LBYTES, p += LBYTES)
237		la_b64_encode(&state->encoded_buff, p, LBYTES);
238
239	/* Save remaining bytes. */
240	if (length > 0) {
241		memcpy(state->hold, p, length);
242		state->hold_len = length;
243	}
244	while (archive_strlen(&state->encoded_buff) >= state->bs) {
245		ret = __archive_write_filter(f->next_filter,
246		    state->encoded_buff.s, state->bs);
247		memmove(state->encoded_buff.s,
248		    state->encoded_buff.s + state->bs,
249		    state->encoded_buff.length - state->bs);
250		state->encoded_buff.length -= state->bs;
251	}
252
253	return (ret);
254}
255
256
257/*
258 * Finish the compression...
259 */
260static int
261archive_filter_b64encode_close(struct archive_write_filter *f)
262{
263	struct private_b64encode *state = (struct private_b64encode *)f->data;
264
265	/* Flush remaining bytes. */
266	if (state->hold_len != 0)
267		la_b64_encode(&state->encoded_buff, state->hold, state->hold_len);
268	archive_string_sprintf(&state->encoded_buff, "====\n");
269	/* Write the last block */
270	archive_write_set_bytes_in_last_block(f->archive, 1);
271	return __archive_write_filter(f->next_filter,
272	    state->encoded_buff.s, archive_strlen(&state->encoded_buff));
273}
274
275static int
276archive_filter_b64encode_free(struct archive_write_filter *f)
277{
278	struct private_b64encode *state = (struct private_b64encode *)f->data;
279
280	archive_string_free(&state->name);
281	archive_string_free(&state->encoded_buff);
282	free(state);
283	return (ARCHIVE_OK);
284}
285
286static int64_t
287atol8(const char *p, size_t char_cnt)
288{
289	int64_t l;
290	int digit;
291
292	l = 0;
293	while (char_cnt-- > 0) {
294		if (*p >= '0' && *p <= '7')
295			digit = *p - '0';
296		else
297			break;
298		p++;
299		l <<= 3;
300		l |= digit;
301	}
302	return (l);
303}
304
305