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