Coverage Report

Created: 2026-10-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libzip/lib/zip_open.c
Line
Count
Source
1
/*
2
  zip_open.c -- open zip archive by name
3
  Copyright (C) 1999-2024 Dieter Baron and Thomas Klausner
4
5
  This file is part of libzip, a library to manipulate ZIP archives.
6
  The authors can be contacted at <info@libzip.org>
7
8
  Redistribution and use in source and binary forms, with or without
9
  modification, are permitted provided that the following conditions
10
  are met:
11
  1. Redistributions of source code must retain the above copyright
12
     notice, this list of conditions and the following disclaimer.
13
  2. Redistributions in binary form must reproduce the above copyright
14
     notice, this list of conditions and the following disclaimer in
15
     the documentation and/or other materials provided with the
16
     distribution.
17
  3. The names of the authors may not be used to endorse or promote
18
     products derived from this software without specific prior
19
     written permission.
20
21
  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
22
  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24
  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
25
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27
  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28
  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
29
  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30
  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
31
  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
*/
33
34
#include <limits.h>
35
#include <stdio.h>
36
#include <stdlib.h>
37
#include <string.h>
38
39
#include "zipint.h"
40
41
typedef enum {
42
    EXISTS_ERROR = -1,
43
    EXISTS_NOT = 0,
44
    EXISTS_OK
45
} exists_t;
46
47
typedef enum {
48
    CDIR_OK,
49
    CDIR_INVALID,
50
    CDIR_NOT_FOUND
51
} cdir_status_t;
52
53
static bool check_eocd(zip_cdir_t *cd, unsigned int flags, zip_error_t *error);
54
static bool check_magic(zip_uint64_t offset, zip_buffer_t *buffer, zip_uint64_t buffer_offset, zip_source_t *src, const char *magic);
55
static zip_t *_zip_allocate_new(zip_source_t *src, unsigned int flags, zip_error_t *error);
56
static zip_int64_t _zip_checkcons(zip_t *za, zip_cdir_t *cdir, zip_error_t *error);
57
static void zip_check_torrentzip(zip_t *za, const zip_cdir_t *cdir);
58
static zip_cdir_t *_zip_find_central_dir(zip_t *za, zip_uint64_t len);
59
static exists_t _zip_file_exists(zip_source_t *src, zip_error_t *error);
60
static int _zip_headercomp(const zip_dirent_t *, const zip_dirent_t *);
61
static bool _zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_cdir_t **cdirp, zip_error_t *error);
62
static zip_cdir_t *_zip_read_eocd(zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error);
63
static cdir_status_t _zip_read_eocd64(zip_cdir_t *cdir, zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error);
64
static const unsigned char *find_eocd(zip_buffer_t *buffer, const unsigned char *last);
65
66
67
8.78k
ZIP_EXTERN zip_t *zip_open(const char *fn, int _flags, int *zep) {
68
8.78k
    zip_t *za;
69
8.78k
    zip_source_t *src;
70
8.78k
    struct zip_error error;
71
72
8.78k
    zip_error_init(&error);
73
8.78k
    if ((src = zip_source_file_create(fn, 0, -1, &error)) == NULL) {
74
0
        _zip_set_open_error(zep, &error, 0);
75
0
        zip_error_fini(&error);
76
0
        return NULL;
77
0
    }
78
79
8.78k
    if ((za = zip_open_from_source(src, _flags, &error)) == NULL) {
80
4.15k
        zip_source_free(src);
81
4.15k
        _zip_set_open_error(zep, &error, 0);
82
4.15k
        zip_error_fini(&error);
83
4.15k
        return NULL;
84
4.15k
    }
85
86
4.63k
    zip_error_fini(&error);
87
4.63k
    return za;
88
8.78k
}
89
90
91
8.78k
ZIP_EXTERN zip_t *zip_open_from_source(zip_source_t *src, int _flags, zip_error_t *error) {
92
8.78k
    unsigned int flags;
93
8.78k
    zip_int64_t supported;
94
8.78k
    exists_t exists;
95
96
8.78k
    if (_flags < 0 || src == NULL) {
97
0
        zip_error_set(error, ZIP_ER_INVAL, 0);
98
0
        return NULL;
99
0
    }
100
8.78k
    flags = (unsigned int)_flags;
101
102
8.78k
    supported = zip_source_supports(src);
103
8.78k
    if ((supported & ZIP_SOURCE_SUPPORTS_SEEKABLE) != ZIP_SOURCE_SUPPORTS_SEEKABLE) {
104
0
        zip_error_set(error, ZIP_ER_OPNOTSUPP, 0);
105
0
        return NULL;
106
0
    }
107
8.78k
    if ((supported & ZIP_SOURCE_SUPPORTS_WRITABLE) != ZIP_SOURCE_SUPPORTS_WRITABLE) {
108
0
        flags |= ZIP_RDONLY;
109
0
    }
110
111
8.78k
    if ((flags & (ZIP_RDONLY | ZIP_TRUNCATE)) == (ZIP_RDONLY | ZIP_TRUNCATE)) {
112
0
        zip_error_set(error, ZIP_ER_RDONLY, 0);
113
0
        return NULL;
114
0
    }
115
116
8.78k
    exists = _zip_file_exists(src, error);
117
8.78k
    switch (exists) {
118
0
    case EXISTS_ERROR:
119
0
        return NULL;
120
121
0
    case EXISTS_NOT:
122
0
        if ((flags & ZIP_CREATE) == 0) {
123
0
            zip_error_set(error, ZIP_ER_NOENT, 0);
124
0
            return NULL;
125
0
        }
126
0
        return _zip_allocate_new(src, flags, error);
127
128
8.78k
    default: {
129
8.78k
        zip_t *za;
130
8.78k
        if (flags & ZIP_EXCL) {
131
0
            zip_error_set(error, ZIP_ER_EXISTS, 0);
132
0
            return NULL;
133
0
        }
134
8.78k
        if (zip_source_open(src) < 0) {
135
0
            zip_error_set_from_source(error, src);
136
0
            return NULL;
137
0
        }
138
139
8.78k
        if (flags & ZIP_TRUNCATE) {
140
0
            za = _zip_allocate_new(src, flags, error);
141
0
        }
142
8.78k
        else {
143
            /* ZIP_CREATE gets ignored if file exists and not ZIP_EXCL, just like open() */
144
8.78k
            za = _zip_open(src, flags, error);
145
8.78k
        }
146
147
8.78k
        if (za == NULL) {
148
4.15k
            zip_source_close(src);
149
4.15k
            return NULL;
150
4.15k
        }
151
4.63k
        return za;
152
8.78k
    }
153
8.78k
    }
154
8.78k
}
155
156
157
1.56k
static bool _is_truncated_zip(zip_source_t *src) {
158
1.56k
    unsigned char data[4];
159
    /* check if the source is a truncated zip archive: true if yes, no
160
       if not or can't be determined */
161
1.56k
    if (zip_source_seek(src, 0, SEEK_SET) < 0) {
162
0
        return false;
163
0
    }
164
165
1.56k
    if (zip_source_read(src, data, 4) != 4) {
166
102
        return false;
167
102
    }
168
169
1.46k
    if (memcmp(data, LOCAL_MAGIC, 4) == 0) {
170
        /* file starts with a ZIP local header signature */
171
134
        return true;
172
134
    }
173
1.33k
    return false;
174
1.46k
}
175
176
177
8.78k
zip_t *_zip_open(zip_source_t *src, unsigned int flags, zip_error_t *error) {
178
8.78k
    zip_t *za;
179
8.78k
    zip_cdir_t *cdir;
180
8.78k
    struct zip_stat st;
181
8.78k
    zip_uint64_t len, idx;
182
183
8.78k
    zip_stat_init(&st);
184
8.78k
    if (zip_source_stat(src, &st) < 0) {
185
0
        zip_error_set_from_source(error, src);
186
0
        return NULL;
187
0
    }
188
8.78k
    if ((st.valid & ZIP_STAT_SIZE) == 0) {
189
0
        zip_error_set(error, ZIP_ER_SEEK, EOPNOTSUPP);
190
0
        return NULL;
191
0
    }
192
8.78k
    len = st.size;
193
194
195
8.78k
    if ((za = _zip_allocate_new(src, flags, error)) == NULL) {
196
0
        return NULL;
197
0
    }
198
199
    /* treat empty files as empty archives */
200
8.78k
    if (len == 0 && zip_source_accept_empty(src)) {
201
0
        return za;
202
0
    }
203
204
8.78k
    if ((cdir = _zip_find_central_dir(za, len)) == NULL) {
205
4.15k
        _zip_error_copy(error, &za->error);
206
4.15k
        if (zip_error_code_zip(&za->error) == ZIP_ER_NOZIP) {
207
            /* not a zip - find out if it's truncated */
208
1.56k
            if (_is_truncated_zip(src)) {
209
134
                zip_error_set(error, ZIP_ER_TRUNCATED_ZIP, 0);
210
134
            }
211
1.56k
        }
212
        /* keep src so discard does not get rid of it */
213
4.15k
        zip_source_keep(src);
214
4.15k
        zip_discard(za);
215
4.15k
        return NULL;
216
4.15k
    }
217
218
4.63k
    za->entry = cdir->entry;
219
4.63k
    za->nentry = cdir->nentry;
220
4.63k
    za->nentry_alloc = cdir->nentry_alloc;
221
222
4.63k
    zip_check_torrentzip(za, cdir);
223
224
4.63k
    if (ZIP_IS_TORRENTZIP(za)) {
225
        /* Torrentzip uses the archive comment to detect changes by tools that are not torrentzip aware. */
226
0
        _zip_string_free(cdir->comment);
227
0
    }
228
4.63k
    else {
229
4.63k
        za->comment_orig = cdir->comment;
230
4.63k
    }
231
232
4.63k
    free(cdir);
233
234
4.63k
    _zip_hash_reserve_capacity(za->names, za->nentry, &za->error);
235
236
30.8k
    for (idx = 0; idx < za->nentry; idx++) {
237
26.2k
        const zip_uint8_t *name = _zip_string_get(za->entry[idx].orig->filename, NULL, 0, error);
238
26.2k
        if (name == NULL) {
239
            /* keep src so discard does not get rid of it */
240
0
            zip_source_keep(src);
241
0
            zip_discard(za);
242
0
            return NULL;
243
0
        }
244
245
26.2k
        if (_zip_hash_add(za->names, name, idx, ZIP_FL_UNCHANGED, &za->error) == false) {
246
24
            if (za->error.zip_err != ZIP_ER_EXISTS || (flags & ZIP_CHECKCONS)) {
247
0
                _zip_error_copy(error, &za->error);
248
                /* keep src so discard does not get rid of it */
249
0
                zip_source_keep(src);
250
0
                zip_discard(za);
251
0
                return NULL;
252
0
            }
253
24
        }
254
26.2k
    }
255
256
4.63k
    za->ch_flags = za->flags;
257
258
4.63k
    return za;
259
4.63k
}
260
261
262
4.15k
void _zip_set_open_error(int *zep, const zip_error_t *err, int ze) {
263
4.15k
    if (err) {
264
4.15k
        ze = zip_error_code_zip(err);
265
4.15k
        switch (zip_error_system_type(err)) {
266
126
        case ZIP_ET_SYS:
267
2.16k
        case ZIP_ET_LIBZIP:
268
2.16k
            errno = zip_error_code_system(err);
269
2.16k
            break;
270
271
1.98k
        default:
272
1.98k
            break;
273
4.15k
        }
274
4.15k
    }
275
276
4.15k
    if (zep) {
277
4.15k
        *zep = ze;
278
4.15k
    }
279
4.15k
}
280
281
282
/* _zip_readcdir:
283
   tries to find a valid end-of-central-directory at the beginning of
284
   buf, and then the corresponding central directory entries.
285
   Returns a struct zip_cdir which contains the central directory
286
   entries, or NULL if unsuccessful. */
