1/*
2 * Copyright 2009-2014, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de>
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include <package/hpkg/v1/PackageReaderImpl.h>
9
10#include <errno.h>
11#include <fcntl.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <sys/stat.h>
16#include <unistd.h>
17
18#include <algorithm>
19#include <new>
20
21#include <ByteOrder.h>
22
23#include <package/hpkg/v1/HPKGDefsPrivate.h>
24
25#include <package/hpkg/ErrorOutput.h>
26#include <package/hpkg/v1/PackageData.h>
27#include <package/hpkg/v1/PackageEntry.h>
28#include <package/hpkg/v1/PackageEntryAttribute.h>
29
30
31namespace BPackageKit {
32
33namespace BHPKG {
34
35namespace V1 {
36
37namespace BPrivate {
38
39
40//#define TRACE(format...)	printf(format)
41#define TRACE(format...)	do {} while (false)
42
43
44// maximum TOC size we support reading
45static const size_t kMaxTOCSize					= 64 * 1024 * 1024;
46
47// maximum package attributes size we support reading
48static const size_t kMaxPackageAttributesSize	= 1 * 1024 * 1024;
49
50
51// #pragma mark - DataAttributeHandler
52
53
54struct PackageReaderImpl::DataAttributeHandler : AttributeHandler {
55	DataAttributeHandler(BPackageData* data)
56		:
57		fData(data)
58	{
59	}
60
61	static status_t InitData(AttributeHandlerContext* context,
62		BPackageData* data, const AttributeValue& value)
63	{
64		if (value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE)
65			data->SetData(value.data.size, value.data.raw);
66		else
67			data->SetData(value.data.size, value.data.offset);
68
69		data->SetUncompressedSize(value.data.size);
70
71		return B_OK;
72	}
73
74	static status_t Create(AttributeHandlerContext* context,
75		BPackageData* data, const AttributeValue& value,
76		AttributeHandler*& _handler)
77	{
78		DataAttributeHandler* handler = new(std::nothrow) DataAttributeHandler(
79			data);
80		if (handler == NULL)
81			return B_NO_MEMORY;
82
83		InitData(context, data, value);
84
85		_handler = handler;
86		return B_OK;
87	}
88
89	virtual status_t HandleAttribute(AttributeHandlerContext* context,
90		uint8 id, const AttributeValue& value, AttributeHandler** _handler)
91	{
92		switch (id) {
93			case B_HPKG_ATTRIBUTE_ID_DATA_SIZE:
94				fData->SetUncompressedSize(value.unsignedInt);
95				return B_OK;
96
97			case B_HPKG_ATTRIBUTE_ID_DATA_COMPRESSION:
98			{
99				switch (value.unsignedInt) {
100					case B_HPKG_COMPRESSION_NONE:
101					case B_HPKG_COMPRESSION_ZLIB:
102						break;
103					default:
104						context->errorOutput->PrintError("Error: Invalid "
105							"compression type for data (%llu)\n",
106							value.unsignedInt);
107						return B_BAD_DATA;
108				}
109
110				fData->SetCompression(value.unsignedInt);
111				return B_OK;
112			}
113
114			case B_HPKG_ATTRIBUTE_ID_DATA_CHUNK_SIZE:
115				fData->SetChunkSize(value.unsignedInt);
116				return B_OK;
117		}
118
119		return AttributeHandler::HandleAttribute(context, id, value, _handler);
120	}
121
122private:
123	BPackageData*	fData;
124};
125
126
127// #pragma mark - AttributeAttributeHandler
128
129
130struct PackageReaderImpl::AttributeAttributeHandler : AttributeHandler {
131	AttributeAttributeHandler(BPackageEntry* entry, const char* name)
132		:
133		fEntry(entry),
134		fAttribute(name)
135	{
136	}
137
138	virtual status_t HandleAttribute(AttributeHandlerContext* context,
139		uint8 id, const AttributeValue& value, AttributeHandler** _handler)
140	{
141		switch (id) {
142			case B_HPKG_ATTRIBUTE_ID_DATA:
143				if (_handler != NULL) {
144					return DataAttributeHandler::Create(context,
145						&fAttribute.Data(), value, *_handler);
146				}
147				return DataAttributeHandler::InitData(context,
148					&fAttribute.Data(), value);
149
150			case B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE_TYPE:
151				fAttribute.SetType(value.unsignedInt);
152				return B_OK;
153		}
154
155		return AttributeHandler::HandleAttribute(context, id, value, _handler);
156	}
157
158	virtual status_t Delete(AttributeHandlerContext* context)
159	{
160		status_t error = context->packageContentHandler->HandleEntryAttribute(
161			fEntry, &fAttribute);
162
163		delete this;
164		return error;
165	}
166
167private:
168	BPackageEntry*			fEntry;
169	BPackageEntryAttribute	fAttribute;
170};
171
172
173// #pragma mark - EntryAttributeHandler
174
175
176struct PackageReaderImpl::EntryAttributeHandler : AttributeHandler {
177	EntryAttributeHandler(AttributeHandlerContext* context,
178		BPackageEntry* parentEntry, const char* name)
179		:
180		fEntry(parentEntry, name),
181		fNotified(false)
182	{
183		_SetFileType(context, B_HPKG_DEFAULT_FILE_TYPE);
184	}
185
186	static status_t Create(AttributeHandlerContext* context,
187		BPackageEntry* parentEntry, const char* name,
188		AttributeHandler*& _handler)
189	{
190		// check name
191		if (name[0] == '\0' || strcmp(name, ".") == 0
192			|| strcmp(name, "..") == 0 || strchr(name, '/') != NULL) {
193			context->errorOutput->PrintError("Error: Invalid package: Invalid "
194				"entry name: \"%s\"\n", name);
195			return B_BAD_DATA;
196		}
197
198		// create handler
199		EntryAttributeHandler* handler = new(std::nothrow)
200			EntryAttributeHandler(context, parentEntry, name);
201		if (handler == NULL)
202			return B_NO_MEMORY;
203
204		_handler = handler;
205		return B_OK;
206	}
207
208	virtual status_t HandleAttribute(AttributeHandlerContext* context,
209		uint8 id, const AttributeValue& value, AttributeHandler** _handler)
210	{
211		switch (id) {
212			case B_HPKG_ATTRIBUTE_ID_DIRECTORY_ENTRY:
213			{
214				status_t error = _Notify(context);
215				if (error != B_OK)
216					return error;
217
218//TRACE("%*sentry \"%s\"\n", fLevel * 2, "", value.string);
219				if (_handler != NULL) {
220					return EntryAttributeHandler::Create(context, &fEntry,
221						value.string, *_handler);
222				}
223				return B_OK;
224			}
225
226			case B_HPKG_ATTRIBUTE_ID_FILE_TYPE:
227				return _SetFileType(context, value.unsignedInt);
228
229			case B_HPKG_ATTRIBUTE_ID_FILE_PERMISSIONS:
230				fEntry.SetPermissions(value.unsignedInt);
231				return B_OK;
232
233			case B_HPKG_ATTRIBUTE_ID_FILE_USER:
234			case B_HPKG_ATTRIBUTE_ID_FILE_GROUP:
235				// TODO:...
236				break;
237
238			case B_HPKG_ATTRIBUTE_ID_FILE_ATIME:
239				fEntry.SetAccessTime(value.unsignedInt);
240				return B_OK;
241
242			case B_HPKG_ATTRIBUTE_ID_FILE_MTIME:
243				fEntry.SetModifiedTime(value.unsignedInt);
244				return B_OK;
245
246			case B_HPKG_ATTRIBUTE_ID_FILE_CRTIME:
247				fEntry.SetCreationTime(value.unsignedInt);
248				return B_OK;
249
250			case B_HPKG_ATTRIBUTE_ID_FILE_ATIME_NANOS:
251				fEntry.SetAccessTimeNanos(value.unsignedInt);
252				return B_OK;
253
254			case B_HPKG_ATTRIBUTE_ID_FILE_MTIME_NANOS:
255				fEntry.SetModifiedTimeNanos(value.unsignedInt);
256				return B_OK;
257
258			case B_HPKG_ATTRIBUTE_ID_FILE_CRTIM_NANOS:
259				fEntry.SetCreationTimeNanos(value.unsignedInt);
260				return B_OK;
261
262			case B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE:
263			{
264				status_t error = _Notify(context);
265				if (error != B_OK)
266					return error;
267
268				if (_handler != NULL) {
269					*_handler = new(std::nothrow) AttributeAttributeHandler(
270						&fEntry, value.string);
271					if (*_handler == NULL)
272						return B_NO_MEMORY;
273					return B_OK;
274				} else {
275					BPackageEntryAttribute attribute(value.string);
276					return context->packageContentHandler->HandleEntryAttribute(
277						&fEntry, &attribute);
278				}
279			}
280
281			case B_HPKG_ATTRIBUTE_ID_DATA:
282				if (_handler != NULL) {
283					return DataAttributeHandler::Create(context, &fEntry.Data(),
284						value, *_handler);
285				}
286				return DataAttributeHandler::InitData(context, &fEntry.Data(),
287					value);
288
289			case B_HPKG_ATTRIBUTE_ID_SYMLINK_PATH:
290				fEntry.SetSymlinkPath(value.string);
291				return B_OK;
292		}
293
294		return AttributeHandler::HandleAttribute(context, id, value, _handler);
295	}
296
297	virtual status_t Delete(AttributeHandlerContext* context)
298	{
299		// notify if not done yet
300		status_t error = _Notify(context);
301
302		// notify done
303		if (error == B_OK)
304			error = context->packageContentHandler->HandleEntryDone(&fEntry);
305		else
306			context->packageContentHandler->HandleEntryDone(&fEntry);
307
308		delete this;
309		return error;
310	}
311
312private:
313	status_t _Notify(AttributeHandlerContext* context)
314	{
315		if (fNotified)
316			return B_OK;
317
318		fNotified = true;
319		return context->packageContentHandler->HandleEntry(&fEntry);
320	}
321
322	status_t _SetFileType(AttributeHandlerContext* context, uint64 fileType)
323	{
324		switch (fileType) {
325			case B_HPKG_FILE_TYPE_FILE:
326				fEntry.SetType(S_IFREG);
327				fEntry.SetPermissions(B_HPKG_DEFAULT_FILE_PERMISSIONS);
328				break;
329
330			case B_HPKG_FILE_TYPE_DIRECTORY:
331				fEntry.SetType(S_IFDIR);
332				fEntry.SetPermissions(B_HPKG_DEFAULT_DIRECTORY_PERMISSIONS);
333				break;
334
335			case B_HPKG_FILE_TYPE_SYMLINK:
336				fEntry.SetType(S_IFLNK);
337				fEntry.SetPermissions(B_HPKG_DEFAULT_SYMLINK_PERMISSIONS);
338				break;
339
340			default:
341				context->errorOutput->PrintError("Error: Invalid file type for "
342					"package entry (%llu)\n", fileType);
343				return B_BAD_DATA;
344		}
345		return B_OK;
346	}
347
348private:
349	BPackageEntry	fEntry;
350	bool			fNotified;
351};
352
353
354// #pragma mark - RootAttributeHandler
355
356
357struct PackageReaderImpl::RootAttributeHandler : PackageAttributeHandler {
358	typedef PackageAttributeHandler inherited;
359
360	virtual status_t HandleAttribute(AttributeHandlerContext* context,
361		uint8 id, const AttributeValue& value, AttributeHandler** _handler)
362	{
363		if (id == B_HPKG_ATTRIBUTE_ID_DIRECTORY_ENTRY) {
364			if (_handler != NULL) {
365				return EntryAttributeHandler::Create(context, NULL,
366					value.string, *_handler);
367			}
368			return B_OK;
369		}
370
371		return inherited::HandleAttribute(context, id, value, _handler);
372	}
373};
374
375
376// #pragma mark - PackageReaderImpl
377
378
379PackageReaderImpl::PackageReaderImpl(BErrorOutput* errorOutput)
380	:
381	inherited(errorOutput),
382	fTOCSection("TOC")
383{
384}
385
386
387PackageReaderImpl::~PackageReaderImpl()
388{
389}
390
391
392status_t
393PackageReaderImpl::Init(const char* fileName)
394{
395	// open file
396	int fd = open(fileName, O_RDONLY);
397	if (fd < 0) {
398		ErrorOutput()->PrintError("Error: Failed to open package file \"%s\": "
399			"%s\n", fileName, strerror(errno));
400		return errno;
401	}
402
403	return Init(fd, true);
404}
405
406
407status_t
408PackageReaderImpl::Init(int fd, bool keepFD)
409{
410	status_t error = inherited::Init(fd, keepFD);
411	if (error != B_OK)
412		return error;
413
414	// stat it
415	struct stat st;
416	if (fstat(FD(), &st) < 0) {
417		ErrorOutput()->PrintError("Error: Failed to access package file: %s\n",
418			strerror(errno));
419		return errno;
420	}
421
422	// read the header
423	hpkg_header header;
424	if ((error = ReadBuffer(0, &header, sizeof(header))) != B_OK)
425		return error;
426
427	// check the header
428
429	// magic
430	if (B_BENDIAN_TO_HOST_INT32(header.magic) != B_HPKG_MAGIC) {
431		ErrorOutput()->PrintError("Error: Invalid package file: Invalid "
432			"magic\n");
433		return B_BAD_DATA;
434	}
435
436	// version
437	if (B_BENDIAN_TO_HOST_INT16(header.version) != B_HPKG_VERSION) {
438		ErrorOutput()->PrintError("Error: Invalid/unsupported package file "
439			"version (%d)\n", B_BENDIAN_TO_HOST_INT16(header.version));
440		return B_MISMATCHED_VALUES;
441	}
442
443	// header size
444	fHeapOffset = B_BENDIAN_TO_HOST_INT16(header.header_size);
445	if ((size_t)fHeapOffset < sizeof(hpkg_header)) {
446		ErrorOutput()->PrintError("Error: Invalid package file: Invalid header "
447			"size (%llu)\n", fHeapOffset);
448		return B_BAD_DATA;
449	}
450
451	// total size
452	fTotalSize = B_BENDIAN_TO_HOST_INT64(header.total_size);
453	if (fTotalSize != (uint64)st.st_size) {
454		ErrorOutput()->PrintError("Error: Invalid package file: Total size in "
455			"header (%llu) doesn't agree with total file size (%lld)\n",
456			fTotalSize, st.st_size);
457		return B_BAD_DATA;
458	}
459
460	// package attributes length and compression
461	fPackageAttributesSection.compression
462		= B_BENDIAN_TO_HOST_INT32(header.attributes_compression);
463	fPackageAttributesSection.compressedLength
464		= B_BENDIAN_TO_HOST_INT32(header.attributes_length_compressed);
465	fPackageAttributesSection.uncompressedLength
466		= B_BENDIAN_TO_HOST_INT32(header.attributes_length_uncompressed);
467	fPackageAttributesSection.stringsLength
468		= B_BENDIAN_TO_HOST_INT32(header.attributes_strings_length);
469	fPackageAttributesSection.stringsCount
470		= B_BENDIAN_TO_HOST_INT32(header.attributes_strings_count);
471
472	if (const char* errorString = CheckCompression(
473		fPackageAttributesSection)) {
474		ErrorOutput()->PrintError("Error: Invalid package file: package "
475			"attributes section: %s\n", errorString);
476		return B_BAD_DATA;
477	}
478
479	// TOC length and compression
480	fTOCSection.compression = B_BENDIAN_TO_HOST_INT32(header.toc_compression);
481	fTOCSection.compressedLength
482		= B_BENDIAN_TO_HOST_INT64(header.toc_length_compressed);
483	fTOCSection.uncompressedLength
484		= B_BENDIAN_TO_HOST_INT64(header.toc_length_uncompressed);
485
486	if (const char* errorString = CheckCompression(fTOCSection)) {
487		ErrorOutput()->PrintError("Error: Invalid package file: TOC section: "
488			"%s\n", errorString);
489		return B_BAD_DATA;
490	}
491
492	// TOC subsections
493	fTOCSection.stringsLength
494		= B_BENDIAN_TO_HOST_INT64(header.toc_strings_length);
495	fTOCSection.stringsCount
496		= B_BENDIAN_TO_HOST_INT64(header.toc_strings_count);
497
498	if (fTOCSection.stringsLength > fTOCSection.uncompressedLength
499		|| fTOCSection.stringsCount > fTOCSection.stringsLength) {
500		ErrorOutput()->PrintError("Error: Invalid package file: Invalid TOC "
501			"subsections description\n");
502		return B_BAD_DATA;
503	}
504
505	// check whether the sections fit together
506	if (fPackageAttributesSection.compressedLength > fTotalSize
507		|| fTOCSection.compressedLength
508			> fTotalSize - fPackageAttributesSection.compressedLength
509		|| fHeapOffset
510			> fTotalSize - fPackageAttributesSection.compressedLength
511				- fTOCSection.compressedLength) {
512		ErrorOutput()->PrintError("Error: Invalid package file: The sum of the "
513			"sections sizes is greater than the package size\n");
514		return B_BAD_DATA;
515	}
516
517	fPackageAttributesSection.offset
518		= fTotalSize - fPackageAttributesSection.compressedLength;
519	fTOCSection.offset = fPackageAttributesSection.offset
520		- fTOCSection.compressedLength;
521	fHeapSize = fTOCSection.offset - fHeapOffset;
522
523	// TOC size sanity check
524	if (fTOCSection.uncompressedLength > kMaxTOCSize) {
525		ErrorOutput()->PrintError("Error: Package file TOC section size "
526			"is %llu bytes. This is beyond the reader's sanity limit\n",
527			fTOCSection.uncompressedLength);
528		return B_UNSUPPORTED;
529	}
530
531	// package attributes size sanity check
532	if (fPackageAttributesSection.uncompressedLength
533			> kMaxPackageAttributesSize) {
534		ErrorOutput()->PrintError(
535			"Error: Package file package attributes section size "
536			"is %llu bytes. This is beyond the reader's sanity limit\n",
537			fPackageAttributesSection.uncompressedLength);
538		return B_UNSUPPORTED;
539	}
540
541	// read in the complete TOC
542	fTOCSection.data
543		= new(std::nothrow) uint8[fTOCSection.uncompressedLength];
544	if (fTOCSection.data == NULL) {
545		ErrorOutput()->PrintError("Error: Out of memory!\n");
546		return B_NO_MEMORY;
547	}
548	error = ReadCompressedBuffer(fTOCSection);
549	if (error != B_OK)
550		return error;
551
552	// read in the complete package attributes section
553	fPackageAttributesSection.data
554		= new(std::nothrow) uint8[fPackageAttributesSection.uncompressedLength];
555	if (fPackageAttributesSection.data == NULL) {
556		ErrorOutput()->PrintError("Error: Out of memory!\n");
557		return B_NO_MEMORY;
558	}
559	error = ReadCompressedBuffer(fPackageAttributesSection);
560	if (error != B_OK)
561		return error;
562
563	// start parsing the TOC
564	fTOCSection.currentOffset = 0;
565	SetCurrentSection(&fTOCSection);
566
567	// strings
568	error = ParseStrings();
569	if (error != B_OK)
570		return error;
571
572	// parse strings from package attributes section
573	fPackageAttributesSection.currentOffset = 0;
574	SetCurrentSection(&fPackageAttributesSection);
575
576	// strings
577	error = ParseStrings();
578	if (error != B_OK)
579		return error;
580
581	SetCurrentSection(NULL);
582
583	return B_OK;
584}
585
586
587status_t
588PackageReaderImpl::ParseContent(BPackageContentHandler* contentHandler)
589{
590	AttributeHandlerContext context(ErrorOutput(), contentHandler,
591		B_HPKG_SECTION_PACKAGE_ATTRIBUTES);
592	RootAttributeHandler rootAttributeHandler;
593
594	status_t error
595		= ParsePackageAttributesSection(&context, &rootAttributeHandler);
596
597	if (error == B_OK) {
598		context.section = B_HPKG_SECTION_PACKAGE_TOC;
599		error = _ParseTOC(&context, &rootAttributeHandler);
600	}
601
602	return error;
603}
604
605
606status_t
607PackageReaderImpl::ParseContent(BLowLevelPackageContentHandler* contentHandler)
608{
609	AttributeHandlerContext context(ErrorOutput(), contentHandler,
610		B_HPKG_SECTION_PACKAGE_ATTRIBUTES);
611	LowLevelAttributeHandler rootAttributeHandler;
612
613	status_t error
614		= ParsePackageAttributesSection(&context, &rootAttributeHandler);
615
616	if (error == B_OK) {
617		context.section = B_HPKG_SECTION_PACKAGE_TOC;
618		error = _ParseTOC(&context, &rootAttributeHandler);
619	}
620
621	return error;
622}
623
624
625status_t
626PackageReaderImpl::_ParseTOC(AttributeHandlerContext* context,
627	AttributeHandler* rootAttributeHandler)
628{
629	// parse the TOC
630	fTOCSection.currentOffset = fTOCSection.stringsLength;
631	SetCurrentSection(&fTOCSection);
632
633	// prepare attribute handler context
634	context->heapOffset = fHeapOffset;
635	context->heapSize = fHeapSize;
636
637	// init the attribute handler stack
638	rootAttributeHandler->SetLevel(0);
639	ClearAttributeHandlerStack();
640	PushAttributeHandler(rootAttributeHandler);
641
642	bool sectionHandled;
643	status_t error = ParseAttributeTree(context, sectionHandled);
644	if (error == B_OK && sectionHandled) {
645		if (fTOCSection.currentOffset < fTOCSection.uncompressedLength) {
646			ErrorOutput()->PrintError("Error: %llu excess byte(s) in TOC "
647				"section\n",
648				fTOCSection.uncompressedLength - fTOCSection.currentOffset);
649			error = B_BAD_DATA;
650		}
651	}
652
653	// clean up on error
654	if (error != B_OK) {
655		context->ErrorOccurred();
656		while (AttributeHandler* handler = PopAttributeHandler()) {
657			if (handler != rootAttributeHandler)
658				handler->Delete(context);
659		}
660		return error;
661	}
662
663	return B_OK;
664}
665
666
667status_t
668PackageReaderImpl::ReadAttributeValue(uint8 type, uint8 encoding,
669	AttributeValue& _value)
670{
671	switch (type) {
672		case B_HPKG_ATTRIBUTE_TYPE_RAW:
673		{
674			uint64 size;
675			status_t error = ReadUnsignedLEB128(size);
676			if (error != B_OK)
677				return error;
678
679			if (encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP) {
680				uint64 offset;
681				error = ReadUnsignedLEB128(offset);
682				if (error != B_OK)
683					return error;
684
685				if (offset > fHeapSize || size > fHeapSize - offset) {
686					ErrorOutput()->PrintError("Error: Invalid %s section: "
687						"invalid data reference\n", CurrentSection()->name);
688					return B_BAD_DATA;
689				}
690
691				_value.SetToData(size, fHeapOffset + offset);
692			} else if (encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE) {
693				if (size > B_HPKG_MAX_INLINE_DATA_SIZE) {
694					ErrorOutput()->PrintError("Error: Invalid %s section: "
695						"inline data too long\n", CurrentSection()->name);
696					return B_BAD_DATA;
697				}
698
699				const void* buffer;
700				error = _GetTOCBuffer(size, buffer);
701				if (error != B_OK)
702					return error;
703				_value.SetToData(size, buffer);
704			} else {
705				ErrorOutput()->PrintError("Error: Invalid %s section: invalid "
706					"raw encoding (%u)\n", CurrentSection()->name, encoding);
707				return B_BAD_DATA;
708			}
709
710			return B_OK;
711		}
712
713		default:
714			return inherited::ReadAttributeValue(type, encoding, _value);
715	}
716}
717
718
719status_t
720PackageReaderImpl::_GetTOCBuffer(size_t size, const void*& _buffer)
721{
722	if (size > fTOCSection.uncompressedLength - fTOCSection.currentOffset) {
723		ErrorOutput()->PrintError("_GetTOCBuffer(%lu): read beyond TOC end\n",
724			size);
725		return B_BAD_DATA;
726	}
727
728	_buffer = fTOCSection.data + fTOCSection.currentOffset;
729	fTOCSection.currentOffset += size;
730	return B_OK;
731}
732
733
734}	// namespace BPrivate
735
736}	// namespace V1
737
738}	// namespace BHPKG
739
740}	// namespace BPackageKit
741