| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574 | 
							- //	Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
 
- //
 
- //	This file is part of libzipper.
 
- //
 
- //	libzipper is free software: you can redistribute it and/or modify
 
- //	it under the terms of the GNU General Public License as published by
 
- //	the Free Software Foundation, either version 3 of the License, or
 
- //	(at your option) any later version.
 
- //
 
- //	libzipper is distributed in the hope that it will be useful,
 
- //	but WITHOUT ANY WARRANTY; without even the implied warranty of
 
- //	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
- //	GNU General Public License for more details.
 
- //
 
- //	You should have received a copy of the GNU General Public License
 
- //	along with libzipper.  If not, see <http://www.gnu.org/licenses/>.
 
- #include "zipper.hh"
 
- #include "zip.hh"
 
- #include "util.hh"
 
- #include "deflate.hh"
 
- #include <algorithm>
 
- #include <cassert>
 
- #include <iostream>
 
- #include <time.h>
 
- #include <string.h>
 
- #include "config.h"
 
- using namespace zipper;
 
- namespace
 
- {
 
- 	time_t convertDosDateTime(uint16_t date, uint16_t time)
 
- 	{
 
- 		struct tm parts;
 
- 		memset(&parts, 0, sizeof(parts));
 
- 		parts.tm_sec = time & 0x1F;
 
- 		parts.tm_min = (time & 0x7E0) >> 5;
 
- 		parts.tm_hour = (time  >> 11);
 
- 		parts.tm_mday = date & 0x1F;
 
- 		parts.tm_mon = ((date & 0x1E0) >> 5) - 1;
 
- 		parts.tm_year = (date >> 9) + 80;
 
- 		return mktime(&parts);
 
- 	}
 
- 	void convertDosDateTime(time_t in, uint16_t& date, uint16_t& time)
 
- 	{
 
- 		struct tm buf;
 
- 		struct tm* parts(localtime_r(&in, &buf));
 
- 		time =
 
- 			parts->tm_sec +
 
- 			(parts->tm_min << 5) +
 
- 			(parts->tm_hour << 11);
 
- 		date =
 
- 			parts->tm_mday +
 
- 			((parts->tm_mon + 1) << 5) +
 
- 			((parts->tm_year - 80) << 9);
 
- 	}
 
- 	class FileEntry : public CompressedFile
 
- 	{
 
- 	public:
 
- 		FileEntry(
 
- 			const ReaderPtr& reader,
 
- 			uint16_t versionNeeded,
 
- 			uint16_t gpFlag,
 
- 			uint16_t compressionMethod,
 
- 			uint32_t crc,
 
- 			zsize_t compressedSize,
 
- 			zsize_t uncompressedSize,
 
- 			time_t modTime,
 
- 			zsize_t localHeaderOffset,
 
- 			std::string fileName
 
- 			) :
 
- 			m_reader(reader),
 
- 			m_versionNeeded(versionNeeded),
 
- 			m_gpFlag(gpFlag),
 
- 			m_compressionMethod(compressionMethod),
 
- 			m_crc(crc),
 
- 			m_compressedSize(compressedSize),
 
- 			m_uncompressedSize(uncompressedSize),
 
- 			m_localHeaderOffset(localHeaderOffset),
 
- 			m_fileName(fileName)
 
- 		{
 
- 			m_modTime.tv_sec = modTime;
 
- 			m_modTime.tv_usec = 0;
 
- 		}
 
- 		virtual bool isDecompressSupported() const
 
- 		{
 
- 			return ((m_versionNeeded & 0xf) <= 20) &&
 
- 				((m_gpFlag & 0x1) == 0) && // Not encrypted
 
- 				((m_compressionMethod == 0) || (m_compressionMethod == 8));
 
- 		}
 
- 		virtual const std::string& getPath() const
 
- 		{
 
- 			return m_fileName;
 
- 		}
 
- 		virtual zsize_t getCompressedSize() const { return m_compressedSize; }
 
- 		virtual zsize_t getUncompressedSize() const
 
- 		{
 
- 			return m_uncompressedSize;
 
- 		}
 
- 		virtual const timeval& getModificationTime() const { return m_modTime; }
 
- 		virtual void decompress(Writer& writer)
 
- 		{
 
- 			enum
 
- 			{
 
- 				Signature = 0x04034b50,
 
- 				MinRecordBytes = 30,
 
- 				ChunkSize = 64*1024
 
- 			};
 
- 			std::vector<uint8_t> localRecord(MinRecordBytes);
 
- 			m_reader->readData(
 
- 				m_localHeaderOffset, MinRecordBytes, &localRecord[0]
 
- 				);
 
- 			if (read32_le(localRecord, 0) != Signature)
 
- 			{
 
- 				throw FormatException("Invalid local ZIP record");
 
- 			}
 
- 			// Don't trust the lengths for filename and extra content read from
 
- 			// the central records.  At least for extra, these DO differ for
 
- 			// unknown reasons
 
- 			zsize_t filenameLength(read16_le(localRecord, 26));
 
- 			zsize_t extraLength(read16_le(localRecord, 28));
 
- 			zsize_t startCompressedBytes(
 
- 				m_localHeaderOffset +
 
- 				MinRecordBytes +
 
- 				filenameLength +
 
- 				extraLength
 
- 				);
 
- 			zsize_t endCompressedBytes(
 
- 				startCompressedBytes + m_compressedSize
 
- 				);
 
- 			if (endCompressedBytes > m_reader->getSize())
 
- 			{
 
- 				throw FormatException("Compressed file size is too long");
 
- 			}
 
- 			switch (m_compressionMethod)
 
- 			{
 
- 			case 0: // No compression
 
- 			{
 
- 				for (zsize_t pos(startCompressedBytes);
 
- 					pos < endCompressedBytes;
 
- 					pos += ChunkSize
 
- 					)
 
- 				{
 
- 					uint8_t buf[ChunkSize];
 
- 					zsize_t bytes(
 
- 						std::min(zsize_t(ChunkSize), endCompressedBytes - pos)
 
- 						);
 
- 					m_reader->readData(pos, bytes, &buf[0]);
 
- 					writer.writeData(pos, bytes, &buf[0]);
 
- 				}
 
- 			}; break;
 
- 			case 8: // Deflate
 
- 			{
 
- 				uint32_t crc(0);
 
- 				zsize_t inPos(startCompressedBytes);
 
- 				zsize_t outPos(0);
 
- 				inflate(
 
- 					m_reader,
 
- 					writer,
 
- 					inPos,
 
- 					endCompressedBytes,
 
- 					outPos,
 
- 					crc);
 
- 				if (m_gpFlag & 0x4) // CRC is after compressed data
 
- 				{
 
- 					uint8_t dataDescriptor[12];
 
- 					m_reader->readData(
 
- 						inPos, sizeof(dataDescriptor), &dataDescriptor[0]);
 
- 					m_crc = read32_le(dataDescriptor, 0);
 
- 					m_compressedSize = read32_le(dataDescriptor, 4);
 
- 					m_uncompressedSize = read32_le(dataDescriptor, 8);
 
- 				}
 
- 				if (crc != m_crc)
 
- 				{
 
- 					throw FormatException("Corrupt Data (CRC failure)");
 
- 				}
 
- 			}; break;
 
- 			default:
 
- 				throw UnsupportedException("Unsupported compression scheme");
 
- 			};
 
- 		}
 
- 	private:
 
- 		ReaderPtr m_reader;
 
- 		uint16_t m_versionNeeded;
 
- 		uint16_t m_gpFlag;
 
- 		uint16_t m_compressionMethod;
 
- 		uint32_t m_crc;
 
- 		zsize_t m_compressedSize;
 
- 		zsize_t m_uncompressedSize;
 
- 		timeval m_modTime;
 
- 		zsize_t m_localHeaderOffset;
 
- 		std::string m_fileName;
 
- 	};
 
- 	bool readEndCentralDirectory(
 
- 		const ReaderPtr& reader,
 
- 		zsize_t& centralDirectoryBytes,
 
- 		zsize_t& centralDirectoryOffset,
 
- 		zsize_t& centralDirectoryEntries
 
- 		)
 
- 	{
 
- 		// Read the end of central directory record. This
 
- 		// record enables us to find the remainding
 
- 		// records without searching for record signatures.
 
- 		// TODO does not consider the Zip64 entries.
 
- 		enum
 
- 		{
 
- 			MinRecordBytes = 22, // Minimum size with no comment
 
- 			MaxCommentBytes = 65535, // 2 bytes to store comment length
 
- 			Signature = 0x06054b50
 
- 		};
 
- 		zsize_t providerSize(reader->getSize());
 
- 		if (providerSize < MinRecordBytes)
 
- 		{
 
- 			throw FormatException("Too small");
 
- 		}
 
- 		size_t bufSize(
 
- 			std::min(zsize_t(MinRecordBytes + MaxCommentBytes), providerSize)
 
- 			);
 
- 		std::vector<uint8_t> buffer(bufSize);
 
- 		reader->readData(providerSize - bufSize, bufSize, &buffer[0]);
 
- 		// Need to search for this record, as it ends in a variable-length
 
- 		// comment field. Search backwards, with the assumption that the
 
- 		// comment doesn't exist, or is much smaller than the maximum
 
- 		// length
 
- 		bool recordFound(false);
 
- 		ssize_t pos(bufSize - MinRecordBytes);
 
- 		for (; pos >= 0; --pos)
 
- 		{
 
- 			recordFound = (read32_le(buffer, pos) == Signature);
 
- 			break;
 
- 		}
 
- 		if (recordFound)
 
- 		{
 
- 			if (read16_le(buffer, pos + 4) != 0)
 
- 			{
 
- 				throw UnsupportedException("Spanned disks not supported");
 
- 			}
 
- 			centralDirectoryBytes = read32_le(buffer, pos + 12);
 
- 			centralDirectoryOffset = read32_le(buffer, pos + 16);
 
- 			centralDirectoryEntries = read16_le(buffer, pos + 10);
 
- 		}
 
- 		return recordFound;
 
- 	}
 
- 	std::vector<CompressedFilePtr>
 
- 	readCentralDirectory(const ReaderPtr& reader)
 
- 	{
 
- 		enum Constants
 
- 		{
 
- 			MinRecordBytes = 46,
 
- 			Signature = 0x02014b50
 
- 		};
 
- 		zsize_t centralDirectoryBytes(0);
 
- 		zsize_t centralDirectoryOffset(0);
 
- 		zsize_t centralDirectoryEntries(0);
 
- 		bool isZip(
 
- 			readEndCentralDirectory(
 
- 				reader,
 
- 				centralDirectoryBytes,
 
- 				centralDirectoryOffset,
 
- 				centralDirectoryEntries
 
- 				)
 
- 			);
 
- 		(void) isZip; // Avoid unused warning.
 
- 		assert(isZip);
 
- 		std::vector<uint8_t> buffer(centralDirectoryBytes);
 
- 		reader->readData(
 
- 			centralDirectoryOffset,
 
- 			centralDirectoryBytes,
 
- 			&buffer[0]
 
- 			);
 
- 		zsize_t pos(0);
 
- 		std::vector<CompressedFilePtr> entries;
 
- 		while ((pos + MinRecordBytes) < buffer.size())
 
- 		{
 
- 			if (read32_le(buffer, pos) != Signature)
 
- 			{
 
- 				// Unknown record type.
 
- 				pos += 1;
 
- 				continue;
 
- 			}
 
- 			uint16_t versionNeeded(read16_le(buffer, pos + 6));
 
- 			uint16_t gpFlag(read16_le(buffer, pos + 8));
 
- 			uint16_t compressionMethod(read16_le(buffer, pos + 10));
 
- 			uint16_t modTime(read16_le(buffer, pos + 12));
 
- 			uint16_t modDate(read16_le(buffer, pos + 14));
 
- 			uint32_t crc(read32_le(buffer, pos + 16));
 
- 			uint32_t compressedSize(read32_le(buffer, pos + 20));
 
- 			uint32_t uncompressedSize(read32_le(buffer, pos + 24));
 
- 			size_t fileNameLen(read16_le(buffer, pos + 28));
 
- 			size_t extraLen(read16_le(buffer, pos + 30));
 
- 			size_t commentLen(read16_le(buffer, pos + 32));
 
- 			uint32_t localHeaderOffset(read32_le(buffer, pos + 42));
 
- 			if ((fileNameLen + extraLen + commentLen + MinRecordBytes + pos) >
 
- 				buffer.size()
 
- 				)
 
- 			{
 
- 				throw FormatException("File comments are too long");
 
- 			}
 
- 			std::string fileName(
 
- 				&buffer[pos + MinRecordBytes],
 
- 				&buffer[pos + MinRecordBytes + fileNameLen]
 
- 				);
 
- 			entries.push_back(
 
- 				CompressedFilePtr(
 
- 					new FileEntry(
 
- 						reader,
 
- 						versionNeeded,
 
- 						gpFlag,
 
- 						compressionMethod,
 
- 						crc,
 
- 						compressedSize,
 
- 						uncompressedSize,
 
- 						convertDosDateTime(modDate, modTime),
 
- 						localHeaderOffset,
 
- 						fileName
 
- 						)
 
- 					)
 
- 				);
 
- 			pos += MinRecordBytes + fileNameLen + extraLen + commentLen;
 
- 		}
 
- 		return entries;
 
- 	}
 
- }
 