287
288
58.9k
static bool _zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_cdir_t **cdirp, zip_error_t *error) {
289
58.9k
    zip_cdir_t *cd;
290
58.9k
    zip_uint16_t comment_len;
291
58.9k
    zip_uint64_t i, left;
292
58.9k
    zip_uint64_t eocd_offset = _zip_buffer_offset(buffer);
293
58.9k
    zip_buffer_t *cd_buffer;
294
58.9k
    bool eocd64_found = false;
295
296
58.9k
    *cdirp = NULL;
297
298
58.9k
    if ((cd = _zip_read_eocd(buffer, buf_offset, error)) == NULL) {
299
186
        return false;
300
186
    }
301
302
58.7k
    if (eocd_offset >= EOCD64LOCLEN && memcmp(_zip_buffer_data(buffer) + eocd_offset - EOCD64LOCLEN, EOCD64LOC_MAGIC, 4) == 0) {
303
2.58k
        eocd64_found = true;
304
2.58k
        _zip_buffer_set_offset(buffer, eocd_offset - EOCD64LOCLEN);
305
2.58k
        switch (_zip_read_eocd64(cd, za->src, buffer, buf_offset, za->open_flags, error)) {
306
181
        case CDIR_OK:
307
181
            break;
308
309
1.35k
        case CDIR_INVALID:
310
1.35k
            _zip_cdir_free(cd);
311
1.35k
            return true;
312
313
1.04k
        case CDIR_NOT_FOUND:
314
1.04k
            _zip_cdir_free(cd);
315
1.04k
            return false;
316
2.58k
        }
317
2.58k
    }
318
319
56.3k
    if ((cd->eocd_disk != 0 || cd->this_disk != 0) && !eocd64_found && cd->eocd_disk != cd->this_disk) {
320
        /* If the central directory doesn't start on this disk, we can't check that offset is valid. Check as much as we can instead. */
321
26.6k
        if (cd->this_disk < cd->eocd_disk) {
322
            /* Disks before the start of the central directory don't contain an EOCD. */
323
7.63k
            _zip_cdir_free(cd);
324
7.63k
            return false;
325
7.63k
        }
326
19.0k
        if (cd->size <= cd->eocd_offset) {
327
            /* The complete central directory would fit on this disk. */
328
688
            _zip_cdir_free(cd);
329
688
            return false;
330
688
        }
331
19.0k
    }
332
333
48.0k
    if (!eocd64_found) {
334
47.8k
        if (cd->this_disk == 0 && cd->eocd_disk == 0 && cd->eocd_offset == 0 && cd->offset == 0 && cd->num_entries == 0) {
335
            /* An empty archive doesn't contain central directory entries. */
336
272
        }
337
47.6k
        else if (!check_magic(cd->offset, buffer, buf_offset, za->src, CENTRAL_MAGIC)) {
338
41.9k
            _zip_cdir_free(cd);
339
41.9k
            return false;
340
41.9k
        }
341
47.8k
    }
342
343
    /* We accept this EOCD as valid and won't search for an earlier one if it is unusable. */
344
345
6.11k
    if (!check_eocd(cd, za->open_flags, error)) {
346
109
        _zip_cdir_free(cd);
347
109
        return true;
348
109
    }
349
350
6.00k
    _zip_buffer_set_offset(buffer, eocd_offset + 20);
351
6.00k
    comment_len = _zip_buffer_get_16(buffer);
352
353
6.00k
    if (cd->offset + cd->size > buf_offset + eocd_offset) {
354
        /* cdir spans past EOCD record */
355
166
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_OVERLAPS_EOCD);
356
166
        _zip_cdir_free(cd);
357
166
        return true;
358
166
    }
