xref: /haiku/src/kits/package/hpkg/RepositoryReaderImpl.cpp (revision ed24eb5ff12640d052171c6a7feba37fab8a75d1)
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 
27 namespace BPackageKit {
28 
29 namespace BHPKG {
30 
31 namespace 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
39 static const size_t kMaxRepositoryInfoSize		= 1 * 1024 * 1024;
40 
41 // maximum package attributes size we support reading
42 static const size_t kMaxPackageAttributesSize	= 64 * 1024 * 1024;
43 
44 
45 // #pragma mark - PackagesAttributeHandler
46 
47 
48 class RepositoryReaderImpl::PackagesAttributeHandler
49 	: public AttributeHandler {
50 private:
51 	typedef AttributeHandler super;
52 public:
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 
107 private:
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 
118 private:
119 	BRepositoryContentHandler*	fContentHandler;
120 	const char*					fPackageName;
121 };
122 
123 
124 // #pragma mark - PackageContentHandlerAdapter
125 
126 
127 class RepositoryReaderImpl::PackageContentHandlerAdapter
128 	: public BPackageContentHandler {
129 public:
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 
163 private:
164 	BRepositoryContentHandler*	fContentHandler;
165 };
166 
167 
168 // #pragma mark - RepositoryReaderImpl
169 
170 
171 RepositoryReaderImpl::RepositoryReaderImpl(BErrorOutput* errorOutput)
172 	:
173 	inherited("repository", errorOutput)
174 {
175 }
176 
177 
178 RepositoryReaderImpl::~RepositoryReaderImpl()
179 {
180 }
181 
182 
183 status_t
184 RepositoryReaderImpl::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 
199 status_t
200 RepositoryReaderImpl::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 
213 status_t
214 RepositoryReaderImpl::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 
270 status_t
271 RepositoryReaderImpl::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 
281 status_t
282 RepositoryReaderImpl::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