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