359
360
5.83k
    if (comment_len || (za->open_flags & ZIP_CHECKCONS)) {
361
2.26k
        zip_uint64_t tail_len;
362
363
2.26k
        _zip_buffer_set_offset(buffer, eocd_offset + EOCDLEN);
364
2.26k
        tail_len = _zip_buffer_left(buffer);
365
366
2.26k
        if (tail_len != comment_len) {
367
2.23k
            if (za->open_flags & ZIP_CHECKCONS) {
368
0
                zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_COMMENT_LENGTH_INVALID);
369
0
                _zip_cdir_free(cd);
370
0
                return true;
371
0
            }
372
2.23k
            if (tail_len < comment_len) {
373
2.13k
                comment_len = (zip_uint16_t)tail_len;
374
2.13k
            }
375
2.23k
        }
376
377
2.26k
        if (comment_len) {
378
1.54k
            if ((cd->comment = _zip_string_new(_zip_buffer_get(buffer, comment_len), comment_len, ZIP_FL_ENC_GUESS, error)) == NULL) {
379
0
                _zip_cdir_free(cd);
380
0
                return true;
381
0
            }
382
1.54k
        }
383
2.26k
    }
384
385
5.83k
    if (cd->offset >= buf_offset) {
386
5.61k
        zip_uint8_t *data;
387
        /* if buffer already read in, use it */
388
5.61k
        _zip_buffer_set_offset(buffer, cd->offset - buf_offset);
389
390
5.61k
        if ((data = _zip_buffer_get(buffer, cd->size)) == NULL) {
391
0
            zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_LENGTH_INVALID);
392
0
            _zip_cdir_free(cd);
393
0
            return true;
394
0
        }
395
5.61k
        if ((cd_buffer = _zip_buffer_new(data, cd->size)) == NULL) {
396
0
            zip_error_set(error, ZIP_ER_MEMORY, 0);
397
0
            _zip_cdir_free(cd);
398
0
            return true;
399
0
        }
400
5.61k
    }
