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