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 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 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 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 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 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 93 InstalledPackageInfo::~InstalledPackageInfo() 94 { 95 _ClearItemList(); 96 } 97 98 99 status_t 100 InstalledPackageInfo::InitCheck() 101 { 102 return fStatus; 103 } 104 105 106 status_t 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 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 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 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 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