401
218
    else {
402
218
        cd_buffer = NULL;
403
404
218
        if (zip_source_seek(za->src, (zip_int64_t)cd->offset, SEEK_SET) < 0) {
405
0
            zip_error_set_from_source(error, za->src);
406
0
            _zip_cdir_free(cd);
407
0
            return true;
408
0
        }
409
410
        /* possible consistency check: cd->offset = len-(cd->size+cd->comment_len+EOCDLEN) ? */
411
218
        if (zip_source_tell(za->src) != (zip_int64_t)cd->offset) {
412
0
            zip_error_set(error, ZIP_ER_NOZIP, 0);
413
0
            _zip_cdir_free(cd);
414
0
            return true;
415
0
        }
416
218
    }
417
418
5.83k
    if (!_zip_cdir_grow(cd, cd->num_entries, error)) {
419
0
        _zip_cdir_free(cd);
420
0
        _zip_buffer_free(cd_buffer);
421
0
        return true;
422
0
    }
423
5.83k
    left = (zip_uint64_t)cd->size;
424
5.83k
    i = 0;
425
34.4k
    while (left > 0) {
426
29.8k
        bool grown = false;
427
29.8k
        zip_int64_t entry_size;
428
429
29.8k
        if (i == cd->nentry) {
430
            /* InfoZIP has a hack to avoid using Zip64: it stores nentries % 0x10000 */
431
            /* This hack isn't applicable if we're using Zip64, or if there is no central directory entry following. */
432
433
934
            if (cd->is_zip64 || left < CDENTRYSIZE) {
434
97
                break;
435
97
            }
436
437
837
            if (!_zip_cdir_grow(cd, 0x10000, error)) {
438
0
                _zip_cdir_free(cd);
439
0
                _zip_buffer_free(cd_buffer);
440
0
                return true;
441
0
            }
442
837
            grown = true;
443
837
        }
444
445
29.7k
        if ((cd->entry[i].orig = _zip_dirent_new()) == NULL || (entry_size = _zip_dirent_read(cd->entry[i].orig, za->src, cd_buffer, false, cd->is_zip64, 0, za->open_flags & ZIP_CHECKCONS, error)) < 0) {
446
1.07k
            if (zip_error_code_zip(error) == ZIP_ER_INCONS) {
447
676
                zip_error_set(error, ZIP_ER_INCONS, ADD_INDEX_TO_DETAIL(zip_error_code_system(error), i));
448
676
            }
449
403
            else if (grown && zip_error_code_zip(error) == ZIP_ER_NOZIP) {
450
58
                zip_error_set(error, ZIP_ER_INCONS, MAKE_DETAIL_WITH_INDEX(ZIP_ER_DETAIL_CDIR_ENTRY_INVALID, i));
451
58
            }
452
1.07k
            _zip_cdir_free(cd);
453
1.07k
            _zip_buffer_free(cd_buffer);
454
1.07k
            return true;
455
1.07k
        }
456
28.6k
        i++;
457
28.6k
        left -= (zip_uint64_t)entry_size;
458
28.6k
    }
459
460
    /* If we didn't fill all we grew, cd->num_entries was wrong. */
461
4.75k
    if (i != cd->nentry || left > 0) {
462
122
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_WRONG_ENTRIES_COUNT);
463
122
        _zip_buffer_free(cd_buffer);
464
122
        _zip_cdir_free(cd);
465
122
        return true;
466
122
    }
467
468
4.63k
    if (za->open_flags & ZIP_CHECKCONS) {
469
0
        bool ok;
470
471
0
        if (cd_buffer) {
472
0
            ok = _zip_buffer_eof(cd_buffer);
473
0
        }
474
0
        else {
475
0
            zip_int64_t offset = zip_source_tell(za->src);
476
477
0
            if (offset < 0) {
478
0
                zip_error_set_from_source(error, za->src);
479
0
                _zip_cdir_free(cd);
480
0
                return true;
481
0
            }
482
0
            ok = ((zip_uint64_t)offset == cd->offset + cd->size);
483
0
        }
484
485
0
        if (!ok) {
486
0
            zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_LENGTH_INVALID);
487
0
            _zip_buffer_free(cd_buffer);
488
0
            _zip_cdir_free(cd);
489
0
            return true;
490
0
        }
491
0
    }
492
493
4.63k
    _zip_buffer_free(cd_buffer);
494
4.63k
    *cdirp = cd;
495
4.63k
    return true;
496
4.63k
}
497
498
499
50.2k
static bool check_magic(zip_uint64_t offset, zip_buffer_t *buffer, zip_uint64_t buffer_offset, zip_source_t *src, const char *magic) {
500
50.2k
    if (buffer_offset <= offset) {
501
48.5k
        zip_uint8_t *data;
502
48.5k
        if (_zip_buffer_set_offset(buffer, offset - buffer_offset) < 0 || (data = _zip_buffer_get(buffer, MAGIC_LEN)) == NULL) {
503
39.1k
            return false;
504
39.1k
        }
505
9.45k
        return memcmp(data, magic, MAGIC_LEN) == 0;
506
48.5k
    }
507
1.62k
    else {
508
1.62k
        zip_uint8_t data[MAGIC_LEN];
509
510
1.62k
        if (zip_source_seek(src, offset, SEEK_SET) < 0 || zip_source_read(src, data, MAGIC_LEN) != MAGIC_LEN) {
511
0
            return false;
512
0
        }
513
1.62k
        return memcmp(data, magic, MAGIC_LEN) == 0;
514
1.62k
    }
515
50.2k
}
516
517
518
/* _zip_checkcons:
519
   Checks the consistency of the central directory by comparing central
520
   directory entries with local headers and checking for plausible
521
   file and header offsets. Returns -1 if not plausible, else the
522
   difference between the lowest and the highest fileposition reached */
