xref: /haiku/src/kits/package/manager/PackageManager.cpp (revision 6eafb4b041ad79cb936b2041fdb9c56b1209cc10)
1 /*
2  * Copyright 2013-2015, 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(BSupportKit::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(BSupportKit::BJob* job)
382 {
383 	if (dynamic_cast<FetchFileJob*>(job) != NULL) {
384 		FetchFileJob* fetchJob = (FetchFileJob*)job;
385 		fUserInteractionHandler->ProgressPackageDownloadActive(
386 			fetchJob->DownloadFileName(), fetchJob->DownloadProgress(),
387 			fetchJob->DownloadBytes(), fetchJob->DownloadTotalBytes());
388 	}
389 }
390 
391 
392 void
393 BPackageManager::JobSucceeded(BSupportKit::BJob* job)
394 {
395 	if (dynamic_cast<FetchFileJob*>(job) != NULL) {
396 		FetchFileJob* fetchJob = (FetchFileJob*)job;
397 		fUserInteractionHandler->ProgressPackageDownloadComplete(
398 			fetchJob->DownloadFileName());
399 	} else if (dynamic_cast<ValidateChecksumJob*>(job) != NULL) {
400 		fUserInteractionHandler->ProgressPackageChecksumComplete(
401 			job->Title().String());
402 	}
403 }
404 
405 
406 void
407 BPackageManager::_HandleProblems()
408 {
409 	while (fSolver->HasProblems()) {
410 		fUserInteractionHandler->HandleProblems();
411 
412 		status_t error = fSolver->SolveAgain();
413 		if (error != B_OK)
414 			DIE(error, "failed to recompute packages to un/-install");
415 	}
416 }
417 
418 
419 void
420 BPackageManager::_AnalyzeResult()
421 {
422 	BSolverResult result;
423 	status_t error = fSolver->GetResult(result);
424 	if (error != B_OK)
425 		DIE(error, "failed to compute packages to un/-install");
426 
427 	InstalledRepository& installationRepository = InstallationRepository();
428 	PackageList& packagesToActivate
429 		= installationRepository.PackagesToActivate();
430 	PackageList& packagesToDeactivate
431 		= installationRepository.PackagesToDeactivate();
432 
433 	PackageList potentialBasePackages;
434 
435 	for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i);
436 			i++) {
437 		BSolverPackage* package = element->Package();
438 
439 		switch (element->Type()) {
440 			case BSolverResultElement::B_TYPE_INSTALL:
441 			{
442 				PackageList& packageList
443 					= dynamic_cast<InstalledRepository*>(package->Repository())
444 							!= NULL
445 						? potentialBasePackages
446 						: packagesToActivate;
447 				if (!packageList.AddItem(package))
448 					throw std::bad_alloc();
449 				break;
450 			}
451 
452 			case BSolverResultElement::B_TYPE_UNINSTALL:
453 				if (!packagesToDeactivate.AddItem(package))
454 					throw std::bad_alloc();
455 				break;
456 		}
457 	}
458 
459 	// Make sure base packages are installed in the same location.
460 	for (int32 i = 0; i < packagesToActivate.CountItems(); i++) {
461 		BSolverPackage* package = packagesToActivate.ItemAt(i);
462 		int32 index = _FindBasePackage(potentialBasePackages, package->Info());
463 		if (index < 0)
464 			continue;
465 
466 		BSolverPackage* basePackage = potentialBasePackages.RemoveItemAt(index);
467 		if (!packagesToActivate.AddItem(basePackage))
468 			throw std::bad_alloc();
469 	}
470 
471 	fInstallationInterface->ResultComputed(installationRepository);
472 }
473 
474 
475 void
476 BPackageManager::_ConfirmChanges(bool fromMostSpecific)
477 {
478 	// check, if there are any changes at all
479 	int32 count = fInstalledRepositories.CountItems();
480 	bool hasChanges = false;
481 	for (int32 i = 0; i < count; i++) {
482 		if (fInstalledRepositories.ItemAt(i)->HasChanges()) {
483 			hasChanges = true;
484 			break;
485 		}
486 	}
487 
488 	if (!hasChanges)
489 		throw BNothingToDoException();
490 
491 	fUserInteractionHandler->ConfirmChanges(fromMostSpecific);
492 }
493 
494 
495 void
496 BPackageManager::_ApplyPackageChanges(bool fromMostSpecific)
497 {
498 	int32 count = fInstalledRepositories.CountItems();
499 	if (fromMostSpecific) {
500 		for (int32 i = count - 1; i >= 0; i--)
501 			_PreparePackageChanges(*fInstalledRepositories.ItemAt(i));
502 	} else {
503 		for (int32 i = 0; i < count; i++)
504 			_PreparePackageChanges(*fInstalledRepositories.ItemAt(i));
505 	}
506 
507 	for (int32 i = 0; Transaction* transaction = fTransactions.ItemAt(i); i++)
508 		_CommitPackageChanges(*transaction);
509 
510 // TODO: Clean up the transaction directories on error!
511 }
512 
513 
514 void
515 BPackageManager::_PreparePackageChanges(
516 	InstalledRepository& installationRepository)
517 {
518 	if (!installationRepository.HasChanges())
519 		return;
520 
521 	PackageList& packagesToActivate
522 		= installationRepository.PackagesToActivate();
523 	PackageList& packagesToDeactivate
524 		= installationRepository.PackagesToDeactivate();
525 
526 	// create the transaction
527 	Transaction* transaction = new Transaction(installationRepository);
528 	if (!fTransactions.AddItem(transaction)) {
529 		delete transaction;
530 		throw std::bad_alloc();
531 	}
532 
533 	status_t error = fInstallationInterface->PrepareTransaction(*transaction);
534 	if (error != B_OK)
535 		DIE(error, "failed to create transaction");
536 
537 	// download the new packages and prepare the transaction
538 	for (int32 i = 0; BSolverPackage* package = packagesToActivate.ItemAt(i);
539 		i++) {
540 		// get package URL and target entry
541 
542 		BString fileName(package->Info().FileName());
543 		if (fileName.IsEmpty())
544 			throw std::bad_alloc();
545 
546 		BEntry entry;
547 		error = entry.SetTo(&transaction->TransactionDirectory(), fileName);
548 		if (error != B_OK)
549 			DIE(error, "failed to create package entry");
550 
551 		RemoteRepository* remoteRepository
552 			= dynamic_cast<RemoteRepository*>(package->Repository());
553 		if (remoteRepository != NULL) {
554 			// download the package
555 			BString url = remoteRepository->Config().PackagesURL();
556 			url << '/' << fileName;
557 
558 			status_t error = DownloadPackage(url, entry,
559 				package->Info().Checksum());
560 			if (error != B_OK)
561 				DIE(error, "failed to download package");
562 		} else if (package->Repository() != &installationRepository) {
563 			// clone the existing package
564 			LocalRepository* localRepository
565 				= dynamic_cast<LocalRepository*>(package->Repository());
566 			if (localRepository == NULL) {
567 				DIE("internal error: repository %s is not a local repository",
568 					package->Repository()->Name().String());
569 			}
570 			_ClonePackageFile(localRepository, package, entry);
571 		}
572 
573 		// add package to transaction
574 		if (!transaction->ActivationTransaction().AddPackageToActivate(
575 				fileName)) {
576 			throw std::bad_alloc();
577 		}
578 	}
579 
580 	for (int32 i = 0; BSolverPackage* package = packagesToDeactivate.ItemAt(i);
581 		i++) {
582 		// add package to transaction
583 		if (!transaction->ActivationTransaction().AddPackageToDeactivate(
584 				package->Info().FileName())) {
585 			throw std::bad_alloc();
586 		}
587 	}
588 }
589 
590 
591 void
592 BPackageManager::_CommitPackageChanges(Transaction& transaction)
593 {
594 	InstalledRepository& installationRepository = transaction.Repository();
595 
596 	fUserInteractionHandler->ProgressStartApplyingChanges(
597 		installationRepository);
598 
599 	// commit the transaction
600 	BCommitTransactionResult transactionResult;
601 	status_t error = fInstallationInterface->CommitTransaction(transaction,
602 		transactionResult);
603 	if (error != B_OK)
604 		DIE(error, "failed to commit transaction");
605 	if (transactionResult.Error() != B_TRANSACTION_OK)
606 		DIE(transactionResult);
607 
608 	fUserInteractionHandler->ProgressTransactionCommitted(
609 		installationRepository, transactionResult);
610 
611 	BEntry transactionDirectoryEntry;
612 	if ((error = transaction.TransactionDirectory()
613 			.GetEntry(&transactionDirectoryEntry)) != B_OK
614 		|| (error = transactionDirectoryEntry.Remove()) != B_OK) {
615 		fUserInteractionHandler->Warn(error,
616 			"failed to remove transaction directory");
617 	}
618 
619 	fUserInteractionHandler->ProgressApplyingChangesDone(
620 		installationRepository);
621 }
622 
623 
624 void
625 BPackageManager::_ClonePackageFile(LocalRepository* repository,
626 	BSolverPackage* package, const BEntry& entry)
627 {
628 	// get source and destination path
629 	BPath sourcePath;
630 	repository->GetPackagePath(package, sourcePath);
631 
632 	BPath destinationPath;
633 	status_t error = entry.GetPath(&destinationPath);
634 	if (error != B_OK) {
635 		DIE(error, "failed to entry path of package file to install \"%s\"",
636 			package->Info().FileName().String());
637 	}
638 
639 	// Copy the package. Ideally we would just hard-link it, but BFS doesn't
640 	// support that.
641 	error = BCopyEngine().CopyEntry(sourcePath.Path(), destinationPath.Path());
642 	if (error != B_OK)
643 		DIE(error, "failed to copy package file \"%s\"", sourcePath.Path());
644 }
645 
646 
647 int32
648 BPackageManager::_FindBasePackage(const PackageList& packages,
649 	const BPackageInfo& info)
650 {
651 	if (info.BasePackage().IsEmpty())
652 		return -1;
653 
654 	// find the requirement matching the base package
655 	BPackageResolvableExpression* basePackage = NULL;
656 	int32 count = info.RequiresList().CountItems();
657 	for (int32 i = 0; i < count; i++) {
658 		BPackageResolvableExpression* requires = info.RequiresList().ItemAt(i);
659 		if (requires->Name() == info.BasePackage()) {
660 			basePackage = requires;
661 			break;
662 		}
663 	}
664 
665 	if (basePackage == NULL) {
666 		fUserInteractionHandler->Warn(B_OK, "package %s-%s doesn't have a "
667 			"matching requires for its base package \"%s\"",
668 			info.Name().String(), info.Version().ToString().String(),
669 			info.BasePackage().String());
670 		return -1;
671 	}
672 
673 	// find the first package matching the base package requires
674 	count = packages.CountItems();
675 	for (int32 i = 0; i < count; i++) {
676 		BSolverPackage* package = packages.ItemAt(i);
677 		if (package->Name() == basePackage->Name()
678 			&& package->Info().Matches(*basePackage)) {
679 			return i;
680 		}
681 	}
682 
683 	return -1;
684 }
685 
686 
687 void
688 BPackageManager::_AddInstalledRepository(InstalledRepository* repository)
689 {
690 	fInstallationInterface->InitInstalledRepository(*repository);
691 
692 	BRepositoryBuilder(*repository)
693 		.AddToSolver(fSolver, repository->Location() == fLocation);
694 	repository->SetPriority(repository->InitialPriority());
695 
696 	if (!fInstalledRepositories.AddItem(repository))
697 		throw std::bad_alloc();
698 }
699 
700 
701 void
702 BPackageManager::_AddRemoteRepository(BPackageRoster& roster, const char* name,
703 	bool refresh)
704 {
705 	BRepositoryConfig config;
706 	status_t error = roster.GetRepositoryConfig(name, &config);
707 	if (error != B_OK) {
708 		fUserInteractionHandler->Warn(error,
709 			"failed to get config for repository \"%s\". Skipping.", name);
710 		return;
711 	}
712 
713 	BRepositoryCache cache;
714 	error = _GetRepositoryCache(roster, config, refresh, cache);
715 	if (error != B_OK) {
716 		fUserInteractionHandler->Warn(error,
717 			"failed to get cache for repository \"%s\". Skipping.", name);
718 		return;
719 	}
720 
721 	RemoteRepository* repository = new RemoteRepository(config);
722 	if (!fOtherRepositories.AddItem(repository)) {
723 		delete repository;
724 		throw std::bad_alloc();
725 	}
726 
727 	BRepositoryBuilder(*repository, cache, config.Name())
728 		.AddToSolver(fSolver, false);
729 }
730 
731 
732 status_t
733 BPackageManager::_GetRepositoryCache(BPackageRoster& roster,
734 	const BRepositoryConfig& config, bool refresh, BRepositoryCache& _cache)
735 {
736 	if (!refresh && roster.GetRepositoryCache(config.Name(), &_cache) == B_OK)
737 		return B_OK;
738 
739 	status_t error = RefreshRepository(config);
740 	if (error != B_OK) {
741 		fUserInteractionHandler->Warn(error,
742 			"refreshing repository \"%s\" failed", config.Name().String());
743 	}
744 
745 	return roster.GetRepositoryCache(config.Name(), &_cache);
746 }
747 
748 
749 void
750 BPackageManager::_AddPackageSpecifiers(const char* const* searchStrings,
751 	int searchStringCount, BSolverPackageSpecifierList& specifierList)
752 {
753 	for (int i = 0; i < searchStringCount; i++) {
754 		const char* searchString = searchStrings[i];
755 		if (_IsLocalPackage(searchString)) {
756 			BSolverPackage* package = _AddLocalPackage(searchString);
757 			if (!specifierList.AppendSpecifier(package))
758 				throw std::bad_alloc();
759 		} else {
760 			if (!specifierList.AppendSpecifier(searchString))
761 				throw std::bad_alloc();
762 		}
763 	}
764 }
765 
766 
767 bool
768 BPackageManager::_IsLocalPackage(const char* fileName)
769 {
770 	// Simple heuristic: fileName contains ".hpkg" and there's actually a file
771 	// it refers to.
772 	struct stat st;
773 	return strstr(fileName, ".hpkg") != NULL && stat(fileName, &st) == 0
774 		&& S_ISREG(st.st_mode);
775 }
776 
777 
778 BSolverPackage*
779 BPackageManager::_AddLocalPackage(const char* fileName)
780 {
781 	if (fLocalRepository == NULL)
782 		throw std::bad_alloc();
783 	return fLocalRepository->AddLocalPackage(fileName);
784 }
785 
786 
787 bool
788 BPackageManager::_NextSpecificInstallationLocation()
789 {
790 	if (fLocation == B_PACKAGE_INSTALLATION_LOCATION_SYSTEM) {
791 		fLocation = B_PACKAGE_INSTALLATION_LOCATION_HOME;
792 		fSystemRepository->SetInstalled(false);
793 		_AddInstalledRepository(fHomeRepository);
794 		return true;
795 	}
796 
797 	return false;
798 }
799 
800 
801 status_t
802 BPackageManager::DownloadPackage(const BString& fileURL,
803 	const BEntry& targetEntry, const BString& checksum)
804 {
805 	BDecisionProvider provider;
806 	BContext context(provider, *this);
807 	return DownloadFileRequest(context, fileURL, targetEntry, checksum)
808 		.Process();
809 }
810 
811 
812 status_t
813 BPackageManager::RefreshRepository(const BRepositoryConfig& repoConfig)
814 {
815 	BDecisionProvider provider;
816 	BContext context(provider, *this);
817 	return BRefreshRepositoryRequest(context, repoConfig).Process();
818 }
819 
820 
821 // #pragma mark - RemoteRepository
822 
823 
824 BPackageManager::RemoteRepository::RemoteRepository(
825 	const BRepositoryConfig& config)
826 	:
827 	BSolverRepository(),
828 	fConfig(config)
829 {
830 }
831 
832 
833 const BRepositoryConfig&
834 BPackageManager::RemoteRepository::Config() const
835 {
836 	return fConfig;
837 }
838 
839 
840 // #pragma mark - LocalRepository
841 
842 
843 BPackageManager::LocalRepository::LocalRepository()
844 	:
845 	BSolverRepository()
846 {
847 }
848 
849 
850 BPackageManager::LocalRepository::LocalRepository(const BString& name)
851 	:
852 	BSolverRepository(name)
853 {
854 }
855 
856 
857 // #pragma mark - MiscLocalRepository
858 
859 
860 BPackageManager::MiscLocalRepository::MiscLocalRepository()
861 	:
862 	LocalRepository("local"),
863 	fPackagePaths()
864 {
865 	SetPriority(-127);
866 }
867 
868 
869 BSolverPackage*
870 BPackageManager::MiscLocalRepository::AddLocalPackage(const char* fileName)
871 {
872 	BSolverPackage* package;
873 	BRepositoryBuilder(*this).AddPackage(fileName, &package);
874 
875 	fPackagePaths[package] = fileName;
876 
877 	return package;
878 }
879 
880 
881 void
882 BPackageManager::MiscLocalRepository::GetPackagePath(BSolverPackage* package,
883 	BPath& _path)
884 {
885 	PackagePathMap::const_iterator it = fPackagePaths.find(package);
886 	if (it == fPackagePaths.end()) {
887 		DIE("package %s not in local repository",
888 			package->VersionedName().String());
889 	}
890 
891 	status_t error = _path.SetTo(it->second.c_str());
892 	if (error != B_OK)
893 		DIE(error, "failed to init package path %s", it->second.c_str());
894 }
895 
896 
897 // #pragma mark - InstalledRepository
898 
899 
900 BPackageManager::InstalledRepository::InstalledRepository(const char* name,
901 	BPackageInstallationLocation location, int32 priority)
902 	:
903 	LocalRepository(),
904 	fDisabledPackages(10, true),
905 	fPackagesToActivate(),
906 	fPackagesToDeactivate(),
907 	fInitialName(name),
908 	fLocation(location),
909 	fInitialPriority(priority)
910 {
911 }
912 
913 
914 void
915 BPackageManager::InstalledRepository::GetPackagePath(BSolverPackage* package,
916 	BPath& _path)
917 {
918 	directory_which packagesWhich;
919 	switch (fLocation) {
920 		case B_PACKAGE_INSTALLATION_LOCATION_SYSTEM:
921 			packagesWhich = B_SYSTEM_PACKAGES_DIRECTORY;
922 			break;
923 		case B_PACKAGE_INSTALLATION_LOCATION_HOME:
924 			packagesWhich = B_USER_PACKAGES_DIRECTORY;
925 			break;
926 		default:
927 			DIE("don't know packages directory path for installation location "
928 				"\"%s\"", Name().String());
929 	}
930 
931 	BString fileName(package->Info().FileName());
932 	status_t error = find_directory(packagesWhich, &_path);
933 	if (error != B_OK || (error = _path.Append(fileName)) != B_OK) {
934 		DIE(error, "failed to get path of package file \"%s\" in installation "
935 			"location \"%s\"", fileName.String(), Name().String());
936 	}
937 }
938 
939 
940 void
941 BPackageManager::InstalledRepository::DisablePackage(BSolverPackage* package)
942 {
943 	if (fDisabledPackages.HasItem(package))
944 		DIE("package %s already disabled", package->VersionedName().String());
945 
946 	if (package->Repository() != this) {
947 		DIE("package %s not in repository %s",
948 			package->VersionedName().String(), Name().String());
949 	}
950 
951 	// move to disabled list
952 	if (!fDisabledPackages.AddItem(package))
953 		throw std::bad_alloc();
954 
955 	RemovePackage(package);
956 }
957 
958 
959 bool
960 BPackageManager::InstalledRepository::EnablePackage(BSolverPackage* package)
961 {
962 	return fDisabledPackages.RemoveItem(package);
963 }
964 
965 
966 bool
967 BPackageManager::InstalledRepository::HasChanges() const
968 {
969 	return !fPackagesToActivate.IsEmpty() || !fPackagesToDeactivate.IsEmpty();
970 }
971 
972 
973 void
974 BPackageManager::InstalledRepository::ApplyChanges()
975 {
976 	// disable packages to deactivate
977 	for (int32 i = 0; BSolverPackage* package = fPackagesToDeactivate.ItemAt(i);
978 		i++) {
979 		if (!fDisabledPackages.HasItem(package))
980 			DisablePackage(package);
981 	}
982 
983 	// add packages to activate
984 	for (int32 i = 0; BSolverPackage* package = fPackagesToActivate.ItemAt(i);
985 		i++) {
986 		status_t error = AddPackage(package->Info());
987 		if (error != B_OK) {
988 			DIE(error, "failed to add package %s to %s repository",
989 				package->Name().String(), Name().String());
990 		}
991 	}
992 }
993 
994 
995 // #pragma mark - Transaction
996 
997 
998 BPackageManager::Transaction::Transaction(InstalledRepository& repository)
999 	:
1000 	fRepository(repository),
1001 	fTransaction(),
1002 	fTransactionDirectory()
1003 {
1004 }
1005 
1006 
1007 BPackageManager::Transaction::~Transaction()
1008 {
1009 }
1010 
1011 
1012 // #pragma mark - InstallationInterface
1013 
1014 
1015 BPackageManager::InstallationInterface::~InstallationInterface()
1016 {
1017 }
1018 
1019 
1020 void
1021 BPackageManager::InstallationInterface::ResultComputed(
1022 	InstalledRepository& repository)
1023 {
1024 }
1025 
1026 
1027 // #pragma mark - ClientInstallationInterface
1028 
1029 
1030 BPackageManager::ClientInstallationInterface::ClientInstallationInterface()
1031 	:
1032 	fDaemonClient()
1033 {
1034 }
1035 
1036 
1037 BPackageManager::ClientInstallationInterface::~ClientInstallationInterface()
1038 {
1039 }
1040 
1041 
1042 void
1043 BPackageManager::ClientInstallationInterface::InitInstalledRepository(
1044 	InstalledRepository& repository)
1045 {
1046 	const char* name = repository.InitialName();
1047 	BRepositoryBuilder(repository, name)
1048 		.AddPackages(repository.Location(), name);
1049 }
1050 
1051 
1052 status_t
1053 BPackageManager::ClientInstallationInterface::PrepareTransaction(
1054 	Transaction& transaction)
1055 {
1056 	return fDaemonClient.CreateTransaction(transaction.Repository().Location(),
1057 		transaction.ActivationTransaction(),
1058 		transaction.TransactionDirectory());
1059 }
1060 
1061 
1062 status_t
1063 BPackageManager::ClientInstallationInterface::CommitTransaction(
1064 	Transaction& transaction, BCommitTransactionResult& _result)
1065 {
1066 	return fDaemonClient.CommitTransaction(transaction.ActivationTransaction(),
1067 		_result);
1068 }
1069 
1070 
1071 // #pragma mark - UserInteractionHandler
1072 
1073 
1074 BPackageManager::UserInteractionHandler::~UserInteractionHandler()
1075 {
1076 }
1077 
1078 
1079 void
1080 BPackageManager::UserInteractionHandler::HandleProblems()
1081 {
1082 	throw BAbortedByUserException();
1083 }
1084 
1085 
1086 void
1087 BPackageManager::UserInteractionHandler::ConfirmChanges(bool fromMostSpecific)
1088 {
1089 	throw BAbortedByUserException();
1090 }
1091 
1092 
1093 void
1094 BPackageManager::UserInteractionHandler::Warn(status_t error,
1095 	const char* format, ...)
1096 {
1097 }
1098 
1099 
1100 void
1101 BPackageManager::UserInteractionHandler::ProgressPackageDownloadStarted(
1102 	const char* packageName)
1103 {
1104 }
1105 
1106 
1107 void
1108 BPackageManager::UserInteractionHandler::ProgressPackageDownloadActive(
1109 	const char* packageName, float completionPercentage, off_t bytes,
1110 	off_t totalBytes)
1111 {
1112 }
1113 
1114 
1115 void
1116 BPackageManager::UserInteractionHandler::ProgressPackageDownloadComplete(
1117 	const char* packageName)
1118 {
1119 }
1120 
1121 
1122 void
1123 BPackageManager::UserInteractionHandler::ProgressPackageChecksumStarted(
1124 	const char* title)
1125 {
1126 }
1127 
1128 
1129 void
1130 BPackageManager::UserInteractionHandler::ProgressPackageChecksumComplete(
1131 	const char* title)
1132 {
1133 }
1134 
1135 
1136 void
1137 BPackageManager::UserInteractionHandler::ProgressStartApplyingChanges(
1138 	InstalledRepository& repository)
1139 {
1140 }
1141 
1142 
1143 void
1144 BPackageManager::UserInteractionHandler::ProgressTransactionCommitted(
1145 	InstalledRepository& repository, const BCommitTransactionResult& result)
1146 {
1147 }
1148 
1149 
1150 void
1151 BPackageManager::UserInteractionHandler::ProgressApplyingChangesDone(
1152 	InstalledRepository& repository)
1153 {
1154 }
1155 
1156 
1157 }	// namespace BPrivate
1158 
1159 }	// namespace BManager
1160 
1161 }	// namespace BPackageKit
1162