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