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