1228753Smm/*-
2228753Smm * Copyright (c) 2009 Michihiro NAKAJIMA
3228753Smm * All rights reserved.
4228753Smm *
5228753Smm * Redistribution and use in source and binary forms, with or without
6228753Smm * modification, are permitted provided that the following conditions
7228753Smm * are met:
8228753Smm * 1. Redistributions of source code must retain the above copyright
9228753Smm *    notice, this list of conditions and the following disclaimer.
10228753Smm * 2. Redistributions in binary form must reproduce the above copyright
11228753Smm *    notice, this list of conditions and the following disclaimer in the
12228753Smm *    documentation and/or other materials provided with the distribution.
13228753Smm *
14228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24228753Smm */
25228753Smm
26228753Smm#include "archive_platform.h"
27229592Smm__FBSDID("$FreeBSD$");
28228753Smm
29228753Smm#ifdef HAVE_ERRNO_H
30228753Smm#include <errno.h>
31228753Smm#endif
32228753Smm#ifdef HAVE_STDLIB_H
33228753Smm#include <stdlib.h>
34228753Smm#endif
35228753Smm#ifdef HAVE_STRING_H
36228753Smm#include <string.h>
37228753Smm#endif
38228753Smm
39228753Smm#include "archive.h"
40228753Smm#include "archive_private.h"
41228753Smm#include "archive_read_private.h"
42228753Smm
43228753Smmstruct uudecode {
44228753Smm	int64_t		 total;
45228753Smm	unsigned char	*in_buff;
46228753Smm#define IN_BUFF_SIZE	(1024)
47228753Smm	int		 in_cnt;
48228753Smm	size_t		 in_allocated;
49228753Smm	unsigned char	*out_buff;
50228753Smm#define OUT_BUFF_SIZE	(64 * 1024)
51228753Smm	int		 state;
52228753Smm#define ST_FIND_HEAD	0
53228753Smm#define ST_READ_UU	1
54228753Smm#define ST_UUEND	2
55228753Smm#define ST_READ_BASE64	3
56228753Smm};
57228753Smm
58228753Smmstatic int	uudecode_bidder_bid(struct archive_read_filter_bidder *,
59228753Smm		    struct archive_read_filter *filter);
60228753Smmstatic int	uudecode_bidder_init(struct archive_read_filter *);
61228753Smm
62228753Smmstatic ssize_t	uudecode_filter_read(struct archive_read_filter *,
63228753Smm		    const void **);
64228753Smmstatic int	uudecode_filter_close(struct archive_read_filter *);
65228753Smm
66228753Smmint
67228753Smmarchive_read_support_compression_uu(struct archive *_a)
68228753Smm{
69228753Smm	struct archive_read *a = (struct archive_read *)_a;
70228753Smm	struct archive_read_filter_bidder *bidder;
71228753Smm
72228753Smm	bidder = __archive_read_get_bidder(a);
73228753Smm	archive_clear_error(_a);
74228753Smm	if (bidder == NULL)
75228753Smm		return (ARCHIVE_FATAL);
76228753Smm
77228753Smm	bidder->data = NULL;
78228753Smm	bidder->bid = uudecode_bidder_bid;
79228753Smm	bidder->init = uudecode_bidder_init;
80228753Smm	bidder->options = NULL;
81228753Smm	bidder->free = NULL;
82228753Smm	return (ARCHIVE_OK);
83228753Smm}
84228753Smm
85228753Smmstatic const unsigned char ascii[256] = {
86228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\n', 0, 0, '\r', 0, 0, /* 00 - 0F */
87228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
88228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
89228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */
90228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
91228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */
92228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
93228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */
94228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
95228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
96228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
97228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
98228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
99228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
100228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
101228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
102228753Smm};
103228753Smm
104228753Smmstatic const unsigned char uuchar[256] = {
105228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
106228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
107228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
108228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */
109228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
110228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */
111228753Smm	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F */
112228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 70 - 7F */
113228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
114228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
115228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
116228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
117228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
118228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
119228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
120228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
121228753Smm};
122228753Smm
123228753Smmstatic const unsigned char base64[256] = {
124228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
125228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
126228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, /* 20 - 2F */
127228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, /* 30 - 3F */
128228753Smm	0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
129228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 50 - 5F */
130228753Smm	0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
131228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 70 - 7F */
132228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
133228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
134228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
135228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
136228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
137228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
138228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
139228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
140228753Smm};
141228753Smm
142228753Smmstatic const int base64num[128] = {
143228753Smm	 0,  0,  0,  0,  0,  0,  0,  0,
144228753Smm	 0,  0,  0,  0,  0,  0,  0,  0, /* 00 - 0F */
145228753Smm	 0,  0,  0,  0,  0,  0,  0,  0,
146228753Smm	 0,  0,  0,  0,  0,  0,  0,  0, /* 10 - 1F */
147228753Smm	 0,  0,  0,  0,  0,  0,  0,  0,
148228753Smm	 0,  0,  0, 62,  0,  0,  0, 63, /* 20 - 2F */
149228753Smm	52, 53, 54, 55, 56, 57, 58, 59,
150228753Smm	60, 61,  0,  0,  0,  0,  0,  0, /* 30 - 3F */
151228753Smm	 0,  0,  1,  2,  3,  4,  5,  6,
152228753Smm	 7,  8,  9, 10, 11, 12, 13, 14, /* 40 - 4F */
153228753Smm	15, 16, 17, 18, 19, 20, 21, 22,
154228753Smm	23, 24, 25,  0,  0,  0,  0,  0, /* 50 - 5F */
155228753Smm	 0, 26, 27, 28, 29, 30, 31, 32,
156228753Smm	33, 34, 35, 36, 37, 38, 39, 40, /* 60 - 6F */
157228753Smm	41, 42, 43, 44, 45, 46, 47, 48,
158228753Smm	49, 50, 51,  0,  0,  0,  0,  0, /* 70 - 7F */
159228753Smm};
160228753Smm
161228753Smmstatic ssize_t
162228753Smmget_line(const unsigned char *b, ssize_t avail, ssize_t *nlsize)
163228753Smm{
164228753Smm	ssize_t len;
165228753Smm
166228753Smm	len = 0;
167228753Smm	while (len < avail) {
168228753Smm		switch (ascii[*b]) {
169228753Smm		case 0:	/* Non-ascii character or control character. */
170228753Smm			if (nlsize != NULL)
171228753Smm				*nlsize = 0;
172228753Smm			return (-1);
173228753Smm		case '\r':
174228753Smm			if (avail-len > 1 && b[1] == '\n') {
175228753Smm				if (nlsize != NULL)
176228753Smm					*nlsize = 2;
177228753Smm				return (len+2);
178228753Smm			}
179228753Smm			/* FALL THROUGH */
180228753Smm		case '\n':
181228753Smm			if (nlsize != NULL)
182228753Smm				*nlsize = 1;
183228753Smm			return (len+1);
184228753Smm		case 1:
185228753Smm			b++;
186228753Smm			len++;
187228753Smm			break;
188228753Smm		}
189228753Smm	}
190228753Smm	if (nlsize != NULL)
191228753Smm		*nlsize = 0;
192228753Smm	return (avail);
193228753Smm}
194228753Smm
195228753Smmstatic ssize_t
196228753Smmbid_get_line(struct archive_read_filter *filter,
197228753Smm    const unsigned char **b, ssize_t *avail, ssize_t *ravail, ssize_t *nl)
198228753Smm{
199228753Smm	ssize_t len;
200228753Smm	int quit;
201228753Smm
202228753Smm	quit = 0;
203228753Smm	if (*avail == 0) {
204228753Smm		*nl = 0;
205228753Smm		len = 0;
206228753Smm	} else
207228753Smm		len = get_line(*b, *avail, nl);
208228753Smm	/*
209228753Smm	 * Read bytes more while it does not reach the end of line.
210228753Smm	 */
211228753Smm	while (*nl == 0 && len == *avail && !quit) {
212228753Smm		ssize_t diff = *ravail - *avail;
213228753Smm
214228753Smm		*b = __archive_read_filter_ahead(filter, 160 + *ravail, avail);
215228753Smm		if (*b == NULL) {
216228753Smm			if (*ravail >= *avail)
217228753Smm				return (0);
218228753Smm			/* Reading bytes reaches the end of file. */
219228753Smm			*b = __archive_read_filter_ahead(filter, *avail, avail);
220228753Smm			quit = 1;
221228753Smm		}
222228753Smm		*ravail = *avail;
223228753Smm		*b += diff;
224228753Smm		*avail -= diff;
225228753Smm		len = get_line(*b, *avail, nl);
226228753Smm	}
227228753Smm	return (len);
228228753Smm}
229228753Smm
230228753Smm#define UUDECODE(c) (((c) - 0x20) & 0x3f)
231228753Smm
232228753Smmstatic int
233228753Smmuudecode_bidder_bid(struct archive_read_filter_bidder *self,
234228753Smm    struct archive_read_filter *filter)
235228753Smm{
236228753Smm	const unsigned char *b;
237228753Smm	ssize_t avail, ravail;
238228753Smm	ssize_t len, nl;
239228753Smm	int l;
240228753Smm	int firstline;
241228753Smm
242228753Smm	(void)self; /* UNUSED */
243228753Smm
244228753Smm	b = __archive_read_filter_ahead(filter, 1, &avail);
245228753Smm	if (b == NULL)
246228753Smm		return (0);
247228753Smm
248228753Smm	firstline = 20;
249228753Smm	ravail = avail;
250228753Smm	for (;;) {
251228753Smm		len = bid_get_line(filter, &b, &avail, &ravail, &nl);
252228753Smm		if (len < 0 || nl == 0)
253228753Smm			return (0);/* Binary data. */
254228753Smm		if (memcmp(b, "begin ", 6) == 0 && len - nl >= 11)
255228753Smm			l = 6;
256228753Smm		else if (memcmp(b, "begin-base64 ", 13) == 0 && len - nl >= 18)
257228753Smm			l = 13;
258228753Smm		else
259228753Smm			l = 0;
260228753Smm
261228753Smm		if (l > 0 && (b[l] < '0' || b[l] > '7' ||
262228753Smm		    b[l+1] < '0' || b[l+1] > '7' ||
263228753Smm		    b[l+2] < '0' || b[l+2] > '7' || b[l+3] != ' '))
264228753Smm			l = 0;
265228753Smm
266228753Smm		b += len;
267228753Smm		avail -= len;
268228753Smm		if (l)
269228753Smm			break;
270228753Smm		firstline = 0;
271228753Smm	}
272228753Smm	if (!avail)
273228753Smm		return (0);
274228753Smm	len = bid_get_line(filter, &b, &avail, &ravail, &nl);
275228753Smm	if (len < 0 || nl == 0)
276228753Smm		return (0);/* There are non-ascii characters. */
277228753Smm	avail -= len;
278228753Smm
279228753Smm	if (l == 6) {
280228753Smm		if (!uuchar[*b])
281228753Smm			return (0);
282228753Smm		/* Get a length of decoded bytes. */
283228753Smm		l = UUDECODE(*b++); len--;
284228753Smm		if (l > 45)
285228753Smm			/* Normally, maximum length is 45(character 'M'). */
286228753Smm			return (0);
287228753Smm		while (l && len-nl > 0) {
288228753Smm			if (l > 0) {
289228753Smm				if (!uuchar[*b++])
290228753Smm					return (0);
291228753Smm				if (!uuchar[*b++])
292228753Smm					return (0);
293228753Smm				len -= 2;
294228753Smm				--l;
295228753Smm			}
296228753Smm			if (l > 0) {
297228753Smm				if (!uuchar[*b++])
298228753Smm					return (0);
299228753Smm				--len;
300228753Smm				--l;
301228753Smm			}
302228753Smm			if (l > 0) {
303228753Smm				if (!uuchar[*b++])
304228753Smm					return (0);
305228753Smm				--len;
306228753Smm				--l;
307228753Smm			}
308228753Smm		}
309228753Smm		if (len-nl < 0)
310228753Smm			return (0);
311228753Smm		if (len-nl == 1 &&
312228753Smm		    (uuchar[*b] ||		 /* Check sum. */
313228753Smm		     (*b >= 'a' && *b <= 'z'))) {/* Padding data(MINIX). */
314228753Smm			++b;
315228753Smm			--len;
316228753Smm		}
317228753Smm		b += nl;
318228753Smm		if (avail && uuchar[*b])
319228753Smm			return (firstline+30);
320228753Smm	}
321228753Smm	if (l == 13) {
322228753Smm		while (len-nl > 0) {
323228753Smm			if (!base64[*b++])
324228753Smm				return (0);
325228753Smm			--len;
326228753Smm		}
327228753Smm		b += nl;
328228753Smm
329228753Smm		if (avail >= 5 && memcmp(b, "====\n", 5) == 0)
330228753Smm			return (firstline+40);
331228753Smm		if (avail >= 6 && memcmp(b, "====\r\n", 6) == 0)
332228753Smm			return (firstline+40);
333228753Smm		if (avail > 0 && base64[*b])
334228753Smm			return (firstline+30);
335228753Smm	}
336228753Smm
337228753Smm	return (0);
338228753Smm}
339228753Smm
340228753Smmstatic int
341228753Smmuudecode_bidder_init(struct archive_read_filter *self)
342228753Smm{
343228753Smm	struct uudecode   *uudecode;
344228753Smm	void *out_buff;
345228753Smm	void *in_buff;
346228753Smm
347228753Smm	self->code = ARCHIVE_COMPRESSION_UU;
348228753Smm	self->name = "uu";
349228753Smm	self->read = uudecode_filter_read;
350228753Smm	self->skip = NULL; /* not supported */
351228753Smm	self->close = uudecode_filter_close;
352228753Smm
353228753Smm	uudecode = (struct uudecode *)calloc(sizeof(*uudecode), 1);
354228753Smm	out_buff = malloc(OUT_BUFF_SIZE);
355228753Smm	in_buff = malloc(IN_BUFF_SIZE);
356228753Smm	if (uudecode == NULL || out_buff == NULL || in_buff == NULL) {
357228753Smm		archive_set_error(&self->archive->archive, ENOMEM,
358228753Smm		    "Can't allocate data for uudecode");
359228753Smm		free(uudecode);
360228753Smm		free(out_buff);
361228753Smm		free(in_buff);
362228753Smm		return (ARCHIVE_FATAL);
363228753Smm	}
364228753Smm
365228753Smm	self->data = uudecode;
366228753Smm	uudecode->in_buff = in_buff;
367228753Smm	uudecode->in_cnt = 0;
368228753Smm	uudecode->in_allocated = IN_BUFF_SIZE;
369228753Smm	uudecode->out_buff = out_buff;
370228753Smm	uudecode->state = ST_FIND_HEAD;
371228753Smm
372228753Smm	return (ARCHIVE_OK);
373228753Smm}
374228753Smm
375228753Smmstatic int
376228753Smmensure_in_buff_size(struct archive_read_filter *self,
377228753Smm    struct uudecode *uudecode, size_t size)
378228753Smm{
379228753Smm
380228753Smm	if (size > uudecode->in_allocated) {
381228753Smm		unsigned char *ptr;
382228753Smm		size_t newsize;
383228753Smm
384228753Smm		/*
385228753Smm		 * Calculate a new buffer size for in_buff.
386228753Smm		 * Increase its value until it has enough size we need.
387228753Smm		 */
388228753Smm		newsize = uudecode->in_allocated;
389228753Smm		do {
390228753Smm			if (newsize < IN_BUFF_SIZE*32)
391228753Smm				newsize <<= 1;
392228753Smm			else
393228753Smm				newsize += IN_BUFF_SIZE;
394228753Smm		} while (size > newsize);
395228753Smm		ptr = malloc(newsize);
396228753Smm		if (ptr == NULL ||
397228753Smm		    newsize < uudecode->in_allocated) {
398228753Smm			free(ptr);
399228753Smm			archive_set_error(&self->archive->archive,
400228753Smm			    ENOMEM,
401228753Smm    			    "Can't allocate data for uudecode");
402228753Smm			return (ARCHIVE_FATAL);
403228753Smm		}
404228753Smm		if (uudecode->in_cnt)
405228753Smm			memmove(ptr, uudecode->in_buff,
406228753Smm			    uudecode->in_cnt);
407228753Smm		free(uudecode->in_buff);
408228753Smm		uudecode->in_buff = ptr;
409228753Smm		uudecode->in_allocated = newsize;
410228753Smm	}
411228753Smm	return (ARCHIVE_OK);
412228753Smm}
413228753Smm
414228753Smmstatic ssize_t
415228753Smmuudecode_filter_read(struct archive_read_filter *self, const void **buff)
416228753Smm{
417228753Smm	struct uudecode *uudecode;
418228753Smm	const unsigned char *b, *d;
419228753Smm	unsigned char *out;
420228753Smm	ssize_t avail_in, ravail;
421228753Smm	ssize_t used;
422228753Smm	ssize_t total;
423228753Smm	ssize_t len, llen, nl;
424228753Smm
425228753Smm	uudecode = (struct uudecode *)self->data;
426228753Smm
427228753Smmread_more:
428228753Smm	d = __archive_read_filter_ahead(self->upstream, 1, &avail_in);
429228753Smm	if (d == NULL && avail_in < 0)
430228753Smm		return (ARCHIVE_FATAL);
431228753Smm	/* Quiet a code analyzer; make sure avail_in must be zero
432228753Smm	 * when d is NULL. */
433228753Smm	if (d == NULL)
434228753Smm		avail_in = 0;
435228753Smm	used = 0;
436228753Smm	total = 0;
437228753Smm	out = uudecode->out_buff;
438228753Smm	ravail = avail_in;
439228753Smm	if (uudecode->in_cnt) {
440228753Smm		/*
441228753Smm		 * If there is remaining data which is saved by
442228753Smm		 * previous calling, use it first.
443228753Smm		 */
444228753Smm		if (ensure_in_buff_size(self, uudecode,
445228753Smm		    avail_in + uudecode->in_cnt) != ARCHIVE_OK)
446228753Smm			return (ARCHIVE_FATAL);
447228753Smm		memcpy(uudecode->in_buff + uudecode->in_cnt,
448228753Smm		    d, avail_in);
449228753Smm		d = uudecode->in_buff;
450228753Smm		avail_in += uudecode->in_cnt;
451228753Smm		uudecode->in_cnt = 0;
452228753Smm	}
453228753Smm	for (;used < avail_in; d += llen, used += llen) {
454228753Smm		int l, body;
455228753Smm
456228753Smm		b = d;
457228753Smm		len = get_line(b, avail_in - used, &nl);
458228753Smm		if (len < 0) {
459228753Smm			/* Non-ascii character is found. */
460228753Smm			archive_set_error(&self->archive->archive,
461228753Smm			    ARCHIVE_ERRNO_MISC,
462228753Smm			    "Insufficient compressed data");
463228753Smm			return (ARCHIVE_FATAL);
464228753Smm		}
465228753Smm		llen = len;
466228753Smm		if (nl == 0) {
467228753Smm			/*
468228753Smm			 * Save remaining data which does not contain
469228753Smm			 * NL('\n','\r').
470228753Smm			 */
471228753Smm			if (ensure_in_buff_size(self, uudecode, len)
472228753Smm			    != ARCHIVE_OK)
473228753Smm				return (ARCHIVE_FATAL);
474228753Smm			if (uudecode->in_buff != b)
475228753Smm				memmove(uudecode->in_buff, b, len);
476228753Smm			uudecode->in_cnt = len;
477228753Smm			if (total == 0) {
478228753Smm				/* Do not return 0; it means end-of-file.
479228753Smm				 * We should try to read bytes more. */
480228753Smm				__archive_read_filter_consume(
481228753Smm				    self->upstream, ravail);
482228753Smm				goto read_more;
483228753Smm			}
484228753Smm			break;
485228753Smm		}
486228753Smm		if (total + len * 2 > OUT_BUFF_SIZE)
487228753Smm			break;
488228753Smm		switch (uudecode->state) {
489228753Smm		default:
490228753Smm		case ST_FIND_HEAD:
491228753Smm			if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0)
492228753Smm				l = 6;
493228753Smm			else if (len - nl >= 18 &&
494228753Smm			    memcmp(b, "begin-base64 ", 13) == 0)
495228753Smm				l = 13;
496228753Smm			else
497228753Smm				l = 0;
498228753Smm			if (l != 0 && b[l] >= '0' && b[l] <= '7' &&
499228753Smm			    b[l+1] >= '0' && b[l+1] <= '7' &&
500228753Smm			    b[l+2] >= '0' && b[l+2] <= '7' && b[l+3] == ' ') {
501228753Smm				if (l == 6)
502228753Smm					uudecode->state = ST_READ_UU;
503228753Smm				else
504228753Smm					uudecode->state = ST_READ_BASE64;
505228753Smm			}
506228753Smm			break;
507228753Smm		case ST_READ_UU:
508228753Smm			body = len - nl;
509228753Smm			if (!uuchar[*b] || body <= 0) {
510228753Smm				archive_set_error(&self->archive->archive,
511228753Smm				    ARCHIVE_ERRNO_MISC,
512228753Smm				    "Insufficient compressed data");
513228753Smm				return (ARCHIVE_FATAL);
514228753Smm			}
515228753Smm			/* Get length of undecoded bytes of curent line. */
516228753Smm			l = UUDECODE(*b++);
517228753Smm			body--;
518228753Smm			if (l > body) {
519228753Smm				archive_set_error(&self->archive->archive,
520228753Smm				    ARCHIVE_ERRNO_MISC,
521228753Smm				    "Insufficient compressed data");
522228753Smm				return (ARCHIVE_FATAL);
523228753Smm			}
524228753Smm			if (l == 0) {
525228753Smm				uudecode->state = ST_UUEND;
526228753Smm				break;
527228753Smm			}
528228753Smm			while (l > 0) {
529228753Smm				int n = 0;
530228753Smm
531228753Smm				if (l > 0) {
532228753Smm					if (!uuchar[b[0]] || !uuchar[b[1]])
533228753Smm						break;
534228753Smm					n = UUDECODE(*b++) << 18;
535228753Smm					n |= UUDECODE(*b++) << 12;
536228753Smm					*out++ = n >> 16; total++;
537228753Smm					--l;
538228753Smm				}
539228753Smm				if (l > 0) {
540228753Smm					if (!uuchar[b[0]])
541228753Smm						break;
542228753Smm					n |= UUDECODE(*b++) << 6;
543228753Smm					*out++ = (n >> 8) & 0xFF; total++;
544228753Smm					--l;
545228753Smm				}
546228753Smm				if (l > 0) {
547228753Smm					if (!uuchar[b[0]])
548228753Smm						break;
549228753Smm					n |= UUDECODE(*b++);
550228753Smm					*out++ = n & 0xFF; total++;
551228753Smm					--l;
552228753Smm				}
553228753Smm			}
554228753Smm			if (l) {
555228753Smm				archive_set_error(&self->archive->archive,
556228753Smm				    ARCHIVE_ERRNO_MISC,
557228753Smm				    "Insufficient compressed data");
558228753Smm				return (ARCHIVE_FATAL);
559228753Smm			}
560228753Smm			break;
561228753Smm		case ST_UUEND:
562228753Smm			if (len - nl == 3 && memcmp(b, "end ", 3) == 0)
563228753Smm				uudecode->state = ST_FIND_HEAD;
564228753Smm			else {
565228753Smm				archive_set_error(&self->archive->archive,
566228753Smm				    ARCHIVE_ERRNO_MISC,
567228753Smm				    "Insufficient compressed data");
568228753Smm				return (ARCHIVE_FATAL);
569228753Smm			}
570228753Smm			break;
571228753Smm		case ST_READ_BASE64:
572228753Smm			l = len - nl;
573228753Smm			if (l >= 3 && b[0] == '=' && b[1] == '=' &&
574228753Smm			    b[2] == '=') {
575228753Smm				uudecode->state = ST_FIND_HEAD;
576228753Smm				break;
577228753Smm			}
578228753Smm			while (l > 0) {
579228753Smm				int n = 0;
580228753Smm
581228753Smm				if (l > 0) {
582228753Smm					if (!base64[b[0]] || !base64[b[1]])
583228753Smm						break;
584228753Smm					n = base64num[*b++] << 18;
585228753Smm					n |= base64num[*b++] << 12;
586228753Smm					*out++ = n >> 16; total++;
587228753Smm					l -= 2;
588228753Smm				}
589228753Smm				if (l > 0) {
590228753Smm					if (*b == '=')
591228753Smm						break;
592228753Smm					if (!base64[*b])
593228753Smm						break;
594228753Smm					n |= base64num[*b++] << 6;
595228753Smm					*out++ = (n >> 8) & 0xFF; total++;
596228753Smm					--l;
597228753Smm				}
598228753Smm				if (l > 0) {
599228753Smm					if (*b == '=')
600228753Smm						break;
601228753Smm					if (!base64[*b])
602228753Smm						break;
603228753Smm					n |= base64num[*b++];
604228753Smm					*out++ = n & 0xFF; total++;
605228753Smm					--l;
606228753Smm				}
607228753Smm			}
608228753Smm			if (l && *b != '=') {
609228753Smm				archive_set_error(&self->archive->archive,
610228753Smm				    ARCHIVE_ERRNO_MISC,
611228753Smm				    "Insufficient compressed data");
612228753Smm				return (ARCHIVE_FATAL);
613228753Smm			}
614228753Smm			break;
615228753Smm		}
616228753Smm	}
617228753Smm
618228753Smm	__archive_read_filter_consume(self->upstream, ravail);
619228753Smm
620228753Smm	*buff = uudecode->out_buff;
621228753Smm	uudecode->total += total;
622228753Smm	return (total);
623228753Smm}
624228753Smm
625228753Smmstatic int
626228753Smmuudecode_filter_close(struct archive_read_filter *self)
627228753Smm{
628228753Smm	struct uudecode *uudecode;
629228753Smm
630228753Smm	uudecode = (struct uudecode *)self->data;
631228753Smm	free(uudecode->in_buff);
632228753Smm	free(uudecode->out_buff);
633228753Smm	free(uudecode);
634228753Smm
635228753Smm	return (ARCHIVE_OK);
636228753Smm}
637228753Smm
638