/*
 * Copyright 2009-2014, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de>
 * Distributed under the terms of the MIT License.
 */
 
 
#include <package/hpkg/PackageReaderImpl.h>
 
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
 
#include <algorithm>
#include <new>
 
#include <ByteOrder.h>
 
#include <FdIO.h>
 
#include <package/hpkg/HPKGDefsPrivate.h>
 
#include <package/hpkg/PackageData.h>
#include <package/hpkg/PackageEntry.h>
#include <package/hpkg/PackageEntryAttribute.h>
 
 
namespace BPackageKit {
 
namespace BHPKG {
 
namespace BPrivate {
 
 
//#define TRACE(format...)	printf(format)
#define TRACE(format...)	do {} while (false)
 
 
// maximum TOC size we support reading
static const size_t kMaxTOCSize					= 64 * 1024 * 1024;
 
// maximum package attributes size we support reading
static const size_t kMaxPackageAttributesSize	= 1 * 1024 * 1024;
 
 
static status_t
set_package_data_from_attribute_value(const BPackageAttributeValue& value,
	BPackageData& data)
{
	if (value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE)
		data.SetData(value.data.size, value.data.raw);
	else
		data.SetData(value.data.size, value.data.offset);
	return B_OK;
}
 
 
// #pragma mark - AttributeAttributeHandler
 
 
struct PackageReaderImpl::AttributeAttributeHandler : AttributeHandler {
	AttributeAttributeHandler(BPackageEntry* entry, const char* name)
		:
		fEntry(entry),
		fAttribute(name)
	{
	}
 
	virtual status_t HandleAttribute(AttributeHandlerContext* context,
		uint8 id, const AttributeValue& value, AttributeHandler** _handler)
	{
		switch (id) {
			case B_HPKG_ATTRIBUTE_ID_DATA:
				return set_package_data_from_attribute_value(value,
					fAttribute.Data());
 
			case B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE_TYPE:
				fAttribute.SetType(value.unsignedInt);
				return B_OK;
		}
 
		return AttributeHandler::HandleAttribute(context, id, value, _handler);
	}
 
	virtual status_t Delete(AttributeHandlerContext* context)
	{
		status_t error = context->packageContentHandler->HandleEntryAttribute(
			fEntry, &fAttribute);
 
		delete this;
		return error;
	}
 
private:
	BPackageEntry*			fEntry;
	BPackageEntryAttribute	fAttribute;
};
 
 
// #pragma mark - EntryAttributeHandler
 
 
struct PackageReaderImpl::EntryAttributeHandler : AttributeHandler {
	EntryAttributeHandler(AttributeHandlerContext* context,
		BPackageEntry* parentEntry, const char* name)
		:
		fEntry(parentEntry, name),
		fNotified(false)
	{
		_SetFileType(context, B_HPKG_DEFAULT_FILE_TYPE);
	}
 
	static status_t Create(AttributeHandlerContext* context,
		BPackageEntry* parentEntry, const char* name,
		AttributeHandler*& _handler)
	{
		// check name
		if (name[0] == '\0' || strcmp(name, ".") == 0
			|| strcmp(name, "..") == 0 || strchr(name, '/') != NULL) {
			context->errorOutput->PrintError("Error: Invalid package: Invalid "
				"entry name: \"%s\"\n", name);
			return B_BAD_DATA;
		}
 
		// create handler
		EntryAttributeHandler* handler = new(std::nothrow)
			EntryAttributeHandler(context, parentEntry, name);
		if (handler == NULL)
			return B_NO_MEMORY;
 
		_handler = handler;
		return B_OK;
	}
 
