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