523
524
0
static zip_int64_t _zip_checkcons(zip_t *za, zip_cdir_t *cd, zip_error_t *error) {
525
0
    zip_uint64_t i;
526
0
    zip_uint64_t min, max, j, tail_length;
527
0
    struct zip_dirent temp;
528
0
    int detail;
529
530
0
    _zip_dirent_init(&temp);
531
0
    if (cd->nentry) {
532
0
        max = cd->entry[0].orig->offset;
533
0
        min = cd->entry[0].orig->offset;
534
0
    }
535
0
    else {
536
0
        min = max = 0;
537
0
    }
538
539
    /*
540
        Currently we use the presence of a Zip64 End of Central Directory do decide
541
        if the archive is Zip64 and use that to decide the size of data descriptors
542
        in local headers. This is not guaranteed to be correct. We could also assume
543
        Zip64 format if any of the central directory entries has a Zip64 extra field.
544
545
        Since we only read local headers when checking consistency, this is not a
546
        problem for normal use, but only when checking consistency for archives using
547
        data descriptors.
548
    */
549
550
0
    for (i = 0; i < cd->nentry; i++) {
551
0
        if (cd->entry[i].orig->offset < min) {
552
0
            min = cd->entry[i].orig->offset;
553
0
        }
554
0
        if (min > (zip_uint64_t)cd->offset) {
555
0
            zip_error_set(error, ZIP_ER_NOZIP, 0);
556
0
            return -1;
557
0
        }
558
559
0
        tail_length = _zip_string_length(cd->entry[i].orig->filename) + LENTRYSIZE;
560
0
        if (ZIP_CHECK_ADD_OVERFLOW(cd->entry[i].orig->comp_size, tail_length) || ZIP_CHECK_ADD_OVERFLOW(cd->entry[i].orig->offset + tail_length, cd->entry[i].orig->comp_size)) {
561
0
            zip_error_set(error, ZIP_ER_NOZIP, 0);
562
0
            return -1;
563
0
        }
564
0
        j = cd->entry[i].orig->offset + cd->entry[i].orig->comp_size + tail_length;
565
566
0
        if (j > max) {
567
0
            max = j;
568
0
        }
569
0
        if (max > (zip_uint64_t)cd->offset) {
570
0
            zip_error_set(error, ZIP_ER_NOZIP, 0);
571
0
            return -1;
572
0
        }
573
574
0
        if (zip_source_seek(za->src, (zip_int64_t)cd->entry[i].orig->offset, SEEK_SET) < 0) {
575
0
            zip_error_set_from_source(error, za->src);
576
0
            return -1;
577
0
        }
578
579
0
        if (_zip_dirent_read(&temp, za->src, NULL, true, cd->is_zip64, cd->entry[i].orig->comp_size, true, error) == -1) {
580
0
            if (zip_error_code_zip(error) == ZIP_ER_INCONS) {
581
0
                zip_error_set(error, ZIP_ER_INCONS, ADD_INDEX_TO_DETAIL(zip_error_code_system(error), i));
582
0
            }
583
0
            _zip_dirent_finalize(&temp);
584
0
            return -1;
585
0
        }
586
587
0
        if (_zip_headercomp(cd->entry[i].orig, &temp) != 0) {
588
0
            zip_error_set(error, ZIP_ER_INCONS, MAKE_DETAIL_WITH_INDEX(ZIP_ER_DETAIL_ENTRY_HEADER_MISMATCH, i));
589
0
            _zip_dirent_finalize(&temp);
590
0
            return -1;
591
0
        }
592
593
0
        cd->entry[i].orig->extra_fields.local = temp.extra_fields.local;
594
0
        cd->entry[i].orig->local_extra_fields_read = 1;
595
0
        temp.extra_fields.local = NULL;
596
597
0
        _zip_dirent_finalize(&temp);
598
599
0
        if ((detail = zip_dirent_check_consistency(cd->entry[i].orig)) != 0) {
600
0
            zip_error_set(error, ZIP_ER_INCONS, MAKE_DETAIL_WITH_INDEX(detail, i));
601
0
            return -1;
602
0
        }
603
0
    }
604
605
0
    return (max - min) < ZIP_INT64_MAX ? (zip_int64_t)(max - min) : ZIP_INT64_MAX;
606
0
}
607
608
609
/* _zip_headercomp:
610
   compares a central directory entry and a local file header
611
   Return 0 if they are consistent, -1 if not. */
