xref: /freebsd/contrib/libarchive/libarchive/archive_read_support_filter_uu.c (revision bd66c1b43e33540205dbc1187c2f2a15c58b57ba)
1 /*-
2  * Copyright (c) 2009-2011 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 #ifdef HAVE_ERRNO_H
29 #include <errno.h>
30 #endif
31 #ifdef HAVE_STDLIB_H
32 #include <stdlib.h>
33 #endif
34 #ifdef HAVE_STRING_H
35 #include <string.h>
36 #endif
37 
38 #include "archive.h"
39 #include "archive_entry.h"
40 #include "archive_private.h"
41 #include "archive_read_private.h"
42 
43 /* Maximum lookahead during bid phase */
44 #define UUENCODE_BID_MAX_READ 128*1024 /* in bytes */
45 
46 #define UUENCODE_MAX_LINE_LENGTH 34*1024 /* in bytes */
47 
48 struct uudecode {
49 	int64_t		 total;
50 	unsigned char	*in_buff;
51 #define IN_BUFF_SIZE	(1024)
52 	ssize_t		 in_cnt;
53 	size_t		 in_allocated;
54 	unsigned char	*out_buff;
55 #define OUT_BUFF_SIZE	(64 * 1024)
56 	int		 state;
57 #define ST_FIND_HEAD	0
58 #define ST_READ_UU	1
59 #define ST_UUEND	2
60 #define ST_READ_BASE64	3
61 #define ST_IGNORE	4
62 	mode_t		mode;
63 	int		mode_set;
64 	char		*name;
65 };
66 
67 static int	uudecode_bidder_bid(struct archive_read_filter_bidder *,
68 		    struct archive_read_filter *filter);
69 static int	uudecode_bidder_init(struct archive_read_filter *);
70 
71 static int	uudecode_read_header(struct archive_read_filter *,
72 		    struct archive_entry *entry);
73 static ssize_t	uudecode_filter_read(struct archive_read_filter *,
74 		    const void **);
75 static int	uudecode_filter_close(struct archive_read_filter *);
76 
77 #if ARCHIVE_VERSION_NUMBER < 4000000
78 /* Deprecated; remove in libarchive 4.0 */
79 int
archive_read_support_compression_uu(struct archive * a)80 archive_read_support_compression_uu(struct archive *a)
81 {
82 	return archive_read_support_filter_uu(a);
83 }
84 #endif
85 
86 static const struct archive_read_filter_bidder_vtable
87 uudecode_bidder_vtable = {
88 	.bid = uudecode_bidder_bid,
89 	.init = uudecode_bidder_init,
90 };
91 
92 int
archive_read_support_filter_uu(struct archive * _a)93 archive_read_support_filter_uu(struct archive *_a)
94 {
95 	struct archive_read *a = (struct archive_read *)_a;
96 
97 	return __archive_read_register_bidder(a, NULL, "uu",
98 			&uudecode_bidder_vtable);
99 }
100 
101 static const unsigned char ascii[256] = {
102 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\n', 0, 0, '\r', 0, 0, /* 00 - 0F */
103 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
104 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
105 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */
106 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
107 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */
108 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
109 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */
110 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
111 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
112 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
113 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
114 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
115 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
116 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
117 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
118 };
119 
120 static const unsigned char uuchar[256] = {
121 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
122 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
123 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
124 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */
125 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
126 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */
127 	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F */
128 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 70 - 7F */
129 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
130 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
131 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
132 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
133 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
134 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
135 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
136 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
137 };
138 
139 static const unsigned char base64[256] = {
140 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
141 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
142 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, /* 20 - 2F */
143 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, /* 30 - 3F */
144 	0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
145 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 50 - 5F */
146 	0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
147 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 70 - 7F */
148 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
149 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
150 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
151 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
152 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
153 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
154 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
155 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
156 };
157 
158 static const int base64num[128] = {
159 	 0,  0,  0,  0,  0,  0,  0,  0,
160 	 0,  0,  0,  0,  0,  0,  0,  0, /* 00 - 0F */
161 	 0,  0,  0,  0,  0,  0,  0,  0,
162 	 0,  0,  0,  0,  0,  0,  0,  0, /* 10 - 1F */
163 	 0,  0,  0,  0,  0,  0,  0,  0,
164 	 0,  0,  0, 62,  0,  0,  0, 63, /* 20 - 2F */
165 	52, 53, 54, 55, 56, 57, 58, 59,
166 	60, 61,  0,  0,  0,  0,  0,  0, /* 30 - 3F */
167 	 0,  0,  1,  2,  3,  4,  5,  6,
168 	 7,  8,  9, 10, 11, 12, 13, 14, /* 40 - 4F */
169 	15, 16, 17, 18, 19, 20, 21, 22,
170 	23, 24, 25,  0,  0,  0,  0,  0, /* 50 - 5F */
171 	 0, 26, 27, 28, 29, 30, 31, 32,
172 	33, 34, 35, 36, 37, 38, 39, 40, /* 60 - 6F */
173 	41, 42, 43, 44, 45, 46, 47, 48,
174 	49, 50, 51,  0,  0,  0,  0,  0, /* 70 - 7F */
175 };
176 
177 static ssize_t
get_line(const unsigned char * b,ssize_t avail,ssize_t * nlsize)178 get_line(const unsigned char *b, ssize_t avail, ssize_t *nlsize)
179 {
180 	ssize_t len;
181 
182 	len = 0;
183 	while (len < avail) {
184 		switch (ascii[*b]) {
185 		case 0:	/* Non-ascii character or control character. */
186 			if (nlsize != NULL)
187 				*nlsize = 0;
188 			return (-1);
189 		case '\r':
190 			if (avail-len > 1 && b[1] == '\n') {
191 				if (nlsize != NULL)
192 					*nlsize = 2;
193 				return (len+2);
194 			}
195 			/* FALL THROUGH */
196 		case '\n':
197 			if (nlsize != NULL)
198 				*nlsize = 1;
199 			return (len+1);
200 		case 1:
201 			b++;
202 			len++;
203 			break;
204 		}
205 	}
206 	if (nlsize != NULL)
207 		*nlsize = 0;
208 	return (avail);
209 }
210 
211 static ssize_t
bid_get_line(struct archive_read_filter * filter,const unsigned char ** b,ssize_t * avail,ssize_t * ravail,ssize_t * nl,size_t * nbytes_read)212 bid_get_line(struct archive_read_filter *filter,
213     const unsigned char **b, ssize_t *avail, ssize_t *ravail,
214     ssize_t *nl, size_t* nbytes_read)
215 {
216 	ssize_t len;
217 	int quit;
218 
219 	quit = 0;
220 	if (*avail == 0) {
221 		*nl = 0;
222 		len = 0;
223 	} else
224 		len = get_line(*b, *avail, nl);
225 
226 	/*
227 	 * Read bytes more while it does not reach the end of line.
228 	 */
229 	while (*nl == 0 && len == *avail && !quit &&
230 	    *nbytes_read < UUENCODE_BID_MAX_READ) {
231 		ssize_t diff = *ravail - *avail;
232 		size_t nbytes_req = (*ravail+1023) & ~1023U;
233 		ssize_t tested;
234 
235 		/* Increase reading bytes if it is not enough to at least
236 		 * new two lines. */
237 		if (nbytes_req < (size_t)*ravail + 160)
238 			nbytes_req <<= 1;
239 
240 		*b = __archive_read_filter_ahead(filter, nbytes_req, avail);
241 		if (*b == NULL) {
242 			if (*ravail >= *avail)
243 				return (0);
244 			/* Reading bytes reaches the end of a stream. */
245 			*b = __archive_read_filter_ahead(filter, *avail, avail);
246 			quit = 1;
247 		}
248 		*nbytes_read = *avail;
249 		*ravail = *avail;
250 		*b += diff;
251 		*avail -= diff;
252 		tested = len;/* Skip some bytes we already determined. */
253 		len = get_line(*b + tested, *avail - tested, nl);
254 		if (len >= 0)
255 			len += tested;
256 	}
257 	return (len);
258 }
259 
260 #define UUDECODE(c) (((c) - 0x20) & 0x3f)
261 
262 static int
uudecode_bidder_bid(struct archive_read_filter_bidder * self,struct archive_read_filter * filter)263 uudecode_bidder_bid(struct archive_read_filter_bidder *self,
264     struct archive_read_filter *filter)
265 {
266 	const unsigned char *b;
267 	ssize_t avail, ravail;
268 	ssize_t len, nl;
269 	int l;
270 	int firstline;
271 	size_t nbytes_read;
272 
273 	(void)self; /* UNUSED */
274 
275 	b = __archive_read_filter_ahead(filter, 1, &avail);
276 	if (b == NULL)
277 		return (0);
278 
279 	firstline = 20;
280 	ravail = avail;
281 	nbytes_read = avail;
282 	for (;;) {
283 		len = bid_get_line(filter, &b, &avail, &ravail, &nl, &nbytes_read);
284 		if (len < 0 || nl == 0)
285 			return (0); /* No match found. */
286 		if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0)
287 			l = 6;
288 		else if (len -nl >= 18 && memcmp(b, "begin-base64 ", 13) == 0)
289 			l = 13;
290 		else
291 			l = 0;
292 
293 		if (l > 0 && (b[l] < '0' || b[l] > '7' ||
294 		    b[l+1] < '0' || b[l+1] > '7' ||
295 		    b[l+2] < '0' || b[l+2] > '7' || b[l+3] != ' '))
296 			l = 0;
297 
298 		b += len;
299 		avail -= len;
300 		if (l)
301 			break;
302 		firstline = 0;
303 
304 		/* Do not read more than UUENCODE_BID_MAX_READ bytes */
305 		if (nbytes_read >= UUENCODE_BID_MAX_READ)
306 			return (0);
307 	}
308 	if (!avail)
309 		return (0);
310 	len = bid_get_line(filter, &b, &avail, &ravail, &nl, &nbytes_read);
311 	if (len < 0 || nl == 0)
312 		return (0);/* There are non-ascii characters. */
313 	avail -= len;
314 
315 	if (l == 6) {
316 		/* "begin " */
317 		if (!uuchar[*b])
318 			return (0);
319 		/* Get a length of decoded bytes. */
320 		l = UUDECODE(*b++); len--;
321 		if (l > 45)
322 			/* Normally, maximum length is 45(character 'M'). */
323 			return (0);
324 		if (l > len - nl)
325 			return (0); /* Line too short. */
326 		while (l) {
327 			if (!uuchar[*b++])
328 				return (0);
329 			--len;
330 			--l;
331 		}
332 		if (len-nl == 1 &&
333 		    (uuchar[*b] ||		 /* Check sum. */
334 		     (*b >= 'a' && *b <= 'z'))) {/* Padding data(MINIX). */
335 			++b;
336 			--len;
337 		}
338 		b += nl;
339 		if (avail && uuchar[*b])
340 			return (firstline+30);
341 	} else if (l == 13) {
342 		/* "begin-base64 " */
343 		while (len-nl > 0) {
344 			if (!base64[*b++])
345 				return (0);
346 			--len;
347 		}
348 		b += nl;
349 
350 		if (avail >= 5 && memcmp(b, "====\n", 5) == 0)
351 			return (firstline+40);
352 		if (avail >= 6 && memcmp(b, "====\r\n", 6) == 0)
353 			return (firstline+40);
354 		if (avail > 0 && base64[*b])
355 			return (firstline+30);
356 	}
357 
358 	return (0);
359 }
360 
361 static const struct archive_read_filter_vtable
362 uudecode_reader_vtable = {
363 	.read = uudecode_filter_read,
364 	.close = uudecode_filter_close,
365 	.read_header = uudecode_read_header
366 };
367 
368 static int
uudecode_bidder_init(struct archive_read_filter * self)369 uudecode_bidder_init(struct archive_read_filter *self)
370 {
371 	struct uudecode   *uudecode;
372 	void *out_buff;
373 	void *in_buff;
374 
375 	self->code = ARCHIVE_FILTER_UU;
376 	self->name = "uu";
377 
378 	uudecode = calloc(1, sizeof(*uudecode));
379 	out_buff = malloc(OUT_BUFF_SIZE);
380 	in_buff = malloc(IN_BUFF_SIZE);
381 	if (uudecode == NULL || out_buff == NULL || in_buff == NULL) {
382 		archive_set_error(&self->archive->archive, ENOMEM,
383 		    "Can't allocate data for uudecode");
384 		free(uudecode);
385 		free(out_buff);
386 		free(in_buff);
387 		return (ARCHIVE_FATAL);
388 	}
389 
390 	self->data = uudecode;
391 	uudecode->in_buff = in_buff;
392 	uudecode->in_cnt = 0;
393 	uudecode->in_allocated = IN_BUFF_SIZE;
394 	uudecode->out_buff = out_buff;
395 	uudecode->state = ST_FIND_HEAD;
396 	uudecode->mode_set = 0;
397 	uudecode->name = NULL;
398 	self->vtable = &uudecode_reader_vtable;
399 
400 	return (ARCHIVE_OK);
401 }
402 
403 static int
ensure_in_buff_size(struct archive_read_filter * self,struct uudecode * uudecode,size_t size)404 ensure_in_buff_size(struct archive_read_filter *self,
405     struct uudecode *uudecode, size_t size)
406 {
407 
408 	if (size > uudecode->in_allocated) {
409 		unsigned char *ptr;
410 		size_t newsize;
411 
412 		/*
413 		 * Calculate a new buffer size for in_buff.
414 		 * Increase its value until it has enough size we need.
415 		 */
416 		newsize = uudecode->in_allocated;
417 		do {
418 			if (newsize < IN_BUFF_SIZE*32)
419 				newsize <<= 1;
420 			else
421 				newsize += IN_BUFF_SIZE;
422 		} while (size > newsize);
423 		/* Allocate the new buffer. */
424 		ptr = malloc(newsize);
425 		if (ptr == NULL) {
426 			free(ptr);
427 			archive_set_error(&self->archive->archive,
428 			    ENOMEM,
429     			    "Can't allocate data for uudecode");
430 			return (ARCHIVE_FATAL);
431 		}
432 		/* Move the remaining data in in_buff into the new buffer. */
433 		if (uudecode->in_cnt)
434 			memmove(ptr, uudecode->in_buff, uudecode->in_cnt);
435 		/* Replace in_buff with the new buffer. */
436 		free(uudecode->in_buff);
437 		uudecode->in_buff = ptr;
438 		uudecode->in_allocated = newsize;
439 	}
440 	return (ARCHIVE_OK);
441 }
442 
443 static int
uudecode_read_header(struct archive_read_filter * self,struct archive_entry * entry)444 uudecode_read_header(struct archive_read_filter *self, struct archive_entry *entry)
445 {
446 
447 	struct uudecode *uudecode;
448 	uudecode = (struct uudecode *)self->data;
449 
450 	if (uudecode->mode_set != 0)
451 		archive_entry_set_mode(entry, S_IFREG | uudecode->mode);
452 
453 	if (uudecode->name != NULL)
454 		archive_entry_set_pathname(entry, uudecode->name);
455 
456 	return (ARCHIVE_OK);
457 }
458 
459 static ssize_t
uudecode_filter_read(struct archive_read_filter * self,const void ** buff)460 uudecode_filter_read(struct archive_read_filter *self, const void **buff)
461 {
462 	struct uudecode *uudecode;
463 	const unsigned char *b, *d;
464 	unsigned char *out;
465 	ssize_t avail_in, ravail;
466 	ssize_t used;
467 	ssize_t total;
468 	ssize_t len, llen, nl, namelen;
469 
470 	uudecode = (struct uudecode *)self->data;
471 
472 read_more:
473 	d = __archive_read_filter_ahead(self->upstream, 1, &avail_in);
474 	if (d == NULL && avail_in < 0)
475 		return (ARCHIVE_FATAL);
476 	/* Quiet a code analyzer; make sure avail_in must be zero
477 	 * when d is NULL. */
478 	if (d == NULL)
479 		avail_in = 0;
480 	used = 0;
481 	total = 0;
482 	out = uudecode->out_buff;
483 	ravail = avail_in;
484 	if (uudecode->state == ST_IGNORE) {
485 		used = avail_in;
486 		goto finish;
487 	}
488 	if (uudecode->in_cnt) {
489 		if (uudecode->in_cnt > UUENCODE_MAX_LINE_LENGTH) {
490 			archive_set_error(&self->archive->archive,
491 			    ARCHIVE_ERRNO_FILE_FORMAT,
492 			    "Invalid format data");
493 			return (ARCHIVE_FATAL);
494 		}
495 		/*
496 		 * If there is remaining data which is saved by
497 		 * previous calling, use it first.
498 		 */
499 		if (ensure_in_buff_size(self, uudecode,
500 		    avail_in + uudecode->in_cnt) != ARCHIVE_OK)
501 			return (ARCHIVE_FATAL);
502 		memcpy(uudecode->in_buff + uudecode->in_cnt,
503 		    d, avail_in);
504 		d = uudecode->in_buff;
505 		avail_in += uudecode->in_cnt;
506 		uudecode->in_cnt = 0;
507 	}
508 	for (;used < avail_in; d += llen, used += llen) {
509 		ssize_t l, body;
510 
511 		b = d;
512 		len = get_line(b, avail_in - used, &nl);
513 		if (len < 0) {
514 			/* Non-ascii character is found. */
515 			if (uudecode->state == ST_FIND_HEAD &&
516 			    (uudecode->total > 0 || total > 0)) {
517 				uudecode->state = ST_IGNORE;
518 				used = avail_in;
519 				goto finish;
520 			}
521 			archive_set_error(&self->archive->archive,
522 			    ARCHIVE_ERRNO_MISC,
523 			    "Insufficient compressed data");
524 			return (ARCHIVE_FATAL);
525 		}
526 		llen = len;
527 		if ((nl == 0) && (uudecode->state != ST_UUEND)) {
528 			if (total == 0 && ravail <= 0) {
529 				/* There is nothing more to read, fail */
530 				archive_set_error(&self->archive->archive,
531 				    ARCHIVE_ERRNO_FILE_FORMAT,
532 				    "Missing format data");
533 				return (ARCHIVE_FATAL);
534 			}
535 			/*
536 			 * Save remaining data which does not contain
537 			 * NL('\n','\r').
538 			 */
539 			if (ensure_in_buff_size(self, uudecode, len)
540 			    != ARCHIVE_OK)
541 				return (ARCHIVE_FATAL);
542 			if (uudecode->in_buff != b)
543 				memmove(uudecode->in_buff, b, len);
544 			uudecode->in_cnt = len;
545 			if (total == 0) {
546 				/* Do not return 0; it means end-of-file.
547 				 * We should try to read bytes more. */
548 				__archive_read_filter_consume(
549 				    self->upstream, ravail);
550 				goto read_more;
551 			}
552 			used += len;
553 			break;
554 		}
555 		switch (uudecode->state) {
556 		default:
557 		case ST_FIND_HEAD:
558 			/* Do not read more than UUENCODE_BID_MAX_READ bytes */
559 			if (total + len >= UUENCODE_BID_MAX_READ) {
560 				archive_set_error(&self->archive->archive,
561 				    ARCHIVE_ERRNO_FILE_FORMAT,
562 				    "Invalid format data");
563 				return (ARCHIVE_FATAL);
564 			}
565 			if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0)
566 				l = 6;
567 			else if (len - nl >= 18 &&
568 			    memcmp(b, "begin-base64 ", 13) == 0)
569 				l = 13;
570 			else
571 				l = 0;
572 			if (l != 0 && b[l] >= '0' && b[l] <= '7' &&
573 			    b[l+1] >= '0' && b[l+1] <= '7' &&
574 			    b[l+2] >= '0' && b[l+2] <= '7' && b[l+3] == ' ') {
575 				if (l == 6)
576 					uudecode->state = ST_READ_UU;
577 				else
578 					uudecode->state = ST_READ_BASE64;
579 				uudecode->mode = (mode_t)(
580 				    ((int)(b[l] - '0') * 64) +
581 				    ((int)(b[l+1] - '0') * 8) +
582 				     (int)(b[l+2] - '0'));
583 				uudecode->mode_set = 1;
584 				namelen = len - nl - 4 - l;
585 				if (namelen > 1) {
586 					if (uudecode->name != NULL)
587 						free(uudecode->name);
588 					uudecode->name = malloc(namelen + 1);
589 			                if (uudecode->name == NULL) {
590 					archive_set_error(
591 					    &self->archive->archive,
592 					    ENOMEM,
593 					    "Can't allocate data for uudecode");
594 						return (ARCHIVE_FATAL);
595 					}
596 					strncpy(uudecode->name,
597 					    (const char *)(b + l + 4),
598 					    namelen);
599 					uudecode->name[namelen] = '\0';
600 				}
601 			}
602 			break;
603 		case ST_READ_UU:
604 			if (total + len * 2 > OUT_BUFF_SIZE)
605 				goto finish;
606 			body = len - nl;
607 			if (!uuchar[*b] || body <= 0) {
608 				archive_set_error(&self->archive->archive,
609 				    ARCHIVE_ERRNO_MISC,
610 				    "Insufficient compressed data");
611 				return (ARCHIVE_FATAL);
612 			}
613 			/* Get length of undecoded bytes of current line. */
614 			l = UUDECODE(*b++);
615 			body--;
616 			if (l > body) {
617 				archive_set_error(&self->archive->archive,
618 				    ARCHIVE_ERRNO_MISC,
619 				    "Insufficient compressed data");
620 				return (ARCHIVE_FATAL);
621 			}
622 			if (l == 0) {
623 				uudecode->state = ST_UUEND;
624 				break;
625 			}
626 			while (l > 0) {
627 				int n = 0;
628 
629 				if (!uuchar[b[0]] || !uuchar[b[1]])
630 					break;
631 				n = UUDECODE(*b++) << 18;
632 				n |= UUDECODE(*b++) << 12;
633 				*out++ = n >> 16; total++;
634 				--l;
635 
636 				if (l > 0) {
637 					if (!uuchar[b[0]])
638 						break;
639 					n |= UUDECODE(*b++) << 6;
640 					*out++ = (n >> 8) & 0xFF; total++;
641 					--l;
642 				}
643 				if (l > 0) {
644 					if (!uuchar[b[0]])
645 						break;
646 					n |= UUDECODE(*b++);
647 					*out++ = n & 0xFF; total++;
648 					--l;
649 				}
650 			}
651 			if (l) {
652 				archive_set_error(&self->archive->archive,
653 				    ARCHIVE_ERRNO_MISC,
654 				    "Insufficient compressed data");
655 				return (ARCHIVE_FATAL);
656 			}
657 			break;
658 		case ST_UUEND:
659 			if (len - nl == 3 && memcmp(b, "end ", 3) == 0)
660 				uudecode->state = ST_FIND_HEAD;
661 			else {
662 				archive_set_error(&self->archive->archive,
663 				    ARCHIVE_ERRNO_MISC,
664 				    "Insufficient compressed data");
665 				return (ARCHIVE_FATAL);
666 			}
667 			break;
668 		case ST_READ_BASE64:
669 			if (total + len * 2 > OUT_BUFF_SIZE)
670 				goto finish;
671 			l = len - nl;
672 			if (l >= 3 && b[0] == '=' && b[1] == '=' &&
673 			    b[2] == '=') {
674 				uudecode->state = ST_FIND_HEAD;
675 				break;
676 			}
677 			while (l > 0) {
678 				int n = 0;
679 
680 				if (!base64[b[0]] || !base64[b[1]])
681 					break;
682 				n = base64num[*b++] << 18;
683 				n |= base64num[*b++] << 12;
684 				*out++ = n >> 16; total++;
685 				l -= 2;
686 
687 				if (l > 0) {
688 					if (*b == '=')
689 						break;
690 					if (!base64[*b])
691 						break;
692 					n |= base64num[*b++] << 6;
693 					*out++ = (n >> 8) & 0xFF; total++;
694 					--l;
695 				}
696 				if (l > 0) {
697 					if (*b == '=')
698 						break;
699 					if (!base64[*b])
700 						break;
701 					n |= base64num[*b++];
702 					*out++ = n & 0xFF; total++;
703 					--l;
704 				}
705 			}
706 			if (l && *b != '=') {
707 				archive_set_error(&self->archive->archive,
708 				    ARCHIVE_ERRNO_MISC,
709 				    "Insufficient compressed data");
710 				return (ARCHIVE_FATAL);
711 			}
712 			break;
713 		}
714 	}
715 finish:
716 	if (ravail < avail_in)
717 		used -= avail_in - ravail;
718 	__archive_read_filter_consume(self->upstream, used);
719 
720 	*buff = uudecode->out_buff;
721 	uudecode->total += total;
722 	return (total);
723 }
724 
725 static int
uudecode_filter_close(struct archive_read_filter * self)726 uudecode_filter_close(struct archive_read_filter *self)
727 {
728 	struct uudecode *uudecode;
729 
730 	uudecode = (struct uudecode *)self->data;
731 	free(uudecode->in_buff);
732 	free(uudecode->out_buff);
733 	free(uudecode->name);
734 	free(uudecode);
735 
736 	return (ARCHIVE_OK);
737 }
738 
739