- void
 
- zipper::zip(
 
- 	const std::string& filename,
 
- 	const Reader& reader,
 
- 	const WriterPtr& writer,
 
- 	ZipFileRecord& outRecord)
 
- {
 
- 	enum Constants
 
- 	{
 
- 		ChunkSize = 64*1024,
 
- 		WindowBits = 15,
 
- 		TimePos = 10
 
- 	};
 
- 	static uint8_t Header[] =
 
- 	{
 
- 		0x50, 0x4b, 0x03, 0x04,  // Header
 
- 		20, // Version (2.0)
 
- 		0, // File attributes
 
- 		0,0, // gp flag.
 
- 		8,0, // deflate method
 
- 		0,0, // file time
 
- 		0,0, // file date
 
- 		0,0,0,0, // CRC32
 
- 		0,0,0,0, // Compressed size
 
- 		0,0,0,0 // Uncompressed size
 
- 	};
 
- 	zsize_t outPos(writer->getSize());
 
- 	outRecord.localHeaderOffset = outPos;
 
- 	outRecord.filename = filename;
 
- 	// Write header
 
- 	{
 
- 		uint8_t buffer[ChunkSize];
 
- 		memcpy(buffer, Header, sizeof(Header));
 
- 		zsize_t pos(sizeof(Header));
 
- 		std::string::size_type filenameSize(filename.size());
 
- 		if (filenameSize > (ChunkSize - pos))
 
- 		{
 
- 			filenameSize = ChunkSize - pos;
 
- 		}
 
- 		buffer[pos++] = filenameSize & 0xff;
 
- 		buffer[pos++] = (filenameSize >> 8);
 
- 		buffer[pos++] = 0; // extra field len
 
- 		buffer[pos++] = 0; // extra field len
 
- 		memcpy(buffer + pos, filename.data(), filenameSize);
 
- 		pos += filenameSize;
 
- 		writer->writeData(outPos, pos, &buffer[0]);
 
- 		outPos += pos;
 
- 	}
 
- 	// Write compressed data
 
- 	deflate(
 
- 		reader,
 
- 		writer,
 
- 		outPos,
 
- 		outRecord.uncompressedSize,
 
- 		outRecord.compressedSize,
 
- 		outRecord.crc32);
 
- 	// Go back and complete the header.
 
- 	convertDosDateTime(
 
- 		reader.getModTime().tv_sec, outRecord.dosDate, outRecord.dosTime);
 
- 	uint8_t trailer[16];
 
- 	write16_le(outRecord.dosTime, &trailer[0]);
 
- 	write16_le(outRecord.dosDate, &trailer[2]);
 
- 	write32_le(outRecord.crc32, &trailer[4]);
 
- 	write32_le(outRecord.compressedSize, &trailer[8]);
 
- 	write32_le(outRecord.uncompressedSize, &trailer[12]);
 
- 	writer->writeData(
 
- 		outRecord.localHeaderOffset + TimePos, sizeof(trailer), &trailer[0]);
 
- }
 
