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) 27 return B_ERROR; 28 29 BPath path; 30 if (find_directory(B_USER_CONFIG_DIRECTORY, &path) != B_OK 31 || path.Append(kPackagesDir) != B_OK 32 || path.Append(filename) != B_OK) 33 return B_ERROR; 34 35 file->SetTo(path.Path(), B_READ_ONLY); 36 if (file->InitCheck() != B_OK) 37 return B_ERROR; 38 39 status_t ret = info->Unflatten(file); 40 if (ret != B_OK || info->what != P_PACKAGE_INFO) 41 return B_ERROR; 42 43 return B_OK; 44 } 45 46 47 const char * 48 info_get_package_name(const char *filename) 49 { 50 BFile file; 51 BMessage info; 52 if (info_prepare(filename, &file, &info) != B_OK) 53 return NULL; 54 BString name; 55 info.FindString("package_name", &name); 56 return name.String(); 57 } 58 59 60 const char * 61 info_get_package_version(const char *filename) 62 { 63 BFile file; 64 BMessage info; 65 if (info_prepare(filename, &file, &info) != B_OK) 66 return NULL; 67 BString version; 68 info.FindString("package_version", &version); 69 return version.String(); 70 } 71 72 73 InstalledPackageInfo::InstalledPackageInfo() 74 : 75 fStatus(B_NO_INIT), 76 fIsUpToDate(false), 77 fCreate(false), 78 fInstalledItems(10) 79 { 80 } 81 82 83 InstalledPackageInfo::InstalledPackageInfo(const char *packageName, 84 const char *version, bool create) 85 : 86 fStatus(B_NO_INIT), 87 fIsUpToDate(false), 88 fInstalledItems(10) 89 { 90 SetTo(packageName, version, create); 91 } 92 93 94 InstalledPackageInfo::~InstalledPackageInfo() 95 { 96 _ClearItemList(); 97 } 98 99 100 status_t 101 InstalledPackageInfo::InitCheck() 102 { 103 return fStatus; 104 } 105 106 107 status_t 108 InstalledPackageInfo::SetTo(const char *packageName, const char *version, 109 bool create) 110 { 111 _ClearItemList(); 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 } 184 else if (fStatus == B_ENTRY_NOT_FOUND) { 185 if (create) { 186 fStatus = B_OK; 187 fIsUpToDate = false; 188 } 189 } 190 191 return fStatus; 192 } 193 194 195 status_t 196 InstalledPackageInfo::AddItem(const char *itemName) 197 { 198 if (!itemName) 199 return B_ERROR; 200 201 return fInstalledItems.AddItem(new BString(itemName)); 202 } 203 204 205 status_t 206 InstalledPackageInfo::Uninstall() 207 { 208 if (fStatus != B_OK) 209 return fStatus; 210 211 BString *iter; 212 uint32 i, count = fInstalledItems.CountItems(); 213 BEntry entry; 214 status_t ret; 215 216 // Try to remove all entries that are present in the list 217 for (i = 0; i < count; i++) { 218 iter = static_cast<BString *>(fInstalledItems.ItemAt(count - i - 1)); 219 fprintf(stderr, "Removing: %s (%ld/%ld)\n", iter->String(), i, count); 220 ret = entry.SetTo(iter->String()); 221 if (ret == B_BUSY) { 222 // The entry's directory is locked - wait a few cycles for it to 223 // unlock itself 224 int32 tries = 0; 225 for (tries = 0; tries < P_BUSY_TRIES; tries++) { 226 ret = entry.SetTo(iter->String()); 227 if (ret != B_BUSY) 228 break; 229 // Wait a moment 230 usleep(1000); 231 } 232 } 233 234 if (ret == B_ENTRY_NOT_FOUND) 235 continue; 236 else if (ret != B_OK) { 237 fStatus = B_ERROR; 238 return fStatus; 239 } 240 241 fprintf(stderr, "...we continue\n"); 242 243 if (entry.Exists() && entry.Remove() != B_OK) { 244 fprintf(stderr, "\n%s\n", strerror(ret)); 245 fStatus = B_ERROR; 246 return fStatus; 247 } 248 fInstalledItems.RemoveItem(count - i - 1); 249 } 250 251 if (entry.SetTo(fPathToInfo.Path()) != B_OK) { 252 fStatus = B_ERROR; 253 return fStatus; 254 } 255 if (entry.Exists() && entry.Remove() != B_OK) { 256 fStatus = B_ERROR; 257 return fStatus; 258 } 259 260 return fStatus; 261 } 262 263 264 status_t 265 InstalledPackageInfo::Save() 266 { 267 // If the package info is not up to date and everything till now was 268 // done correctly, we will save all data as a flattened BMessage to the 269 // package info file 270 if (fIsUpToDate || fStatus != B_OK) 271 return fStatus; 272 273 BFile package; 274 if (fCreate) { 275 fStatus = package.SetTo(fPathToInfo.Path(), B_WRITE_ONLY | B_CREATE_FILE 276 | B_ERASE_FILE); 277 } 278 else { 279 fStatus = package.SetTo(fPathToInfo.Path(), B_WRITE_ONLY | B_ERASE_FILE); 280 } 281 282 if (fStatus != B_OK) 283 return fStatus; 284 285 status_t ret; 286 int32 i, count = fInstalledItems.CountItems(); 287 BMessage info(P_PACKAGE_INFO); 288 ret = info.AddString("package_name", fName); 289 ret |= info.AddString("package_desc", fDescription); 290 ret |= info.AddString("package_version", fVersion); 291 ret |= info.AddInt64("package_size", fSpaceNeeded); 292 ret |= info.AddInt32("file_count", count); 293 if (ret != B_OK) { 294 fStatus = B_ERROR; 295 return fStatus; 296 } 297 298 BString *iter; 299 for (i = 0; i < count; i++) { 300 iter = static_cast<BString *>(fInstalledItems.ItemAt(i)); 301 if (info.AddString("items", *iter) != B_OK) { 302 fStatus = B_ERROR; 303 return fStatus; 304 } 305 } 306 307 if (info.Flatten(&package) != B_OK) { 308 fStatus = B_ERROR; 309 return fStatus; 310 } 311 fIsUpToDate = true; 312 313 return fStatus; 314 } 315 316 317 // #pragma mark - 318 319 320 void 321 InstalledPackageInfo::_ClearItemList() 322 { 323 // Clear the items list 324 BString *iter; 325 uint32 i, count = fInstalledItems.CountItems(); 326 for (i = 0; i < count; i++) { 327 iter = static_cast<BString *>(fInstalledItems.ItemAt(0)); 328 fInstalledItems.RemoveItem((int32)0); 329 delete iter; 330 } 331 } 332 333