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