1248590Smm/*-
2248590Smm * Copyright (c) 2012 Michihiro NAKAJIMA
3248590Smm * All rights reserved.
4248590Smm *
5248590Smm * Redistribution and use in source and binary forms, with or without
6248590Smm * modification, are permitted provided that the following conditions
7248590Smm * are met:
8248590Smm * 1. Redistributions of source code must retain the above copyright
9248590Smm *    notice, this list of conditions and the following disclaimer.
10248590Smm * 2. Redistributions in binary form must reproduce the above copyright
11248590Smm *    notice, this list of conditions and the following disclaimer in the
12248590Smm *    documentation and/or other materials provided with the distribution.
13248590Smm *
14248590Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15248590Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16248590Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17248590Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18248590Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19248590Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20248590Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21248590Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22248590Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23248590Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24248590Smm */
25248590Smm
26248590Smm#include "archive_platform.h"
27248590Smm
28248590Smm__FBSDID("$FreeBSD$");
29248590Smm
30248590Smm#ifdef HAVE_ERRNO_H
31248590Smm#include <errno.h>
32248590Smm#endif
33248590Smm#ifdef HAVE_STDLIB_H
34248590Smm#include <stdlib.h>
35248590Smm#endif
36248590Smm#ifdef HAVE_STRING_H
37248590Smm#include <string.h>
38248590Smm#endif
39248590Smm
40248590Smm#include "archive.h"
41248590Smm#include "archive_private.h"
42248590Smm#include "archive_string.h"
43248590Smm#include "archive_write_private.h"
44248590Smm
45248590Smm#define LBYTES	57
46248590Smm
47248590Smmstruct private_b64encode {
48248590Smm	int			mode;
49248590Smm	struct archive_string	name;
50248590Smm	struct archive_string	encoded_buff;
51248590Smm	size_t			bs;
52248590Smm	size_t			hold_len;
53248590Smm	unsigned char		hold[LBYTES];
54248590Smm};
55248590Smm
56248590Smmstatic int archive_filter_b64encode_options(struct archive_write_filter *,
57248590Smm    const char *, const char *);
58248590Smmstatic int archive_filter_b64encode_open(struct archive_write_filter *);
59248590Smmstatic int archive_filter_b64encode_write(struct archive_write_filter *,
60248590Smm    const void *, size_t);
61248590Smmstatic int archive_filter_b64encode_close(struct archive_write_filter *);
62248590Smmstatic int archive_filter_b64encode_free(struct archive_write_filter *);
63248590Smmstatic void b64_encode(struct archive_string *, const unsigned char *, size_t);
64248590Smmstatic int64_t atol8(const char *, size_t);
65248590Smm
66248590Smmstatic const char base64[] = {
67248590Smm	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
68248590Smm	'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
69248590Smm	'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
70248590Smm	'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
71248590Smm	'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
72248590Smm	'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
73248590Smm	'w', 'x', 'y', 'z', '0', '1', '2', '3',
74248590Smm	'4', '5', '6', '7', '8', '9', '+', '/'
75248590Smm};
76248590Smm
77248590Smm/*
78248590Smm * Add a compress filter to this write handle.
79248590Smm */
80248590Smmint
81248590Smmarchive_write_add_filter_b64encode(struct archive *_a)
82248590Smm{
83248590Smm	struct archive_write *a = (struct archive_write *)_a;
84248590Smm	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
85248590Smm	struct private_b64encode *state;
86248590Smm
87248590Smm	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
88248590Smm	    ARCHIVE_STATE_NEW, "archive_write_add_filter_uu");
89248590Smm
90248590Smm	state = (struct private_b64encode *)calloc(1, sizeof(*state));
91248590Smm	if (state == NULL) {
92248590Smm		archive_set_error(f->archive, ENOMEM,
93248590Smm		    "Can't allocate data for b64encode filter");
94248590Smm		return (ARCHIVE_FATAL);
95248590Smm	}
96248590Smm	archive_strcpy(&state->name, "-");
97248590Smm	state->mode = 0644;
98248590Smm
99248590Smm	f->data = state;
100248590Smm	f->name = "b64encode";
101248590Smm	f->code = ARCHIVE_FILTER_UU;
102248590Smm	f->open = archive_filter_b64encode_open;
103248590Smm	f->options = archive_filter_b64encode_options;
104248590Smm	f->write = archive_filter_b64encode_write;
105248590Smm	f->close = archive_filter_b64encode_close;
106248590Smm	f->free = archive_filter_b64encode_free;
107248590Smm
108248590Smm	return (ARCHIVE_OK);
109248590Smm}
110248590Smm
111248590Smm/*
112248590Smm * Set write options.
113248590Smm */
114248590Smmstatic int
115248590Smmarchive_filter_b64encode_options(struct archive_write_filter *f, const char *key,
116248590Smm    const char *value)
117248590Smm{
118248590Smm	struct private_b64encode *state = (struct private_b64encode *)f->data;
119248590Smm
120248590Smm	if (strcmp(key, "mode") == 0) {
121248590Smm		if (value == NULL) {
122248590Smm			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
123248590Smm			    "mode option requires octal digits");
124248590Smm			return (ARCHIVE_FAILED);
125248590Smm		}
126248590Smm		state->mode = (int)atol8(value, strlen(value)) & 0777;
127248590Smm		return (ARCHIVE_OK);
128248590Smm	} else if (strcmp(key, "name") == 0) {
129248590Smm		if (value == NULL) {
130248590Smm			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
131248590Smm			    "name option requires a string");
132248590Smm			return (ARCHIVE_FAILED);
133248590Smm		}
134248590Smm		archive_strcpy(&state->name, value);
135248590Smm		return (ARCHIVE_OK);
136248590Smm	}
137248590Smm
138248590Smm	/* Note: The "warn" return is just to inform the options
139248590Smm	 * supervisor that we didn't handle it.  It will generate
140248590Smm	 * a suitable error if no one used this option. */
141248590Smm	return (ARCHIVE_WARN);
142248590Smm}
143248590Smm
144248590Smm/*
145248590Smm * Setup callback.
146248590Smm */
147248590Smmstatic int
148248590Smmarchive_filter_b64encode_open(struct archive_write_filter *f)
149248590Smm{
150248590Smm	struct private_b64encode *state = (struct private_b64encode *)f->data;
151248590Smm	size_t bs = 65536, bpb;
152248590Smm	int ret;
153248590Smm
154248590Smm	ret = __archive_write_open_filter(f->next_filter);
155248590Smm	if (ret != ARCHIVE_OK)
156248590Smm		return (ret);
157248590Smm
158248590Smm	if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
159248590Smm		/* Buffer size should be a multiple number of the of bytes
160248590Smm		 * per block for performance. */
161248590Smm		bpb = archive_write_get_bytes_per_block(f->archive);
162248590Smm		if (bpb > bs)
163248590Smm			bs = bpb;
164248590Smm		else if (bpb != 0)
165248590Smm			bs -= bs % bpb;
166248590Smm	}
167248590Smm
168248590Smm	state->bs = bs;
169248590Smm	if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) {
170248590Smm		archive_set_error(f->archive, ENOMEM,
171248590Smm		    "Can't allocate data for b64encode buffer");
172248590Smm		return (ARCHIVE_FATAL);
173248590Smm	}
174248590Smm
175248590Smm	archive_string_sprintf(&state->encoded_buff, "begin-base64 %o %s\n",
176248590Smm	    state->mode, state->name.s);
177248590Smm
178248590Smm	f->data = state;
179248590Smm	return (0);
180248590Smm}
181248590Smm
182248590Smmstatic void
183248590Smmb64_encode(struct archive_string *as, const unsigned char *p, size_t len)
184248590Smm{
185248590Smm	int c;
186248590Smm
187248590Smm	for (; len >= 3; p += 3, len -= 3) {
188248590Smm		c = p[0] >> 2;
189248590Smm		archive_strappend_char(as, base64[c]);
190248590Smm		c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4);
191248590Smm		archive_strappend_char(as, base64[c]);
192248590Smm		c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6);
193248590Smm		archive_strappend_char(as, base64[c]);
194248590Smm		c = p[2] & 0x3f;
195248590Smm		archive_strappend_char(as, base64[c]);
196248590Smm	}
197248590Smm	if (len > 0) {
198248590Smm		c = p[0] >> 2;
199248590Smm		archive_strappend_char(as, base64[c]);
200248590Smm		c = (p[0] & 0x03) << 4;
201248590Smm		if (len == 1) {
202248590Smm			archive_strappend_char(as, base64[c]);
203248590Smm			archive_strappend_char(as, '=');
204248590Smm			archive_strappend_char(as, '=');
205248590Smm		} else {
206248590Smm			c |= (p[1] & 0xf0) >> 4;
207248590Smm			archive_strappend_char(as, base64[c]);
208248590Smm			c = (p[1] & 0x0f) << 2;
209248590Smm			archive_strappend_char(as, base64[c]);
210248590Smm			archive_strappend_char(as, '=');
211248590Smm		}
212248590Smm	}
213248590Smm	archive_strappend_char(as, '\n');
214248590Smm}
215248590Smm
216248590Smm/*
217248590Smm * Write data to the encoded stream.
218248590Smm */
219248590Smmstatic int
220248590Smmarchive_filter_b64encode_write(struct archive_write_filter *f, const void *buff,
221248590Smm    size_t length)
222248590Smm{
223248590Smm	struct private_b64encode *state = (struct private_b64encode *)f->data;
224248590Smm	const unsigned char *p = buff;
225248590Smm	int ret = ARCHIVE_OK;
226248590Smm
227248590Smm	if (length == 0)
228248590Smm		return (ret);
229248590Smm
230248590Smm	if (state->hold_len) {
231248590Smm		while (state->hold_len < LBYTES && length > 0) {
232248590Smm			state->hold[state->hold_len++] = *p++;
233248590Smm			length--;
234248590Smm		}
235248590Smm		if (state->hold_len < LBYTES)
236248590Smm			return (ret);
237248590Smm		b64_encode(&state->encoded_buff, state->hold, LBYTES);
238248590Smm		state->hold_len = 0;
239248590Smm	}
240248590Smm
241248590Smm	for (; length >= LBYTES; length -= LBYTES, p += LBYTES)
242248590Smm		b64_encode(&state->encoded_buff, p, LBYTES);
243248590Smm
244248590Smm	/* Save remaining bytes. */
245248590Smm	if (length > 0) {
246248590Smm		memcpy(state->hold, p, length);
247248590Smm		state->hold_len = length;
248248590Smm	}
249248590Smm	while (archive_strlen(&state->encoded_buff) >= state->bs) {
250248590Smm		ret = __archive_write_filter(f->next_filter,
251248590Smm		    state->encoded_buff.s, state->bs);
252248590Smm		memmove(state->encoded_buff.s,
253248590Smm		    state->encoded_buff.s + state->bs,
254248590Smm		    state->encoded_buff.length - state->bs);
255248590Smm		state->encoded_buff.length -= state->bs;
256248590Smm	}
257248590Smm
258248590Smm	return (ret);
259248590Smm}
260248590Smm
261248590Smm
262248590Smm/*
263248590Smm * Finish the compression...
264248590Smm */
265248590Smmstatic int
266248590Smmarchive_filter_b64encode_close(struct archive_write_filter *f)
267248590Smm{
268248590Smm	struct private_b64encode *state = (struct private_b64encode *)f->data;
269248590Smm	int ret, ret2;
270248590Smm
271248590Smm	/* Flush remaining bytes. */
272248590Smm	if (state->hold_len != 0)
273248590Smm		b64_encode(&state->encoded_buff, state->hold, state->hold_len);
274248590Smm	archive_string_sprintf(&state->encoded_buff, "====\n");
275248590Smm	/* Write the last block */
276248590Smm	archive_write_set_bytes_in_last_block(f->archive, 1);
277248590Smm	ret = __archive_write_filter(f->next_filter,
278248590Smm	    state->encoded_buff.s, archive_strlen(&state->encoded_buff));
279248590Smm	ret2 = __archive_write_close_filter(f->next_filter);
280248590Smm	if (ret > ret2)
281248590Smm		ret = ret2;
282248590Smm	return (ret);
283248590Smm}
284248590Smm
285248590Smmstatic int
286248590Smmarchive_filter_b64encode_free(struct archive_write_filter *f)
287248590Smm{
288248590Smm	struct private_b64encode *state = (struct private_b64encode *)f->data;
289248590Smm
290248590Smm	archive_string_free(&state->name);
291248590Smm	archive_string_free(&state->encoded_buff);
292248590Smm	free(state);
293248590Smm	return (ARCHIVE_OK);
294248590Smm}
295248590Smm
296248590Smmstatic int64_t
297248590Smmatol8(const char *p, size_t char_cnt)
298248590Smm{
299248590Smm	int64_t l;
300248590Smm	int digit;
301248590Smm
302248590Smm	l = 0;
303248590Smm	while (char_cnt-- > 0) {
304248590Smm		if (*p >= '0' && *p <= '7')
305248590Smm			digit = *p - '0';
306248590Smm		else
307248590Smm			break;
308248590Smm		p++;
309248590Smm		l <<= 3;
310248590Smm		l |= digit;
311248590Smm	}
312248590Smm	return (l);
313248590Smm}
314248590Smm
315