612
613
0
static int _zip_headercomp(const zip_dirent_t *central, const zip_dirent_t *local) {
614
0
    if ((central->version_needed < local->version_needed)
615
#if 0
616
  /* some zip-files have different values in local
617
     and global headers for the bitflags */
618
  || (central->bitflags != local->bitflags)
619
#endif
620
0
        || (central->comp_method != local->comp_method) || (central->last_mod.time != local->last_mod.time) || (central->last_mod.date != local->last_mod.date) || !_zip_string_equal(central->filename, local->filename))
621
0
        return -1;
622
623
0
    if ((central->crc != local->crc) || (central->comp_size != local->comp_size) || (central->uncomp_size != local->uncomp_size)) {
624
        /* InfoZip stores valid values in local header even when data descriptor is used.
625
           This is in violation of the appnote.
626
           macOS Archive sets the compressed size even when data descriptor is used ( but not the others),
627
           also in violation of the appnote.
628
        */
629
        /* if data descriptor is not used, the values must match */
630
0
        if ((local->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0) {
631
0
            return -1;
632
0
        }
633
        /* when using a data descriptor, the local header value must be zero or match */
634
0
        if ((local->crc != 0 && central->crc != local->crc) || (local->comp_size != 0 && central->comp_size != local->comp_size) || (local->uncomp_size != 0 && central->uncomp_size != local->uncomp_size)) {
635
0
            return -1;
636
0
        }
637
0
    }
638
639
0
    return 0;
640
0
}
641
642
643
8.78k
static zip_t *_zip_allocate_new(zip_source_t *src, unsigned int flags, zip_error_t *error) {
644
8.78k
    zip_t *za;
645
646
8.78k
    if ((za = _zip_new(error)) == NULL) {
647
0
        return NULL;
648
0
    }
649
650
8.78k
    za->src = src;
651
8.78k
    za->open_flags = flags;
652
8.78k
    za->flags = 0;
653
8.78k
    za->ch_flags = 0;
654
8.78k
    za->write_crc = NULL;
655
656
8.78k
    if (flags & ZIP_RDONLY) {
657
0
        za->flags |= ZIP_AFL_RDONLY;
658
0
        za->ch_flags |= ZIP_AFL_RDONLY;
659
0
    }
660
661
8.78k
    return za;
662
8.78k
}
663
664
665
/*
666
 * tests for file existence
667
 */
668
8.78k
static exists_t _zip_file_exists(zip_source_t *src, zip_error_t *error) {
669
8.78k
    struct zip_stat st;
670
671
8.78k
    zip_stat_init(&st);
672
8.78k
    if (zip_source_stat(src, &st) != 0) {
673
0
        zip_error_t *src_error = zip_source_error(src);
674
0
        if (zip_error_code_zip(src_error) == ZIP_ER_READ && zip_error_code_system(src_error) == ENOENT) {
675
0
            return EXISTS_NOT;
676
0
        }
677
0
        _zip_error_copy(error, src_error);
678
0
        return EXISTS_ERROR;
679
0
    }
680
681
8.78k
    return EXISTS_OK;
682
8.78k
}
683
684
685
8.78k
static zip_cdir_t *_zip_find_central_dir(zip_t *za, zip_uint64_t len) {
686
8.78k
    zip_cdir_t *cdir;
687
8.78k
    const zip_uint8_t *match;
688
8.78k
    zip_int64_t buf_offset;
689
8.78k
    zip_uint64_t buflen;
690
8.78k
    zip_error_t error;
691
8.78k
    zip_buffer_t *buffer;
692
693
8.78k
    if (len < EOCDLEN) {
694
349
        zip_error_set(&za->error, ZIP_ER_NOZIP, 0);
695
349
        return NULL;
696
349
    }
697
698
8.43k
    buflen = (len < CDBUFSIZE ? len : CDBUFSIZE);
699
8.43k
    if (zip_source_seek(za->src, -(zip_int64_t)buflen, SEEK_END) < 0) {
700
0
        zip_error_t *src_error = zip_source_error(za->src);
701
0
        if (zip_error_code_zip(src_error) != ZIP_ER_SEEK || zip_error_code_system(src_error) != EFBIG) {
702
            /* seek before start of file on my machine */
703
0
            _zip_error_copy(&za->error, src_error);
704
0
            return NULL;
705
0
        }
706
0
    }
707
8.43k
    if ((buf_offset = zip_source_tell(za->src)) < 0) {
708
0
        zip_error_set_from_source(&za->error, za->src);
709
0
        return NULL;
710
0
    }
711
712
8.43k
    if ((buffer = _zip_buffer_new_from_source(za->src, buflen, NULL, &za->error)) == NULL) {
713
0
        return NULL;
714
0
    }
715
716
8.43k
    cdir = NULL;
717
8.43k
    if (buflen >= CDBUFSIZE) {
718
        /* EOCD64 locator is before EOCD, so leave place for it */
719
437
        _zip_buffer_set_offset(buffer, EOCD64LOCLEN);
720
437
    }
721
8.43k
    zip_error_set(&error, ZIP_ER_NOZIP, 0);
722
723
8.43k
    match = NULL;
724
59.9k
    while ((match = find_eocd(buffer, match)) != NULL) {
725
58.9k
        _zip_buffer_set_offset(buffer, (zip_uint64_t)(match - _zip_buffer_data(buffer)));
726
58.9k
        if (_zip_read_cdir(za, buffer, (zip_uint64_t)buf_offset, &cdir, &error)) {
727
7.47k
            if (cdir != NULL && (za->open_flags & ZIP_CHECKCONS) && _zip_checkcons(za, cdir, &error) < 0) {
728
0
                _zip_cdir_free(cdir);
729
0
                cdir = NULL;
730
0
            }
731
7.47k
            break;
732
7.47k
        }
733
58.9k
    }
734
735
8.43k
    _zip_buffer_free(buffer);
736
737
8.43k
    if (cdir == NULL) {
738
3.80k
        _zip_error_copy(&za->error, &error);
739
3.80k
    }
740
8.43k
    return cdir;
741
8.43k
}
742
743
744
59.9k
static const unsigned char *find_eocd(zip_buffer_t *buffer, const unsigned char *last) {
745
59.9k
    const unsigned char *data = _zip_buffer_data(buffer);
746
59.9k
    const unsigned char *p;
747
748
59.9k
    if (last == NULL) {
749
8.43k
        if (_zip_buffer_size(buffer) < MAGIC_LEN) {
750
0
            return NULL;
751
0
        }
752
8.43k
        last = data + _zip_buffer_size(buffer) - MAGIC_LEN;
753
8.43k
    }
754
51.5k
    else {
755
51.5k
        if (last == _zip_buffer_data(buffer)) {
756
177
            return NULL;
757
177
        }
758
51.3k
        last -= 1;
759
51.3k
    }
760
761
16.9M
    for (p = last; p >= data; p -= 1) {
762
16.9M
        if (*p == EOCD_MAGIC[0]) {
763
213k
            if (memcmp(p, EOCD_MAGIC, MAGIC_LEN) == 0) {
764
58.9k
                return p;
765
58.9k
            }
766
213k
        }
767
16.8M
        if (p == data) {
768
            /* Avoid undefined behavior by creating pointer outside buffer */
769
791
            break;
770
791
        }
771
16.8M
    }
772
773
791
    return NULL;
774
59.7k
}
775
776
777
58.9k
static zip_cdir_t *_zip_read_eocd(zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error) {
778
58.9k
    zip_cdir_t *cd;
779
780
58.9k
    if (_zip_buffer_left(buffer) < EOCDLEN) {
781
186
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD_LENGTH_INVALID);
782
186
        return NULL;
783
186
    }
784
785
58.7k
    if ((cd = _zip_cdir_new(error)) == NULL) {
786
0
        return NULL;
787
0
    }
788
789
58.7k
    cd->eocd_offset = buf_offset + _zip_buffer_offset(buffer);
790
    /* This function is only called where EOCD magic was found, so no need to check that here. */
791
58.7k
    _zip_buffer_skip(buffer, MAGIC_LEN);
792
58.7k
    cd->is_zip64 = false;
793
58.7k
    cd->this_disk = _zip_buffer_get_16(buffer);
794
58.7k
    cd->eocd_disk = _zip_buffer_get_16(buffer);
795
796
    /* number of cdir-entries on this disk */
797
58.7k
    cd->disk_entries = _zip_buffer_get_16(buffer);
798
    /* number of cdir-entries */
799
58.7k
    cd->num_entries = _zip_buffer_get_16(buffer);
800
58.7k
    cd->size = _zip_buffer_get_32(buffer);
801
58.7k
    cd->offset = _zip_buffer_get_32(buffer);
802
803
58.7k
    return cd;
804
58.7k
}
805
806
6.11k
static bool check_eocd(zip_cdir_t *cd, unsigned int flags, zip_error_t *error) {
807
6.11k
    if (cd->disk_entries != cd->num_entries || cd->this_disk != 0 || cd->eocd_disk != 0) {
808
109
        zip_error_set(error, ZIP_ER_MULTIDISK, 0);
809
109
        return false;
810
109
    }
811
812
6.00k
    if (cd->offset + cd->size < cd->offset) {
813
0
        zip_error_set(error, ZIP_ER_SEEK, EFBIG);
814
0
        return false;
815
0
    }
816
817
6.00k
    if ((flags & ZIP_CHECKCONS) && cd->offset + cd->size != cd->eocd_offset) {
818
0
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_OVERLAPS_EOCD);
819
0
        return false;
820
0
    }
