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 45
46 
47 struct private_uuencode {
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 
56 static int archive_filter_uuencode_options(struct archive_write_filter *,
57     const char *, const char *);
58 static int archive_filter_uuencode_open(struct archive_write_filter *);
59 static int archive_filter_uuencode_write(struct archive_write_filter *,
60     const void *, size_t);
61 static int archive_filter_uuencode_close(struct archive_write_filter *);
62 static int archive_filter_uuencode_free(struct archive_write_filter *);
63 static void uu_encode(struct archive_string *, const unsigned char *, size_t);
64 static int64_t atol8(const char *, size_t);
65 
66 /*
67  * Add a compress filter to this write handle.
68  */
69 int
70 archive_write_add_filter_uuencode(struct archive *_a)
71 {
72 	struct archive_write *a = (struct archive_write *)_a;
73 	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
74 	struct private_uuencode *state;
75 
76 	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
77 	    ARCHIVE_STATE_NEW, "archive_write_add_filter_uu");
78 
79 	state = (struct private_uuencode *)calloc(1, sizeof(*state));
80 	if (state == NULL) {
81 		archive_set_error(f->archive, ENOMEM,
82 		    "Can't allocate data for uuencode filter");
83 		return (ARCHIVE_FATAL);
84 	}
85 	archive_strcpy(&state->name, "-");
86 	state->mode = 0644;
87 
88 	f->data = state;
89 	f->name = "uuencode";
90 	f->code = ARCHIVE_FILTER_UU;
91 	f->open = archive_filter_uuencode_open;
92 	f->options = archive_filter_uuencode_options;
93 	f->write = archive_filter_uuencode_write;
94 	f->close = archive_filter_uuencode_close;
95 	f->free = archive_filter_uuencode_free;
96 
97 	return (ARCHIVE_OK);
98 }
99 
100 /*
101  * Set write options.
102  */
103 static int
104 archive_filter_uuencode_options(struct archive_write_filter *f, const char *key,
105     const char *value)
106 {
107 	struct private_uuencode *state = (struct private_uuencode *)f->data;
108 
109 	if (strcmp(key, "mode") == 0) {
110 		if (value == NULL) {
111 			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
112 			    "mode option requires octal digits");
113 			return (ARCHIVE_FAILED);
114 		}
115 		state->mode = (int)atol8(value, strlen(value)) & 0777;
116 		return (ARCHIVE_OK);
117 	} else if (strcmp(key, "name") == 0) {
118 		if (value == NULL) {
119 			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
120 			    "name option requires a string");
121 			return (ARCHIVE_FAILED);
122 		}
123 		archive_strcpy(&state->name, value);
124 		return (ARCHIVE_OK);
125 	}
126 
127 	/* Note: The "warn" return is just to inform the options
128 	 * supervisor that we didn't handle it.  It will generate
129 	 * a suitable error if no one used this option. */
130 	return (ARCHIVE_WARN);
131 }
132 
133 /*
134  * Setup callback.
135  */
136 static int
137 archive_filter_uuencode_open(struct archive_write_filter *f)
138 {
139 	struct private_uuencode *state = (struct private_uuencode *)f->data;
140 	size_t bs = 65536, bpb;
141 	int ret;
142 
143 	ret = __archive_write_open_filter(f->next_filter);
144 	if (ret != ARCHIVE_OK)
145 		return (ret);
146 
147 	if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
148 		/* Buffer size should be a multiple number of the of bytes
149 		 * per block for performance. */
150 		bpb = archive_write_get_bytes_per_block(f->archive);
151 		if (bpb > bs)
152 			bs = bpb;
153 		else if (bpb != 0)
154 			bs -= bs % bpb;
155 	}
156 
157 	state->bs = bs;
158 	if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) {
159 		archive_set_error(f->archive, ENOMEM,
160 		    "Can't allocate data for uuencode buffer");
161 		return (ARCHIVE_FATAL);
162 	}
163 
164 	archive_string_sprintf(&state->encoded_buff, "begin %o %s\n",
165 	    state->mode, state->name.s);
166 
167 	f->data = state;
168 	return (0);
169 }
170 
171 static void
172 uu_encode(struct archive_string *as, const unsigned char *p, size_t len)
173 {
174 	int c;
175 
176 	c = (int)len;
177 	archive_strappend_char(as, c?c + 0x20:'`');
178 	for (; len >= 3; p += 3, len -= 3) {
179 		c = p[0] >> 2;
180 		archive_strappend_char(as, c?c + 0x20:'`');
181 		c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4);
182 		archive_strappend_char(as, c?c + 0x20:'`');
183 		c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6);
184 		archive_strappend_char(as, c?c + 0x20:'`');
185 		c = p[2] & 0x3f;
186 		archive_strappend_char(as, c?c + 0x20:'`');
187 	}
188 	if (len > 0) {
189 		c = p[0] >> 2;
190 		archive_strappend_char(as, c?c + 0x20:'`');
191 		c = (p[0] & 0x03) << 4;
192 		if (len == 1) {
193 			archive_strappend_char(as, c?c + 0x20:'`');
194 			archive_strappend_char(as, '`');
195 			archive_strappend_char(as, '`');
196 		} else {
197 			c |= (p[1] & 0xf0) >> 4;
198 			archive_strappend_char(as, c?c + 0x20:'`');
199 			c = (p[1] & 0x0f) << 2;
200 			archive_strappend_char(as, c?c + 0x20:'`');
201 			archive_strappend_char(as, '`');
202 		}
203 	}
204 	archive_strappend_char(as, '\n');
205 }
206 
207 /*
208  * Write data to the encoded stream.
209  */
210 static int
211 archive_filter_uuencode_write(struct archive_write_filter *f, const void *buff,
212     size_t length)
213 {
214 	struct private_uuencode *state = (struct private_uuencode *)f->data;
215 	const unsigned char *p = buff;
216 	int ret = ARCHIVE_OK;
217 
218 	if (length == 0)
219 		return (ret);
220 
221 	if (state->hold_len) {
222 		while (state->hold_len < LBYTES && length > 0) {
223 			state->hold[state->hold_len++] = *p++;
224 			length--;
225 		}
226 		if (state->hold_len < LBYTES)
227 			return (ret);
228 		uu_encode(&state->encoded_buff, state->hold, LBYTES);
229 		state->hold_len = 0;
230 	}
231 
232 	for (; length >= LBYTES; length -= LBYTES, p += LBYTES)
233 		uu_encode(&state->encoded_buff, p, LBYTES);
234 
235 	/* Save remaining bytes. */
236 	if (length > 0) {
237 		memcpy(state->hold, p, length);
238 		state->hold_len = length;
239 	}
240 	while (archive_strlen(&state->encoded_buff) >= state->bs) {
241 		ret = __archive_write_filter(f->next_filter,
242 		    state->encoded_buff.s, state->bs);
243 		memmove(state->encoded_buff.s,
244 		    state->encoded_buff.s + state->bs,
245 		    state->encoded_buff.length - state->bs);
246 		state->encoded_buff.length -= state->bs;
247 	}
248 
249 	return (ret);
250 }
251 
252 
253 /*
254  * Finish the compression...
255  */
256 static int
257 archive_filter_uuencode_close(struct archive_write_filter *f)
258 {
259 	struct private_uuencode *state = (struct private_uuencode *)f->data;
260 	int ret, ret2;
261 
262 	/* Flush remaining bytes. */
263 	if (state->hold_len != 0)
264 		uu_encode(&state->encoded_buff, state->hold, state->hold_len);
265 	archive_string_sprintf(&state->encoded_buff, "`\nend\n");
266 	/* Write the last block */
267 	archive_write_set_bytes_in_last_block(f->archive, 1);
268 	ret = __archive_write_filter(f->next_filter,
269 	    state->encoded_buff.s, archive_strlen(&state->encoded_buff));
270 	ret2 = __archive_write_close_filter(f->next_filter);
271 	if (ret > ret2)
272 		ret = ret2;
273 	return (ret);
274 }
275 
276 static int
277 archive_filter_uuencode_free(struct archive_write_filter *f)
278 {
279 	struct private_uuencode *state = (struct private_uuencode *)f->data;
280 
281 	archive_string_free(&state->name);
282 	archive_string_free(&state->encoded_buff);
283 	free(state);
284 	return (ARCHIVE_OK);
285 }
286 
287 static int64_t
288 atol8(const char *p, size_t char_cnt)
289 {
290 	int64_t l;
291 	int digit;
292 
293 	l = 0;
294 	while (char_cnt-- > 0) {
295 		if (*p >= '0' && *p <= '7')
296 			digit = *p - '0';
297 		else
298 			break;
299 		p++;
300 		l <<= 3;
301 		l |= digit;
302 	}
303 	return (l);
304 }
305 
306