archive_read_support_filter_uu.c revision 232153
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
46struct 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
61static int	uudecode_bidder_bid(struct archive_read_filter_bidder *,
62		    struct archive_read_filter *filter);
63static int	uudecode_bidder_init(struct archive_read_filter *);
64
65static ssize_t	uudecode_filter_read(struct archive_read_filter *,
66		    const void **);
67static int	uudecode_filter_close(struct archive_read_filter *);
68
69#if ARCHIVE_VERSION_NUMBER < 4000000
70/* Deprecated; remove in libarchive 4.0 */
71int
72archive_read_support_compression_uu(struct archive *a)
73{
74	return archive_read_support_filter_uu(a);
75}
76#endif
77
78int
79archive_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
98static 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
117static 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
136static 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
155static 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
174static ssize_t
175get_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
208static ssize_t
209bid_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
259static int
260uudecode_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
373static int
374uudecode_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
408static int
409ensure_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
448static ssize_t
449uudecode_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
461read_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
668static int
669uudecode_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