xref: /haiku/src/kits/package/manager/PackageManager.cpp (revision b3fe70844e087a579563b43cf4c1b2525946ca27)
1 /*
2  * Copyright 2013, 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  *		Rene Gollent <rene@gollent.com>
8  */
9 
10 
11 #include <package/manager/PackageManager.h>
12 
13 #include <Directory.h>
14 #include <package/DownloadFileRequest.h>
15 #include <package/PackageRoster.h>
16 #include <package/RefreshRepositoryRequest.h>
17 #include <package/RepositoryCache.h>
18 #include <package/solver/SolverPackage.h>
19 #include <package/solver/SolverPackageSpecifier.h>
20 #include <package/solver/SolverPackageSpecifierList.h>
21 #include <package/solver/SolverProblem.h>
22 #include <package/solver/SolverProblemSolution.h>
23 #include <package/solver/SolverResult.h>
24 
25 #include <CopyEngine.h>
26 #include <package/ActivationTransaction.h>
27 #include <package/DaemonClient.h>
28 #include <package/FetchFileJob.h>
29 #include <package/manager/RepositoryBuilder.h>
30 #include <package/ValidateChecksumJob.h>
31 
32 #include "PackageManagerUtils.h"
33 
34 
35 using BPackageKit::BPrivate::FetchFileJob;
36 using BPackageKit::BPrivate::ValidateChecksumJob;
37 
38 
39 namespace BPackageKit {
40 
41 namespace BManager {
42 
43 namespace BPrivate {
44 
45 
46 // #pragma mark - BPackageManager
47 
48 
49 BPackageManager::BPackageManager(BPackageInstallationLocation location,
50 	InstallationInterface* installationInterface,
51 	UserInteractionHandler* userInteractionHandler)
52 	:
53 	fLocation(location),
54 	fSolver(NULL),
55 	fSystemRepository(new (std::nothrow) InstalledRepository("system",
56 		B_PACKAGE_INSTALLATION_LOCATION_SYSTEM, -1)),
57 	fHomeRepository(new (std::nothrow) InstalledRepository("home",
58 		B_PACKAGE_INSTALLATION_LOCATION_HOME, -3)),
59 	fInstalledRepositories(10),
60 	fOtherRepositories(10, true),
61 	fTransactions(5, true),
62 	fInstallationInterface(installationInterface),
63 	fUserInteractionHandler(userInteractionHandler)
64 {
65 }
66 
67 
68 BPackageManager::~BPackageManager()
69 {
70 	delete fSolver;
71 	delete fSystemRepository;
72 	delete fHomeRepository;
73 }
74 
75 
76 void
77 BPackageManager::Init(uint32 flags)
78 {
79 	if (fSolver != NULL)
80 		return;
81 
82 	// create the solver
83 	status_t error = BSolver::Create(fSolver);
84 	if (error != B_OK)
85 		DIE(error, "failed to create solver");
86 
87 	if (fSystemRepository == NULL || fHomeRepository == NULL)
88 		throw std::bad_alloc();
89 
90 	// add installation location repositories
91 	if ((flags & B_ADD_INSTALLED_REPOSITORIES) != 0) {
92 		// We add only the repository of our actual installation location as the
93 		// "installed" repository. The repositories for the more general
94 		// installation locations are added as regular repositories, but with
95 		// better priorities than the actual (remote) repositories. This
96 		// prevents the solver from showing conflicts when a package in a more
97 		// specific installation location overrides a package in a more general
98 		// one. Instead any requirement that is already installed in a more
99 		// general installation location will turn up as to be installed as
100 		// well. But we can easily filter those out.
101 		_AddInstalledRepository(fSystemRepository);
102 
103 		if (!fSystemRepository->IsInstalled())
104 			_AddInstalledRepository(fHomeRepository);
105 	}
106 
107 	// add other repositories
108 	if ((flags & B_ADD_REMOTE_REPOSITORIES) != 0) {
109 		BPackageRoster roster;
110 		BStringList repositoryNames;
111 		error = roster.GetRepositoryNames(repositoryNames);
112 		if (error != B_OK) {
113 			fUserInteractionHandler->Warn(error,
114 				"failed to get repository names");
115 		}
116 
117 		int32 repositoryNameCount = repositoryNames.CountStrings();
118 		for (int32 i = 0; i < repositoryNameCount; i++) {
119 			_AddRemoteRepository(roster, repositoryNames.StringAt(i),
120 				(flags & B_REFRESH_REPOSITORIES) != 0);
121 		}
122 	}
123 }
124 
125 
126 void
127 BPackageManager::Install(const char* const* packages, int packageCount)
128 {
129 	BSolverPackageSpecifierList packagesToInstall;
130 	if (!packagesToInstall.AppendSpecifiers(packages, packageCount))
131 		throw std::bad_alloc();
132 	Install(packagesToInstall);
133 }
134 
135 
136 void
137 BPackageManager::Install(const BSolverPackageSpecifierList& packages)
138 {
139 	Init(B_ADD_INSTALLED_REPOSITORIES | B_ADD_REMOTE_REPOSITORIES
140 		| B_REFRESH_REPOSITORIES);
141 
142 	// solve
143 	const BSolverPackageSpecifier* unmatchedSpecifier;
144 	status_t error = fSolver->Install(packages, &unmatchedSpecifier);
145 	if (error != B_OK) {
146 		if (unmatchedSpecifier != NULL) {
147 			DIE(error, "failed to find a match for \"%s\"",
148 				unmatchedSpecifier->SelectString().String());
149 		} else
150 			DIE(error, "failed to compute packages to install");
151 	}
152 
153 	_HandleProblems();
154 
155 	// install/uninstall packages
156 	_AnalyzeResult();
157 	_ConfirmChanges();
158 	_ApplyPackageChanges();
159 }
160 
161 
162 void
163 BPackageManager::Uninstall(const char* const* packages, int packageCount)
164 {
165 	BSolverPackageSpecifierList packagesToUninstall;
166 	if (!packagesToUninstall.AppendSpecifiers(packages, packageCount))
167 		throw std::bad_alloc();
168 	Uninstall(packagesToUninstall);
169 }
170 
171 
172 void
173 BPackageManager::Uninstall(const BSolverPackageSpecifierList& packages)
174 {
175 	Init(B_ADD_INSTALLED_REPOSITORIES);
176 
177 	// find the packages that match the specification
178 	const BSolverPackageSpecifier* unmatchedSpecifier;
179 	PackageList foundPackages;
180 	status_t error = fSolver->FindPackages(packages,
181 		BSolver::B_FIND_INSTALLED_ONLY, foundPackages, &unmatchedSpecifier);
182 	if (error != B_OK) {
183 		if (unmatchedSpecifier != NULL) {
184 			DIE(error, "failed to find a match for \"%s\"",
185 				unmatchedSpecifier->SelectString().String());
186 		} else
187 			DIE(error, "failed to compute packages to uninstall");
188 	}
189 
190 	// determine the inverse base package closure for the found packages
191 // TODO: Optimize!
192 	InstalledRepository& installationRepository = InstallationRepository();
193 	bool foundAnotherPackage;
194 	do {
195 		foundAnotherPackage = false;
196 		int32 count = installationRepository.CountPackages();
197 		for (int32 i = 0; i < count; i++) {
198 			BSolverPackage* package = installationRepository.PackageAt(i);
199 			if (foundPackages.HasItem(package))
200 				continue;
201 
202 			if (_FindBasePackage(foundPackages, package->Info()) >= 0) {
203 				foundPackages.AddItem(package);
204 				foundAnotherPackage = true;
205 			}
206 		}
207 	} while (foundAnotherPackage);
208 
209 	// remove the packages from the repository
210 	for (int32 i = 0; BSolverPackage* package = foundPackages.ItemAt(i); i++)
211 		installationRepository.DisablePackage(package);
212 
213 	for (;;) {
214 		error = fSolver->VerifyInstallation(BSolver::B_VERIFY_ALLOW_UNINSTALL);
215 		if (error != B_OK)
216 			DIE(error, "failed to compute packages to uninstall");
217 
218 		_HandleProblems();
219 
220 		// (virtually) apply the result to this repository
221 		_AnalyzeResult();
222 
223 		for (int32 i = foundPackages.CountItems() - 1; i >= 0; i--) {
224 			if (!installationRepository.PackagesToDeactivate()
225 					.AddItem(foundPackages.ItemAt(i))) {
226 				throw std::bad_alloc();
227 			}
228 		}
229 
230 		installationRepository.ApplyChanges();
231 
232 		// verify the next specific respository
233 		if (!_NextSpecificInstallationLocation())
234 			break;
235 
236 		foundPackages.MakeEmpty();
237 
238 		// NOTE: In theory, after verifying a more specific location, it would
239 		// be more correct to compute the inverse base package closure for the
240 		// packages we need to uninstall and (if anything changed) verify again.
241 		// In practice, however, base packages are always required with an exact
242 		// version (ATM). If that base package still exist in a more general
243 		// location (the only reason why the package requiring the base package
244 		// wouldn't be marked to be uninstalled as well) there shouldn't have
245 		// been any reason to remove it from the more specific location in the
246 		// first place.
247 	}
248 
249 	_ConfirmChanges(true);
250 	_ApplyPackageChanges(true);
251 }
252 
253 
254 void
255 BPackageManager::Update(const char* const* packages, int packageCount)
256 {
257 	BSolverPackageSpecifierList packagesToUpdate;
258 	if (!packagesToUpdate.AppendSpecifiers(packages, packageCount))
259 		throw std::bad_alloc();
260 	Update(packagesToUpdate);
261 }
262 
263 
264 void
265 BPackageManager::Update(const BSolverPackageSpecifierList& packages)
266 {
267 	Init(B_ADD_INSTALLED_REPOSITORIES | B_ADD_REMOTE_REPOSITORIES
268 		| B_REFRESH_REPOSITORIES);
269 
270 	// solve
271 	const BSolverPackageSpecifier* unmatchedSpecifier;
272 	status_t error = fSolver->Update(packages, true,
273 		&unmatchedSpecifier);
274 	if (error != B_OK) {
275 		if (unmatchedSpecifier != NULL) {
276 			DIE(error, "failed to find a match for \"%s\"",
277 				unmatchedSpecifier->SelectString().String());
278 		} else
279 			DIE(error, "failed to compute packages to update");
280 	}
281 
282 	_HandleProblems();
283 
284 	// install/uninstall packages
285 	_AnalyzeResult();
286 	_ConfirmChanges();
287 	_ApplyPackageChanges();
288 }
289 
290 
291 void
292 BPackageManager::FullSync()
293 {
294 	Init(B_ADD_INSTALLED_REPOSITORIES | B_ADD_REMOTE_REPOSITORIES
295 		| B_REFRESH_REPOSITORIES);
296 
297 	// solve
298 	status_t error = fSolver->FullSync();
299 	if (error != B_OK)
300 		DIE(error, "failed to compute packages to synchronize");
301 
302 	_HandleProblems();
303 
304 	// install/uninstall packages
305 	_AnalyzeResult();
306 	_ConfirmChanges();
307 	_ApplyPackageChanges();
308 }
309 
310 
311 void
312 BPackageManager::VerifyInstallation()
313 {
314 	Init(B_ADD_INSTALLED_REPOSITORIES | B_ADD_REMOTE_REPOSITORIES
315 		| B_REFRESH_REPOSITORIES);
316 
317 	for (;;) {
318 		status_t error = fSolver->VerifyInstallation();
319 		if (error != B_OK)
320 			DIE(error, "failed to compute package dependencies");
321 
322 		_HandleProblems();
323 
324 		// (virtually) apply the result to this repository
325 		_AnalyzeResult();
326 		InstallationRepository().ApplyChanges();
327 
328 		// verify the next specific respository
329 		if (!_NextSpecificInstallationLocation())
330 			break;
331 	}
332 
333 	_ConfirmChanges();
334 	_ApplyPackageChanges();
335 }
336 
337 
338 BPackageManager::InstalledRepository&
339 BPackageManager::InstallationRepository()
340 {
341 	if (fInstalledRepositories.IsEmpty())
342 		DIE("no installation repository");
343 
344 	return *fInstalledRepositories.LastItem();
345 }
346 
347 
348 void
349 BPackageManager::JobStarted(BJob* job)
350 {
351 	if (dynamic_cast<FetchFileJob*>(job) != NULL) {
352 		FetchFileJob* fetchJob = (FetchFileJob*)job;
353 		fUserInteractionHandler->ProgressPackageDownloadStarted(
354 			fetchJob->DownloadFileName());
355 	} else if (dynamic_cast<ValidateChecksumJob*>(job) != NULL) {
356 		fUserInteractionHandler->ProgressPackageChecksumStarted(
357 			job->Title().String());
358 	}
359 }
360 
361 
362 void
363 BPackageManager::JobProgress(BJob* job)
364 {
365 	if (dynamic_cast<FetchFileJob*>(job) != NULL) {
366 		FetchFileJob* fetchJob = (FetchFileJob*)job;
367 		fUserInteractionHandler->ProgressPackageDownloadActive(
368 			fetchJob->DownloadFileName(), fetchJob->DownloadProgress());
369 	}
370 }
371 
372 
373 void
374 BPackageManager::JobSucceeded(BJob* job)
375 {
376 	if (dynamic_cast<FetchFileJob*>(job) != NULL) {
377 		FetchFileJob* fetchJob = (FetchFileJob*)job;
378 		fUserInteractionHandler->ProgressPackageDownloadComplete(
379 			fetchJob->DownloadFileName());
380 	} else if (dynamic_cast<ValidateChecksumJob*>(job) != NULL) {
381 		fUserInteractionHandler->ProgressPackageChecksumComplete(
382 			job->Title().String());
383 	}
384 }
385 
386 
387 void
388 BPackageManager::_HandleProblems()
389 {
390 	while (fSolver->HasProblems()) {
391 		fUserInteractionHandler->HandleProblems();
392 
393 		status_t error = fSolver->SolveAgain();
394 		if (error != B_OK)
395 			DIE(error, "failed to recompute packages to un/-install");
396 	}
397 }
398 
399 
400 void
401 BPackageManager::_AnalyzeResult()
402 {
403 	BSolverResult result;
404 	status_t error = fSolver->GetResult(result);
405 	if (error != B_OK)
406 		DIE(error, "failed to compute packages to un/-install");
407 
408 	InstalledRepository& installationRepository = InstallationRepository();
409 	PackageList& packagesToActivate
410 		= installationRepository.PackagesToActivate();
411 	PackageList& packagesToDeactivate
412 		= installationRepository.PackagesToDeactivate();
413 
414 	PackageList potentialBasePackages;
415 
416 	for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i);
417 			i++) {
418 		BSolverPackage* package = element->Package();
419 
420 		switch (element->Type()) {
421 			case BSolverResultElement::B_TYPE_INSTALL:
422 			{
423 				PackageList& packageList
424 					= dynamic_cast<InstalledRepository*>(package->Repository())
425 							!= NULL
426 						? potentialBasePackages
427 						: packagesToActivate;
428 				if (!packageList.AddItem(package))
429 					throw std::bad_alloc();
430 				break;
431 			}
432 
433 			case BSolverResultElement::B_TYPE_UNINSTALL:
434 				if (!packagesToDeactivate.AddItem(package))
435 					throw std::bad_alloc();
436 				break;
437 		}
438 	}
439 
440 	// Make sure base packages are installed in the same location.
441 	for (int32 i = 0; i < packagesToActivate.CountItems(); i++) {
442 		BSolverPackage* package = packagesToActivate.ItemAt(i);
443 		int32 index = _FindBasePackage(potentialBasePackages, package->Info());
444 		if (index < 0)
445 			continue;
446 
447 		BSolverPackage* basePackage = potentialBasePackages.RemoveItemAt(index);
448 		if (!packagesToActivate.AddItem(basePackage))
449 			throw std::bad_alloc();
450 	}
451 
452 	fInstallationInterface->ResultComputed(installationRepository);
453 }
454 
455 
456 void
457 BPackageManager::_ConfirmChanges(bool fromMostSpecific)
458 {
459 	// check, if there are any changes at all
460 	int32 count = fInstalledRepositories.CountItems();
461 	bool hasChanges = false;
462 	for (int32 i = 0; i < count; i++) {
463 		if (fInstalledRepositories.ItemAt(i)->HasChanges()) {
464 			hasChanges = true;
465 			break;
466 		}
467 	}
468 
469 	if (!hasChanges)
470 		throw BNothingToDoException();
471 
472 	fUserInteractionHandler->ConfirmChanges(fromMostSpecific);
473 }
474 
475 
476 void
477 BPackageManager::_ApplyPackageChanges(bool fromMostSpecific)
478 {
479 	int32 count = fInstalledRepositories.CountItems();
480 	if (fromMostSpecific) {
481 		for (int32 i = count - 1; i >= 0; i--)
482 			_PreparePackageChanges(*fInstalledRepositories.ItemAt(i));
483 	} else {
484 		for (int32 i = 0; i < count; i++)
485 			_PreparePackageChanges(*fInstalledRepositories.ItemAt(i));
486 	}
487 
488 	for (int32 i = 0; Transaction* transaction = fTransactions.ItemAt(i); i++)
489 		_CommitPackageChanges(*transaction);
490 
491 // TODO: Clean up the transaction directories on error!
492 }
493 
494 
495 void
496 BPackageManager::_PreparePackageChanges(
497 	InstalledRepository& installationRepository)
498 {
499 	if (!installationRepository.HasChanges())
500 		return;
501 
502 	PackageList& packagesToActivate
503 		= installationRepository.PackagesToActivate();
504 	PackageList& packagesToDeactivate
505 		= installationRepository.PackagesToDeactivate();
506 
507 	// create the transaction
508 	Transaction* transaction = new Transaction(installationRepository);
509 	if (!fTransactions.AddItem(transaction)) {
510 		delete transaction;
511 		throw std::bad_alloc();
512 	}
513 
514 	status_t error = fInstallationInterface->PrepareTransaction(*transaction);
515 	if (error != B_OK)
516 		DIE(error, "failed to create transaction");
517 
518 	// download the new packages and prepare the transaction
519 	for (int32 i = 0; BSolverPackage* package = packagesToActivate.ItemAt(i);
520 		i++) {
521 		// get package URL and target entry
522 
523 		BString fileName(package->Info().FileName());
524 		if (fileName.IsEmpty())
525 			throw std::bad_alloc();
526 
527 		BEntry entry;
528 		error = entry.SetTo(&transaction->TransactionDirectory(), fileName);
529 		if (error != B_OK)
530 			DIE(error, "failed to create package entry");
531 
532 		RemoteRepository* remoteRepository
533 			= dynamic_cast<RemoteRepository*>(package->Repository());
534 		if (remoteRepository == NULL) {
535 			// clone the existing package (unless already present)
536 			if (package->Repository() != &installationRepository) {
537 				_ClonePackageFile(
538 					dynamic_cast<InstalledRepository*>(package->Repository()),
539 					fileName, entry);
540 			}
541 		} else {
542 			// download the package
543 			BString url = remoteRepository->Config().PackagesURL();
544 			url << '/' << fileName;
545 
546 			status_t error = DownloadPackage(url, entry,
547 				package->Info().Checksum());
548 			if (error != B_OK)
549 				DIE(error, "failed to download package");
550 		}
551 
552 		// add package to transaction
553 		if (!transaction->ActivationTransaction().AddPackageToActivate(
554 				fileName)) {
555 			throw std::bad_alloc();
556 		}
557 	}
558 
559 	for (int32 i = 0; BSolverPackage* package = packagesToDeactivate.ItemAt(i);
560 		i++) {
561 		// add package to transaction
562 		if (!transaction->ActivationTransaction().AddPackageToDeactivate(
563 				package->Info().FileName())) {
564 			throw std::bad_alloc();
565 		}
566 	}
567 }
568 
569 
570 void
571 BPackageManager::_CommitPackageChanges(Transaction& transaction)
572 {
573 	InstalledRepository& installationRepository = transaction.Repository();
574 
575 	fUserInteractionHandler->ProgressStartApplyingChanges(
576 		installationRepository);
577 
578 	// commit the transaction
579 	BDaemonClient::BCommitTransactionResult transactionResult;
580 	status_t error = fInstallationInterface->CommitTransaction(transaction,
581 		transactionResult);
582 	if (error != B_OK)
583 		DIE(error, "failed to commit transaction");
584 	if (transactionResult.Error() != B_OK) {
585 		DIE("failed to commit transaction: %s",
586 			transactionResult.FullErrorMessage().String());
587 	}
588 
589 	fUserInteractionHandler->ProgressTransactionCommitted(
590 		installationRepository, transactionResult.OldStateDirectory());
591 
592 	BEntry transactionDirectoryEntry;
593 	if ((error = transaction.TransactionDirectory()
594 			.GetEntry(&transactionDirectoryEntry)) != B_OK
595 		|| (error = transactionDirectoryEntry.Remove()) != B_OK) {
596 		fUserInteractionHandler->Warn(error,
597 			"failed to remove transaction directory");
598 	}
599 
600 	fUserInteractionHandler->ProgressApplyingChangesDone(
601 		installationRepository);
602 }
603 
604 
605 void
606 BPackageManager::_ClonePackageFile(InstalledRepository* repository,
607 	const BString& fileName, const BEntry& entry)
608 {
609 	// get the source and destination file paths
610 	directory_which packagesWhich;
611 	if (repository == fSystemRepository) {
612 		packagesWhich = B_SYSTEM_PACKAGES_DIRECTORY;
613 	} else {
614 		DIE("don't know packages directory path for installation location "
615 			"\"%s\"", repository->Name().String());
616 	}
617 
618 	BPath sourcePath;
619 	status_t error = find_directory(packagesWhich, &sourcePath);
620 	if (error != B_OK || (error = sourcePath.Append(fileName)) != B_OK) {
621 		DIE(error, "failed to get path of package file \"%s\" in installation "
622 			"location \"%s\"", fileName.String(), repository->Name().String());
623 	}
624 
625 	BPath destinationPath;
626 	error = entry.GetPath(&destinationPath);
627 	if (error != B_OK) {
628 		DIE(error, "failed to entry path of package file to install \"%s\"",
629 			fileName.String());
630 	}
631 
632 	// Copy the package. Ideally we would just hard-link it, but BFS doesn't
633 	// support that.
634 	error = BCopyEngine().CopyEntry(sourcePath.Path(), destinationPath.Path());
635 	if (error != B_OK)
636 		DIE(error, "failed to copy package file \"%s\"", sourcePath.Path());
637 }
638 
639 
640 int32
641 BPackageManager::_FindBasePackage(const PackageList& packages,
642 	const BPackageInfo& info)
643 {
644 	if (info.BasePackage().IsEmpty())
645 		return -1;
646 
647 	// find the requirement matching the base package
648 	BPackageResolvableExpression* basePackage = NULL;
649 	int32 count = info.RequiresList().CountItems();
650 	for (int32 i = 0; i < count; i++) {
651 		BPackageResolvableExpression* requires = info.RequiresList().ItemAt(i);
652 		if (requires->Name() == info.BasePackage()) {
653 			basePackage = requires;
654 			break;
655 		}
656 	}
657 
658 	if (basePackage == NULL) {
659 		fUserInteractionHandler->Warn(B_OK, "package %s-%s doesn't have a "
660 			"matching requires for its base package \"%s\"",
661 			info.Name().String(), info.Version().ToString().String(),
662 			info.BasePackage().String());
663 		return -1;
664 	}
665 
666 	// find the first package matching the base package requires
667 	count = packages.CountItems();
668 	for (int32 i = 0; i < count; i++) {
669 		BSolverPackage* package = packages.ItemAt(i);
670 		if (package->Name() == basePackage->Name()
671 			&& package->Info().Matches(*basePackage)) {
672 			return i;
673 		}
674 	}
675 
676 	return -1;
677 }
678 
679 
680 void
681 BPackageManager::_AddInstalledRepository(InstalledRepository* repository)
682 {
683 	fInstallationInterface->InitInstalledRepository(*repository);
684 
685 	BRepositoryBuilder(*repository)
686 		.AddToSolver(fSolver, repository->Location() == fLocation);
687 	repository->SetPriority(repository->InitialPriority());
688 
689 	if (!fInstalledRepositories.AddItem(repository))
690 		throw std::bad_alloc();
691 }
692 
693 
694 void
695 BPackageManager::_AddRemoteRepository(BPackageRoster& roster, const char* name,
696 	bool refresh)
697 {
698 	BRepositoryConfig config;
699 	status_t error = roster.GetRepositoryConfig(name, &config);
700 	if (error != B_OK) {
701 		fUserInteractionHandler->Warn(error,
702 			"failed to get config for repository \"%s\". Skipping.", name);
703 		return;
704 	}
705 
706 	BRepositoryCache cache;
707 	error = _GetRepositoryCache(roster, config, refresh, cache);
708 	if (error != B_OK) {
709 		fUserInteractionHandler->Warn(error,
710 			"failed to get cache for repository \"%s\". Skipping.", name);
711 		return;
712 	}
713 
714 	RemoteRepository* repository = new RemoteRepository(config);
715 	if (!fOtherRepositories.AddItem(repository)) {
716 		delete repository;
717 		throw std::bad_alloc();
718 	}
719 
720 	BRepositoryBuilder(*repository, cache, config.Name())
721 		.AddToSolver(fSolver, false);
722 }
723 
724 
725 status_t
726 BPackageManager::_GetRepositoryCache(BPackageRoster& roster,
727 	const BRepositoryConfig& config, bool refresh, BRepositoryCache& _cache)
728 {
729 	if (!refresh && roster.GetRepositoryCache(config.Name(), &_cache) == B_OK)
730 		return B_OK;
731 
732 	status_t error = RefreshRepository(config);
733 	if (error != B_OK) {
734 		fUserInteractionHandler->Warn(error,
735 			"refreshing repository \"%s\" failed", config.Name().String());
736 	}
737 
738 	return roster.GetRepositoryCache(config.Name(), &_cache);
739 }
740 
741 
742 bool
743 BPackageManager::_NextSpecificInstallationLocation()
744 {
745 	if (fLocation == B_PACKAGE_INSTALLATION_LOCATION_SYSTEM) {
746 		fLocation = B_PACKAGE_INSTALLATION_LOCATION_HOME;
747 		fSystemRepository->SetInstalled(false);
748 		_AddInstalledRepository(fHomeRepository);
749 		return true;
750 	}
751 
752 	return false;
753 }
754 
755 
756 status_t
757 BPackageManager::DownloadPackage(const BString& fileURL,
758 	const BEntry& targetEntry, const BString& checksum)
759 {
760 	BDecisionProvider provider;
761 	BContext context(provider, *this);
762 	return DownloadFileRequest(context, fileURL, targetEntry, checksum)
763 		.Process();
764 }
765 
766 
767 status_t
768 BPackageManager::RefreshRepository(const BRepositoryConfig& repoConfig)
769 {
770 	BDecisionProvider provider;
771 	BContext context(provider, *this);
772 	return BRefreshRepositoryRequest(context, repoConfig).Process();
773 }
774 
775 
776 // #pragma mark - RemoteRepository
777 
778 
779 BPackageManager::RemoteRepository::RemoteRepository(
780 	const BRepositoryConfig& config)
781 	:
782 	BSolverRepository(),
783 	fConfig(config)
784 {
785 }
786 
787 
788 const BRepositoryConfig&
789 BPackageManager::RemoteRepository::Config() const
790 {
791 	return fConfig;
792 }
793 
794 
795 // #pragma mark - InstalledRepository
796 
797 
798 BPackageManager::InstalledRepository::InstalledRepository(const char* name,
799 	BPackageInstallationLocation location, int32 priority)
800 	:
801 	BSolverRepository(),
802 	fDisabledPackages(10, true),
803 	fPackagesToActivate(),
804 	fPackagesToDeactivate(),
805 	fInitialName(name),
806 	fLocation(location),
807 	fInitialPriority(priority)
808 {
809 }
810 
811 
812 void
813 BPackageManager::InstalledRepository::DisablePackage(BSolverPackage* package)
814 {
815 	if (fDisabledPackages.HasItem(package))
816 		DIE("package %s already disabled", package->VersionedName().String());
817 
818 	if (package->Repository() != this) {
819 		DIE("package %s not in repository %s",
820 			package->VersionedName().String(), Name().String());
821 	}
822 
823 	// move to disabled list
824 	if (!fDisabledPackages.AddItem(package))
825 		throw std::bad_alloc();
826 
827 	RemovePackage(package);
828 }
829 
830 
831 bool
832 BPackageManager::InstalledRepository::EnablePackage(BSolverPackage* package)
833 {
834 	return fDisabledPackages.RemoveItem(package);
835 }
836 
837 
838 bool
839 BPackageManager::InstalledRepository::HasChanges() const
840 {
841 	return !fPackagesToActivate.IsEmpty() || !fPackagesToDeactivate.IsEmpty();
842 }
843 
844 
845 void
846 BPackageManager::InstalledRepository::ApplyChanges()
847 {
848 	// disable packages to deactivate
849 	for (int32 i = 0; BSolverPackage* package = fPackagesToDeactivate.ItemAt(i);
850 		i++) {
851 		if (!fDisabledPackages.HasItem(package))
852 			DisablePackage(package);
853 	}
854 
855 	// add packages to activate
856 	for (int32 i = 0; BSolverPackage* package = fPackagesToActivate.ItemAt(i);
857 		i++) {
858 		status_t error = AddPackage(package->Info());
859 		if (error != B_OK) {
860 			DIE(error, "failed to add package %s to %s repository",
861 				package->Name().String(), Name().String());
862 		}
863 	}
864 }
865 
866 
867 // #pragma mark - Transaction
868 
869 
870 BPackageManager::Transaction::Transaction(InstalledRepository& repository)
871 	:
872 	fRepository(repository),
873 	fTransaction(),
874 	fTransactionDirectory()
875 {
876 }
877 
878 
879 BPackageManager::Transaction::~Transaction()
880 {
881 }
882 
883 
884 // #pragma mark - InstallationInterface
885 
886 
887 BPackageManager::InstallationInterface::~InstallationInterface()
888 {
889 }
890 
891 
892 void
893 BPackageManager::InstallationInterface::ResultComputed(
894 	InstalledRepository& repository)
895 {
896 }
897 
898 
899 // #pragma mark - ClientInstallationInterface
900 
901 
902 BPackageManager::ClientInstallationInterface::ClientInstallationInterface()
903 	:
904 	fDaemonClient()
905 {
906 }
907 
908 
909 BPackageManager::ClientInstallationInterface::~ClientInstallationInterface()
910 {
911 }
912 
913 
914 void
915 BPackageManager::ClientInstallationInterface::InitInstalledRepository(
916 	InstalledRepository& repository)
917 {
918 	const char* name = repository.InitialName();
919 	BRepositoryBuilder(repository, name)
920 		.AddPackages(repository.Location(), name);
921 }
922 
923 
924 status_t
925 BPackageManager::ClientInstallationInterface::PrepareTransaction(
926 	Transaction& transaction)
927 {
928 	return fDaemonClient.CreateTransaction(transaction.Repository().Location(),
929 		transaction.ActivationTransaction(),
930 		transaction.TransactionDirectory());
931 }
932 
933 
934 status_t
935 BPackageManager::ClientInstallationInterface::CommitTransaction(
936 	Transaction& transaction, BDaemonClient::BCommitTransactionResult& _result)
937 {
938 	return fDaemonClient.CommitTransaction(transaction.ActivationTransaction(),
939 		_result);
940 }
941 
942 
943 // #pragma mark - UserInteractionHandler
944 
945 
946 BPackageManager::UserInteractionHandler::~UserInteractionHandler()
947 {
948 }
949 
950 
951 void
952 BPackageManager::UserInteractionHandler::HandleProblems()
953 {
954 	throw BAbortedByUserException();
955 }
956 
957 
958 void
959 BPackageManager::UserInteractionHandler::ConfirmChanges(bool fromMostSpecific)
960 {
961 	throw BAbortedByUserException();
962 }
963 
964 
965 void
966 BPackageManager::UserInteractionHandler::Warn(status_t error,
967 	const char* format, ...)
968 {
969 }
970 
971 
972 void
973 BPackageManager::UserInteractionHandler::ProgressPackageDownloadStarted(
974 	const char* packageName)
975 {
976 }
977 
978 
979 void
980 BPackageManager::UserInteractionHandler::ProgressPackageDownloadActive(
981 	const char* packageName, float completionPercentage)
982 {
983 }
984 
985 
986 void
987 BPackageManager::UserInteractionHandler::ProgressPackageDownloadComplete(
988 	const char* packageName)
989 {
990 }
991 
992 
993 void
994 BPackageManager::UserInteractionHandler::ProgressPackageChecksumStarted(
995 	const char* title)
996 {
997 }
998 
999 
1000 void
1001 BPackageManager::UserInteractionHandler::ProgressPackageChecksumComplete(
1002 	const char* title)
1003 {
1004 }
1005 
1006 
1007 void
1008 BPackageManager::UserInteractionHandler::ProgressStartApplyingChanges(
1009 	InstalledRepository& repository)
1010 {
1011 }
1012 
1013 
1014 void
1015 BPackageManager::UserInteractionHandler::ProgressTransactionCommitted(
1016 	InstalledRepository& repository, const char* transactionDirectoryName)
1017 {
1018 }
1019 
1020 
1021 void
1022 BPackageManager::UserInteractionHandler::ProgressApplyingChangesDone(
1023 	InstalledRepository& repository)
1024 {
1025 }
1026 
1027 
1028 }	// namespace BPrivate
1029 
1030 }	// namespace BManager
1031 
1032 }	// namespace BPackageKit
1033