xref: /haiku/src/apps/packageinstaller/InstalledPackageInfo.cpp (revision 4450737441e20843ab545054d9a2f4accd2242b0)
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 
20 const char * kPackagesDir = "packages";
21 
22 
23 static status_t
info_prepare(const char * filename,BFile * file,BMessage * info)24 info_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 
46 status_t
info_get_package_name(const char * filename,BString & name)47 info_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 
58 status_t
info_get_package_version(const char * filename,BString & version)59 info_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 
InstalledPackageInfo()70 InstalledPackageInfo::InstalledPackageInfo()
71 	:
72 	fStatus(B_NO_INIT),
73 	fIsUpToDate(false),
74 	fCreate(false),
75 	fSpaceNeeded(0),
76 	fInstalledItems(10)
77 {
78 }
79 
80 
InstalledPackageInfo(const char * packageName,const char * version,bool create)81 InstalledPackageInfo::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 
~InstalledPackageInfo()93 InstalledPackageInfo::~InstalledPackageInfo()
94 {
95 	_ClearItemList();
96 }
97 
98 
99 status_t
InitCheck()100 InstalledPackageInfo::InitCheck()
101 {
102 	return fStatus;
103 }
104 
105 
106 status_t
SetTo(const char * packageName,const char * version,bool create)107 InstalledPackageInfo::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 
194 status_t
AddItem(const char * itemName)195 InstalledPackageInfo::AddItem(const char *itemName)
196 {
197 	if (!itemName)
198 		return B_ERROR;
199 
200 	return fInstalledItems.AddItem(new BString(itemName));
201 }
202 
203 
204 status_t
Uninstall()205 InstalledPackageInfo::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 
259 status_t
Save()260 InstalledPackageInfo::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 
315 void
_ClearItemList()316 InstalledPackageInfo::_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