ObjFW  Check-in [53e46a8326]

Overview
Comment:OFZIPArchive: Add support for deflate.

Also adds support for data descriptors.

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 53e46a8326189a142c3a89bad7e248bfe821354b80ec00b9b748850e05349c8c
User & Date: js on 2013-10-10 13:36:13
Other Links: manifest | tags
Context
2013-10-10
21:33
OFDeflateStream: Fix uint_fast16_t != uint16_t. check-in: 5dbf9298bd user: js tags: trunk
13:36
OFZIPArchive: Add support for deflate. check-in: 53e46a8326 user: js tags: trunk
13:18
Add OFDeflateStream. check-in: d83d3aa719 user: js tags: trunk
Changes

Modified src/OFZIPArchive.h from [2b8cc476e1] to [ae45487a3e].

50
51
52
53
54
55
56
57

58
59
60
61
62
63
64
50
51
52
53
54
55
56

57
58
59
60
61
62
63
64







-
+







+ (instancetype)archiveWithPath: (OFString*)path;

/*!
 * @brief Initializes an already allocated OFZIPArchive object for the
 *	  specified file.
 *
 * @param path The path to the ZIP file
 * @return An Initialized OFZIPArchive
 * @return An initialized OFZIPArchive
 */
- initWithPath: (OFString*)path;

/*!
 * @brief Returns the entries in the central directory of the archive as a
 * 	  dictionary.
 *

Modified src/OFZIPArchive.m from [27f1943d33] to [0ac2251133].

20
21
22
23
24
25
26

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

50
51
52
53
54
55
56
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43


44
45
46
47

48
49
50
51
52
53
54
55







+
















-
-




-
+








#import "OFZIPArchive.h"
#import "OFZIPArchiveEntry.h"
#import "OFZIPArchiveEntry+Private.h"
#import "OFDataArray.h"
#import "OFDictionary.h"
#import "OFFile.h"
#import "OFDeflateStream.h"

#import "OFChecksumFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFNotImplementedException.h"
#import "OFOpenFileFailedException.h"
#import "OFReadFailedException.h"
#import "OFUnsupportedVersionException.h"

#import "autorelease.h"
#import "macros.h"

#define CRC32_MAGIC 0xEDB88320

/*
 * FIXME: Current limitations:
 *  - Compressed files cannot be read.
 *  - Encrypted files cannot be read.
 *  - Split archives are not supported.
 *  - Write support is missing.
 *  - The ZIP has to be a file on the local file system.
 *  - No support for ZIP64.
 *  - No support for data descriptors (useless without compression anyway).
 *  - Encrypted files cannot be read.
 */

@interface OFZIPArchive (OF_PRIVATE_CATEGORY)
- (void)OF_readZIPInfo;
- (void)OF_readEntries;
@end

66
67
68
69
70
71
72

73


74
75

76
77
78
79
80
81

82
83
84
85
86
87
88
89
65
66
67
68
69
70
71
72
73
74
75
76

77
78
79
80
81
82

83

84
85
86
87
88
89
90







+

+
+

-
+





-
+
-








- initWithFile: (OFFile*)file;
- (bool)matchesEntry: (OFZIPArchiveEntry*)entry;
@end

@interface OFZIPArchive_FileStream: OFStream
{
	OFStream *_stream;
	OFFile *_file;
	OFZIPArchive_LocalFileHeader *_localFileHeader;
	bool _hasDataDescriptor;
	size_t _size;
	uint32_t _expectedCRC32, _CRC32;
	uint32_t _CRC32;
	bool _atEndOfStream;
}

- initWithArchiveFile: (OFString*)path
	       offset: (off_t)offset
		 size: (size_t)size
      localFileHeader: (OFZIPArchive_LocalFileHeader*)localFileHeader;
		CRC32: (uint32_t)CRC32;
@end

static uint32_t
crc32(uint32_t crc, uint8_t *bytes, size_t length)
{
	size_t i;

100
101
102
103
104
105
106












107
108
109
110
111
112
113
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126







+
+
+
+
+
+
+
+
+
+
+
+







}

@implementation OFZIPArchive
+ (instancetype)archiveWithPath: (OFString*)path
{
	return [[[self alloc] initWithPath: path] autorelease];
}

