1 /* 2 * Copyright 2013-2024, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Ingo Weinhold <ingo_weinhold@gmx.de> 7 * Stephan Aßmus <superstippi@gmx.de> 8 * Rene Gollent <rene@gollent.com> 9 * Julian Harnath <julian.harnath@rwth-aachen.de> 10 * Andrew Lindesay <apl@lindesay.co.nz> 11 */ 12 13 14 #include "PackageManager.h" 15 16 #include <Alert.h> 17 #include <Catalog.h> 18 #include <Entry.h> 19 #include <FindDirectory.h> 20 #include <Path.h> 21 #include <Roster.h> 22 23 #include <package/DownloadFileRequest.h> 24 #include <package/manager/Exceptions.h> 25 #include <package/RefreshRepositoryRequest.h> 26 #include <package/hpkg/NoErrorOutput.h> 27 #include <package/hpkg/PackageContentHandler.h> 28 #include <package/hpkg/PackageEntry.h> 29 #include <package/hpkg/PackageEntryAttribute.h> 30 #include <package/hpkg/PackageInfoAttributeValue.h> 31 #include <package/hpkg/PackageReader.h> 32 #include <package/solver/SolverPackage.h> 33 #include <package/solver/SolverProblem.h> 34 #include <package/solver/SolverProblemSolution.h> 35 36 #include "AutoDeleter.h" 37 #include "AutoLocker.h" 38 #include "HaikuDepotConstants.h" 39 #include "Logger.h" 40 #include "OpenPackageProcess.h" 41 #include "PackageInfo.h" 42 #include "PackageUtils.h" 43 #include "ProblemWindow.h" 44 #include "ResultWindow.h" 45 46 47 #undef B_TRANSLATION_CONTEXT 48 #define B_TRANSLATION_CONTEXT "PackageManager" 49 50 51 using namespace BPackageKit; 52 using namespace BPackageKit::BPrivate; 53 using namespace BPackageKit::BManager::BPrivate; 54 55 using BPackageKit::BRefreshRepositoryRequest; 56 using BPackageKit::DownloadFileRequest; 57 using BPackageKit::BSolver; 58 using BPackageKit::BSolverPackage; 59 using BPackageKit::BSolverRepository; 60 using BPackageKit::BHPKG::BNoErrorOutput; 61 using BPackageKit::BHPKG::BPackageContentHandler; 62 using BPackageKit::BHPKG::BPackageEntry; 63 using BPackageKit::BHPKG::BPackageEntryAttribute; 64 using BPackageKit::BHPKG::BPackageInfoAttributeValue; 65 using BPackageKit::BHPKG::BPackageReader; 66 67 68 // #pragma mark - PackageProgressListener 69 70 71 PackageProgressListener::~PackageProgressListener() 72 { 73 } 74 75 76 void 77 PackageProgressListener::DownloadProgressChanged(const char* packageName, 78 float progress) 79 { 80 } 81 82 83 void 84 PackageProgressListener::DownloadProgressComplete(const char* packageName) 85 { 86 } 87 88 89 void 90 PackageProgressListener::ConfirmedChanges( 91 BPackageManager::InstalledRepository& repository) 92 { 93 } 94 95 96 void 97 PackageProgressListener::StartApplyingChanges( 98 BPackageManager::InstalledRepository& repository) 99 { 100 } 101 102 103 void 104 PackageProgressListener::ApplyingChangesDone( 105 BPackageManager::InstalledRepository& repository) 106 { 107 } 108 109 110 // #pragma mark - PackageManager 111 112 113 PackageManager::PackageManager(BPackageInstallationLocation location) 114 : 115 BPackageManager(location, &fClientInstallationInterface, this), 116 BPackageManager::UserInteractionHandler(), 117 fDecisionProvider(), 118 fClientInstallationInterface(), 119 fProblemWindow(NULL), 120 fCurrentInstallPackage(NULL), 121 fCurrentUninstallPackage(NULL) 122 { 123 } 124 125 126 PackageManager::~PackageManager() 127 { 128 if (fProblemWindow != NULL) 129 fProblemWindow->PostMessage(B_QUIT_REQUESTED); 130 } 131 132 133 PackageState 134 PackageManager::GetPackageState(const PackageInfo& package) 135 { 136 // TODO: Fetch information from the PackageKit 137 return NONE; 138 } 139 140 141 void 142 PackageManager::CollectPackageActions(PackageInfoRef package, 143 Collector<PackageActionRef>& actionList) 144 { 145 if (!package.IsSet()) 146 return; 147 148 PackageLocalInfoRef localInfo = package->LocalInfo(); 149 150 if (!localInfo.IsSet()) 151 return; 152 153 if (localInfo->IsSystemPackage() || localInfo->IsSystemDependency()) 154 return; 155 156 switch (PackageUtils::State(package)) { 157 case ACTIVATED: 158 case INSTALLED: 159 _CollectPackageActionsForActivatedOrInstalled(package, actionList); 160 break; 161 case NONE: 162 case UNINSTALLED: 163 actionList.Add(_CreateInstallPackageAction(package)); 164 break; 165 case DOWNLOADING: 166 HDINFO("no package actions for [%s] (downloading)", 167 package->Name().String()); 168 break; 169 case PENDING: 170 HDINFO("no package actions for [%s] (pending)", 171 package->Name().String()); 172 break; 173 default: 174 HDFATAL("unexpected status for package [%s]", 175 package->Name().String()); 176 break; 177 } 178 } 179 180 181 void 182 PackageManager::_CollectPackageActionsForActivatedOrInstalled( 183 PackageInfoRef package, 184 Collector<PackageActionRef>& actionList) 185 { 186 actionList.Add(_CreateUninstallPackageAction(package)); 187 188 // Add OpenPackageActions for each deskbar link found in the 189 // package 190 std::vector<DeskbarLink> foundLinks; 191 if (OpenPackageProcess::FindAppToLaunch(package, foundLinks) && foundLinks.size() < 4) { 192 std::vector<DeskbarLink>::const_iterator it; 193 for (it = foundLinks.begin(); it != foundLinks.end(); it++) { 194 const DeskbarLink& aLink = *it; 195 actionList.Add(_CreateOpenPackageAction(package, aLink)); 196 } 197 } 198 } 199 200 201 PackageActionRef 202 PackageManager::_CreateUninstallPackageAction(const PackageInfoRef& package) 203 { 204 BString actionTitle = B_TRANSLATE("Uninstall %PackageTitle%"); 205 BString packageTitle; 206 PackageUtils::TitleOrName(package, packageTitle); 207 actionTitle.ReplaceAll("%PackageTitle%", packageTitle); 208 209 BMessage message(MSG_PKG_UNINSTALL); 210 message.AddString(KEY_TITLE, actionTitle); 211 message.AddString(KEY_PACKAGE_NAME, package->Name()); 212 213 return PackageActionRef(new PackageAction(actionTitle, message), true); 214 } 215 216 217 PackageActionRef 218 PackageManager::_CreateInstallPackageAction(const PackageInfoRef& package) 219 { 220 BString actionTitle = B_TRANSLATE("Install %PackageTitle%"); 221 BString packageTitle; 222 PackageUtils::TitleOrName(package, packageTitle); 223 actionTitle.ReplaceAll("%PackageTitle%", packageTitle); 224 225 BMessage message(MSG_PKG_INSTALL); 226 message.AddString(KEY_TITLE, actionTitle); 227 message.AddString(KEY_PACKAGE_NAME, package->Name()); 228 229 return PackageActionRef(new PackageAction(actionTitle, message), true); 230 } 231 232 233 PackageActionRef 234 PackageManager::_CreateOpenPackageAction(const PackageInfoRef& package, const DeskbarLink& link) 235 { 236 BString title = B_TRANSLATE("Open %DeskbarLink%"); 237 title.ReplaceAll("%DeskbarLink%", link.Title()); 238 239 BMessage deskbarLinkMessage; 240 if (link.Archive(&deskbarLinkMessage) != B_OK) 241 HDFATAL("unable to archive the deskbar link"); 242 243 BMessage message(MSG_PKG_OPEN); 244 message.AddString(KEY_TITLE, title); 245 message.AddMessage(KEY_DESKBAR_LINK, &deskbarLinkMessage); 246 message.AddString(KEY_PACKAGE_NAME, package->Name()); 247 248 return PackageActionRef(new PackageAction(title, message), true); 249 } 250 251 252 void 253 PackageManager::SetCurrentActionPackage(PackageInfoRef package, bool install) 254 { 255 BSolverPackage* solverPackage = _GetSolverPackage(package); 256 fCurrentInstallPackage = install ? solverPackage : NULL; 257 fCurrentUninstallPackage = install ? NULL : solverPackage; 258 } 259 260 261 status_t 262 PackageManager::RefreshRepository(const BRepositoryConfig& repoConfig) 263 { 264 status_t result; 265 try { 266 result = BPackageManager::RefreshRepository(repoConfig); 267 } catch (BFatalErrorException& ex) { 268 HDERROR("Fatal error occurred while refreshing repository: " 269 "%s (%s)", ex.Message().String(), ex.Details().String()); 270 result = ex.Error(); 271 } catch (BException& ex) { 272 HDERROR("Exception occurred while refreshing " 273 "repository: %s\n", ex.Message().String()); 274 result = B_ERROR; 275 } 276 277 return result; 278 } 279 280 281 status_t 282 PackageManager::DownloadPackage(const BString& fileURL, 283 const BEntry& targetEntry, const BString& checksum) 284 { 285 status_t result; 286 try { 287 result = BPackageManager::DownloadPackage(fileURL, targetEntry, 288 checksum); 289 } catch (BFatalErrorException& ex) { 290 HDERROR("Fatal error occurred while downloading package: " 291 "%s: %s (%s)", fileURL.String(), ex.Message().String(), 292 ex.Details().String()); 293 result = ex.Error(); 294 } catch (BException& ex) { 295 HDERROR("Exception occurred while downloading package " 296 "%s: %s", fileURL.String(), ex.Message().String()); 297 result = B_ERROR; 298 } 299 300 return result; 301 } 302 303 304 void 305 PackageManager::AddProgressListener(PackageProgressListener* listener) 306 { 307 fPackageProgressListeners.AddItem(listener); 308 } 309 310 311 void 312 PackageManager::RemoveProgressListener(PackageProgressListener* listener) 313 { 314 fPackageProgressListeners.RemoveItem(listener); 315 } 316 317 318 void 319 PackageManager::HandleProblems() 320 { 321 if (fProblemWindow == NULL) 322 fProblemWindow = new ProblemWindow; 323 324 ProblemWindow::SolverPackageSet installPackages; 325 ProblemWindow::SolverPackageSet uninstallPackages; 326 if (fCurrentInstallPackage != NULL) 327 installPackages.insert(fCurrentInstallPackage); 328 329 if (fCurrentUninstallPackage != NULL) 330 uninstallPackages.insert(fCurrentUninstallPackage); 331 332 if (!fProblemWindow->Go(fSolver,installPackages, uninstallPackages)) 333 throw BAbortedByUserException(); 334 } 335 336 337 void 338 PackageManager::ConfirmChanges(bool fromMostSpecific) 339 { 340 ResultWindow* window = new ResultWindow; 341 ObjectDeleter<ResultWindow> windowDeleter(window); 342 343 bool hasOtherChanges = false; 344 int32 count = fInstalledRepositories.CountItems(); 345 346 if (fromMostSpecific) { 347 for (int32 i = count - 1; i >= 0; i--) 348 hasOtherChanges 349 |= _AddResults(*fInstalledRepositories.ItemAt(i), window); 350 } else { 351 for (int32 i = 0; i < count; i++) 352 hasOtherChanges 353 |= _AddResults(*fInstalledRepositories.ItemAt(i), window); 354 } 355 356 if (!hasOtherChanges) { 357 _NotifyChangesConfirmed(); 358 return; 359 } 360 361 // show the window 362 if (windowDeleter.Detach()->Go() == 0) 363 throw BAbortedByUserException(); 364 365 _NotifyChangesConfirmed(); 366 } 367 368 369 void 370 PackageManager::Warn(status_t error, const char* format, ...) 371 { 372 // TODO: Show alert to user 373 374 va_list args; 375 va_start(args, format); 376 vfprintf(stderr, format, args); 377 va_end(args); 378 379 if (error == B_OK) 380 printf("\n"); 381 else 382 printf(": %s\n", strerror(error)); 383 } 384 385 386 void 387 PackageManager::ProgressPackageDownloadStarted(const char* packageName) 388 { 389 ProgressPackageDownloadActive(packageName, 0.0f, 0, 0); 390 } 391 392 393 void 394 PackageManager::ProgressPackageDownloadActive(const char* packageName, 395 float completionPercentage, off_t bytes, off_t totalBytes) 396 { 397 for (int32 i = 0; i < fPackageProgressListeners.CountItems(); i++) { 398 fPackageProgressListeners.ItemAt(i)->DownloadProgressChanged( 399 packageName, completionPercentage); 400 } 401 } 402 403 404 void 405 PackageManager::ProgressPackageDownloadComplete(const char* packageName) 406 { 407 for (int32 i = 0; i < fPackageProgressListeners.CountItems(); i++) { 408 fPackageProgressListeners.ItemAt(i)->DownloadProgressComplete( 409 packageName); 410 } 411 } 412 413 414 void 415 PackageManager::ProgressPackageChecksumStarted(const char* title) 416 { 417 // TODO: implement 418 } 419 420 421 void 422 PackageManager::ProgressPackageChecksumComplete(const char* title) 423 { 424 // TODO: implement 425 } 426 427 428 void 429 PackageManager::ProgressStartApplyingChanges(InstalledRepository& repository) 430 { 431 for (int32 i = 0; i < fPackageProgressListeners.CountItems(); i++) 432 fPackageProgressListeners.ItemAt(i)->StartApplyingChanges(repository); 433 } 434 435 436 void 437 PackageManager::ProgressTransactionCommitted(InstalledRepository& repository, 438 const BCommitTransactionResult& result) 439 { 440 // TODO: implement 441 } 442 443 444 void 445 PackageManager::ProgressApplyingChangesDone(InstalledRepository& repository) 446 { 447 for (int32 i = 0; i < fPackageProgressListeners.CountItems(); i++) 448 fPackageProgressListeners.ItemAt(i)->ApplyingChangesDone(repository); 449 450 if (BPackageRoster().IsRebootNeeded()) { 451 BString infoString(B_TRANSLATE("A reboot is necessary to complete the " 452 "installation process.")); 453 BAlert* alert = new(std::nothrow) BAlert(B_TRANSLATE("Reboot required"), 454 infoString, B_TRANSLATE("Close"), NULL, NULL, 455 B_WIDTH_AS_USUAL, B_INFO_ALERT); 456 if (alert != NULL) 457 alert->Go(); 458 } 459 } 460 461 462 bool 463 PackageManager::_AddResults(InstalledRepository& repository, 464 ResultWindow* window) 465 { 466 if (!repository.HasChanges()) 467 return false; 468 469 ProblemWindow::SolverPackageSet installPackages; 470 ProblemWindow::SolverPackageSet uninstallPackages; 471 if (fCurrentInstallPackage != NULL) 472 installPackages.insert(fCurrentInstallPackage); 473 474 if (fCurrentUninstallPackage != NULL) 475 uninstallPackages.insert(fCurrentUninstallPackage); 476 477 return window->AddLocationChanges(repository.Name(), 478 repository.PackagesToActivate(), installPackages, 479 repository.PackagesToDeactivate(), uninstallPackages); 480 } 481 482 483 void 484 PackageManager::_NotifyChangesConfirmed() 485 { 486 int32 count = fInstalledRepositories.CountItems(); 487 for (int32 i = 0; i < count; i++) { 488 for (int32 j = 0; j < fPackageProgressListeners.CountItems(); j++) { 489 fPackageProgressListeners.ItemAt(j)->ConfirmedChanges( 490 *fInstalledRepositories.ItemAt(i)); 491 } 492 } 493 } 494 495 496 BSolverPackage* 497 PackageManager::_GetSolverPackage(PackageInfoRef package) 498 { 499 int32 flags = BSolver::B_FIND_IN_NAME; 500 PackageState state = PackageUtils::State(package); 501 502 if (state == ACTIVATED || state == INSTALLED) 503 flags |= BSolver::B_FIND_INSTALLED_ONLY; 504 505 BObjectList<BSolverPackage> packages; 506 status_t result = Solver()->FindPackages(package->Name(), flags, packages); 507 if (result == B_OK) { 508 for (int32 i = 0; i < packages.CountItems(); i++) { 509 BSolverPackage* solverPackage = packages.ItemAt(i); 510 if (solverPackage->Name() != package->Name()) 511 continue; 512 else if (state == NONE 513 && dynamic_cast<BPackageManager::RemoteRepository*>(solverPackage->Repository()) 514 == NULL) { 515 continue; 516 } 517 return solverPackage; 518 } 519 } 520 521 return NULL; 522 } 523