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