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