	virtual status_t HandleAttribute(AttributeHandlerContext* context,
		uint8 id, const AttributeValue& value, AttributeHandler** _handler)
	{
		switch (id) {
			case B_HPKG_ATTRIBUTE_ID_DIRECTORY_ENTRY:
			{
				status_t error = _Notify(context);
				if (error != B_OK)
					return error;
 
//TRACE("%*sentry \"%s\"\n", fLevel * 2, "", value.string);
				if (_handler != NULL) {
					return EntryAttributeHandler::Create(context, &fEntry,
						value.string, *_handler);
				}
				return B_OK;
			}
 
			case B_HPKG_ATTRIBUTE_ID_FILE_TYPE:
				return _SetFileType(context, value.unsignedInt);
 
			case B_HPKG_ATTRIBUTE_ID_FILE_PERMISSIONS:
				fEntry.SetPermissions(value.unsignedInt);
				return B_OK;
 
			case B_HPKG_ATTRIBUTE_ID_FILE_USER:
			case B_HPKG_ATTRIBUTE_ID_FILE_GROUP:
				// TODO:...
				break;
 
			case B_HPKG_ATTRIBUTE_ID_FILE_ATIME:
				fEntry.SetAccessTime(value.unsignedInt);
				return B_OK;
 
			case B_HPKG_ATTRIBUTE_ID_FILE_MTIME:
				fEntry.SetModifiedTime(value.unsignedInt);
				return B_OK;
 
			case B_HPKG_ATTRIBUTE_ID_FILE_CRTIME:
				fEntry.SetCreationTime(value.unsignedInt);
				return B_OK;
 
			case B_HPKG_ATTRIBUTE_ID_FILE_ATIME_NANOS:
				fEntry.SetAccessTimeNanos(value.unsignedInt);
				return B_OK;
 
			case B_HPKG_ATTRIBUTE_ID_FILE_MTIME_NANOS:
				fEntry.SetModifiedTimeNanos(value.unsignedInt);
				return B_OK;
 
			case B_HPKG_ATTRIBUTE_ID_FILE_CRTIM_NANOS:
				fEntry.SetCreationTimeNanos(value.unsignedInt);
				return B_OK;
 
			case B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE:
			{
				status_t error = _Notify(context);
				if (error != B_OK)
					return error;
 
				if (_handler != NULL) {
					*_handler = new(std::nothrow) AttributeAttributeHandler(
						&fEntry, value.string);
					if (*_handler == NULL)
						return B_NO_MEMORY;
					return B_OK;
				} else {
					BPackageEntryAttribute attribute(value.string);
					return context->packageContentHandler->HandleEntryAttribute(
						&fEntry, &attribute);
				}
			}
 
			case B_HPKG_ATTRIBUTE_ID_DATA:
				return set_package_data_from_attribute_value(value,
					fEntry.Data());
 
			case B_HPKG_ATTRIBUTE_ID_SYMLINK_PATH:
				fEntry.SetSymlinkPath(value.string);
				return B_OK;
		}
 
		return AttributeHandler::HandleAttribute(context, id, value, _handler);
	}
 
	virtual status_t Delete(AttributeHandlerContext* context)
	{
		// notify if not done yet
		status_t error = _Notify(context);
 
		// notify done
		if (error == B_OK)
			error = context->packageContentHandler->HandleEntryDone(&fEntry);
		else
			context->packageContentHandler->HandleEntryDone(&fEntry);
 
		delete this;
		return error;
	}
 
private:
	status_t _Notify(AttributeHandlerContext* context)
	{
		if (fNotified)
			return B_OK;
 
		fNotified = true;
		return context->packageContentHandler->HandleEntry(&fEntry);
	}
 
	status_t _SetFileType(AttributeHandlerContext* context, uint64 fileType)
	{
		switch (fileType) {
			case B_HPKG_FILE_TYPE_FILE:
				fEntry.SetType(S_IFREG);
				fEntry.SetPermissions(B_HPKG_DEFAULT_FILE_PERMISSIONS);
				break;
 
			case B_HPKG_FILE_TYPE_DIRECTORY:
				fEntry.SetType(S_IFDIR);
				fEntry.SetPermissions(B_HPKG_DEFAULT_DIRECTORY_PERMISSIONS);
				break;
 
			case B_HPKG_FILE_TYPE_SYMLINK:
				fEntry.SetType(S_IFLNK);
				fEntry.SetPermissions(B_HPKG_DEFAULT_SYMLINK_PERMISSIONS);
				break;
 
			default:
				context->errorOutput->PrintError("Error: Invalid file type for "
					"package entry (%llu)\n", fileType);
				return B_BAD_DATA;
		}
		return B_OK;
	}
 
private:
	BPackageEntry	fEntry;
	bool			fNotified;
};
 
 
// #pragma mark - RootAttributeHandler
 
 
struct PackageReaderImpl::RootAttributeHandler : PackageAttributeHandler {
	typedef PackageAttributeHandler inherited;
 
