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