1228753Smm/*-
2231200Smm * Copyright (c) 2009-2011 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"
27231200Smm__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
43231200Smm/* Maximum lookahead during bid phase */
44231200Smm#define UUENCODE_BID_MAX_READ 128*1024 /* in bytes */
45231200Smm
46228753Smmstruct uudecode {
47228753Smm	int64_t		 total;
48228753Smm	unsigned char	*in_buff;
49228753Smm#define IN_BUFF_SIZE	(1024)
50228753Smm	int		 in_cnt;
51228753Smm	size_t		 in_allocated;
52228753Smm	unsigned char	*out_buff;
53228753Smm#define OUT_BUFF_SIZE	(64 * 1024)
54228753Smm	int		 state;
55228753Smm#define ST_FIND_HEAD	0
56228753Smm#define ST_READ_UU	1
57228753Smm#define ST_UUEND	2
58228753Smm#define ST_READ_BASE64	3
59248616Smm#define ST_IGNORE	4
60228753Smm};
61228753Smm
62228753Smmstatic int	uudecode_bidder_bid(struct archive_read_filter_bidder *,
63228753Smm		    struct archive_read_filter *filter);
64228753Smmstatic int	uudecode_bidder_init(struct archive_read_filter *);
65228753Smm
66228753Smmstatic ssize_t	uudecode_filter_read(struct archive_read_filter *,
67228753Smm		    const void **);
68228753Smmstatic int	uudecode_filter_close(struct archive_read_filter *);
69228753Smm
70231200Smm#if ARCHIVE_VERSION_NUMBER < 4000000
71231200Smm/* Deprecated; remove in libarchive 4.0 */
72228753Smmint
73231200Smmarchive_read_support_compression_uu(struct archive *a)
74228753Smm{
75231200Smm	return archive_read_support_filter_uu(a);
76231200Smm}
77231200Smm#endif
78231200Smm
79231200Smmint
80231200Smmarchive_read_support_filter_uu(struct archive *_a)
81231200Smm{
82228753Smm	struct archive_read *a = (struct archive_read *)_a;
83228753Smm	struct archive_read_filter_bidder *bidder;
84228753Smm
85231200Smm	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
86231200Smm	    ARCHIVE_STATE_NEW, "archive_read_support_filter_uu");
87231200Smm
88231200Smm	if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK)
89228753Smm		return (ARCHIVE_FATAL);
90228753Smm
91228753Smm	bidder->data = NULL;
92248616Smm	bidder->name = "uu";
93228753Smm	bidder->bid = uudecode_bidder_bid;
94228753Smm	bidder->init = uudecode_bidder_init;
95228753Smm	bidder->options = NULL;
96228753Smm	bidder->free = NULL;
97228753Smm	return (ARCHIVE_OK);
98228753Smm}
99228753Smm
100228753Smmstatic const unsigned char ascii[256] = {
101228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\n', 0, 0, '\r', 0, 0, /* 00 - 0F */
102228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
103228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
104228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */
105228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
106228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */
107228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
108228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */
109228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
110228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
111228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
112228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
113228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
114228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
115228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
116228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
117228753Smm};
118228753Smm
119228753Smmstatic const unsigned char uuchar[256] = {
120228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
121228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
122228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
123228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */
124228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
125228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */
126228753Smm	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F */
127228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 70 - 7F */
128228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
129228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
130228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
131228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
132228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
133228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
134228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
135228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
136228753Smm};
137228753Smm
138228753Smmstatic const unsigned char base64[256] = {
139228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
140228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
141228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, /* 20 - 2F */
142228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, /* 30 - 3F */
143228753Smm	0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
144228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 50 - 5F */
145228753Smm	0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
146228753Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 70 - 7F */
147228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
148228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
149228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
150228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
151228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
152228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
153228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
154228753Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
155228753Smm};
156228753Smm
157228753Smmstatic const int base64num[128] = {
158228753Smm	 0,  0,  0,  0,  0,  0,  0,  0,
159228753Smm	 0,  0,  0,  0,  0,  0,  0,  0, /* 00 - 0F */
160228753Smm	 0,  0,  0,  0,  0,  0,  0,  0,
161228753Smm	 0,  0,  0,  0,  0,  0,  0,  0, /* 10 - 1F */
162228753Smm	 0,  0,  0,  0,  0,  0,  0,  0,
163228753Smm	 0,  0,  0, 62,  0,  0,  0, 63, /* 20 - 2F */
164228753Smm	52, 53, 54, 55, 56, 57, 58, 59,
165228753Smm	60, 61,  0,  0,  0,  0,  0,  0, /* 30 - 3F */
166228753Smm	 0,  0,  1,  2,  3,  4,  5,  6,
167228753Smm	 7,  8,  9, 10, 11, 12, 13, 14, /* 40 - 4F */
168228753Smm	15, 16, 17, 18, 19, 20, 21, 22,
169228753Smm	23, 24, 25,  0,  0,  0,  0,  0, /* 50 - 5F */
170228753Smm	 0, 26, 27, 28, 29, 30, 31, 32,
171228753Smm	33, 34, 35, 36, 37, 38, 39, 40, /* 60 - 6F */
172228753Smm	41, 42, 43, 44, 45, 46, 47, 48,
173228753Smm	49, 50, 51,  0,  0,  0,  0,  0, /* 70 - 7F */
174228753Smm};
175228753Smm
176228753Smmstatic ssize_t
177228753Smmget_line(const unsigned char *b, ssize_t avail, ssize_t *nlsize)
178228753Smm{
179228753Smm	ssize_t len;
180228753Smm
181228753Smm	len = 0;
182228753Smm	while (len < avail) {
183228753Smm		switch (ascii[*b]) {
184228753Smm		case 0:	/* Non-ascii character or control character. */
185228753Smm			if (nlsize != NULL)
186228753Smm				*nlsize = 0;
187228753Smm			return (-1);
188228753Smm		case '\r':
189228753Smm			if (avail-len > 1 && b[1] == '\n') {
190228753Smm				if (nlsize != NULL)
191228753Smm					*nlsize = 2;
192228753Smm				return (len+2);
193228753Smm			}
194228753Smm			/* FALL THROUGH */
195228753Smm		case '\n':
196228753Smm			if (nlsize != NULL)
197228753Smm				*nlsize = 1;
198228753Smm			return (len+1);
199228753Smm		case 1:
200228753Smm			b++;
201228753Smm			len++;
202228753Smm			break;
203228753Smm		}
204228753Smm	}
205228753Smm	if (nlsize != NULL)
206228753Smm		*nlsize = 0;
207228753Smm	return (avail);
208228753Smm}
209228753Smm
210228753Smmstatic ssize_t
211228753Smmbid_get_line(struct archive_read_filter *filter,
212231200Smm    const unsigned char **b, ssize_t *avail, ssize_t *ravail,
213231200Smm    ssize_t *nl, size_t* nbytes_read)
214228753Smm{
215228753Smm	ssize_t len;
216228753Smm	int quit;
217228753Smm
218228753Smm	quit = 0;
219228753Smm	if (*avail == 0) {
220228753Smm		*nl = 0;
221228753Smm		len = 0;
222228753Smm	} else
223228753Smm		len = get_line(*b, *avail, nl);
224231200Smm
225228753Smm	/*
226228753Smm	 * Read bytes more while it does not reach the end of line.
227228753Smm	 */
228231200Smm	while (*nl == 0 && len == *avail && !quit &&
229231200Smm	    *nbytes_read < UUENCODE_BID_MAX_READ) {
230228753Smm		ssize_t diff = *ravail - *avail;
231231200Smm		size_t nbytes_req = (*ravail+1023) & ~1023U;
232231200Smm		ssize_t tested;
233228753Smm
234231200Smm		/* Increase reading bytes if it is not enough to at least
235231200Smm		 * new two lines. */
236231200Smm		if (nbytes_req < (size_t)*ravail + 160)
237231200Smm			nbytes_req <<= 1;
238231200Smm
239231200Smm		*b = __archive_read_filter_ahead(filter, nbytes_req, avail);
240228753Smm		if (*b == NULL) {
241228753Smm			if (*ravail >= *avail)
242228753Smm				return (0);
243231200Smm			/* Reading bytes reaches the end of a stream. */
244228753Smm			*b = __archive_read_filter_ahead(filter, *avail, avail);
245228753Smm			quit = 1;
246228753Smm		}
247231200Smm		*nbytes_read = *avail;
248228753Smm		*ravail = *avail;
249228753Smm		*b += diff;
250228753Smm		*avail -= diff;
251231200Smm		tested = len;/* Skip some bytes we already determinated. */
252231200Smm		len = get_line(*b + tested, *avail - tested, nl);
253231200Smm		if (len >= 0)
254231200Smm			len += tested;
255228753Smm	}
256228753Smm	return (len);
257228753Smm}
258228753Smm
259228753Smm#define UUDECODE(c) (((c) - 0x20) & 0x3f)
260228753Smm
261228753Smmstatic int
262228753Smmuudecode_bidder_bid(struct archive_read_filter_bidder *self,
263228753Smm    struct archive_read_filter *filter)
264228753Smm{
265228753Smm	const unsigned char *b;
266228753Smm	ssize_t avail, ravail;
267228753Smm	ssize_t len, nl;
268228753Smm	int l;
269228753Smm	int firstline;
270231200Smm	size_t nbytes_read;
271228753Smm
272228753Smm	(void)self; /* UNUSED */
273228753Smm
274228753Smm	b = __archive_read_filter_ahead(filter, 1, &avail);
275228753Smm	if (b == NULL)
276228753Smm		return (0);
277228753Smm
278228753Smm	firstline = 20;
279228753Smm	ravail = avail;
280231200Smm	nbytes_read = avail;
281228753Smm	for (;;) {
282231200Smm		len = bid_get_line(filter, &b, &avail, &ravail, &nl, &nbytes_read);
283228753Smm		if (len < 0 || nl == 0)
284231200Smm			return (0); /* No match found. */
285231200Smm		if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0)
286228753Smm			l = 6;
287231200Smm		else if (len -nl >= 18 && memcmp(b, "begin-base64 ", 13) == 0)
288228753Smm			l = 13;
289228753Smm		else
290228753Smm			l = 0;
291228753Smm
292228753Smm		if (l > 0 && (b[l] < '0' || b[l] > '7' ||
293228753Smm		    b[l+1] < '0' || b[l+1] > '7' ||
294228753Smm		    b[l+2] < '0' || b[l+2] > '7' || b[l+3] != ' '))
295228753Smm			l = 0;
296228753Smm
297228753Smm		b += len;
298228753Smm		avail -= len;
299228753Smm		if (l)
300228753Smm			break;
301228753Smm		firstline = 0;
302231200Smm
303231200Smm		/* Do not read more than UUENCODE_BID_MAX_READ bytes */
304231200Smm		if (nbytes_read >= UUENCODE_BID_MAX_READ)
305231200Smm			return (0);
306228753Smm	}
307228753Smm	if (!avail)
308228753Smm		return (0);
309231200Smm	len = bid_get_line(filter, &b, &avail, &ravail, &nl, &nbytes_read);
310228753Smm	if (len < 0 || nl == 0)
311228753Smm		return (0);/* There are non-ascii characters. */
312228753Smm	avail -= len;
313228753Smm
314228753Smm	if (l == 6) {
315228753Smm		if (!uuchar[*b])
316228753Smm			return (0);
317228753Smm		/* Get a length of decoded bytes. */
318228753Smm		l = UUDECODE(*b++); len--;
319228753Smm		if (l > 45)
320228753Smm			/* Normally, maximum length is 45(character 'M'). */
321228753Smm			return (0);
322228753Smm		while (l && len-nl > 0) {
323228753Smm			if (l > 0) {
324228753Smm				if (!uuchar[*b++])
325228753Smm					return (0);
326228753Smm				if (!uuchar[*b++])
327228753Smm					return (0);
328228753Smm				len -= 2;
329228753Smm				--l;
330228753Smm			}
331228753Smm			if (l > 0) {
332228753Smm				if (!uuchar[*b++])
333228753Smm					return (0);
334228753Smm				--len;
335228753Smm				--l;
336228753Smm			}
337228753Smm			if (l > 0) {
338228753Smm				if (!uuchar[*b++])
339228753Smm					return (0);
340228753Smm				--len;
341228753Smm				--l;
342228753Smm			}
343228753Smm		}
344228753Smm		if (len-nl < 0)
345228753Smm			return (0);
346228753Smm		if (len-nl == 1 &&
347228753Smm		    (uuchar[*b] ||		 /* Check sum. */
348228753Smm		     (*b >= 'a' && *b <= 'z'))) {/* Padding data(MINIX). */
349228753Smm			++b;
350228753Smm			--len;
351228753Smm		}
352228753Smm		b += nl;
353228753Smm		if (avail && uuchar[*b])
354228753Smm			return (firstline+30);
355228753Smm	}
356228753Smm	if (l == 13) {
357228753Smm		while (len-nl > 0) {
358228753Smm			if (!base64[*b++])
359228753Smm				return (0);
360228753Smm			--len;
361228753Smm		}
362228753Smm		b += nl;
363228753Smm
364228753Smm		if (avail >= 5 && memcmp(b, "====\n", 5) == 0)
365228753Smm			return (firstline+40);
366228753Smm		if (avail >= 6 && memcmp(b, "====\r\n", 6) == 0)
367228753Smm			return (firstline+40);
368228753Smm		if (avail > 0 && base64[*b])
369228753Smm			return (firstline+30);
370228753Smm	}
371228753Smm
372228753Smm	return (0);
373228753Smm}
374228753Smm
375228753Smmstatic int
376228753Smmuudecode_bidder_init(struct archive_read_filter *self)
377228753Smm{
378228753Smm	struct uudecode   *uudecode;
379228753Smm	void *out_buff;
380228753Smm	void *in_buff;
381228753Smm
382248616Smm	self->code = ARCHIVE_FILTER_UU;
383228753Smm	self->name = "uu";
384228753Smm	self->read = uudecode_filter_read;
385228753Smm	self->skip = NULL; /* not supported */
386228753Smm	self->close = uudecode_filter_close;
387228753Smm
388228753Smm	uudecode = (struct uudecode *)calloc(sizeof(*uudecode), 1);
389228753Smm	out_buff = malloc(OUT_BUFF_SIZE);
390228753Smm	in_buff = malloc(IN_BUFF_SIZE);
391228753Smm	if (uudecode == NULL || out_buff == NULL || in_buff == NULL) {
392228753Smm		archive_set_error(&self->archive->archive, ENOMEM,
393228753Smm		    "Can't allocate data for uudecode");
394228753Smm		free(uudecode);
395228753Smm		free(out_buff);
396228753Smm		free(in_buff);
397228753Smm		return (ARCHIVE_FATAL);
398228753Smm	}
399228753Smm
400228753Smm	self->data = uudecode;
401228753Smm	uudecode->in_buff = in_buff;
402228753Smm	uudecode->in_cnt = 0;
403228753Smm	uudecode->in_allocated = IN_BUFF_SIZE;
404228753Smm	uudecode->out_buff = out_buff;
405228753Smm	uudecode->state = ST_FIND_HEAD;
406228753Smm
407228753Smm	return (ARCHIVE_OK);
408228753Smm}
409228753Smm
410228753Smmstatic int
411228753Smmensure_in_buff_size(struct archive_read_filter *self,
412228753Smm    struct uudecode *uudecode, size_t size)
413228753Smm{
414228753Smm
415228753Smm	if (size > uudecode->in_allocated) {
416228753Smm		unsigned char *ptr;
417228753Smm		size_t newsize;
418228753Smm
419228753Smm		/*
420228753Smm		 * Calculate a new buffer size for in_buff.
421228753Smm		 * Increase its value until it has enough size we need.
422228753Smm		 */
423228753Smm		newsize = uudecode->in_allocated;
424228753Smm		do {
425228753Smm			if (newsize < IN_BUFF_SIZE*32)
426228753Smm				newsize <<= 1;
427228753Smm			else
428228753Smm				newsize += IN_BUFF_SIZE;
429228753Smm		} while (size > newsize);
430231200Smm		/* Allocate the new buffer. */
431228753Smm		ptr = malloc(newsize);
432231200Smm		if (ptr == NULL) {
433228753Smm			free(ptr);
434228753Smm			archive_set_error(&self->archive->archive,
435228753Smm			    ENOMEM,
436228753Smm    			    "Can't allocate data for uudecode");
437228753Smm			return (ARCHIVE_FATAL);
438228753Smm		}
439231200Smm		/* Move the remaining data in in_buff into the new buffer. */
440228753Smm		if (uudecode->in_cnt)
441231200Smm			memmove(ptr, uudecode->in_buff, uudecode->in_cnt);
442231200Smm		/* Replace in_buff with the new buffer. */
443228753Smm		free(uudecode->in_buff);
444228753Smm		uudecode->in_buff = ptr;
445228753Smm		uudecode->in_allocated = newsize;
446228753Smm	}
447228753Smm	return (ARCHIVE_OK);
448228753Smm}
449228753Smm
450228753Smmstatic ssize_t
451228753Smmuudecode_filter_read(struct archive_read_filter *self, const void **buff)
452228753Smm{
453228753Smm	struct uudecode *uudecode;
454228753Smm	const unsigned char *b, *d;
455228753Smm	unsigned char *out;
456228753Smm	ssize_t avail_in, ravail;
457228753Smm	ssize_t used;
458228753Smm	ssize_t total;
459228753Smm	ssize_t len, llen, nl;
460228753Smm
461228753Smm	uudecode = (struct uudecode *)self->data;
462228753Smm
463228753Smmread_more:
464228753Smm	d = __archive_read_filter_ahead(self->upstream, 1, &avail_in);
465228753Smm	if (d == NULL && avail_in < 0)
466228753Smm		return (ARCHIVE_FATAL);
467228753Smm	/* Quiet a code analyzer; make sure avail_in must be zero
468228753Smm	 * when d is NULL. */
469228753Smm	if (d == NULL)
470228753Smm		avail_in = 0;
471228753Smm	used = 0;
472228753Smm	total = 0;
473228753Smm	out = uudecode->out_buff;
474228753Smm	ravail = avail_in;
475248616Smm	if (uudecode->state == ST_IGNORE) {
476248616Smm		used = avail_in;
477248616Smm		goto finish;
478248616Smm	}
479228753Smm	if (uudecode->in_cnt) {
480228753Smm		/*
481228753Smm		 * If there is remaining data which is saved by
482228753Smm		 * previous calling, use it first.
483228753Smm		 */
484228753Smm		if (ensure_in_buff_size(self, uudecode,
485228753Smm		    avail_in + uudecode->in_cnt) != ARCHIVE_OK)
486228753Smm			return (ARCHIVE_FATAL);
487228753Smm		memcpy(uudecode->in_buff + uudecode->in_cnt,
488228753Smm		    d, avail_in);
489228753Smm		d = uudecode->in_buff;
490228753Smm		avail_in += uudecode->in_cnt;
491228753Smm		uudecode->in_cnt = 0;
492228753Smm	}
493228753Smm	for (;used < avail_in; d += llen, used += llen) {
494248616Smm		int64_t l, body;
495228753Smm
496228753Smm		b = d;
497228753Smm		len = get_line(b, avail_in - used, &nl);
498228753Smm		if (len < 0) {
499228753Smm			/* Non-ascii character is found. */
500248616Smm			if (uudecode->state == ST_FIND_HEAD &&
501248616Smm			    (uudecode->total > 0 || total > 0)) {
502248616Smm				uudecode->state = ST_IGNORE;
503248616Smm				used = avail_in;
504248616Smm				goto finish;
505248616Smm			}
506228753Smm			archive_set_error(&self->archive->archive,
507228753Smm			    ARCHIVE_ERRNO_MISC,
508228753Smm			    "Insufficient compressed data");
509228753Smm			return (ARCHIVE_FATAL);
510228753Smm		}
511228753Smm		llen = len;
512228753Smm		if (nl == 0) {
513228753Smm			/*
514228753Smm			 * Save remaining data which does not contain
515228753Smm			 * NL('\n','\r').
516228753Smm			 */
517228753Smm			if (ensure_in_buff_size(self, uudecode, len)
518228753Smm			    != ARCHIVE_OK)
519228753Smm				return (ARCHIVE_FATAL);
520228753Smm			if (uudecode->in_buff != b)
521228753Smm				memmove(uudecode->in_buff, b, len);
522248616Smm			uudecode->in_cnt = (int)len;
523228753Smm			if (total == 0) {
524228753Smm				/* Do not return 0; it means end-of-file.
525228753Smm				 * We should try to read bytes more. */
526228753Smm				__archive_read_filter_consume(
527228753Smm				    self->upstream, ravail);
528228753Smm				goto read_more;
529228753Smm			}
530228753Smm			break;
531228753Smm		}
532228753Smm		switch (uudecode->state) {
533228753Smm		default:
534228753Smm		case ST_FIND_HEAD:
535231200Smm			/* Do not read more than UUENCODE_BID_MAX_READ bytes */
536231200Smm			if (total + len >= UUENCODE_BID_MAX_READ) {
537231200Smm				archive_set_error(&self->archive->archive,
538231200Smm				    ARCHIVE_ERRNO_FILE_FORMAT,
539231200Smm				    "Invalid format data");
540231200Smm				return (ARCHIVE_FATAL);
541231200Smm			}
542228753Smm			if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0)
543228753Smm				l = 6;
544228753Smm			else if (len - nl >= 18 &&
545228753Smm			    memcmp(b, "begin-base64 ", 13) == 0)
546228753Smm				l = 13;
547228753Smm			else
548228753Smm				l = 0;
549228753Smm			if (l != 0 && b[l] >= '0' && b[l] <= '7' &&
550228753Smm			    b[l+1] >= '0' && b[l+1] <= '7' &&
551228753Smm			    b[l+2] >= '0' && b[l+2] <= '7' && b[l+3] == ' ') {
552228753Smm				if (l == 6)
553228753Smm					uudecode->state = ST_READ_UU;
554228753Smm				else
555228753Smm					uudecode->state = ST_READ_BASE64;
556228753Smm			}
557228753Smm			break;
558228753Smm		case ST_READ_UU:
559231200Smm			if (total + len * 2 > OUT_BUFF_SIZE)
560248616Smm				goto finish;
561228753Smm			body = len - nl;
562228753Smm			if (!uuchar[*b] || body <= 0) {
563228753Smm				archive_set_error(&self->archive->archive,
564228753Smm				    ARCHIVE_ERRNO_MISC,
565228753Smm				    "Insufficient compressed data");
566228753Smm				return (ARCHIVE_FATAL);
567228753Smm			}
568228753Smm			/* Get length of undecoded bytes of curent line. */
569228753Smm			l = UUDECODE(*b++);
570228753Smm			body--;
571228753Smm			if (l > body) {
572228753Smm				archive_set_error(&self->archive->archive,
573228753Smm				    ARCHIVE_ERRNO_MISC,
574228753Smm				    "Insufficient compressed data");
575228753Smm				return (ARCHIVE_FATAL);
576228753Smm			}
577228753Smm			if (l == 0) {
578228753Smm				uudecode->state = ST_UUEND;
579228753Smm				break;
580228753Smm			}
581228753Smm			while (l > 0) {
582228753Smm				int n = 0;
583228753Smm
584228753Smm				if (l > 0) {
585228753Smm					if (!uuchar[b[0]] || !uuchar[b[1]])
586228753Smm						break;
587228753Smm					n = UUDECODE(*b++) << 18;
588228753Smm					n |= UUDECODE(*b++) << 12;
589228753Smm					*out++ = n >> 16; total++;
590228753Smm					--l;
591228753Smm				}
592228753Smm				if (l > 0) {
593228753Smm					if (!uuchar[b[0]])
594228753Smm						break;
595228753Smm					n |= UUDECODE(*b++) << 6;
596228753Smm					*out++ = (n >> 8) & 0xFF; total++;
597228753Smm					--l;
598228753Smm				}
599228753Smm				if (l > 0) {
600228753Smm					if (!uuchar[b[0]])
601228753Smm						break;
602228753Smm					n |= UUDECODE(*b++);
603228753Smm					*out++ = n & 0xFF; total++;
604228753Smm					--l;
605228753Smm				}
606228753Smm			}
607228753Smm			if (l) {
608228753Smm				archive_set_error(&self->archive->archive,
609228753Smm				    ARCHIVE_ERRNO_MISC,
610228753Smm				    "Insufficient compressed data");
611228753Smm				return (ARCHIVE_FATAL);
612228753Smm			}
613228753Smm			break;
614228753Smm		case ST_UUEND:
615228753Smm			if (len - nl == 3 && memcmp(b, "end ", 3) == 0)
616228753Smm				uudecode->state = ST_FIND_HEAD;
617228753Smm			else {
618228753Smm				archive_set_error(&self->archive->archive,
619228753Smm				    ARCHIVE_ERRNO_MISC,
620228753Smm				    "Insufficient compressed data");
621228753Smm				return (ARCHIVE_FATAL);
622228753Smm			}
623228753Smm			break;
624228753Smm		case ST_READ_BASE64:
625231200Smm			if (total + len * 2 > OUT_BUFF_SIZE)
626248616Smm				goto finish;
627228753Smm			l = len - nl;
628228753Smm			if (l >= 3 && b[0] == '=' && b[1] == '=' &&
629228753Smm			    b[2] == '=') {
630228753Smm				uudecode->state = ST_FIND_HEAD;
631228753Smm				break;
632228753Smm			}
633228753Smm			while (l > 0) {
634228753Smm				int n = 0;
635228753Smm
636228753Smm				if (l > 0) {
637228753Smm					if (!base64[b[0]] || !base64[b[1]])
638228753Smm						break;
639228753Smm					n = base64num[*b++] << 18;
640228753Smm					n |= base64num[*b++] << 12;
641228753Smm					*out++ = n >> 16; total++;
642228753Smm					l -= 2;
643228753Smm				}
644228753Smm				if (l > 0) {
645228753Smm					if (*b == '=')
646228753Smm						break;
647228753Smm					if (!base64[*b])
648228753Smm						break;
649228753Smm					n |= base64num[*b++] << 6;
650228753Smm					*out++ = (n >> 8) & 0xFF; total++;
651228753Smm					--l;
652228753Smm				}
653228753Smm				if (l > 0) {
654228753Smm					if (*b == '=')
655228753Smm						break;
656228753Smm					if (!base64[*b])
657228753Smm						break;
658228753Smm					n |= base64num[*b++];
659228753Smm					*out++ = n & 0xFF; total++;
660228753Smm					--l;
661228753Smm				}
662228753Smm			}
663228753Smm			if (l && *b != '=') {
664228753Smm				archive_set_error(&self->archive->archive,
665228753Smm				    ARCHIVE_ERRNO_MISC,
666228753Smm				    "Insufficient compressed data");
667228753Smm				return (ARCHIVE_FATAL);
668228753Smm			}
669228753Smm			break;
670228753Smm		}
671228753Smm	}
672248616Smmfinish:
673248616Smm	if (ravail < avail_in)
674248616Smm		used -= avail_in - ravail;
675248616Smm	__archive_read_filter_consume(self->upstream, used);
676228753Smm
677228753Smm	*buff = uudecode->out_buff;
678228753Smm	uudecode->total += total;
679228753Smm	return (total);
680228753Smm}
681228753Smm
682228753Smmstatic int
683228753Smmuudecode_filter_close(struct archive_read_filter *self)
684228753Smm{
685228753Smm	struct uudecode *uudecode;
686228753Smm
687228753Smm	uudecode = (struct uudecode *)self->data;
688228753Smm	free(uudecode->in_buff);
689228753Smm	free(uudecode->out_buff);
690228753Smm	free(uudecode);
691228753Smm
692228753Smm	return (ARCHIVE_OK);
693228753Smm}
694228753Smm
695