- init
{
	@try {
		[self doesNotRecognizeSelector: _cmd];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	abort();
}

- initWithPath: (OFString*)path
{
	self = [super init];

	@try {
		_file = [[OFFile alloc] initWithPath: path
213
214
215
216
217
218
219
220

221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238

239
240
241
242
243
244
245
226
227
228
229
230
231
232

233
234
235
236
237
238
239
240
241




242
243
244
245


246
247
248
249
250
251
252
253







-
+








-
-
-
-




-
-
+







		     whence: SEEK_SET];
	localFileHeader = [[[OFZIPArchive_LocalFileHeader alloc]
	    initWithFile: _file] autorelease];

	if (![localFileHeader matchesEntry: entry])
		@throw [OFInvalidFormatException exception];

	if (localFileHeader->_minVersion > 10) {
	if (localFileHeader->_minVersion > 20) {
		OFString *version = [OFString stringWithFormat: @"%u.%u",
		    localFileHeader->_minVersion / 10,
		    localFileHeader->_minVersion % 10];

		@throw [OFUnsupportedVersionException
		    exceptionWithVersion: version];
	}

	if (localFileHeader->_compressionMethod != 0)
		@throw [OFNotImplementedException exceptionWithSelector: _cmd
								 object: self];

	ret = [[OFZIPArchive_FileStream alloc]
	    initWithArchiveFile: _path
			 offset: [_file seekToOffset: 0
					      whence: SEEK_CUR]
			   size: localFileHeader->_uncompressedSize
			  CRC32: localFileHeader->_CRC32];
	        localFileHeader: localFileHeader];

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}
@end

291
292
293
294
295
296
297
298
299
300
301
302










303
304
305
306
307
308
309
310
311
312
313

314
315
316
317
318
319
320
321
322
323

















324

325
326
327
328
329
330
331
332
333
334
335
336

337

338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355



356
357


358




359
360


361
362
363


364
365
366
367
368





369







370
371
372





373
374
375
376
377
299
300
301
302
303
304
305





306
307
308
309
310
311
312
313
314
315
316
317
318
319

320
321
322
323
324

325

326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351

352
353

354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387


388
389
390
391
392
393
394


395
396
397


398
399
400




401
402
403
404
405
406
407
408
409
410
411
412
413



414
415
416
417
418
419
420
421
422
423







-
-
-
-
-
+
+
+
+
+
+
+
+
+
+




-





-
+
-









+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+

-










+

+


















+
+
+
-
-
+
+

+
+
+
+
-
-
+
+

-
-
+
+

-
-
-
-
+
+
+
+
+

+
+
+
+
+
+
+
-
-
-
+
+
+
+
+






- (bool)matchesEntry: (OFZIPArchiveEntry*)entry
{
	if (_minVersion != [entry OF_minVersion] ||
	    _generalPurposeBitFlag != [entry OF_generalPurposeBitFlag] ||
	    _compressionMethod != [entry OF_compressionMethod] ||
	    _lastModifiedFileTime != [entry OF_lastModifiedFileTime] ||
	    _lastModifiedFileDate != [entry OF_lastModifiedFileDate] ||
	    _CRC32 != [entry CRC32] ||
	    _compressedSize != [entry compressedSize] ||
	    _uncompressedSize != [entry uncompressedSize] ||
	    ![_fileName isEqual: [entry fileName]])
	    _lastModifiedFileDate != [entry OF_lastModifiedFileDate])
		return false;

	if (!(_generalPurposeBitFlag & (1 << 3)))
		if (_CRC32 != [entry CRC32] ||
		    _compressedSize != [entry compressedSize] ||
		    _uncompressedSize != [entry uncompressedSize])
			return false;

	if (![_fileName isEqual: [entry fileName]])
		return false;

	return true;
}

@end

@implementation OFZIPArchive_FileStream
- initWithArchiveFile: (OFString*)path
	       offset: (off_t)offset
		 size: (size_t)size
      localFileHeader: (OFZIPArchive_LocalFileHeader*)localFileHeader
		CRC32: (uint32_t)CRC32
{
	self = [super init];

	@try {
		_file = [[OFFile alloc] initWithPath: path
						mode: @"rb"];
		[_file seekToOffset: offset
			     whence: SEEK_SET];

		switch (localFileHeader->_compressionMethod) {
		case 0: /* No compression */
			_stream = [_file retain];
			break;
		case 8: /* Deflate */
			_stream = [[OFDeflateStream alloc]
			    initWithStream: _file];
			break;
		default:
			@throw [OFNotImplementedException
			    exceptionWithSelector: _cmd
					   object: self];
		}

		_localFileHeader = [localFileHeader retain];
		_hasDataDescriptor = (localFileHeader->_generalPurposeBitFlag &
		    (1 << 3));
		_size = size;
		_size = localFileHeader->_uncompressedSize;
		_CRC32 = ~0;
		_expectedCRC32 = CRC32;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_stream release];
	[_file release];
	[_localFileHeader release];

	[super dealloc];
}

- (bool)lowlevelIsAtEndOfStream
{
	return _atEndOfStream;
}

- (size_t)lowlevelReadIntoBuffer: (void*)buffer
			  length: (size_t)length
{
	size_t min, ret;

	if (_atEndOfStream)
		@throw [OFReadFailedException exceptionWithStream: self
						  requestedLength: length];

	if (_hasDataDescriptor) {
		if ([_stream isAtEndOfStream]) {
			uint32_t CRC32;
	if (_size == 0) {
		_atEndOfStream = true;

			_atEndOfStream = true;

			CRC32 = [_file readLittleEndianInt32];
			if (CRC32 == 0x08074B50)
				CRC32 = [_file readLittleEndianInt32];

		if (~_CRC32 != _expectedCRC32)
			@throw [OFChecksumFailedException exception];
			if (~_CRC32 != CRC32)
				@throw [OFChecksumFailedException exception];

		return 0;
	}
			return 0;
		}

	if (length < _size)
		min = length;
	else
		min = _size;
		ret = [_stream readIntoBuffer: buffer
				       length: length];
	} else {
		if (_size == 0) {
			_atEndOfStream = true;

			if (~_CRC32 != _localFileHeader->_CRC32)
				@throw [OFChecksumFailedException exception];

			return 0;
		}

		min = (length < _size ? length : _size);
	ret = [_file readIntoBuffer: buffer
			     length: min];
	_size -= ret;
		ret = [_stream readIntoBuffer: buffer
				       length: min];
		_size -= ret;
	}

	_CRC32 = crc32(_CRC32, buffer, ret);

	return ret;
}
@end