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