2 * Copyright (c) 2009 Michihiro NAKAJIMA
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
26 #include "archive_platform.h"
27 __FBSDID("$FreeBSD$");
40 #include "archive_private.h"
41 #include "archive_read_private.h"
45 unsigned char *in_buff;
46 #define IN_BUFF_SIZE (1024)
49 unsigned char *out_buff;
50 #define OUT_BUFF_SIZE (64 * 1024)
52 #define ST_FIND_HEAD 0
55 #define ST_READ_BASE64 3
58 static int uudecode_bidder_bid(struct archive_read_filter_bidder *,
59 struct archive_read_filter *filter);
60 static int uudecode_bidder_init(struct archive_read_filter *);
62 static ssize_t uudecode_filter_read(struct archive_read_filter *,
64 static int uudecode_filter_close(struct archive_read_filter *);
67 archive_read_support_compression_uu(struct archive *_a)
69 struct archive_read *a = (struct archive_read *)_a;
70 struct archive_read_filter_bidder *bidder;
72 bidder = __archive_read_get_bidder(a);
73 archive_clear_error(_a);
75 return (ARCHIVE_FATAL);
78 bidder->bid = uudecode_bidder_bid;
79 bidder->init = uudecode_bidder_init;
80 bidder->options = NULL;
85 static const unsigned char ascii[256] = {
86 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\n', 0, 0, '\r', 0, 0, /* 00 - 0F */
87 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
88 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
89 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */
90 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
91 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */
92 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
93 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */
94 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
95 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
96 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
97 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
98 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
99 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
100 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
101 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
104 static const unsigned char uuchar[256] = {
105 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
106 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
107 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
108 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */
109 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
110 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */
111 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F */
112 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 70 - 7F */
113 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
114 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
115 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
116 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
117 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
118 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
119 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
120 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
123 static const unsigned char base64[256] = {
124 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
125 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
126 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, /* 20 - 2F */
127 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, /* 30 - 3F */
128 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
129 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 50 - 5F */
130 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
131 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 70 - 7F */
132 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
133 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
134 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
135 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
136 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
137 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
138 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
139 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
142 static const int base64num[128] = {
143 0, 0, 0, 0, 0, 0, 0, 0,
144 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
145 0, 0, 0, 0, 0, 0, 0, 0,
146 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
147 0, 0, 0, 0, 0, 0, 0, 0,
148 0, 0, 0, 62, 0, 0, 0, 63, /* 20 - 2F */
149 52, 53, 54, 55, 56, 57, 58, 59,
150 60, 61, 0, 0, 0, 0, 0, 0, /* 30 - 3F */
151 0, 0, 1, 2, 3, 4, 5, 6,
152 7, 8, 9, 10, 11, 12, 13, 14, /* 40 - 4F */
153 15, 16, 17, 18, 19, 20, 21, 22,
154 23, 24, 25, 0, 0, 0, 0, 0, /* 50 - 5F */
155 0, 26, 27, 28, 29, 30, 31, 32,
156 33, 34, 35, 36, 37, 38, 39, 40, /* 60 - 6F */
157 41, 42, 43, 44, 45, 46, 47, 48,
158 49, 50, 51, 0, 0, 0, 0, 0, /* 70 - 7F */
162 get_line(const unsigned char *b, ssize_t avail, ssize_t *nlsize)
167 while (len < avail) {
169 case 0: /* Non-ascii character or control character. */
174 if (avail-len > 1 && b[1] == '\n') {
196 bid_get_line(struct archive_read_filter *filter,
197 const unsigned char **b, ssize_t *avail, ssize_t *ravail, ssize_t *nl)
207 len = get_line(*b, *avail, nl);
209 * Read bytes more while it does not reach the end of line.
211 while (*nl == 0 && len == *avail && !quit) {
212 ssize_t diff = *ravail - *avail;
214 *b = __archive_read_filter_ahead(filter, 160 + *ravail, avail);
216 if (*ravail >= *avail)
218 /* Reading bytes reaches the end of file. */
219 *b = __archive_read_filter_ahead(filter, *avail, avail);
225 len = get_line(*b, *avail, nl);
230 #define UUDECODE(c) (((c) - 0x20) & 0x3f)
233 uudecode_bidder_bid(struct archive_read_filter_bidder *self,
234 struct archive_read_filter *filter)
236 const unsigned char *b;
237 ssize_t avail, ravail;
242 (void)self; /* UNUSED */
244 b = __archive_read_filter_ahead(filter, 1, &avail);
251 len = bid_get_line(filter, &b, &avail, &ravail, &nl);
252 if (len < 0 || nl == 0)
253 return (0);/* Binary data. */
254 if (memcmp(b, "begin ", 6) == 0 && len - nl >= 11)
256 else if (memcmp(b, "begin-base64 ", 13) == 0 && len - nl >= 18)
261 if (l > 0 && (b[l] < '0' || b[l] > '7' ||
262 b[l+1] < '0' || b[l+1] > '7' ||
263 b[l+2] < '0' || b[l+2] > '7' || b[l+3] != ' '))
274 len = bid_get_line(filter, &b, &avail, &ravail, &nl);
275 if (len < 0 || nl == 0)
276 return (0);/* There are non-ascii characters. */
282 /* Get a length of decoded bytes. */
283 l = UUDECODE(*b++); len--;
285 /* Normally, maximum length is 45(character 'M'). */
287 while (l && len-nl > 0) {
312 (uuchar[*b] || /* Check sum. */
313 (*b >= 'a' && *b <= 'z'))) {/* Padding data(MINIX). */
318 if (avail && uuchar[*b])
319 return (firstline+30);
329 if (avail >= 5 && memcmp(b, "====\n", 5) == 0)
330 return (firstline+40);
331 if (avail >= 6 && memcmp(b, "====\r\n", 6) == 0)
332 return (firstline+40);
333 if (avail > 0 && base64[*b])
334 return (firstline+30);
341 uudecode_bidder_init(struct archive_read_filter *self)
343 struct uudecode *uudecode;
347 self->code = ARCHIVE_COMPRESSION_UU;
349 self->read = uudecode_filter_read;
350 self->skip = NULL; /* not supported */
351 self->close = uudecode_filter_close;
353 uudecode = (struct uudecode *)calloc(sizeof(*uudecode), 1);
354 out_buff = malloc(OUT_BUFF_SIZE);
355 in_buff = malloc(IN_BUFF_SIZE);
356 if (uudecode == NULL || out_buff == NULL || in_buff == NULL) {
357 archive_set_error(&self->archive->archive, ENOMEM,
358 "Can't allocate data for uudecode");
362 return (ARCHIVE_FATAL);
365 self->data = uudecode;
366 uudecode->in_buff = in_buff;
367 uudecode->in_cnt = 0;
368 uudecode->in_allocated = IN_BUFF_SIZE;
369 uudecode->out_buff = out_buff;
370 uudecode->state = ST_FIND_HEAD;
376 ensure_in_buff_size(struct archive_read_filter *self,
377 struct uudecode *uudecode, size_t size)
380 if (size > uudecode->in_allocated) {
385 * Calculate a new buffer size for in_buff.
386 * Increase its value until it has enough size we need.
388 newsize = uudecode->in_allocated;
390 if (newsize < IN_BUFF_SIZE*32)
393 newsize += IN_BUFF_SIZE;
394 } while (size > newsize);
395 ptr = malloc(newsize);
397 newsize < uudecode->in_allocated) {
399 archive_set_error(&self->archive->archive,
401 "Can't allocate data for uudecode");
402 return (ARCHIVE_FATAL);
404 if (uudecode->in_cnt)
405 memmove(ptr, uudecode->in_buff,
407 free(uudecode->in_buff);
408 uudecode->in_buff = ptr;
409 uudecode->in_allocated = newsize;
415 uudecode_filter_read(struct archive_read_filter *self, const void **buff)
417 struct uudecode *uudecode;
418 const unsigned char *b, *d;
420 ssize_t avail_in, ravail;
423 ssize_t len, llen, nl;
425 uudecode = (struct uudecode *)self->data;
428 d = __archive_read_filter_ahead(self->upstream, 1, &avail_in);
429 if (d == NULL && avail_in < 0)
430 return (ARCHIVE_FATAL);
431 /* Quiet a code analyzer; make sure avail_in must be zero
437 out = uudecode->out_buff;
439 if (uudecode->in_cnt) {
441 * If there is remaining data which is saved by
442 * previous calling, use it first.
444 if (ensure_in_buff_size(self, uudecode,
445 avail_in + uudecode->in_cnt) != ARCHIVE_OK)
446 return (ARCHIVE_FATAL);
447 memcpy(uudecode->in_buff + uudecode->in_cnt,
449 d = uudecode->in_buff;
450 avail_in += uudecode->in_cnt;
451 uudecode->in_cnt = 0;
453 for (;used < avail_in; d += llen, used += llen) {
457 len = get_line(b, avail_in - used, &nl);
459 /* Non-ascii character is found. */
460 archive_set_error(&self->archive->archive,
462 "Insufficient compressed data");
463 return (ARCHIVE_FATAL);
468 * Save remaining data which does not contain
471 if (ensure_in_buff_size(self, uudecode, len)
473 return (ARCHIVE_FATAL);
474 if (uudecode->in_buff != b)
475 memmove(uudecode->in_buff, b, len);
476 uudecode->in_cnt = len;
478 /* Do not return 0; it means end-of-file.
479 * We should try to read bytes more. */
480 __archive_read_filter_consume(
481 self->upstream, ravail);
486 if (total + len * 2 > OUT_BUFF_SIZE)
488 switch (uudecode->state) {
491 if (len - nl > 13 && memcmp(b, "begin ", 6) == 0)
493 else if (len - nl > 18 &&
494 memcmp(b, "begin-base64 ", 13) == 0)
498 if (l != 0 && b[l] >= '0' && b[l] <= '7' &&
499 b[l+1] >= '0' && b[l+1] <= '7' &&
500 b[l+2] >= '0' && b[l+2] <= '7' && b[l+3] == ' ') {
502 uudecode->state = ST_READ_UU;
504 uudecode->state = ST_READ_BASE64;
509 if (!uuchar[*b] || body <= 0) {
510 archive_set_error(&self->archive->archive,
512 "Insufficient compressed data");
513 return (ARCHIVE_FATAL);
515 /* Get length of undecoded bytes of curent line. */
519 archive_set_error(&self->archive->archive,
521 "Insufficient compressed data");
522 return (ARCHIVE_FATAL);
525 uudecode->state = ST_UUEND;
532 if (!uuchar[b[0]] || !uuchar[b[1]])
534 n = UUDECODE(*b++) << 18;
535 n |= UUDECODE(*b++) << 12;
536 *out++ = n >> 16; total++;
542 n |= UUDECODE(*b++) << 6;
543 *out++ = (n >> 8) & 0xFF; total++;
550 *out++ = n & 0xFF; total++;
555 archive_set_error(&self->archive->archive,
557 "Insufficient compressed data");
558 return (ARCHIVE_FATAL);
562 if (len - nl == 3 && memcmp(b, "end ", 3) == 0)
563 uudecode->state = ST_FIND_HEAD;
565 archive_set_error(&self->archive->archive,
567 "Insufficient compressed data");
568 return (ARCHIVE_FATAL);
573 if (l >= 3 && b[0] == '=' && b[1] == '=' &&
575 uudecode->state = ST_FIND_HEAD;
582 if (!base64[b[0]] || !base64[b[1]])
584 n = base64num[*b++] << 18;
585 n |= base64num[*b++] << 12;
586 *out++ = n >> 16; total++;
594 n |= base64num[*b++] << 6;
595 *out++ = (n >> 8) & 0xFF; total++;
603 n |= base64num[*b++];
604 *out++ = n & 0xFF; total++;
608 if (l && *b != '=') {
609 archive_set_error(&self->archive->archive,
611 "Insufficient compressed data");
612 return (ARCHIVE_FATAL);
618 __archive_read_filter_consume(self->upstream, ravail);
620 *buff = uudecode->out_buff;
621 uudecode->total += total;
626 uudecode_filter_close(struct archive_read_filter *self)
628 struct uudecode *uudecode;
630 uudecode = (struct uudecode *)self->data;
631 free(uudecode->in_buff);
632 free(uudecode->out_buff);