| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 | //	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 "gzip.hh"#include "util.hh"#include "deflate.hh"#include <algorithm>#include <cassert>#include <iostream>#include <vector>#include <string.h>using namespace zipper;namespace{	size_t	findNull(const std::vector<uint8_t>& zipData, size_t start)	{		if (start >= zipData.size())		{			throw FormatException("Unexpected end-of-file");		}		while (zipData[start] != 0)		{			++start;			if (start >= zipData.size())			{				throw FormatException("Unexpected end-of-file");			}		}		return start;	}	class FileEntry : public CompressedFile	{	public:		FileEntry(			const ReaderPtr& reader,			zsize_t dataOffset,			const std::string& filename,			time_t modTime			) :			m_reader(reader),			m_dataOffset(dataOffset),			m_fileName(filename)		{			m_modTime.tv_sec = modTime;			m_modTime.tv_usec = 0;		}		virtual bool isDecompressSupported() const		{			return true;		}		virtual const std::string& getPath() const		{			return m_fileName;		}		virtual zsize_t getCompressedSize() const { return -1; }		virtual zsize_t getUncompressedSize() const { return -1; }		virtual const timeval& getModificationTime() const { return m_modTime; }		virtual void decompress(Writer& writer)		{			zsize_t endCompressedBytes = m_reader->getSize() - 8; // CRC+ISIZE			zsize_t inPos(m_dataOffset);			zsize_t outPos(0);			uint32_t crc(0);			inflate(				m_reader,				writer,				inPos,				endCompressedBytes,				outPos,				crc);			uint8_t crcBuffer[4];			m_reader->readData(inPos, sizeof(crcBuffer), &crcBuffer[0]);			uint32_t savedCRC = read32_le(&crcBuffer[0]);			if (savedCRC != crc)			{				throw FormatException("Corrupt Data (CRC Failure)");			}		}	private:		ReaderPtr m_reader;		zsize_t m_dataOffset;		std::string m_fileName;		timeval m_modTime;	};}std::vector<zipper::CompressedFilePtr>zipper::ungzip(const ReaderPtr& reader){	enum	{		MaxHeader = 64*1024  // Artifical limit to simplify code	};	if (!isGzip(reader))	{		throw FormatException("Invalid gzip file");	}	std::vector<uint8_t> header(		std::min(reader->getSize(), zsize_t(MaxHeader)));	reader->readData(0, header.size(), &header[0]);	if (header[2] != 8) // "deflate" method	{		throw UnsupportedException("Unknown gzip compression method");	}	bool fextra = (header[3] & 4) != 0;	bool fname = (header[3] & 8) != 0;	bool fcomment = (header[3] & 0x10) != 0;	bool fhcrc = (header[3] & 2) != 0;	time_t modTime = read32_le(&header[4]);	size_t offset(10);	if (fextra)	{		if (offset + 2 > header.size())		{			throw FormatException("Unexpected end-of-file");		}		uint16_t fextraBytes(read16_le(header, offset));		offset += 2;		offset += fextraBytes;	}	std::string embeddedName(reader->getSourceName());	if (fname)	{		size_t nullOffset(findNull(header, offset));		embeddedName =			std::string(				reinterpret_cast<char*>(&header[offset]), nullOffset - offset);		offset = nullOffset + 1;	}	if (fcomment)	{		size_t nullOffset(findNull(header, offset));		offset = nullOffset + 1;	}	if (fhcrc)	{		offset += 2;	}	if (offset >= header.size())	{		throw FormatException("Unexpected end-of-file");	}	std::vector<CompressedFilePtr> result;	result.push_back(		CompressedFilePtr(			new FileEntry(reader, offset, embeddedName, modTime)));	return result;}boolzipper::isGzip(const ReaderPtr& reader){	enum Constants	{		MinFileBytes = 18, // Header + CRC + size		ID1 = 0x1f,		ID2 = 0x8b	};	bool isGzip(false);	if (reader->getSize() >= MinFileBytes)	{		uint8_t magic[2];		reader->readData(0, sizeof(magic), &magic[0]);		isGzip = (magic[0] == ID1) && (magic[1] == ID2);	}	return isGzip;}voidzipper::gzip(	const std::string& filename,	const Reader& reader,	const WriterPtr& writer){	enum Constants	{		ChunkSize = 64*1024,		WindowBits = 15	};	static uint8_t Header[] =	{		0x1f, 0x8b, // ID		0x08, // deflate		0x8, // Flags (filename set)		0x0, 0x0, 0x0, 0x0, // mtime		0x0, // Extra flags		0xff  // OS	};	zsize_t outPos(writer->getSize());	// Write header	{		uint8_t buffer[ChunkSize];		memcpy(buffer, Header, sizeof(Header));		write32_le(reader.getModTime().tv_sec, &buffer[4]); // modtime		zsize_t pos(sizeof(Header));		zsize_t filenameSize(filename.size());		if (filenameSize > (ChunkSize - pos - 1))		{			filenameSize = ChunkSize - pos - 1;		}		std::copy(&filename[0], &filename[filenameSize], &buffer[pos]);		pos += filenameSize;		buffer[pos++] = '\0';		writer->writeData(outPos, pos, &buffer[0]);		outPos += pos;	}	// Compress data	zsize_t uncompressedSize(0);	zsize_t compressedSize(0);	uint32_t crc(0);	deflate(reader, writer, outPos, uncompressedSize, compressedSize, crc);	// Write trailer.	uint8_t trailer[8];	write32_le(crc, &trailer[0]);	write32_le(reader.getSize(), &trailer[4]);	writer->writeData(outPos, sizeof(trailer), &trailer[0]);}
 |