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