821
822
6.00k
    return true;
823
6.00k
}
824
825
826
2.58k
cdir_status_t _zip_read_eocd64(zip_cdir_t *cdir, zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error) {
827
2.58k
    zip_uint64_t offset;
828
2.58k
    zip_uint8_t eocd[EOCD64LEN];
829
2.58k
    zip_uint64_t eocd_offset;
830
2.58k
    zip_uint64_t size, nentry, i, eocdloc_offset;
831
2.58k
    bool free_buffer;
832
2.58k
    zip_uint32_t num_disks, eocd_disk, this_disk;
833
834
    /* The offset of the end of the buffer is less than ZIP_UINT64_MAX, so this can't overflow. */
835
2.58k
    eocdloc_offset = buf_offset + _zip_buffer_offset(buffer);
836
837
2.58k
    _zip_buffer_get(buffer, 4); /* magic already verified */
838
839
2.58k
    eocd_disk = _zip_buffer_get_32(buffer);
840
2.58k
    eocd_offset = _zip_buffer_get_64(buffer);
841
2.58k
    num_disks = _zip_buffer_get_32(buffer);
842
843
2.58k
    if (!check_magic(eocd_offset, buffer, buf_offset, src, EOCD64_MAGIC)) {
844
1.04k
        return CDIR_NOT_FOUND;
845
1.04k
    }
846
847
1.54k
    if (num_disks != 1) {
848
58
        zip_error_set(error, ZIP_ER_MULTIDISK, 0);
849
58
        return CDIR_INVALID;
850
58
    }
851
852
    /* valid seek value for start of EOCD */
853
1.48k
    if (eocd_offset > ZIP_INT64_MAX) {
854
0
        zip_error_set(error, ZIP_ER_SEEK, EFBIG);
855
0
        return CDIR_INVALID;
856
0
    }
857
858
    /* does EOCD fit before EOCD locator? */
859
1.48k
    if (eocd_offset + EOCD64LEN > eocdloc_offset) {
860
23
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_OVERLAPS_EOCD);
861
23
        return CDIR_INVALID;
862
23
    }
863
864
    /* make sure current position of buffer is beginning of EOCD */
865
1.45k
    if (eocd_offset >= buf_offset && eocd_offset + EOCD64LEN <= buf_offset + _zip_buffer_size(buffer)) {
866
1.39k
        _zip_buffer_set_offset(buffer, eocd_offset - buf_offset);
867
1.39k
        free_buffer = false;
868
1.39k
    }
869
62
    else {
870
62
        if (zip_source_seek(src, (zip_int64_t)eocd_offset, SEEK_SET) < 0) {
871
0
            zip_error_set_from_source(error, src);
872
0
            return CDIR_INVALID;
873
0
        }
874
62
        if ((buffer = _zip_buffer_new_from_source(src, EOCD64LEN, eocd, error)) == NULL) {
875
0
            return CDIR_INVALID;
876
0
        }
877
62
        free_buffer = true;
878
62
    }
879
880
1.45k
    if (memcmp(_zip_buffer_get(buffer, 4), EOCD64_MAGIC, 4) != 0) {
881
0
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_WRONG_MAGIC);
882
0
        if (free_buffer) {
883
0
            _zip_buffer_free(buffer);
884
0
        }
885
0
        return CDIR_INVALID;
886
0
    }
887
888
    /* size of EOCD */
889
1.45k
    size = _zip_buffer_get_64(buffer);
890
891
    /* Is there a hole between EOCD and EOCD locator, or do they overlap? Also check for overflow. */
892
1.45k
    if ((flags & ZIP_CHECKCONS) && (ZIP_CHECK_ADD_OVERFLOW(size, eocd_offset + 12) || size + eocd_offset + 12 != eocdloc_offset)) {
893
0
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_OVERLAPS_EOCD);
894
0
        if (free_buffer) {
895
0
            _zip_buffer_free(buffer);
896
0
        }
897
0
        return CDIR_INVALID;
898
0
    }
899
900
1.45k
    _zip_buffer_get(buffer, 4); /* skip version made by/needed */
901
902
1.45k
    this_disk = _zip_buffer_get_32(buffer);
903
1.45k
    if (_zip_buffer_get_32(buffer) != eocd_disk) {
904
153
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_LOCATOR_MISMATCH);
905
153
        if (free_buffer) {
906
25
            _zip_buffer_free(buffer);
907
25
        }
908
153
        return CDIR_INVALID;
