1/*
2 * Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de>
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <package/hpkg/RepositoryReaderImpl.h>
8
9#include <errno.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <sys/stat.h>
14
15#include <algorithm>
16#include <new>
17
18#include <ByteOrder.h>
19#include <Message.h>
20
21#include <FdIO.h>
22
23#include <package/hpkg/HPKGDefsPrivate.h>
24#include <package/hpkg/RepositoryContentHandler.h>
25
26
27namespace BPackageKit {
28
29namespace BHPKG {
30
31namespace BPrivate {
32
33
34//#define TRACE(format...)	printf(format)
35#define TRACE(format...)	do {} while (false)
36
37
38// maximum repository info size we support reading
39static const size_t kMaxRepositoryInfoSize		= 1 * 1024 * 1024;
40
41// maximum package attributes size we support reading
42static const size_t kMaxPackageAttributesSize	= 64 * 1024 * 1024;
43
44
45// #pragma mark - PackagesAttributeHandler
46
47
48class RepositoryReaderImpl::PackagesAttributeHandler
49	: public AttributeHandler {
50private:
51	typedef AttributeHandler super;
52public:
53	PackagesAttributeHandler(BRepositoryContentHandler* contentHandler)
54		:
55		fContentHandler(contentHandler),
56		fPackageName(NULL)
57	{
58	}
59
60	virtual status_t HandleAttribute(AttributeHandlerContext* context, uint8 id,
61		const AttributeValue& value, AttributeHandler** _handler)
62	{
63		switch (id) {
64			case B_HPKG_ATTRIBUTE_ID_PACKAGE:
65			{
66				status_t error = _NotifyPackageDone();
67				if (error != B_OK)
68					return error;
69
70				if (_handler != NULL) {
71					if (fContentHandler != NULL) {
72						error = fContentHandler->HandlePackage(value.string);
73						if (error != B_OK)
74							return error;
75					}
76
77					*_handler = new(std::nothrow) PackageAttributeHandler;
78					if (*_handler == NULL)
79						return B_NO_MEMORY;
80
81					fPackageName = value.string;
82				}
83				break;
84			}
85
86			default:
87				if (context->ignoreUnknownAttributes)
88					break;
89
90				context->errorOutput->PrintError(
91					"Error: Invalid package attribute section: unexpected "
92					"top level attribute id %d encountered\n", id);
93				return B_BAD_DATA;
94		}
95
96		return B_OK;
97	}
98
99	virtual status_t NotifyDone(AttributeHandlerContext* context)
100	{
101		status_t result = _NotifyPackageDone();
102		if (result == B_OK)
103			result = super::NotifyDone(context);
104		return result;
105	}
106
107private:
108	status_t _NotifyPackageDone()
109	{
110		if (fPackageName == NULL || fContentHandler == NULL)
111			return B_OK;
112
113		status_t error = fContentHandler->HandlePackageDone(fPackageName);
114		fPackageName = NULL;
115		return error;
116	}
117
118private:
119	BRepositoryContentHandler*	fContentHandler;
120	const char*					fPackageName;
121};
122
123
124// #pragma mark - PackageContentHandlerAdapter
125
126
127class RepositoryReaderImpl::PackageContentHandlerAdapter
128	: public BPackageContentHandler {
129public:
130	PackageContentHandlerAdapter(BRepositoryContentHandler* contentHandler)
131		:
132		fContentHandler(contentHandler)
133	{
134	}
135
136	virtual status_t HandleEntry(BPackageEntry* entry)
137	{
138		return B_OK;
139	}
140
141	virtual status_t HandleEntryAttribute(BPackageEntry* entry,
142		BPackageEntryAttribute* attribute)
143	{
144		return B_OK;
145	}
146
147	virtual status_t HandleEntryDone(BPackageEntry* entry)
148	{
149		return B_OK;
150	}
151
152	virtual status_t HandlePackageAttribute(
153		const BPackageInfoAttributeValue& value)
154	{
155		return fContentHandler->HandlePackageAttribute(value);
156	}
157
158	virtual void HandleErrorOccurred()
159	{
160		return fContentHandler->HandleErrorOccurred();
161	}
162
163private:
164	BRepositoryContentHandler*	fContentHandler;
165};
166
167
168// #pragma mark - RepositoryReaderImpl
169
170
171RepositoryReaderImpl::RepositoryReaderImpl(BErrorOutput* errorOutput)
172	:
173	inherited("repository", errorOutput)
174{
175}
176
177
178RepositoryReaderImpl::~RepositoryReaderImpl()
179{
180}
181
182
183status_t
184RepositoryReaderImpl::Init(const char* fileName)
185{
186	// open file
187	int fd = open(fileName, O_RDONLY);
188	if (fd < 0) {
189		ErrorOutput()->PrintError(
190			"Error: Failed to open repository file \"%s\": %s\n", fileName,
191			strerror(errno));
192		return errno;
193	}
194
195	return Init(fd, true);
196}
197
198
199status_t
200RepositoryReaderImpl::Init(int fd, bool keepFD)
201{
202	BFdIO* file = new(std::nothrow) BFdIO(fd, keepFD);
203	if (file == NULL) {
204		if (keepFD && fd >= 0)
205			close(fd);
206		return B_NO_MEMORY;
207	}
208
209	return Init(file, true);
210}
211
212
213status_t
214RepositoryReaderImpl::Init(BPositionIO* file, bool keepFile)
215{
216	hpkg_repo_header header;
217	status_t error = inherited::Init<hpkg_repo_header, B_HPKG_REPO_MAGIC,
218		B_HPKG_REPO_VERSION, B_HPKG_REPO_MINOR_VERSION>(file, keepFile, header,
219		0);
220	if (error != B_OK)
221		return error;
222
223	// init package attributes section
224	error = InitSection(fPackageAttributesSection,
225		UncompressedHeapSize(),
226		B_BENDIAN_TO_HOST_INT64(header.packages_length),
227		kMaxPackageAttributesSize,
228		B_BENDIAN_TO_HOST_INT64(header.packages_strings_length),
229		B_BENDIAN_TO_HOST_INT64(header.packages_strings_count));
230	if (error != B_OK)
231		return error;
232
233	// init repository info section
234	PackageFileSection repositoryInfoSection("repository info");
235	error = InitSection(repositoryInfoSection,
236		fPackageAttributesSection.offset,
237		B_BENDIAN_TO_HOST_INT32(header.info_length), kMaxRepositoryInfoSize, 0,
238		0);
239	if (error != B_OK)
240		return error;
241
242	// prepare the sections for use
243	error = PrepareSection(repositoryInfoSection);
244	if (error != B_OK)
245		return error;
246
247	error = PrepareSection(fPackageAttributesSection);
248	if (error != B_OK)
249		return error;
250
251	// unarchive repository info
252	BMessage repositoryInfoArchive;
253	error = repositoryInfoArchive.Unflatten((char*)repositoryInfoSection.data);
254	if (error != B_OK) {
255		ErrorOutput()->PrintError(
256			"Error: Unable to unflatten repository info archive!\n");
257		return error;
258	}
259	error = fRepositoryInfo.SetTo(&repositoryInfoArchive);
260	if (error != B_OK) {
261		ErrorOutput()->PrintError(
262			"Error: Unable to unarchive repository info!\n");
263		return error;
264	}
265
266	return B_OK;
267}
268
269
270status_t
271RepositoryReaderImpl::GetRepositoryInfo(BRepositoryInfo* _repositoryInfo) const
272{
273	if (_repositoryInfo == NULL)
274		return B_BAD_VALUE;
275
276	*_repositoryInfo = fRepositoryInfo;
277	return B_OK;
278}
279
280
281status_t
282RepositoryReaderImpl::ParseContent(BRepositoryContentHandler* contentHandler)
283{
284	status_t result = contentHandler->HandleRepositoryInfo(fRepositoryInfo);
285	if (result == B_OK) {
286		PackageContentHandlerAdapter contentHandlerAdapter(contentHandler);
287		AttributeHandlerContext context(ErrorOutput(),
288			contentHandler != NULL ? &contentHandlerAdapter : NULL,
289			B_HPKG_SECTION_PACKAGE_ATTRIBUTES,
290			MinorFormatVersion() > B_HPKG_REPO_MINOR_VERSION);
291		PackagesAttributeHandler rootAttributeHandler(contentHandler);
292		result = ParsePackageAttributesSection(&context, &rootAttributeHandler);
293	}
294	return result;
295}
296
297
298}	// namespace BPrivate
299
300}	// namespace BHPKG
301
302}	// namespace BPackageKit
303