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
+ (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
 */
- initWithPath: (OFString*)path;

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







|







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
 */
- 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

#import "OFZIPArchive.h"
#import "OFZIPArchiveEntry.h"
#import "OFZIPArchiveEntry+Private.h"
#import "OFDataArray.h"
#import "OFDictionary.h"
#import "OFFile.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).
 */

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








>
















<
<




|







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:


 *  - 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.
 *  - 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

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

@interface OFZIPArchive_FileStream: OFStream
{

	OFFile *_file;


	size_t _size;
	uint32_t _expectedCRC32, _CRC32;
	bool _atEndOfStream;
}

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

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








>

>
>

|





|
<







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 _CRC32;
	bool _atEndOfStream;
}

- initWithArchiveFile: (OFString*)path
	       offset: (off_t)offset
      localFileHeader: (OFZIPArchive_LocalFileHeader*)localFileHeader;

@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
}

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













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

	@try {
		_file = [[OFFile alloc] initWithPath: path







>
>
>
>
>
>
>
>
>
>
>
>







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
		     whence: SEEK_SET];
	localFileHeader = [[[OFZIPArchive_LocalFileHeader alloc]
	    initWithFile: _file] autorelease];

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

	if (localFileHeader->_minVersion > 10) {
		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];

	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}
@end








|








<
<
<
<




<
|







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 > 20) {
		OFString *version = [OFString stringWithFormat: @"%u.%u",
		    localFileHeader->_minVersion / 10,
		    localFileHeader->_minVersion % 10];

		@throw [OFUnsupportedVersionException
		    exceptionWithVersion: version];
	}





	ret = [[OFZIPArchive_FileStream alloc]
	    initWithArchiveFile: _path
			 offset: [_file seekToOffset: 0
					      whence: SEEK_CUR]

	        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

- (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]])
		return false;

	return true;
}

@end

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

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


















		_size = size;
		_CRC32 = ~0;
		_expectedCRC32 = CRC32;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{

	[_file 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 (_size == 0) {
		_atEndOfStream = true;





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

		return 0;
	}

	if (length < _size)

		min = length;
	else
		min = _size;









	ret = [_file readIntoBuffer: buffer
			     length: min];
	_size -= ret;


	_CRC32 = crc32(_CRC32, buffer, ret);

	return ret;
}
@end







|
>
>
>
|
|
|
>
>
|




<





|
<









>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|

<










>

>


















>
>
>
|
|

>
>
>
>
|
|

|
|

<
>
|
|
|
>

>
>
>
>
>
>
>
|
|
|
>
>





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])
		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
      localFileHeader: (OFZIPArchive_LocalFileHeader*)localFileHeader

{
	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 = localFileHeader->_uncompressedSize;
		_CRC32 = ~0;

	} @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;

			_atEndOfStream = true;

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

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

			return 0;
		}


		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 = [_stream readIntoBuffer: buffer
				       length: min];
		_size -= ret;
	}

	_CRC32 = crc32(_CRC32, buffer, ret);

	return ret;
}
@end