- void
 
- zipper::zipFinalise(
 
- 	const std::vector<ZipFileRecord>& records,
 
- 	const WriterPtr& writer)
 
- {
 
- 	enum Constants
 
- 	{
 
- 		ChunkSize = 64*1024
 
- 	};
 
- 	static uint8_t FileHeader[] =
 
- 	{
 
- 		0x50, 0x4b, 0x01, 0x02,  // Header
 
- 		20, 0x00, // Version (2.0)
 
- 		20, 0x00, // Version Needed to extract (2.0)
 
- 		0,0, // gp flag.
 
- 		8,0 // deflate method
 
- 	};
 
- 	zsize_t outPos(writer->getSize());
 
- 	uint32_t centralDirOffset(outPos);
 
- 	for (size_t i = 0; i < records.size(); ++i)
 
- 	{
 
- 		uint8_t buffer[ChunkSize];
 
- 		memcpy(buffer, FileHeader, sizeof(FileHeader));
 
- 		zsize_t pos(sizeof(FileHeader));
 
- 		write16_le(records[i].dosTime, &buffer[pos]);
 
- 		pos += 2;
 
- 		write16_le(records[i].dosDate, &buffer[pos]);
 
- 		pos += 2;
 
- 		write32_le(records[i].crc32, &buffer[pos]);
 
- 		pos += 4;
 
- 		write32_le(records[i].compressedSize, &buffer[pos]);
 
- 		pos += 4;
 
- 		write32_le(records[i].uncompressedSize, &buffer[pos]);
 
- 		pos += 4;
 
- 		std::string::size_type filenameSize(records[i].filename.size());
 
- 		if (filenameSize > (ChunkSize - pos))
 
- 		{
 
- 			filenameSize = ChunkSize - pos;
 
- 		}
 
- 		write16_le(filenameSize, &buffer[pos]);
 
- 		pos += 2;
 
- 		write16_le(0, &buffer[pos]); // extra field len
 
- 		pos += 2;
 
- 		write16_le(0, &buffer[pos]); // file comment len
 
- 		pos += 2;
 
- 		write16_le(0, &buffer[pos]); // disk number
 
- 		pos += 2;
 
- 		write16_le(0, &buffer[pos]); // internal file attributes
 
- 		pos += 2;
 
- 		write32_le(0, &buffer[pos]); // external file attributes
 
- 		pos += 4;
 
- 		write32_le(records[i].localHeaderOffset, &buffer[pos]);
 
- 		pos += 4;
 
- 		memcpy(buffer + pos, records[i].filename.data(), filenameSize);
 
- 		pos += filenameSize;
 
- 		writer->writeData(outPos, pos, &buffer[0]);
 
- 		outPos += pos;
 
- 	}
 
- 	uint32_t centralDirSize(writer->getSize() - centralDirOffset);
 
- 	{
 
- 		// End-of-directory record.
 
- 		static uint8_t EndDirectory[] =
 
- 		{
 
- 			0x50, 0x4b, 0x05, 0x06,  // Header
 
- 			0x00, 0x00, // Disk num
 
- 			0x00, 0x00 // Disk with central dir
 
- 		};
 
- 		uint8_t buffer[ChunkSize];
 
- 		memcpy(buffer, EndDirectory, sizeof(EndDirectory));
 
- 		zsize_t pos(sizeof(EndDirectory));
 
- 		write16_le(records.size(), &buffer[pos]); // Entries on this disk
 
- 		pos += 2;
 
- 		write16_le(records.size(), &buffer[pos]); // Total entries
 
- 		pos += 2;
 
- 		write32_le(centralDirSize, &buffer[pos]);
 
- 		pos += 4;
 
- 		write32_le(centralDirOffset, &buffer[pos]);
 
- 		pos += 4;
 
- 		write16_le(0, &buffer[pos]); // Zip comment length
 
- 		pos += 2;
 
- 		writer->writeData(outPos, pos, &buffer[0]);
 
- 		outPos += pos;
 
- 	}
 
- }
 
- std::vector<CompressedFilePtr>
 
- zipper::unzip(const ReaderPtr& reader)
 
- {
 
- 	return readCentralDirectory(reader);
 
- }
 
- bool
 
- zipper::isZip(const ReaderPtr& reader)
 
- {
 
- 	zsize_t centralDirectoryBytes(0);
 
- 	zsize_t centralDirectoryOffset(0);
 
- 	zsize_t centralDirectoryEntries(0);
 
- 	bool result(
 
- 		readEndCentralDirectory(
 
- 			reader,
 
- 			centralDirectoryBytes,
 
- 			centralDirectoryOffset,
 
- 			centralDirectoryEntries
 
- 			)
 
- 		);
 
- 	return result;
 
- }
 
 
  |