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