1/*
2 * Copyright (c) 2007, Haiku, Inc.
3 * Distributed under the terms of the MIT license.
4 *
5 * Author:
6 *		��ukasz 'Sil2100' Zemczak <sil2100@vexillium.org>
7 */
8
9
10#include "InstalledPackageInfo.h"
11
12#include <stdio.h>
13#include <string.h>
14
15#include <Directory.h>
16#include <Entry.h>
17#include <FindDirectory.h>
18
19
20const char * kPackagesDir = "packages";
21
22
23static status_t
24info_prepare(const char *filename, BFile *file, BMessage *info)
25{
26	if (filename == NULL || file == NULL || info == NULL)
27		return B_BAD_VALUE;
28
29	BPath path;
30	status_t ret = find_directory(B_USER_CONFIG_DIRECTORY, &path);
31	if (ret == B_OK)
32		ret = path.Append(kPackagesDir);
33	if (ret == B_OK)
34		ret = path.Append(filename);
35	if (ret == B_OK)
36		ret = file->SetTo(path.Path(), B_READ_ONLY);
37	if (ret == B_OK)
38		ret = info->Unflatten(file);
39	if (ret == B_OK && info->what != P_PACKAGE_INFO)
40		ret = B_ERROR;
41
42	return ret;
43}
44
45
46status_t
47info_get_package_name(const char *filename, BString &name)
48{
49	BFile file;
50	BMessage info;
51	status_t ret = info_prepare(filename, &file, &info);
52	if (ret == B_OK)
53		ret = info.FindString("package_name", &name);
54	return ret;
55}
56
57
58status_t
59info_get_package_version(const char *filename, BString &version)
60{
61	BFile file;
62	BMessage info;
63	status_t ret = info_prepare(filename, &file, &info);
64	if (ret == B_OK)
65		ret = info.FindString("package_version", &version);
66	return ret;
67}
68
69
70InstalledPackageInfo::InstalledPackageInfo()
71	:
72	fStatus(B_NO_INIT),
73	fIsUpToDate(false),
74	fCreate(false),
75	fSpaceNeeded(0),
76	fInstalledItems(10)
77{
78}
79
80
81InstalledPackageInfo::InstalledPackageInfo(const char *packageName,
82		const char *version, bool create)
83	:
84	fStatus(B_NO_INIT),
85	fIsUpToDate(false),
86	fSpaceNeeded(0),
87	fInstalledItems(10)
88{
89	SetTo(packageName, version, create);
90}
91
92
93InstalledPackageInfo::~InstalledPackageInfo()
94{
95	_ClearItemList();
96}
97
98
99status_t
100InstalledPackageInfo::InitCheck()
101{
102	return fStatus;
103}
104
105
106status_t
107InstalledPackageInfo::SetTo(const char *packageName, const char *version,
108	bool create)
109{
110	_ClearItemList();
111
112	fCreate = create;
113	fStatus = B_NO_INIT;
114	fVersion = version;
115
116	if (!packageName)
117		return fStatus;
118
119	BPath configPath;
120	if (find_directory(B_USER_CONFIG_DIRECTORY, &configPath) != B_OK) {
121		fStatus = B_ERROR;
122		return fStatus;
123	}
124
125	if (fPathToInfo.SetTo(configPath.Path(), kPackagesDir) != B_OK) {
126		fStatus = B_ERROR;
127		return fStatus;
128	}
129
130	// Check whether the directory exists
131	BDirectory packageDir(fPathToInfo.Path());
132	fStatus = packageDir.InitCheck();
133	if (fStatus == B_ENTRY_NOT_FOUND) {
134		// If not, create it
135		packageDir.SetTo(configPath.Path());
136		if (packageDir.CreateDirectory(kPackagesDir, &packageDir) != B_OK) {
137			fStatus = B_ERROR;
138			return fStatus;
139		}
140	}
141
142	BString filename = packageName;
143	filename << version << ".pdb";
144	if (fPathToInfo.Append(filename.String()) != B_OK) {
145		fStatus = B_ERROR;
146		return fStatus;
147	}
148
149	BFile package(fPathToInfo.Path(), B_READ_ONLY);
150	fStatus = package.InitCheck();
151	if (fStatus == B_OK) {
152		// The given package exists, so we can unflatten the data to a message
153		// and then pass it further
154		BMessage info;
155		if (info.Unflatten(&package) != B_OK || info.what != P_PACKAGE_INFO) {
156			fStatus = B_ERROR;
157			return fStatus;
158		}
159
160		int32 count;
161		fStatus = info.FindString("package_name", &fName);
162		fStatus |= info.FindString("package_desc", &fDescription);
163		fStatus |= info.FindString("package_version", &fVersion);
164		int64 spaceNeeded = 0;
165		fStatus |= info.FindInt64("package_size", &spaceNeeded);
166		fSpaceNeeded = static_cast<uint64>(spaceNeeded);
167		fStatus |= info.FindInt32("file_count", &count);
168		if (fStatus != B_OK) {
169			fStatus = B_ERROR;
170			return fStatus;
171		}
172
173		int32 i;
174		BString itemPath;
175		for (i = 0; i < count; i++) {
176			if (info.FindString("items", i, &itemPath) != B_OK) {
177				fStatus = B_ERROR;
178				return fStatus;
179			}
180			fInstalledItems.AddItem(new BString(itemPath)); // Or maybe BPath better?
181		}
182		fIsUpToDate = true;
183	} else if (fStatus == B_ENTRY_NOT_FOUND) {
184		if (create) {
185			fStatus = B_OK;
186			fIsUpToDate = false;
187		}
188	}
189
190	return fStatus;
191}
192
193
194status_t
195InstalledPackageInfo::AddItem(const char *itemName)
196{
197	if (!itemName)
198		return B_ERROR;
199
200	return fInstalledItems.AddItem(new BString(itemName));
201}
202
203
204status_t
205InstalledPackageInfo::Uninstall()
206{
207	if (fStatus != B_OK)
208		return fStatus;
209
210	BString *iter;
211	uint32 i, count = fInstalledItems.CountItems();
212	BEntry entry;
213	status_t ret;
214
215	// Try to remove all entries that are present in the list
216	for (i = 0; i < count; i++) {
217		iter = static_cast<BString *>(fInstalledItems.ItemAt(count - i - 1));
218		ret = entry.SetTo(iter->String());
219		if (ret == B_BUSY) {
220			// The entry's directory is locked - wait a few cycles for it to
221			// unlock itself
222			int32 tries = 0;
223			for (tries = 0; tries < P_BUSY_TRIES; tries++) {
224				ret = entry.SetTo(iter->String());
225				if (ret != B_BUSY)
226					break;
227				// Wait a moment
228				usleep(1000);
229			}
230		}
231
232		if (ret == B_ENTRY_NOT_FOUND)
233			continue;
234		else if (ret != B_OK) {
235			fStatus = B_ERROR;
236			return fStatus;
237		}
238
239		if (entry.Exists() && entry.Remove() != B_OK) {
240			fStatus = B_ERROR;
241			return fStatus;
242		}
243		fInstalledItems.RemoveItem(count - i - 1);
244	}
245
246	if (entry.SetTo(fPathToInfo.Path()) != B_OK) {
247		fStatus = B_ERROR;
248		return fStatus;
249	}
250	if (entry.Exists() && entry.Remove() != B_OK) {
251		fStatus = B_ERROR;
252		return fStatus;
253	}
254
255	return fStatus;
256}
257
258
259status_t
260InstalledPackageInfo::Save()
261{
262	// If the package info is not up to date and everything till now was
263	// done correctly, we will save all data as a flattened BMessage to the
264	// package info file
265	if (fIsUpToDate || fStatus != B_OK)
266		return fStatus;
267
268	BFile package;
269	if (fCreate) {
270		fStatus = package.SetTo(fPathToInfo.Path(), B_WRITE_ONLY | B_CREATE_FILE
271			| B_ERASE_FILE);
272	}
273	else {
274		fStatus = package.SetTo(fPathToInfo.Path(), B_WRITE_ONLY | B_ERASE_FILE);
275	}
276
277	if (fStatus != B_OK)
278		return fStatus;
279
280	status_t ret;
281	int32 i, count = fInstalledItems.CountItems();
282	BMessage info(P_PACKAGE_INFO);
283	ret = info.AddString("package_name", fName);
284	ret |= info.AddString("package_desc", fDescription);
285	ret |= info.AddString("package_version", fVersion);
286	ret |= info.AddInt64("package_size", fSpaceNeeded);
287	ret |= info.AddInt32("file_count", count);
288	if (ret != B_OK) {
289		fStatus = B_ERROR;
290		return fStatus;
291	}
292
293	BString *iter;
294	for (i = 0; i < count; i++) {
295		iter = static_cast<BString *>(fInstalledItems.ItemAt(i));
296		if (info.AddString("items", *iter) != B_OK) {
297			fStatus = B_ERROR;
298			return fStatus;
299		}
300	}
301
302	if (info.Flatten(&package) != B_OK) {
303		fStatus = B_ERROR;
304		return fStatus;
305	}
306	fIsUpToDate = true;
307
308	return fStatus;
309}
310
311
312// #pragma mark -
313
314
315void
316InstalledPackageInfo::_ClearItemList()
317{
318	for (int32 i = fInstalledItems.CountItems() - 1; i >= 0; i--)
319		delete static_cast<BString*>(fInstalledItems.ItemAtFast(i));
320	fInstalledItems.MakeEmpty();
321}
322
323