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) {
315311042Smm		/* "begin " */
316228753Smm		if (!uuchar[*b])
317228753Smm			return (0);
318228753Smm		/* Get a length of decoded bytes. */
319228753Smm		l = UUDECODE(*b++); len--;
320228753Smm		if (l > 45)
321228753Smm			/* Normally, maximum length is 45(character 'M'). */
322228753Smm			return (0);
323311042Smm		if (l > len - nl)
324311042Smm			return (0); /* Line too short. */
325311042Smm		while (l) {
326311042Smm			if (!uuchar[*b++])
327311042Smm				return (0);
328311042Smm			--len;
329311042Smm			--l;
330228753Smm		}
331228753Smm		if (len-nl == 1 &&
332228753Smm		    (uuchar[*b] ||		 /* Check sum. */
333228753Smm		     (*b >= 'a' && *b <= 'z'))) {/* Padding data(MINIX). */
334228753Smm			++b;
335228753Smm			--len;
336228753Smm		}
337228753Smm		b += nl;
338228753Smm		if (avail && uuchar[*b])
339228753Smm			return (firstline+30);
340311042Smm	} else if (l == 13) {
341311042Smm		/* "begin-base64 " */
342228753Smm		while (len-nl > 0) {
343228753Smm			if (!base64[*b++])
344228753Smm				return (0);
345228753Smm			--len;
346228753Smm		}
347228753Smm		b += nl;
348228753Smm
349228753Smm		if (avail >= 5 && memcmp(b, "====\n", 5) == 0)
350228753Smm			return (firstline+40);
351228753Smm		if (avail >= 6 && memcmp(b, "====\r\n", 6) == 0)
352228753Smm			return (firstline+40);
353228753Smm		if (avail > 0 && base64[*b])
354228753Smm			return (firstline+30);
355228753Smm	}
356228753Smm
357228753Smm	return (0);
358228753Smm}
359228753Smm
360228753Smmstatic int
361228753Smmuudecode_bidder_init(struct archive_read_filter *self)
362228753Smm{
363228753Smm	struct uudecode   *uudecode;
364228753Smm	void *out_buff;
365228753Smm	void *in_buff;
366228753Smm
367248616Smm	self->code = ARCHIVE_FILTER_UU;
368228753Smm	self->name = "uu";
369228753Smm	self->read = uudecode_filter_read;
370228753Smm	self->skip = NULL; /* not supported */
371228753Smm	self->close = uudecode_filter_close;
372228753Smm
373228753Smm	uudecode = (struct uudecode *)calloc(sizeof(*uudecode), 1);
374228753Smm	out_buff = malloc(OUT_BUFF_SIZE);
375228753Smm	in_buff = malloc(IN_BUFF_SIZE);
376228753Smm	if (uudecode == NULL || out_buff == NULL || in_buff == NULL) {
377228753Smm		archive_set_error(&self->archive->archive, ENOMEM,
378228753Smm		    "Can't allocate data for uudecode");
379228753Smm		free(uudecode);
380228753Smm		free(out_buff);
381228753Smm		free(in_buff);
382228753Smm		return (ARCHIVE_FATAL);
383228753Smm	}
384228753Smm
385228753Smm	self->data = uudecode;
386228753Smm	uudecode->in_buff = in_buff;
387228753Smm	uudecode->in_cnt = 0;
388228753Smm	uudecode->in_allocated = IN_BUFF_SIZE;
389228753Smm	uudecode->out_buff = out_buff;
390228753Smm	uudecode->state = ST_FIND_HEAD;
391228753Smm
392228753Smm	return (ARCHIVE_OK);
393228753Smm}
394228753Smm
395228753Smmstatic int
396228753Smmensure_in_buff_size(struct archive_read_filter *self,
397228753Smm    struct uudecode *uudecode, size_t size)
398228753Smm{
399228753Smm
400228753Smm	if (size > uudecode->in_allocated) {
401228753Smm		unsigned char *ptr;
402228753Smm		size_t newsize;
403228753Smm
404228753Smm		/*
405228753Smm		 * Calculate a new buffer size for in_buff.
406228753Smm		 * Increase its value until it has enough size we need.
407228753Smm		 */
408228753Smm		newsize = uudecode->in_allocated;
409228753Smm		do {
410228753Smm			if (newsize < IN_BUFF_SIZE*32)
411228753Smm				newsize <<= 1;
412228753Smm			else
413228753Smm				newsize += IN_BUFF_SIZE;
414228753Smm		} while (size > newsize);
415231200Smm		/* Allocate the new buffer. */
416228753Smm		ptr = malloc(newsize);
417231200Smm		if (ptr == NULL) {
418228753Smm			free(ptr);
419228753Smm			archive_set_error(&self->archive->archive,
420228753Smm			    ENOMEM,
421228753Smm    			    "Can't allocate data for uudecode");
422228753Smm			return (ARCHIVE_FATAL);
423228753Smm		}
424231200Smm		/* Move the remaining data in in_buff into the new buffer. */
425228753Smm		if (uudecode->in_cnt)
426231200Smm			memmove(ptr, uudecode->in_buff, uudecode->in_cnt);
427231200Smm		/* Replace in_buff with the new buffer. */
428228753Smm		free(uudecode->in_buff);
429228753Smm		uudecode->in_buff = ptr;
430228753Smm		uudecode->in_allocated = newsize;
431228753Smm	}
432228753Smm	return (ARCHIVE_OK);
433228753Smm}
434228753Smm
435228753Smmstatic ssize_t
436228753Smmuudecode_filter_read(struct archive_read_filter *self, const void **buff)
437228753Smm{
438228753Smm	struct uudecode *uudecode;
439228753Smm	const unsigned char *b, *d;
440228753Smm	unsigned char *out;
441228753Smm	ssize_t avail_in, ravail;
442228753Smm	ssize_t used;
443228753Smm	ssize_t total;
444228753Smm	ssize_t len, llen, nl;
445228753Smm
446228753Smm	uudecode = (struct uudecode *)self->data;
447228753Smm
448228753Smmread_more:
449228753Smm	d = __archive_read_filter_ahead(self->upstream, 1, &avail_in);
450228753Smm	if (d == NULL && avail_in < 0)
451228753Smm		return (ARCHIVE_FATAL);
452228753Smm	/* Quiet a code analyzer; make sure avail_in must be zero
453228753Smm	 * when d is NULL. */
454228753Smm	if (d == NULL)
455228753Smm		avail_in = 0;
456228753Smm	used = 0;
457228753Smm	total = 0;
458228753Smm	out = uudecode->out_buff;
459228753Smm	ravail = avail_in;
460248616Smm	if (uudecode->state == ST_IGNORE) {
461248616Smm		used = avail_in;
462248616Smm		goto finish;
463248616Smm	}
464228753Smm	if (uudecode->in_cnt) {
465228753Smm		/*
466228753Smm		 * If there is remaining data which is saved by
467228753Smm		 * previous calling, use it first.
468228753Smm		 */
469228753Smm		if (ensure_in_buff_size(self, uudecode,
470228753Smm		    avail_in + uudecode->in_cnt) != ARCHIVE_OK)
471228753Smm			return (ARCHIVE_FATAL);
472228753Smm		memcpy(uudecode->in_buff + uudecode->in_cnt,
473228753Smm		    d, avail_in);
474228753Smm		d = uudecode->in_buff;
475228753Smm		avail_in += uudecode->in_cnt;
476228753Smm		uudecode->in_cnt = 0;
477228753Smm	}
478228753Smm	for (;used < avail_in; d += llen, used += llen) {
479248616Smm		int64_t l, body;
480228753Smm
481228753Smm		b = d;
482228753Smm		len = get_line(b, avail_in - used, &nl);
483228753Smm		if (len < 0) {
484228753Smm			/* Non-ascii character is found. */
485248616Smm			if (uudecode->state == ST_FIND_HEAD &&
486248616Smm			    (uudecode->total > 0 || total > 0)) {
487248616Smm				uudecode->state = ST_IGNORE;
488248616Smm				used = avail_in;
489248616Smm				goto finish;
490248616Smm			}
491228753Smm			archive_set_error(&self->archive->archive,
492228753Smm			    ARCHIVE_ERRNO_MISC,
493228753Smm			    "Insufficient compressed data");
494228753Smm			return (ARCHIVE_FATAL);
495228753Smm		}
496228753Smm		llen = len;
497302001Smm		if ((nl == 0) && (uudecode->state != ST_UUEND)) {
498311042Smm			if (total == 0 && ravail <= 0) {
499311042Smm				/* There is nothing more to read, fail */
500311042Smm				archive_set_error(&self->archive->archive,
501311042Smm				    ARCHIVE_ERRNO_FILE_FORMAT,
502311042Smm				    "Missing format data");
503311042Smm				return (ARCHIVE_FATAL);
504311042Smm			}
505228753Smm			/*
506228753Smm			 * Save remaining data which does not contain
507228753Smm			 * NL('\n','\r').
508228753Smm			 */
509228753Smm			if (ensure_in_buff_size(self, uudecode, len)
510228753Smm			    != ARCHIVE_OK)
511228753Smm				return (ARCHIVE_FATAL);
512228753Smm			if (uudecode->in_buff != b)
513228753Smm				memmove(uudecode->in_buff, b, len);
514248616Smm			uudecode->in_cnt = (int)len;
515228753Smm			if (total == 0) {
516228753Smm				/* Do not return 0; it means end-of-file.
517228753Smm				 * We should try to read bytes more. */
518228753Smm				__archive_read_filter_consume(
519228753Smm				    self->upstream, ravail);
520228753Smm				goto read_more;
521228753Smm			}
522302001Smm			used += len;
523228753Smm			break;
524228753Smm		}
525228753Smm		switch (uudecode->state) {
526228753Smm		default:
527228753Smm		case ST_FIND_HEAD:
528231200Smm			/* Do not read more than UUENCODE_BID_MAX_READ bytes */
529231200Smm			if (total + len >= UUENCODE_BID_MAX_READ) {
530231200Smm				archive_set_error(&self->archive->archive,
531231200Smm				    ARCHIVE_ERRNO_FILE_FORMAT,
532231200Smm				    "Invalid format data");
533231200Smm				return (ARCHIVE_FATAL);
534231200Smm			}
535228753Smm			if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0)
536228753Smm				l = 6;
537228753Smm			else if (len - nl >= 18 &&
538228753Smm			    memcmp(b, "begin-base64 ", 13) == 0)
539228753Smm				l = 13;
540228753Smm			else
541228753Smm				l = 0;
542228753Smm			if (l != 0 && b[l] >= '0' && b[l] <= '7' &&
543228753Smm			    b[l+1] >= '0' && b[l+1] <= '7' &&
544228753Smm			    b[l+2] >= '0' && b[l+2] <= '7' && b[l+3] == ' ') {
545228753Smm				if (l == 6)
546228753Smm					uudecode->state = ST_READ_UU;
547228753Smm				else
548228753Smm					uudecode->state = ST_READ_BASE64;
549228753Smm			}
550228753Smm			break;
551228753Smm		case ST_READ_UU:
552231200Smm			if (total + len * 2 > OUT_BUFF_SIZE)
553248616Smm				goto finish;
554228753Smm			body = len - nl;
555228753Smm			if (!uuchar[*b] || body <= 0) {
556228753Smm				archive_set_error(&self->archive->archive,
557228753Smm				    ARCHIVE_ERRNO_MISC,
558228753Smm				    "Insufficient compressed data");
559228753Smm				return (ARCHIVE_FATAL);
560228753Smm			}
561311042Smm			/* Get length of undecoded bytes of current line. */
562228753Smm			l = UUDECODE(*b++);
563228753Smm			body--;
564228753Smm			if (l > body) {
565228753Smm				archive_set_error(&self->archive->archive,
566228753Smm				    ARCHIVE_ERRNO_MISC,
567228753Smm				    "Insufficient compressed data");
568228753Smm				return (ARCHIVE_FATAL);
569228753Smm			}
570228753Smm			if (l == 0) {
571228753Smm				uudecode->state = ST_UUEND;
572228753Smm				break;
573228753Smm			}
574228753Smm			while (l > 0) {
575228753Smm				int n = 0;
576228753Smm
577358090Smm				if (!uuchar[b[0]] || !uuchar[b[1]])
578358090Smm					break;
579358090Smm				n = UUDECODE(*b++) << 18;
580358090Smm				n |= UUDECODE(*b++) << 12;
581358090Smm				*out++ = n >> 16; total++;
582358090Smm				--l;
583358090Smm
584228753Smm				if (l > 0) {
585228753Smm					if (!uuchar[b[0]])
586228753Smm						break;
587228753Smm					n |= UUDECODE(*b++) << 6;
588228753Smm					*out++ = (n >> 8) & 0xFF; total++;
589228753Smm					--l;
590228753Smm				}
591228753Smm				if (l > 0) {
592228753Smm					if (!uuchar[b[0]])
593228753Smm						break;
594228753Smm					n |= UUDECODE(*b++);
595228753Smm					*out++ = n & 0xFF; total++;
596228753Smm					--l;
597228753Smm				}
598228753Smm			}
599228753Smm			if (l) {
600228753Smm				archive_set_error(&self->archive->archive,
601228753Smm				    ARCHIVE_ERRNO_MISC,
602228753Smm				    "Insufficient compressed data");
603228753Smm				return (ARCHIVE_FATAL);
604228753Smm			}
605228753Smm			break;
606228753Smm		case ST_UUEND:
607228753Smm			if (len - nl == 3 && memcmp(b, "end ", 3) == 0)
608228753Smm				uudecode->state = ST_FIND_HEAD;
609228753Smm			else {
610228753Smm				archive_set_error(&self->archive->archive,
611228753Smm				    ARCHIVE_ERRNO_MISC,
612228753Smm				    "Insufficient compressed data");
613228753Smm				return (ARCHIVE_FATAL);
614228753Smm			}
615228753Smm			break;
616228753Smm		case ST_READ_BASE64:
617231200Smm			if (total + len * 2 > OUT_BUFF_SIZE)
618248616Smm				goto finish;
619228753Smm			l = len - nl;
620228753Smm			if (l >= 3 && b[0] == '=' && b[1] == '=' &&
621228753Smm			    b[2] == '=') {
622228753Smm				uudecode->state = ST_FIND_HEAD;
623228753Smm				break;
624228753Smm			}
625228753Smm			while (l > 0) {
626228753Smm				int n = 0;
627228753Smm
628358090Smm				if (!base64[b[0]] || !base64[b[1]])
629358090Smm					break;
630358090Smm				n = base64num[*b++] << 18;
631358090Smm				n |= base64num[*b++] << 12;
632358090Smm				*out++ = n >> 16; total++;
633358090Smm				l -= 2;
634358090Smm
635228753Smm				if (l > 0) {
636228753Smm					if (*b == '=')
637228753Smm						break;
638228753Smm					if (!base64[*b])
639228753Smm						break;
640228753Smm					n |= base64num[*b++] << 6;
641228753Smm					*out++ = (n >> 8) & 0xFF; total++;
642228753Smm					--l;
643228753Smm				}
644228753Smm				if (l > 0) {
645228753Smm					if (*b == '=')
646228753Smm						break;
647228753Smm					if (!base64[*b])
648228753Smm						break;
649228753Smm					n |= base64num[*b++];
650228753Smm					*out++ = n & 0xFF; total++;
651228753Smm					--l;
652228753Smm				}
653228753Smm			}
654228753Smm			if (l && *b != '=') {
655228753Smm				archive_set_error(&self->archive->archive,
656228753Smm				    ARCHIVE_ERRNO_MISC,
657228753Smm				    "Insufficient compressed data");
658228753Smm				return (ARCHIVE_FATAL);
659228753Smm			}
660228753Smm			break;
661228753Smm		}
662228753Smm	}
663248616Smmfinish:
664248616Smm	if (ravail < avail_in)
665248616Smm		used -= avail_in - ravail;
666248616Smm	__archive_read_filter_consume(self->upstream, used);
667228753Smm
668228753Smm	*buff = uudecode->out_buff;
669228753Smm	uudecode->total += total;
670228753Smm	return (total);
671228753Smm}
672228753Smm
673228753Smmstatic int
674228753Smmuudecode_filter_close(struct archive_read_filter *self)
675228753Smm{
676228753Smm	struct uudecode *uudecode;
677228753Smm
678228753Smm	uudecode = (struct uudecode *)self->data;
679228753Smm	free(uudecode->in_buff);
680228753Smm	free(uudecode->out_buff);
681228753Smm	free(uudecode);
682228753Smm
683228753Smm	return (ARCHIVE_OK);
684228753Smm}
685228753Smm
686