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