archive_read_support_filter_uu.c revision 248616
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#define ST_IGNORE	4
60};
61
62static int	uudecode_bidder_bid(struct archive_read_filter_bidder *,
63		    struct archive_read_filter *filter);
64static int	uudecode_bidder_init(struct archive_read_filter *);
65
66static ssize_t	uudecode_filter_read(struct archive_read_filter *,
67		    const void **);
68static int	uudecode_filter_close(struct archive_read_filter *);
69
70#if ARCHIVE_VERSION_NUMBER < 4000000
71/* Deprecated; remove in libarchive 4.0 */
72int
73archive_read_support_compression_uu(struct archive *a)
74{
75	return archive_read_support_filter_uu(a);
76}
77#endif
78
79int
80archive_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
100static 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
119static 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
138static 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
157static 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
176static ssize_t
177get_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
210static ssize_t
211bid_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
261static int
262uudecode_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
375static int
376uudecode_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
410static int
411ensure_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
450static ssize_t
451uudecode_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
463read_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	}
672finish:
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
682static int
683uudecode_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