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->IsSystemPackage() || package->IsSystemDependency()) 146 return; 147 148 switch (package->State()) { 149 case ACTIVATED: 150 case INSTALLED: 151 _CollectPackageActionsForActivatedOrInstalled(package, actionList); 152 break; 153 case NONE: 154 case UNINSTALLED: 155 actionList.Add(_CreateInstallPackageAction(package)); 156 break; 157 case DOWNLOADING: 158 HDINFO("no package actions for [%s] (downloading)", 159 package->Name().String()); 160 break; 161 case PENDING: 162 HDINFO("no package actions for [%s] (pending)", 163 package->Name().String()); 164 break; 165 default: 166 HDFATAL("unexpected status for package [%s]", 167 package->Name().String()); 168 break; 169 } 170 } 171 172 173 void 174 PackageManager::_CollectPackageActionsForActivatedOrInstalled( 175 PackageInfoRef package, 176 Collector<PackageActionRef>& actionList) 177 { 178 actionList.Add(_CreateUninstallPackageAction(package)); 179 180 // Add OpenPackageActions for each deskbar link found in the 181 // package 182 std::vector<DeskbarLink> foundLinks; 183 if (OpenPackageProcess::FindAppToLaunch(package, foundLinks) && foundLinks.size() < 4) { 184 std::vector<DeskbarLink>::const_iterator it; 185 for (it = foundLinks.begin(); it != foundLinks.end(); it++) { 186 const DeskbarLink& aLink = *it; 187 actionList.Add(_CreateOpenPackageAction(package, aLink)); 188 } 189 } 190 } 191 192 193 PackageActionRef 194 PackageManager::_CreateUninstallPackageAction(const PackageInfoRef& package) 195 { 196 BString actionTitle = B_TRANSLATE("Uninstall %PackageTitle%"); 197 BString packageTitle; 198 PackageUtils::TitleOrName(package, packageTitle); 199 actionTitle.ReplaceAll("%PackageTitle%", packageTitle); 200 201 BMessage message(MSG_PKG_UNINSTALL); 202 message.AddString(KEY_TITLE, actionTitle); 203 message.AddString(KEY_PACKAGE_NAME, package->Name()); 204 205 return PackageActionRef(new PackageAction(actionTitle, message), true); 206 } 207 208 209 PackageActionRef 210 PackageManager::_CreateInstallPackageAction(const PackageInfoRef& package) 211 { 212 BString actionTitle = B_TRANSLATE("Install %PackageTitle%"); 213 BString packageTitle; 214 PackageUtils::TitleOrName(package, packageTitle); 215 actionTitle.ReplaceAll("%PackageTitle%", packageTitle); 216 217 BMessage message(MSG_PKG_INSTALL); 218 message.AddString(KEY_TITLE, actionTitle); 219 message.AddString(KEY_PACKAGE_NAME, package->Name()); 220 221 return PackageActionRef(new PackageAction(actionTitle, message), true); 222 } 223 224 225 PackageActionRef 226 PackageManager::_CreateOpenPackageAction(const PackageInfoRef& package, const DeskbarLink& link) 227 { 228 BString title = B_TRANSLATE("Open %DeskbarLink%"); 229 title.ReplaceAll("%DeskbarLink%", link.Title()); 230 231 BMessage deskbarLinkMessage; 232 if (link.Archive(&deskbarLinkMessage) != B_OK) 233 HDFATAL("unable to archive the deskbar link"); 234 235 BMessage message(MSG_PKG_OPEN); 236 message.AddString(KEY_TITLE, title); 237 message.AddMessage(KEY_DESKBAR_LINK, &deskbarLinkMessage); 238 message.AddString(KEY_PACKAGE_NAME, package->Name()); 239 240 return PackageActionRef(new PackageAction(title, message), true); 241 } 242 243 244 void 245 PackageManager::SetCurrentActionPackage(PackageInfoRef package, bool install) 246 { 247 BSolverPackage* solverPackage = _GetSolverPackage(package); 248 fCurrentInstallPackage = install ? solverPackage : NULL; 249 fCurrentUninstallPackage = install ? NULL : solverPackage; 250 } 251 252 253 status_t 254 PackageManager::RefreshRepository(const BRepositoryConfig& repoConfig) 255 { 256 status_t result; 257 try { 258 result = BPackageManager::RefreshRepository(repoConfig); 259 } catch (BFatalErrorException& ex) { 260 HDERROR("Fatal error occurred while refreshing repository: " 261 "%s (%s)", ex.Message().String(), ex.Details().String()); 262 result = ex.Error(); 263 } catch (BException& ex) { 264 HDERROR("Exception occurred while refreshing " 265 "repository: %s\n", ex.Message().String()); 266 result = B_ERROR; 267 } 268 269 return result; 270 } 271 272 273 status_t 274 PackageManager::DownloadPackage(const BString& fileURL, 275 const BEntry& targetEntry, const BString& checksum) 276 { 277 status_t result; 278 try { 279 result = BPackageManager::DownloadPackage(fileURL, targetEntry, 280 checksum); 281 } catch (BFatalErrorException& ex) { 282 HDERROR("Fatal error occurred while downloading package: " 283 "%s: %s (%s)", fileURL.String(), ex.Message().String(), 284 ex.Details().String()); 285 result = ex.Error(); 286 } catch (BException& ex) { 287 HDERROR("Exception occurred while downloading package " 288 "%s: %s", fileURL.String(), ex.Message().String()); 289 result = B_ERROR; 290 } 291 292 return result; 293 } 294 295 296 void 297 PackageManager::AddProgressListener(PackageProgressListener* listener) 298 { 299 fPackageProgressListeners.AddItem(listener); 300 } 301 302 303 void 304 PackageManager::RemoveProgressListener(PackageProgressListener* listener) 305 { 306 fPackageProgressListeners.RemoveItem(listener); 307 } 308 309 310 void 311 PackageManager::HandleProblems() 312 { 313 if (fProblemWindow == NULL) 314 fProblemWindow = new ProblemWindow; 315 316 ProblemWindow::SolverPackageSet installPackages; 317 ProblemWindow::SolverPackageSet uninstallPackages; 318 if (fCurrentInstallPackage != NULL) 319 installPackages.insert(fCurrentInstallPackage); 320 321 if (fCurrentUninstallPackage != NULL) 322 uninstallPackages.insert(fCurrentUninstallPackage); 323 324 if (!fProblemWindow->Go(fSolver,installPackages, uninstallPackages)) 325 throw BAbortedByUserException(); 326 } 327 328 329 void 330 PackageManager::ConfirmChanges(bool fromMostSpecific) 331 { 332 ResultWindow* window = new ResultWindow; 333 ObjectDeleter<ResultWindow> windowDeleter(window); 334 335 bool hasOtherChanges = false; 336 int32 count = fInstalledRepositories.CountItems(); 337 338 if (fromMostSpecific) { 339 for (int32 i = count - 1; i >= 0; i--) 340 hasOtherChanges 341 |= _AddResults(*fInstalledRepositories.ItemAt(i), window); 342 } else { 343 for (int32 i = 0; i < count; i++) 344 hasOtherChanges 345 |= _AddResults(*fInstalledRepositories.ItemAt(i), window); 346 } 347 348 if (!hasOtherChanges) { 349 _NotifyChangesConfirmed(); 350 return; 351 } 352 353 // show the window 354 if (windowDeleter.Detach()->Go() == 0) 355 throw BAbortedByUserException(); 356 357 _NotifyChangesConfirmed(); 358 } 359 360 361 void 362 PackageManager::Warn(status_t error, const char* format, ...) 363 { 364 // TODO: Show alert to user 365 366 va_list args; 367 va_start(args, format); 368 vfprintf(stderr, format, args); 369 va_end(args); 370 371 if (error == B_OK) 372 printf("\n"); 373 else 374 printf(": %s\n", strerror(error)); 375 } 376 377 378 void 379 PackageManager::ProgressPackageDownloadStarted(const char* packageName) 380 { 381 ProgressPackageDownloadActive(packageName, 0.0f, 0, 0); 382 } 383 384 385 void 386 PackageManager::ProgressPackageDownloadActive(const char* packageName, 387 float completionPercentage, off_t bytes, off_t totalBytes) 388 { 389 for (int32 i = 0; i < fPackageProgressListeners.CountItems(); i++) { 390 fPackageProgressListeners.ItemAt(i)->DownloadProgressChanged( 391 packageName, completionPercentage); 392 } 393 } 394 395 396 void 397 PackageManager::ProgressPackageDownloadComplete(const char* packageName) 398 { 399 for (int32 i = 0; i < fPackageProgressListeners.CountItems(); i++) { 400 fPackageProgressListeners.ItemAt(i)->DownloadProgressComplete( 401 packageName); 402 } 403 } 404 405 406 void 407 PackageManager::ProgressPackageChecksumStarted(const char* title) 408 { 409 // TODO: implement 410 } 411 412 413 void 414 PackageManager::ProgressPackageChecksumComplete(const char* title) 415 { 416 // TODO: implement 417 } 418 419 420 void 421 PackageManager::ProgressStartApplyingChanges(InstalledRepository& repository) 422 { 423 for (int32 i = 0; i < fPackageProgressListeners.CountItems(); i++) 424 fPackageProgressListeners.ItemAt(i)->StartApplyingChanges(repository); 425 } 426 427 428 void 429 PackageManager::ProgressTransactionCommitted(InstalledRepository& repository, 430 const BCommitTransactionResult& result) 431 { 432 // TODO: implement 433 } 434 435 436 void 437 PackageManager::ProgressApplyingChangesDone(InstalledRepository& repository) 438 { 439 for (int32 i = 0; i < fPackageProgressListeners.CountItems(); i++) 440 fPackageProgressListeners.ItemAt(i)->ApplyingChangesDone(repository); 441 442 if (BPackageRoster().IsRebootNeeded()) { 443 BString infoString(B_TRANSLATE("A reboot is necessary to complete the " 444 "installation process.")); 445 BAlert* alert = new(std::nothrow) BAlert(B_TRANSLATE("Reboot required"), 446 infoString, B_TRANSLATE("Close"), NULL, NULL, 447 B_WIDTH_AS_USUAL, B_INFO_ALERT); 448 if (alert != NULL) 449 alert->Go(); 450 } 451 } 452 453 454 bool 455 PackageManager::_AddResults(InstalledRepository& repository, 456 ResultWindow* window) 457 { 458 if (!repository.HasChanges()) 459 return false; 460 461 ProblemWindow::SolverPackageSet installPackages; 462 ProblemWindow::SolverPackageSet uninstallPackages; 463 if (fCurrentInstallPackage != NULL) 464 installPackages.insert(fCurrentInstallPackage); 465 466 if (fCurrentUninstallPackage != NULL) 467 uninstallPackages.insert(fCurrentUninstallPackage); 468 469 return window->AddLocationChanges(repository.Name(), 470 repository.PackagesToActivate(), installPackages, 471 repository.PackagesToDeactivate(), uninstallPackages); 472 } 473 474 475 void 476 PackageManager::_NotifyChangesConfirmed() 477 { 478 int32 count = fInstalledRepositories.CountItems(); 479 for (int32 i = 0; i < count; i++) { 480 for (int32 j = 0; j < fPackageProgressListeners.CountItems(); j++) { 481 fPackageProgressListeners.ItemAt(j)->ConfirmedChanges( 482 *fInstalledRepositories.ItemAt(i)); 483 } 484 } 485 } 486 487 488 BSolverPackage* 489 PackageManager::_GetSolverPackage(PackageInfoRef package) 490 { 491 int32 flags = BSolver::B_FIND_IN_NAME; 492 if (package->State() == ACTIVATED || package->State() == INSTALLED) 493 flags |= BSolver::B_FIND_INSTALLED_ONLY; 494 495 BObjectList<BSolverPackage> packages; 496 status_t result = Solver()->FindPackages(package->Name(), flags, packages); 497 if (result == B_OK) { 498 for (int32 i = 0; i < packages.CountItems(); i++) { 499 BSolverPackage* solverPackage = packages.ItemAt(i); 500 if (solverPackage->Name() != package->Name()) 501 continue; 502 else if (package->State() == NONE 503 && dynamic_cast<BPackageManager::RemoteRepository*>( 504 solverPackage->Repository()) == NULL) { 505 continue; 506 } 507 return solverPackage; 508 } 509 } 510 511 return NULL; 512 } 513