909
153
    }
910
911
1.30k
    i = _zip_buffer_get_64(buffer);
912
1.30k
    nentry = _zip_buffer_get_64(buffer);
913
914
1.30k
    if (nentry != i) {
915
228
        zip_error_set(error, ZIP_ER_MULTIDISK, 0);
916
228
        if (free_buffer) {
917
22
            _zip_buffer_free(buffer);
918
22
        }
919
228
        return CDIR_INVALID;
920
228
    }
921
922
1.07k
    size = _zip_buffer_get_64(buffer);
923
1.07k
    offset = _zip_buffer_get_64(buffer);
924
925
    /* did we read past the end of the buffer? */
926
1.07k
    if (!_zip_buffer_ok(buffer)) {
927
0
        zip_error_set(error, ZIP_ER_INTERNAL, 0);
928
0
        if (free_buffer) {
929
0
            _zip_buffer_free(buffer);
930
0
        }
931
0
        return CDIR_INVALID;
932
0
    }
933
934
1.07k
    if (free_buffer) {
935
15
        _zip_buffer_free(buffer);
936
15
    }
937
938
1.07k
    if (offset > ZIP_INT64_MAX || offset + size < offset) {
939
108
        zip_error_set(error, ZIP_ER_SEEK, EFBIG);
940
108
        return CDIR_INVALID;
941
108
    }
942
943
970
    if (nentry > size / CDENTRYSIZE) {
944
146
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_INVALID);
945
146
        return CDIR_INVALID;
946
146
    }
947
948
824
    if ((cdir->size != 0xffffffff && cdir->size != size) || (cdir->offset != 0xffffffff && cdir->offset != offset) || (cdir->num_entries != 0xffff && cdir->num_entries != nentry) || (cdir->disk_entries != 0xffff && cdir->disk_entries != i) || (cdir->this_disk != 0xffff && cdir->this_disk != this_disk) || (cdir->eocd_disk != 0xffff && cdir->eocd_disk != eocd_disk)) {
949
643
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_MISMATCH);
950
643
        return CDIR_INVALID;
951
643
    }
952
953
181
    cdir->is_zip64 = true;
954
181
    cdir->size = size;
955
181
    cdir->offset = offset;
956
181
    cdir->disk_entries = i;
957
181
    cdir->num_entries = nentry;
958
181
    cdir->this_disk = this_disk;
959
181
    cdir->eocd_disk = eocd_disk;
960
181
    cdir->eocd_offset = eocd_offset;
961
962
181
    return CDIR_OK;
963
824
}
964
965
966
646
static int decode_hex(char c) {
967
646
    if (c >= '0' && c <= '9') {
968
401
        return c - '0';
969
401
    }
970
245
    else if (c >= 'A' && c <= 'F') {
971
156
        return c - 'A' + 10;
972
156
    }
973
89
    else {
974
89
        return -1;
975
89
    }
976
646
}
977
978
/* _zip_check_torrentzip:
979
   check whether ZA has a valid TORRENTZIP comment, i.e. is torrentzipped */
980
981
4.63k
static void zip_check_torrentzip(zip_t *za, const zip_cdir_t *cdir) {
982
4.63k
    zip_uint32_t crc_should;
983
4.63k
    char buf[8 + 1];
984
4.63k
    size_t i;
985
986
4.63k
    if (cdir == NULL) {
987
0
        return;
988
0
    }
989
990
4.63k
    if (_zip_string_length(cdir->comment) != TORRENTZIP_SIGNATURE_LENGTH + TORRENTZIP_CRC_LENGTH || strncmp((const char *)cdir->comment->raw, TORRENTZIP_SIGNATURE, TORRENTZIP_SIGNATURE_LENGTH) != 0) {
991
4.51k
        return;
992
4.51k
    }
993
994
118
    memcpy(buf, cdir->comment->raw + TORRENTZIP_SIGNATURE_LENGTH, TORRENTZIP_CRC_LENGTH);
995
118
    buf[TORRENTZIP_CRC_LENGTH] = '\0';
996
118
    crc_should = 0;
997
380
    for (i = 0; i < TORRENTZIP_CRC_LENGTH; i += 2) {
998
323
        int low, high;
999
323
        high = decode_hex((buf[i]));
1000
323
        low = decode_hex(buf[i + 1]);
1001
323
        if (high < 0 || low < 0) {
1002
61
            return;
1003
61
        }
1004
262
        crc_should = (crc_should << 8) + (high << 4) + low;
1005
262
    }
1006
1007
57
    {
1008
57
        zip_stat_t st;
1009
57
        zip_source_t *src_window;
1010
57
        zip_source_t *src_crc;
1011
57
        zip_uint8_t buffer[512];
1012
57
        zip_int64_t ret;
1013
1014
57
        zip_stat_init(&st);
1015
57
        st.valid |= ZIP_STAT_SIZE | ZIP_STAT_CRC;
1016
57
        st.size = cdir->size;
1017
57
        st.crc = crc_should;
1018
57
        if ((src_window = _zip_source_window_new(za->src, cdir->offset, cdir->size, &st, 0, NULL, NULL, NULL, 0, false, NULL)) == NULL) {
1019
0
            return;
1020
0
        }
1021
57
        if ((src_crc = zip_source_crc_create(src_window, 1, NULL)) == NULL) {
1022
0
            zip_source_free(src_window);
1023
0
            return;
1024
0
        }
1025
57
        if (zip_source_open(src_crc) != 0) {
1026
0
            zip_source_free(src_crc);
1027
0
            return;
1028
0
        }
1029
106
        while ((ret = zip_source_read(src_crc, buffer, sizeof(buffer))) > 0) {
1030
49
        }
1031
57
        zip_source_free(src_crc);
1032
57
        if (ret < 0) {
1033
57
            return;
1034
57
        }
1035
57
    }
1036
1037
    /* TODO: if check consistency, check cdir entries for valid values */
1038
0
    za->flags |= ZIP_AFL_IS_TORRENTZIP;
1039
0
}