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 49using namespace BPackageKit; 50using namespace BPackageKit::BPrivate; 51using namespace BPackageKit::BManager::BPrivate; 52 53using BPackageKit::BRefreshRepositoryRequest; 54using BPackageKit::DownloadFileRequest; 55using BPackageKit::BSolver; 56using BPackageKit::BSolverPackage; 57using BPackageKit::BSolverRepository; 58using BPackageKit::BHPKG::BNoErrorOutput; 59using BPackageKit::BHPKG::BPackageContentHandler; 60using BPackageKit::BHPKG::BPackageEntry; 61using BPackageKit::BHPKG::BPackageEntryAttribute; 62using BPackageKit::BHPKG::BPackageInfoAttributeValue; 63using BPackageKit::BHPKG::BPackageReader; 64 65 66// #pragma mark - PackageProgressListener 67 68 69PackageProgressListener::~PackageProgressListener() 70{ 71} 72 73 74void 75PackageProgressListener::DownloadProgressChanged(const char* packageName, 76 float progress) 77{ 78} 79 80 81void 82PackageProgressListener::DownloadProgressComplete(const char* packageName) 83{ 84} 85 86 87void 88PackageProgressListener::ConfirmedChanges( 89 BPackageManager::InstalledRepository& repository) 90{ 91} 92 93 94void 95PackageProgressListener::StartApplyingChanges( 96 BPackageManager::InstalledRepository& repository) 97{ 98} 99 100 101void 102PackageProgressListener::ApplyingChangesDone( 103 BPackageManager::InstalledRepository& repository) 104{ 105} 106 107 108// #pragma mark - PackageManager 109 110 111PackageManager::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 124PackageManager::~PackageManager() 125{ 126 if (fProblemWindow != NULL) 127 fProblemWindow->PostMessage(B_QUIT_REQUESTED); 128} 129 130 131PackageState 132PackageManager::GetPackageState(const PackageInfo& package) 133{ 134 // TODO: Fetch information from the PackageKit 135 return NONE; 136} 137 138 139void 140PackageManager::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 171void 172PackageManager::_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 192PackageActionRef 193PackageManager::_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 206PackageActionRef 207PackageManager::_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 220PackageActionRef 221PackageManager::_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 241void 242PackageManager::SetCurrentActionPackage(PackageInfoRef package, bool install) 243{ 244 BSolverPackage* solverPackage = _GetSolverPackage(package); 245 fCurrentInstallPackage = install ? solverPackage : NULL; 246 fCurrentUninstallPackage = install ? NULL : solverPackage; 247} 248 249 250status_t 251PackageManager::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 270status_t 271PackageManager::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 293void 294PackageManager::AddProgressListener(PackageProgressListener* listener) 295{ 296 fPackageProgressListeners.AddItem(listener); 297} 298 299 300void 301PackageManager::RemoveProgressListener(PackageProgressListener* listener) 302{ 303 fPackageProgressListeners.RemoveItem(listener); 304} 305 306 307void 308PackageManager::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 326void 327PackageManager::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 358void 359PackageManager::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 375void 376PackageManager::ProgressPackageDownloadStarted(const char* packageName) 377{ 378 ProgressPackageDownloadActive(packageName, 0.0f, 0, 0); 379} 380 381 382void 383PackageManager::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 393void 394PackageManager::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 403void 404PackageManager::ProgressPackageChecksumStarted(const char* title) 405{ 406 // TODO: implement 407} 408 409 410void 411PackageManager::ProgressPackageChecksumComplete(const char* title) 412{ 413 // TODO: implement 414} 415 416 417void 418PackageManager::ProgressStartApplyingChanges(InstalledRepository& repository) 419{ 420 for (int32 i = 0; i < fPackageProgressListeners.CountItems(); i++) 421 fPackageProgressListeners.ItemAt(i)->StartApplyingChanges(repository); 422} 423 424 425void 426PackageManager::ProgressTransactionCommitted(InstalledRepository& repository, 427 const BCommitTransactionResult& result) 428{ 429 // TODO: implement 430} 431 432 433void 434PackageManager::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 451bool 452PackageManager::_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 472void 473PackageManager::_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 485BSolverPackage* 486PackageManager::_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