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 45
46248590Smm
47248590Smmstruct private_uuencode {
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_uuencode_options(struct archive_write_filter *,
57248590Smm    const char *, const char *);
58248590Smmstatic int archive_filter_uuencode_open(struct archive_write_filter *);
59248590Smmstatic int archive_filter_uuencode_write(struct archive_write_filter *,
60248590Smm    const void *, size_t);
61248590Smmstatic int archive_filter_uuencode_close(struct archive_write_filter *);
62248590Smmstatic int archive_filter_uuencode_free(struct archive_write_filter *);
63248590Smmstatic void uu_encode(struct archive_string *, const unsigned char *, size_t);
64248590Smmstatic int64_t atol8(const char *, size_t);
65248590Smm
66248590Smm/*
67248590Smm * Add a compress filter to this write handle.
68248590Smm */
69248590Smmint
70248590Smmarchive_write_add_filter_uuencode(struct archive *_a)
71248590Smm{
72248590Smm	struct archive_write *a = (struct archive_write *)_a;
73248590Smm	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
74248590Smm	struct private_uuencode *state;
75248590Smm
76248590Smm	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
77248590Smm	    ARCHIVE_STATE_NEW, "archive_write_add_filter_uu");
78248590Smm
79248590Smm	state = (struct private_uuencode *)calloc(1, sizeof(*state));
80248590Smm	if (state == NULL) {
81248590Smm		archive_set_error(f->archive, ENOMEM,
82248590Smm		    "Can't allocate data for uuencode filter");
83248590Smm		return (ARCHIVE_FATAL);
84248590Smm	}
85248590Smm	archive_strcpy(&state->name, "-");
86248590Smm	state->mode = 0644;
87248590Smm
88248590Smm	f->data = state;
89248590Smm	f->name = "uuencode";
90248590Smm	f->code = ARCHIVE_FILTER_UU;
91248590Smm	f->open = archive_filter_uuencode_open;
92248590Smm	f->options = archive_filter_uuencode_options;
93248590Smm	f->write = archive_filter_uuencode_write;
94248590Smm	f->close = archive_filter_uuencode_close;
95248590Smm	f->free = archive_filter_uuencode_free;
96248590Smm
97248590Smm	return (ARCHIVE_OK);
98248590Smm}
99248590Smm
100248590Smm/*
101248590Smm * Set write options.
102248590Smm */
103248590Smmstatic int
104248590Smmarchive_filter_uuencode_options(struct archive_write_filter *f, const char *key,
105248590Smm    const char *value)
106248590Smm{
107248590Smm	struct private_uuencode *state = (struct private_uuencode *)f->data;
108248590Smm
109248590Smm	if (strcmp(key, "mode") == 0) {
110248590Smm		if (value == NULL) {
111248590Smm			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
112248590Smm			    "mode option requires octal digits");
113248590Smm			return (ARCHIVE_FAILED);
114248590Smm		}
115248590Smm		state->mode = (int)atol8(value, strlen(value)) & 0777;
116248590Smm		return (ARCHIVE_OK);
117248590Smm	} else if (strcmp(key, "name") == 0) {
118248590Smm		if (value == NULL) {
119248590Smm			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
120248590Smm			    "name option requires a string");
121248590Smm			return (ARCHIVE_FAILED);
122248590Smm		}
123248590Smm		archive_strcpy(&state->name, value);
124248590Smm		return (ARCHIVE_OK);
125248590Smm	}
126248590Smm
127248590Smm	/* Note: The "warn" return is just to inform the options
128248590Smm	 * supervisor that we didn't handle it.  It will generate
129248590Smm	 * a suitable error if no one used this option. */
130248590Smm	return (ARCHIVE_WARN);
131248590Smm}
132248590Smm
133248590Smm/*
134248590Smm * Setup callback.
135248590Smm */
136248590Smmstatic int
137248590Smmarchive_filter_uuencode_open(struct archive_write_filter *f)
138248590Smm{
139248590Smm	struct private_uuencode *state = (struct private_uuencode *)f->data;
140248590Smm	size_t bs = 65536, bpb;
141248590Smm
142248590Smm	if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
143248590Smm		/* Buffer size should be a multiple number of the of bytes
144248590Smm		 * per block for performance. */
145248590Smm		bpb = archive_write_get_bytes_per_block(f->archive);
146248590Smm		if (bpb > bs)
147248590Smm			bs = bpb;
148248590Smm		else if (bpb != 0)
149248590Smm			bs -= bs % bpb;
150248590Smm	}
151248590Smm
152248590Smm	state->bs = bs;
153248590Smm	if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) {
154248590Smm		archive_set_error(f->archive, ENOMEM,
155248590Smm		    "Can't allocate data for uuencode buffer");
156248590Smm		return (ARCHIVE_FATAL);
157248590Smm	}
158248590Smm
159248590Smm	archive_string_sprintf(&state->encoded_buff, "begin %o %s\n",
160248590Smm	    state->mode, state->name.s);
161248590Smm
162248590Smm	f->data = state;
163248590Smm	return (0);
164248590Smm}
165248590Smm
166248590Smmstatic void
167248590Smmuu_encode(struct archive_string *as, const unsigned char *p, size_t len)
168248590Smm{
169248590Smm	int c;
170248590Smm
171248590Smm	c = (int)len;
172248590Smm	archive_strappend_char(as, c?c + 0x20:'`');
173248590Smm	for (; len >= 3; p += 3, len -= 3) {
174248590Smm		c = p[0] >> 2;
175248590Smm		archive_strappend_char(as, c?c + 0x20:'`');
176248590Smm		c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4);
177248590Smm		archive_strappend_char(as, c?c + 0x20:'`');
178248590Smm		c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6);
179248590Smm		archive_strappend_char(as, c?c + 0x20:'`');
180248590Smm		c = p[2] & 0x3f;
181248590Smm		archive_strappend_char(as, c?c + 0x20:'`');
182248590Smm	}
183248590Smm	if (len > 0) {
184248590Smm		c = p[0] >> 2;
185248590Smm		archive_strappend_char(as, c?c + 0x20:'`');
186248590Smm		c = (p[0] & 0x03) << 4;
187248590Smm		if (len == 1) {
188248590Smm			archive_strappend_char(as, c?c + 0x20:'`');
189248590Smm			archive_strappend_char(as, '`');
190248590Smm			archive_strappend_char(as, '`');
191248590Smm		} else {
192248590Smm			c |= (p[1] & 0xf0) >> 4;
193248590Smm			archive_strappend_char(as, c?c + 0x20:'`');
194248590Smm			c = (p[1] & 0x0f) << 2;
195248590Smm			archive_strappend_char(as, c?c + 0x20:'`');
196248590Smm			archive_strappend_char(as, '`');
197248590Smm		}
198248590Smm	}
199248590Smm	archive_strappend_char(as, '\n');
200248590Smm}
201248590Smm
202248590Smm/*
203248590Smm * Write data to the encoded stream.
204248590Smm */
205248590Smmstatic int
206248590Smmarchive_filter_uuencode_write(struct archive_write_filter *f, const void *buff,
207248590Smm    size_t length)
208248590Smm{
209248590Smm	struct private_uuencode *state = (struct private_uuencode *)f->data;
210248590Smm	const unsigned char *p = buff;
211248590Smm	int ret = ARCHIVE_OK;
212248590Smm
213248590Smm	if (length == 0)
214248590Smm		return (ret);
215248590Smm
216248590Smm	if (state->hold_len) {
217248590Smm		while (state->hold_len < LBYTES && length > 0) {
218248590Smm			state->hold[state->hold_len++] = *p++;
219248590Smm			length--;
220248590Smm		}
221248590Smm		if (state->hold_len < LBYTES)
222248590Smm			return (ret);
223248590Smm		uu_encode(&state->encoded_buff, state->hold, LBYTES);
224248590Smm		state->hold_len = 0;
225248590Smm	}
226248590Smm
227248590Smm	for (; length >= LBYTES; length -= LBYTES, p += LBYTES)
228248590Smm		uu_encode(&state->encoded_buff, p, LBYTES);
229248590Smm
230248590Smm	/* Save remaining bytes. */
231248590Smm	if (length > 0) {
232248590Smm		memcpy(state->hold, p, length);
233248590Smm		state->hold_len = length;
234248590Smm	}
235248590Smm	while (archive_strlen(&state->encoded_buff) >= state->bs) {
236248590Smm		ret = __archive_write_filter(f->next_filter,
237248590Smm		    state->encoded_buff.s, state->bs);
238248590Smm		memmove(state->encoded_buff.s,
239248590Smm		    state->encoded_buff.s + state->bs,
240248590Smm		    state->encoded_buff.length - state->bs);
241248590Smm		state->encoded_buff.length -= state->bs;
242248590Smm	}
243248590Smm
244248590Smm	return (ret);
245248590Smm}
246248590Smm
247248590Smm
248248590Smm/*
249248590Smm * Finish the compression...
250248590Smm */
251248590Smmstatic int
252248590Smmarchive_filter_uuencode_close(struct archive_write_filter *f)
253248590Smm{
254248590Smm	struct private_uuencode *state = (struct private_uuencode *)f->data;
255248590Smm
256248590Smm	/* Flush remaining bytes. */
257248590Smm	if (state->hold_len != 0)
258248590Smm		uu_encode(&state->encoded_buff, state->hold, state->hold_len);
259248590Smm	archive_string_sprintf(&state->encoded_buff, "`\nend\n");
260248590Smm	/* Write the last block */
261248590Smm	archive_write_set_bytes_in_last_block(f->archive, 1);
262358088Smm	return __archive_write_filter(f->next_filter,
263248590Smm	    state->encoded_buff.s, archive_strlen(&state->encoded_buff));
264248590Smm}
265248590Smm
266248590Smmstatic int
267248590Smmarchive_filter_uuencode_free(struct archive_write_filter *f)
268248590Smm{
269248590Smm	struct private_uuencode *state = (struct private_uuencode *)f->data;
270248590Smm
271248590Smm	archive_string_free(&state->name);
272248590Smm	archive_string_free(&state->encoded_buff);
273248590Smm	free(state);
274248590Smm	return (ARCHIVE_OK);
275248590Smm}
276248590Smm
277248590Smmstatic int64_t
278248590Smmatol8(const char *p, size_t char_cnt)
279248590Smm{
280248590Smm	int64_t l;
281248590Smm	int digit;
282248590Smm
283248590Smm	l = 0;
284248590Smm	while (char_cnt-- > 0) {
285248590Smm		if (*p >= '0' && *p <= '7')
286248590Smm			digit = *p - '0';
287248590Smm		else
288248590Smm			break;
289248590Smm		p++;
290248590Smm		l <<= 3;
291248590Smm		l |= digit;
292248590Smm	}
293248590Smm	return (l);
294248590Smm}
295248590Smm
296