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