	virtual status_t HandleAttribute(AttributeHandlerContext* context,
		uint8 id, const AttributeValue& value, AttributeHandler** _handler)
	{
		if (id == B_HPKG_ATTRIBUTE_ID_DIRECTORY_ENTRY) {
			if (_handler != NULL) {
				return EntryAttributeHandler::Create(context, NULL,
					value.string, *_handler);
			}
			return B_OK;
		}
 
		return inherited::HandleAttribute(context, id, value, _handler);
	}
};
 
 
// #pragma mark - PackageReaderImpl
 
 
PackageReaderImpl::PackageReaderImpl(BErrorOutput* errorOutput)
	:
	inherited("package", errorOutput),
	fTOCSection("TOC")
{
}
 
 
PackageReaderImpl::~PackageReaderImpl()
{
}
 
 
status_t
PackageReaderImpl::Init(const char* fileName, uint32 flags)
{
	// open file
	int fd = open(fileName, O_RDONLY);
	if (fd < 0) {
		ErrorOutput()->PrintError("Error: Failed to open package file \"%s\": "
			"%s\n", fileName, strerror(errno));
		return errno;
	}
 
	return Init(fd, true, flags);
}
 
 
status_t
PackageReaderImpl::Init(int fd, bool keepFD, uint32 flags)
{
	BFdIO* file = new(std::nothrow) BFdIO(fd, keepFD);
	if (file == NULL) {
		if (keepFD && fd >= 0)
			close(fd);
		return B_NO_MEMORY;
	}
 
	return Init(file, true, flags);
}
 
 
status_t
PackageReaderImpl::Init(BPositionIO* file, bool keepFile, uint32 flags,
	hpkg_header* _header)
{
	hpkg_header header;
	status_t error = inherited::Init<hpkg_header, B_HPKG_MAGIC, B_HPKG_VERSION,
		B_HPKG_MINOR_VERSION>(file, keepFile, header, flags);
	if (error != B_OK)
		return error;
	fHeapSize = UncompressedHeapSize();
 
	// init package attributes section
	error = InitSection(fPackageAttributesSection, fHeapSize,
		B_BENDIAN_TO_HOST_INT32(header.attributes_length),
		kMaxPackageAttributesSize,
		B_BENDIAN_TO_HOST_INT32(header.attributes_strings_length),
		B_BENDIAN_TO_HOST_INT32(header.attributes_strings_count));
	if (error != B_OK)
		return error;
 
	// init TOC section
	error = InitSection(fTOCSection, fPackageAttributesSection.offset,
		B_BENDIAN_TO_HOST_INT64(header.toc_length), kMaxTOCSize,
		B_BENDIAN_TO_HOST_INT64(header.toc_strings_length),
		B_BENDIAN_TO_HOST_INT64(header.toc_strings_count));
	if (error != B_OK)
		return error;
 
	if (_header != NULL)
		*_header = header;
 
	return B_OK;
}
 
 
status_t
PackageReaderImpl::ParseContent(BPackageContentHandler* contentHandler)
{
	status_t error = _PrepareSections();
	if (error != B_OK)
		return error;
 
	AttributeHandlerContext context(ErrorOutput(), contentHandler,
		B_HPKG_SECTION_PACKAGE_ATTRIBUTES,
		MinorFormatVersion() > B_HPKG_MINOR_VERSION);
	RootAttributeHandler rootAttributeHandler;
 
	error = ParsePackageAttributesSection(&context, &rootAttributeHandler);
 
	if (error == B_OK) {
		context.section = B_HPKG_SECTION_PACKAGE_TOC;
		error = _ParseTOC(&context, &rootAttributeHandler);
	}
 
	return error;
}
 
 
status_t
PackageReaderImpl::ParseContent(BLowLevelPackageContentHandler* contentHandler)
{
	status_t error = _PrepareSections();
	if (error != B_OK)
		return error;
 
	AttributeHandlerContext context(ErrorOutput(), contentHandler,
		B_HPKG_SECTION_PACKAGE_ATTRIBUTES,
		MinorFormatVersion() > B_HPKG_MINOR_VERSION);
	LowLevelAttributeHandler rootAttributeHandler;
 
	error = ParsePackageAttributesSection(&context, &rootAttributeHandler);
 
	if (error == B_OK) {
		context.section = B_HPKG_SECTION_PACKAGE_TOC;
		error = _ParseTOC(&context, &rootAttributeHandler);
	}
 
	return error;
}
 
 
status_t
PackageReaderImpl::_PrepareSections()
{
	status_t error = PrepareSection(fTOCSection);
	if (error != B_OK)
		return error;
 
	error = PrepareSection(fPackageAttributesSection);
	if (error != B_OK)
		return error;
 
	return B_OK;
}
 
 
status_t
PackageReaderImpl::_ParseTOC(AttributeHandlerContext* context,
	AttributeHandler* rootAttributeHandler)
{
	// parse the TOC
	fTOCSection.currentOffset = fTOCSection.stringsLength;
	SetCurrentSection(&fTOCSection);
 
	// init the attribute handler stack
	rootAttributeHandler->SetLevel(0);
	ClearAttributeHandlerStack();
	PushAttributeHandler(rootAttributeHandler);
 
	bool sectionHandled;
	status_t error = ParseAttributeTree(context, sectionHandled);
	if (error == B_OK && sectionHandled) {
		if (fTOCSection.currentOffset < fTOCSection.uncompressedLength) {
			ErrorOutput()->PrintError("Error: %llu excess byte(s) in TOC "
				"section\n",
				fTOCSection.uncompressedLength - fTOCSection.currentOffset);
			error = B_BAD_DATA;
		}
	}
 
	// clean up on error
	if (error != B_OK) {
		context->ErrorOccurred();
		while (AttributeHandler* handler = PopAttributeHandler()) {
			if (handler != rootAttributeHandler)
				handler->Delete(context);
		}
		return error;
	}
 
	return B_OK;
}
 
 
status_t
PackageReaderImpl::ReadAttributeValue(uint8 type, uint8 encoding,
	AttributeValue& _value)
{
	switch (type) {
		case B_HPKG_ATTRIBUTE_TYPE_RAW:
		{
			uint64 size;
			status_t error = ReadUnsignedLEB128(size);
			if (error != B_OK)
				return error;
 
			if (encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP) {
				uint64 offset;
				error = ReadUnsignedLEB128(offset);
				if (error != B_OK)
					return error;
 
				if (offset > fHeapSize || size > fHeapSize - offset) {
					ErrorOutput()->PrintError("Error: Invalid %s section: "
						"invalid data reference\n", CurrentSection()->name);
					return B_BAD_DATA;
				}
 
				_value.SetToData(size, offset);
			} else if (encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE) {
				if (size > B_HPKG_MAX_INLINE_DATA_SIZE) {
					ErrorOutput()->PrintError("Error: Invalid %s section: "
						"inline data too long\n", CurrentSection()->name);
					return B_BAD_DATA;
				}
 
				const void* buffer;
				error = _GetTOCBuffer(size, buffer);
				if (error != B_OK)
					return error;
				_value.SetToData(size, buffer);
			} else {
				ErrorOutput()->PrintError("Error: Invalid %s section: invalid "
					"raw encoding (%u)\n", CurrentSection()->name, encoding);
				return B_BAD_DATA;
			}
 
			return B_OK;
		}
 
		default:
			return inherited::ReadAttributeValue(type, encoding, _value);
	}
}
 
 
status_t
PackageReaderImpl::_GetTOCBuffer(size_t size, const void*& _buffer)
{
	if (size > fTOCSection.uncompressedLength - fTOCSection.currentOffset) {
		ErrorOutput()->PrintError("_GetTOCBuffer(%lu): read beyond TOC end\n",
			size);
		return B_BAD_DATA;
	}
 
	_buffer = fTOCSection.data + fTOCSection.currentOffset;
	fTOCSection.currentOffset += size;
	return B_OK;
}
 
 
}	// namespace BPrivate
 
}	// namespace BHPKG
 
}	// namespace BPackageKit

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fHeapOffset, fHeapSize.