16d367ba4SStephan Aßmus/*
25bd5f161SStephan Aßmus * Copyright 2013-2014, Stephan A��mus <superstippi@gmx.de>.
3634c2c68SAxel Dörfler * Copyright 2014, Axel D��rfler <axeld@pinc-software.de>.
4f85e0300SAndrew Lindesay * Copyright 2016-2019, Andrew Lindesay <apl@lindesay.co.nz>.
56d367ba4SStephan Aßmus * All rights reserved. Distributed under the terms of the MIT License.
66d367ba4SStephan Aßmus */
76d367ba4SStephan Aßmus
86d367ba4SStephan Aßmus#include "Model.h"
96d367ba4SStephan Aßmus
10726db742SStephan Aßmus#include <ctime>
11634c2c68SAxel Dörfler#include <stdarg.h>
126d367ba4SStephan Aßmus#include <stdio.h>
13726db742SStephan Aßmus#include <time.h>
146d367ba4SStephan Aßmus
157eec822dSStephan Aßmus#include <Autolock.h>
16f75ec10dSStephan Aßmus#include <Catalog.h>
1789b16bb4SAndrew Lindesay#include <Collator.h>
18726db742SStephan Aßmus#include <Directory.h>
19726db742SStephan Aßmus#include <Entry.h>
20726db742SStephan Aßmus#include <File.h>
2189ec84a4SStephan Aßmus#include <KeyStore.h>
2289b16bb4SAndrew Lindesay#include <Locale.h>
236e48c81bSStephan Aßmus#include <LocaleRoster.h>
24e3fdc894SStephan Aßmus#include <Message.h>
25726db742SStephan Aßmus#include <Path.h>
26f75ec10dSStephan Aßmus
27051ee9d8SAndrew Lindesay#include "HaikuDepotConstants.h"
28f0665db4SAndrew Lindesay#include "Logger.h"
2989b16bb4SAndrew Lindesay#include "LocaleUtils.h"
30f0665db4SAndrew Lindesay#include "StorageUtils.h"
313b17d8ddSAndrew Lindesay#include "RepositoryUrlUtils.h"
32f0665db4SAndrew Lindesay
33f75ec10dSStephan Aßmus
34f75ec10dSStephan Aßmus#undef B_TRANSLATION_CONTEXT
35f75ec10dSStephan Aßmus#define B_TRANSLATION_CONTEXT "Model"
36f75ec10dSStephan Aßmus
376d367ba4SStephan Aßmus
3889ec84a4SStephan Aßmusstatic const char* kHaikuDepotKeyring = "HaikuDepot";
3989ec84a4SStephan Aßmus
4089ec84a4SStephan Aßmus
418f03a0f9SStephan AßmusPackageFilter::~PackageFilter()
428f03a0f9SStephan Aßmus{
438f03a0f9SStephan Aßmus}
444abd2b71SStephan Aßmus
454abd2b71SStephan Aßmus
468f03a0f9SStephan AßmusModelListener::~ModelListener()
474abd2b71SStephan Aßmus{
484abd2b71SStephan Aßmus}
494abd2b71SStephan Aßmus
504abd2b71SStephan Aßmus
518f03a0f9SStephan Aßmus// #pragma mark - PackageFilters
528f03a0f9SStephan Aßmus
538f03a0f9SStephan Aßmus
544abd2b71SStephan Aßmusclass AnyFilter : public PackageFilter {
554abd2b71SStephan Aßmuspublic:
56779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
574abd2b71SStephan Aßmus	{
584abd2b71SStephan Aßmus		return true;
594abd2b71SStephan Aßmus	}
604abd2b71SStephan Aßmus};
614abd2b71SStephan Aßmus
624abd2b71SStephan Aßmus
634abd2b71SStephan Aßmusclass DepotFilter : public PackageFilter {
644abd2b71SStephan Aßmuspublic:
654abd2b71SStephan Aßmus	DepotFilter(const DepotInfo& depot)
664abd2b71SStephan Aßmus		:
674abd2b71SStephan Aßmus		fDepot(depot)
684abd2b71SStephan Aßmus	{
694abd2b71SStephan Aßmus	}
701f67148fSRene Gollent
71779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
724abd2b71SStephan Aßmus	{
734abd2b71SStephan Aßmus		// TODO: Maybe a PackageInfo ought to know the Depot it came from?
744abd2b71SStephan Aßmus		// But right now the same package could theoretically be provided
754abd2b71SStephan Aßmus		// from different depots and the filter would work correctly.
764abd2b71SStephan Aßmus		// Also the PackageList could actually contain references to packages
774abd2b71SStephan Aßmus		// instead of the packages as objects. The equal operator is quite
784abd2b71SStephan Aßmus		// expensive as is.
79634c2c68SAxel Dörfler		return fDepot.Packages().Contains(package);
804abd2b71SStephan Aßmus	}
811f67148fSRene Gollent
820c169aceSStephan Aßmus	const BString& Depot() const
830c169aceSStephan Aßmus	{
840c169aceSStephan Aßmus		return fDepot.Name();
850c169aceSStephan Aßmus	}
860c169aceSStephan Aßmus
874abd2b71SStephan Aßmusprivate:
884abd2b71SStephan Aßmus	DepotInfo	fDepot;
894abd2b71SStephan Aßmus};
904abd2b71SStephan Aßmus
914abd2b71SStephan Aßmus
9242479955SStephan Aßmusclass CategoryFilter : public PackageFilter {
9342479955SStephan Aßmuspublic:
9442479955SStephan Aßmus	CategoryFilter(const BString& category)
9542479955SStephan Aßmus		:
9642479955SStephan Aßmus		fCategory(category)
9742479955SStephan Aßmus	{
9842479955SStephan Aßmus	}
991f67148fSRene Gollent
100779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
10142479955SStephan Aßmus	{
102779d8213SStephan Aßmus		if (package.Get() == NULL)
103779d8213SStephan Aßmus			return false;
104634c2c68SAxel Dörfler
105779d8213SStephan Aßmus		const CategoryList& categories = package->Categories();
10642479955SStephan Aßmus		for (int i = categories.CountItems() - 1; i >= 0; i--) {
10742479955SStephan Aßmus			const CategoryRef& category = categories.ItemAtFast(i);
10842479955SStephan Aßmus			if (category.Get() == NULL)
10942479955SStephan Aßmus				continue;
11089b16bb4SAndrew Lindesay			if (category->Code() == fCategory)
11142479955SStephan Aßmus				return true;
11242479955SStephan Aßmus		}
11342479955SStephan Aßmus		return false;
11442479955SStephan Aßmus	}
11542479955SStephan Aßmus
1160c169aceSStephan Aßmus	const BString& Category() const
1170c169aceSStephan Aßmus	{
1180c169aceSStephan Aßmus		return fCategory;
1190c169aceSStephan Aßmus	}
1200c169aceSStephan Aßmus
12142479955SStephan Aßmusprivate:
12242479955SStephan Aßmus	BString		fCategory;
12342479955SStephan Aßmus};
12442479955SStephan Aßmus
12542479955SStephan Aßmus
1263a6ccc85SStephan Aßmusclass ContainedInFilter : public PackageFilter {
1273a6ccc85SStephan Aßmuspublic:
1283a6ccc85SStephan Aßmus	ContainedInFilter(const PackageList& packageList)
1293a6ccc85SStephan Aßmus		:
1303a6ccc85SStephan Aßmus		fPackageList(packageList)
1313a6ccc85SStephan Aßmus	{
1323a6ccc85SStephan Aßmus	}
1331f67148fSRene Gollent
134779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
1353a6ccc85SStephan Aßmus	{
1363a6ccc85SStephan Aßmus		return fPackageList.Contains(package);
1373a6ccc85SStephan Aßmus	}
1383a6ccc85SStephan Aßmus
1393a6ccc85SStephan Aßmusprivate:
1403a6ccc85SStephan Aßmus	const PackageList&	fPackageList;
1413a6ccc85SStephan Aßmus};
1423a6ccc85SStephan Aßmus
1433a6ccc85SStephan Aßmus
1443a6ccc85SStephan Aßmusclass ContainedInEitherFilter : public PackageFilter {
1453a6ccc85SStephan Aßmuspublic:
1463a6ccc85SStephan Aßmus	ContainedInEitherFilter(const PackageList& packageListA,
1473a6ccc85SStephan Aßmus		const PackageList& packageListB)
1483a6ccc85SStephan Aßmus		:
1493a6ccc85SStephan Aßmus		fPackageListA(packageListA),
1503a6ccc85SStephan Aßmus		fPackageListB(packageListB)
1513a6ccc85SStephan Aßmus	{
1523a6ccc85SStephan Aßmus	}
1531f67148fSRene Gollent
154779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
1553a6ccc85SStephan Aßmus	{
1563a6ccc85SStephan Aßmus		return fPackageListA.Contains(package)
1573a6ccc85SStephan Aßmus			|| fPackageListB.Contains(package);
1583a6ccc85SStephan Aßmus	}
1593a6ccc85SStephan Aßmus
1603a6ccc85SStephan Aßmusprivate:
1613a6ccc85SStephan Aßmus	const PackageList&	fPackageListA;
1623a6ccc85SStephan Aßmus	const PackageList&	fPackageListB;
1633a6ccc85SStephan Aßmus};
1643a6ccc85SStephan Aßmus
1653a6ccc85SStephan Aßmus
166634c2c68SAxel Dörflerclass NotContainedInFilter : public PackageFilter {
167634c2c68SAxel Dörflerpublic:
168634c2c68SAxel Dörfler	NotContainedInFilter(const PackageList* packageList, ...)
169634c2c68SAxel Dörfler	{
170634c2c68SAxel Dörfler		va_list args;
171634c2c68SAxel Dörfler		va_start(args, packageList);
172634c2c68SAxel Dörfler		while (true) {
173634c2c68SAxel Dörfler			const PackageList* packageList = va_arg(args, const PackageList*);
174634c2c68SAxel Dörfler			if (packageList == NULL)
175634c2c68SAxel Dörfler				break;
176634c2c68SAxel Dörfler			fPackageLists.Add(packageList);
177634c2c68SAxel Dörfler		}
178634c2c68SAxel Dörfler		va_end(args);
179634c2c68SAxel Dörfler	}
180634c2c68SAxel Dörfler
181634c2c68SAxel Dörfler	virtual bool AcceptsPackage(const PackageInfoRef& package) const
182634c2c68SAxel Dörfler	{
183be298474SAndrew Lindesay		if (package.Get() == NULL)
184634c2c68SAxel Dörfler			return false;
185a10ad54cSJonathan Schleifer
18672992391SStephan Aßmus		printf("TEST %s\n", package->Name().String());
187a10ad54cSJonathan Schleifer
188634c2c68SAxel Dörfler		for (int32 i = 0; i < fPackageLists.CountItems(); i++) {
189634c2c68SAxel Dörfler			if (fPackageLists.ItemAtFast(i)->Contains(package)) {
190998f4043SJonathan Schleifer				printf("  contained in %" B_PRId32 "\n", i);
191634c2c68SAxel Dörfler				return false;
192634c2c68SAxel Dörfler			}
193634c2c68SAxel Dörfler		}
194634c2c68SAxel Dörfler		return true;
195634c2c68SAxel Dörfler	}
196634c2c68SAxel Dörfler
197634c2c68SAxel Dörflerprivate:
198634c2c68SAxel Dörfler	List<const PackageList*, true>	fPackageLists;
199634c2c68SAxel Dörfler};
200634c2c68SAxel Dörfler
201634c2c68SAxel Dörfler
202634c2c68SAxel Dörflerclass StateFilter : public PackageFilter {
203634c2c68SAxel Dörflerpublic:
204634c2c68SAxel Dörfler	StateFilter(PackageState state)
205634c2c68SAxel Dörfler		:
206634c2c68SAxel Dörfler		fState(state)
207634c2c68SAxel Dörfler	{
208634c2c68SAxel Dörfler	}
209634c2c68SAxel Dörfler
210634c2c68SAxel Dörfler	virtual bool AcceptsPackage(const PackageInfoRef& package) const
211634c2c68SAxel Dörfler	{
212634c2c68SAxel Dörfler		return package->State() == NONE;
213634c2c68SAxel Dörfler	}
214634c2c68SAxel Dörfler
215634c2c68SAxel Dörflerprivate:
216634c2c68SAxel Dörfler	PackageState	fState;
217634c2c68SAxel Dörfler};
218634c2c68SAxel Dörfler
219634c2c68SAxel Dörfler
22009d44cecSStephan Aßmusclass SearchTermsFilter : public PackageFilter {
22109d44cecSStephan Aßmuspublic:
22209d44cecSStephan Aßmus	SearchTermsFilter(const BString& searchTerms)
22309d44cecSStephan Aßmus	{
22409d44cecSStephan Aßmus		// Separate the string into terms at spaces
22509d44cecSStephan Aßmus		int32 index = 0;
22609d44cecSStephan Aßmus		while (index < searchTerms.Length()) {
22709d44cecSStephan Aßmus			int32 nextSpace = searchTerms.FindFirst(" ", index);
22809d44cecSStephan Aßmus			if (nextSpace < 0)
22909d44cecSStephan Aßmus				nextSpace = searchTerms.Length();
23009d44cecSStephan Aßmus			if (nextSpace > index) {
23109d44cecSStephan Aßmus				BString term;
23209d44cecSStephan Aßmus				searchTerms.CopyInto(term, index, nextSpace - index);
23309d44cecSStephan Aßmus				term.ToLower();
23409d44cecSStephan Aßmus				fSearchTerms.Add(term);
23509d44cecSStephan Aßmus			}
23609d44cecSStephan Aßmus			index = nextSpace + 1;
23709d44cecSStephan Aßmus		}
23809d44cecSStephan Aßmus	}
2391f67148fSRene Gollent
240779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
24109d44cecSStephan Aßmus	{
242779d8213SStephan Aßmus		if (package.Get() == NULL)
243779d8213SStephan Aßmus			return false;
24409d44cecSStephan Aßmus		// Every search term must be found in one of the package texts
24509d44cecSStephan Aßmus		for (int32 i = fSearchTerms.CountItems() - 1; i >= 0; i--) {
24609d44cecSStephan Aßmus			const BString& term = fSearchTerms.ItemAtFast(i);
24772992391SStephan Aßmus			if (!_TextContains(package->Name(), term)
24872992391SStephan Aßmus				&& !_TextContains(package->Title(), term)
249779d8213SStephan Aßmus				&& !_TextContains(package->Publisher().Name(), term)
250779d8213SStephan Aßmus				&& !_TextContains(package->ShortDescription(), term)
251779d8213SStephan Aßmus				&& !_TextContains(package->FullDescription(), term)) {
25209d44cecSStephan Aßmus				return false;
25309d44cecSStephan Aßmus			}
25409d44cecSStephan Aßmus		}
25509d44cecSStephan Aßmus		return true;
25609d44cecSStephan Aßmus	}
25709d44cecSStephan Aßmus
2580c169aceSStephan Aßmus	BString SearchTerms() const
2590c169aceSStephan Aßmus	{
2600c169aceSStephan Aßmus		BString searchTerms;
2610c169aceSStephan Aßmus		for (int32 i = 0; i < fSearchTerms.CountItems(); i++) {
2620c169aceSStephan Aßmus			const BString& term = fSearchTerms.ItemAtFast(i);
2630c169aceSStephan Aßmus			if (term.IsEmpty())
2640c169aceSStephan Aßmus				continue;
2650c169aceSStephan Aßmus			if (!searchTerms.IsEmpty())
2660c169aceSStephan Aßmus				searchTerms.Append(" ");
2670c169aceSStephan Aßmus			searchTerms.Append(term);
2680c169aceSStephan Aßmus		}
2690c169aceSStephan Aßmus		return searchTerms;
2700c169aceSStephan Aßmus	}
2710c169aceSStephan Aßmus
27209d44cecSStephan Aßmusprivate:
27309d44cecSStephan Aßmus	bool _TextContains(BString text, const BString& string) const
27409d44cecSStephan Aßmus	{
27509d44cecSStephan Aßmus		text.ToLower();
27609d44cecSStephan Aßmus		int32 index = text.FindFirst(string);
27709d44cecSStephan Aßmus		return index >= 0;
27809d44cecSStephan Aßmus	}
27909d44cecSStephan Aßmus
28009d44cecSStephan Aßmusprivate:
2810c169aceSStephan Aßmus	StringList fSearchTerms;
28209d44cecSStephan Aßmus};
28309d44cecSStephan Aßmus
28409d44cecSStephan Aßmus
28512107be2SStephan Aßmusclass IsFeaturedFilter : public PackageFilter {
28612107be2SStephan Aßmuspublic:
28712107be2SStephan Aßmus	IsFeaturedFilter()
28812107be2SStephan Aßmus	{
28912107be2SStephan Aßmus	}
29012107be2SStephan Aßmus
29112107be2SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
29212107be2SStephan Aßmus	{
29312107be2SStephan Aßmus		return package.Get() != NULL && package->IsProminent();
29412107be2SStephan Aßmus	}
29512107be2SStephan Aßmus};
29612107be2SStephan Aßmus
29712107be2SStephan Aßmus
298870554acSStephan Aßmusstatic inline bool
299870554acSStephan Aßmusis_source_package(const PackageInfoRef& package)
300870554acSStephan Aßmus{
30172992391SStephan Aßmus	const BString& packageName = package->Name();
302870554acSStephan Aßmus	return packageName.EndsWith("_source");
303870554acSStephan Aßmus}
304870554acSStephan Aßmus
305870554acSStephan Aßmus
306870554acSStephan Aßmusstatic inline bool
307870554acSStephan Aßmusis_develop_package(const PackageInfoRef& package)
308870554acSStephan Aßmus{
30972992391SStephan Aßmus	const BString& packageName = package->Name();
31074e41611SJulian Harnath	return packageName.EndsWith("_devel")
31174e41611SJulian Harnath		|| packageName.EndsWith("_debuginfo");
312870554acSStephan Aßmus}
313870554acSStephan Aßmus
314870554acSStephan Aßmus
3154abd2b71SStephan Aßmus// #pragma mark - Model
3164abd2b71SStephan Aßmus
3174abd2b71SStephan Aßmus
31889b16bb4SAndrew Lindesaystatic int32
31989b16bb4SAndrew LindesayPackageCategoryCompareFn(const CategoryRef& c1, const CategoryRef& c2)
32089b16bb4SAndrew Lindesay{
32189b16bb4SAndrew Lindesay	BCollator* collator = LocaleUtils::GetSharedCollator();
32289b16bb4SAndrew Lindesay	int32 result = collator->Compare(c1->Name().String(),
32389b16bb4SAndrew Lindesay		c2->Name().String());
32489b16bb4SAndrew Lindesay	if (result == 0)
32589b16bb4SAndrew Lindesay		result = c1->Code().Compare(c2->Code());
32689b16bb4SAndrew Lindesay	return result;
32789b16bb4SAndrew Lindesay}
32889b16bb4SAndrew Lindesay
32989b16bb4SAndrew Lindesay
3306d367ba4SStephan AßmusModel::Model()
3316d367ba4SStephan Aßmus	:
332f75ec10dSStephan Aßmus	fDepots(),
33389b16bb4SAndrew Lindesay	fCategories(&PackageCategoryCompareFn, NULL),
3344abd2b71SStephan Aßmus	fCategoryFilter(PackageFilterRef(new AnyFilter(), true)),
33509d44cecSStephan Aßmus	fDepotFilter(""),
336870554acSStephan Aßmus	fSearchTermsFilter(PackageFilterRef(new AnyFilter(), true)),
33712107be2SStephan Aßmus	fIsFeaturedFilter(),
3384fade61fSStephan Aßmus	fShowFeaturedPackages(true),
339ab172803SStephan Aßmus	fShowAvailablePackages(true),
3404fade61fSStephan Aßmus	fShowInstalledPackages(true),
341870554acSStephan Aßmus	fShowSourcePackages(false),
3423094fef3SAndrew Lindesay	fShowDevelopPackages(false)
3436d367ba4SStephan Aßmus{
34412107be2SStephan Aßmus	_UpdateIsFeaturedFilter();
345f85e0300SAndrew Lindesay}
346d07ca069SStephan Aßmus
3474ff3d17aSStephan Aßmus
348f85e0300SAndrew LindesayModel::~Model()
349f85e0300SAndrew Lindesay{
3506d367ba4SStephan Aßmus}
3516d367ba4SStephan Aßmus
3526d367ba4SStephan Aßmus
353f85e0300SAndrew LindesayLanguageModel&
354f85e0300SAndrew LindesayModel::Language()
3555bd5f161SStephan Aßmus{
356f85e0300SAndrew Lindesay	return fLanguageModel;
3575bd5f161SStephan Aßmus}
3585bd5f161SStephan Aßmus
3595bd5f161SStephan Aßmus
3608f03a0f9SStephan Aßmusbool
3618f03a0f9SStephan AßmusModel::AddListener(const ModelListenerRef& listener)
3628f03a0f9SStephan Aßmus{
3638f03a0f9SStephan Aßmus	return fListeners.Add(listener);
3648f03a0f9SStephan Aßmus}
3658f03a0f9SStephan Aßmus
3668f03a0f9SStephan Aßmus
367706edd80SStephan AßmusPackageList
3686d367ba4SStephan AßmusModel::CreatePackageList() const
3696d367ba4SStephan Aßmus{
370ab172803SStephan Aßmus	// Iterate all packages from all depots.
371ab172803SStephan Aßmus	// If configured, restrict depot, filter by search terms, status, name ...
372706edd80SStephan Aßmus	PackageList resultList;
3736d367ba4SStephan Aßmus
3746d367ba4SStephan Aßmus	for (int32 i = 0; i < fDepots.CountItems(); i++) {
37509d44cecSStephan Aßmus		const DepotInfo& depot = fDepots.ItemAtFast(i);
3761f67148fSRene Gollent
37709d44cecSStephan Aßmus		if (fDepotFilter.Length() > 0 && fDepotFilter != depot.Name())
37809d44cecSStephan Aßmus			continue;
3791f67148fSRene Gollent
38009d44cecSStephan Aßmus		const PackageList& packages = depot.Packages();
3816d367ba4SStephan Aßmus
382706edd80SStephan Aßmus		for (int32 j = 0; j < packages.CountItems(); j++) {
383779d8213SStephan Aßmus			const PackageInfoRef& package = packages.ItemAtFast(j);
38439f49f70SJulian Harnath			if (MatchesFilter(package))
3854abd2b71SStephan Aßmus				resultList.Add(package);
3866d367ba4SStephan Aßmus		}
3876d367ba4SStephan Aßmus	}
3886d367ba4SStephan Aßmus
3896d367ba4SStephan Aßmus	return resultList;
3906d367ba4SStephan Aßmus}
3916d367ba4SStephan Aßmus
3926d367ba4SStephan Aßmus
39339f49f70SJulian Harnathbool
39439f49f70SJulian HarnathModel::MatchesFilter(const PackageInfoRef& package) const
39539f49f70SJulian Harnath{
39639f49f70SJulian Harnath	return fCategoryFilter->AcceptsPackage(package)
39739f49f70SJulian Harnath			&& fSearchTermsFilter->AcceptsPackage(package)
39839f49f70SJulian Harnath			&& fIsFeaturedFilter->AcceptsPackage(package)
39939f49f70SJulian Harnath			&& (fShowAvailablePackages || package->State() != NONE)
40039f49f70SJulian Harnath			&& (fShowInstalledPackages || package->State() != ACTIVATED)
40139f49f70SJulian Harnath			&& (fShowSourcePackages || !is_source_package(package))
40239f49f70SJulian Harnath			&& (fShowDevelopPackages || !is_develop_package(package));
40339f49f70SJulian Harnath}
40439f49f70SJulian Harnath
40539f49f70SJulian Harnath
4066d367ba4SStephan Aßmusbool
4076d367ba4SStephan AßmusModel::AddDepot(const DepotInfo& depot)
4086d367ba4SStephan Aßmus{
4096d367ba4SStephan Aßmus	return fDepots.Add(depot);
4106d367ba4SStephan Aßmus}
4116d367ba4SStephan Aßmus
41242479955SStephan Aßmus
4137b2c59b9SStephan Aßmusbool
4147b2c59b9SStephan AßmusModel::HasDepot(const BString& name) const
415ebb1a01cSAndrew Lindesay{
416ebb1a01cSAndrew Lindesay	return NULL != DepotForName(name);
417ebb1a01cSAndrew Lindesay}
418ebb1a01cSAndrew Lindesay
419ebb1a01cSAndrew Lindesay
420ebb1a01cSAndrew Lindesayconst DepotInfo*
421ebb1a01cSAndrew LindesayModel::DepotForName(const BString& name) const
4227b2c59b9SStephan Aßmus{
4237b2c59b9SStephan Aßmus	for (int32 i = fDepots.CountItems() - 1; i >= 0; i--) {
4247b2c59b9SStephan Aßmus		if (fDepots.ItemAtFast(i).Name() == name)
425ebb1a01cSAndrew Lindesay			return &fDepots.ItemAtFast(i);
4267b2c59b9SStephan Aßmus	}
427ebb1a01cSAndrew Lindesay	return NULL;
4287b2c59b9SStephan Aßmus}
4297b2c59b9SStephan Aßmus
4307b2c59b9SStephan Aßmus
4317b2c59b9SStephan Aßmusbool
4327b2c59b9SStephan AßmusModel::SyncDepot(const DepotInfo& depot)
4337b2c59b9SStephan Aßmus{
4347b2c59b9SStephan Aßmus	for (int32 i = fDepots.CountItems() - 1; i >= 0; i--) {
4357b2c59b9SStephan Aßmus		const DepotInfo& existingDepot = fDepots.ItemAtFast(i);
4367b2c59b9SStephan Aßmus		if (existingDepot.Name() == depot.Name()) {
4377b2c59b9SStephan Aßmus			DepotInfo mergedDepot(existingDepot);
4387b2c59b9SStephan Aßmus			mergedDepot.SyncPackages(depot.Packages());
4397b2c59b9SStephan Aßmus			fDepots.Replace(i, mergedDepot);
4407b2c59b9SStephan Aßmus			return true;
4417b2c59b9SStephan Aßmus		}
4427b2c59b9SStephan Aßmus	}
4437b2c59b9SStephan Aßmus	return false;
4447b2c59b9SStephan Aßmus}
4457b2c59b9SStephan Aßmus
4467b2c59b9SStephan Aßmus
4472c287f5bSRene Gollentvoid
4482c287f5bSRene GollentModel::Clear()
4492c287f5bSRene Gollent{
4502c287f5bSRene Gollent	fDepots.Clear();
4512c287f5bSRene Gollent}
4522c287f5bSRene Gollent
4532c287f5bSRene Gollent
454706edd80SStephan Aßmusvoid
455779d8213SStephan AßmusModel::SetPackageState(const PackageInfoRef& package, PackageState state)
456706edd80SStephan Aßmus{
457706edd80SStephan Aßmus	switch (state) {
458706edd80SStephan Aßmus		default:
459706edd80SStephan Aßmus		case NONE:
460706edd80SStephan Aßmus			fInstalledPackages.Remove(package);
461706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
462706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
463706edd80SStephan Aßmus			break;
464706edd80SStephan Aßmus		case INSTALLED:
465706edd80SStephan Aßmus			if (!fInstalledPackages.Contains(package))
466706edd80SStephan Aßmus				fInstalledPackages.Add(package);
467706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
468706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
469706edd80SStephan Aßmus			break;
470706edd80SStephan Aßmus		case ACTIVATED:
471706edd80SStephan Aßmus			if (!fInstalledPackages.Contains(package))
472706edd80SStephan Aßmus				fInstalledPackages.Add(package);
473706edd80SStephan Aßmus			if (!fActivatedPackages.Contains(package))
474706edd80SStephan Aßmus				fActivatedPackages.Add(package);
475706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
476706edd80SStephan Aßmus			break;
477706edd80SStephan Aßmus		case UNINSTALLED:
478706edd80SStephan Aßmus			fInstalledPackages.Remove(package);
479706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
480ab172803SStephan Aßmus			if (!fUninstalledPackages.Contains(package))
481ab172803SStephan Aßmus				fUninstalledPackages.Add(package);
482706edd80SStephan Aßmus			break;
483706edd80SStephan Aßmus	}
484b4c8d2ffSRene Gollent
485b4c8d2ffSRene Gollent	package->SetState(state);
486706edd80SStephan Aßmus}
487706edd80SStephan Aßmus
488706edd80SStephan Aßmus
48909d44cecSStephan Aßmus// #pragma mark - filters
49009d44cecSStephan Aßmus
49109d44cecSStephan Aßmus
49242479955SStephan Aßmusvoid
49342479955SStephan AßmusModel::SetCategory(const BString& category)
49442479955SStephan Aßmus{
4953a6ccc85SStephan Aßmus	PackageFilter* filter;
4961f67148fSRene Gollent
49742479955SStephan Aßmus	if (category.Length() == 0)
4983a6ccc85SStephan Aßmus		filter = new AnyFilter();
49942479955SStephan Aßmus	else
5003a6ccc85SStephan Aßmus		filter = new CategoryFilter(category);
5013a6ccc85SStephan Aßmus
5023a6ccc85SStephan Aßmus	fCategoryFilter.SetTo(filter, true);
50342479955SStephan Aßmus}
50442479955SStephan Aßmus
50509d44cecSStephan Aßmus
5060c169aceSStephan AßmusBString
5070c169aceSStephan AßmusModel::Category() const
5080c169aceSStephan Aßmus{
5090c169aceSStephan Aßmus	CategoryFilter* filter
5100c169aceSStephan Aßmus		= dynamic_cast<CategoryFilter*>(fCategoryFilter.Get());
5110c169aceSStephan Aßmus	if (filter == NULL)
5120c169aceSStephan Aßmus		return "";
5130c169aceSStephan Aßmus	return filter->Category();
5140c169aceSStephan Aßmus}
5150c169aceSStephan Aßmus
5160c169aceSStephan Aßmus
51709d44cecSStephan Aßmusvoid
51809d44cecSStephan AßmusModel::SetDepot(const BString& depot)
51909d44cecSStephan Aßmus{
52009d44cecSStephan Aßmus	fDepotFilter = depot;
52109d44cecSStephan Aßmus}
52209d44cecSStephan Aßmus
52309d44cecSStephan Aßmus
5240c169aceSStephan AßmusBString
5250c169aceSStephan AßmusModel::Depot() const
5260c169aceSStephan Aßmus{
5270c169aceSStephan Aßmus	return fDepotFilter;
5280c169aceSStephan Aßmus}
5290c169aceSStephan Aßmus
5300c169aceSStephan Aßmus
53109d44cecSStephan Aßmusvoid
53209d44cecSStephan AßmusModel::SetSearchTerms(const BString& searchTerms)
53309d44cecSStephan Aßmus{
53409d44cecSStephan Aßmus	PackageFilter* filter;
5351f67148fSRene Gollent
53609d44cecSStephan Aßmus	if (searchTerms.Length() == 0)
53709d44cecSStephan Aßmus		filter = new AnyFilter();
53809d44cecSStephan Aßmus	else
53909d44cecSStephan Aßmus		filter = new SearchTermsFilter(searchTerms);
54009d44cecSStephan Aßmus
54109d44cecSStephan Aßmus	fSearchTermsFilter.SetTo(filter, true);
54212107be2SStephan Aßmus	_UpdateIsFeaturedFilter();
54309d44cecSStephan Aßmus}
54409d44cecSStephan Aßmus
5457eec822dSStephan Aßmus
5460c169aceSStephan AßmusBString
5470c169aceSStephan AßmusModel::SearchTerms() const
5480c169aceSStephan Aßmus{
5490c169aceSStephan Aßmus	SearchTermsFilter* filter
5500c169aceSStephan Aßmus		= dynamic_cast<SearchTermsFilter*>(fSearchTermsFilter.Get());
5510c169aceSStephan Aßmus	if (filter == NULL)
5520c169aceSStephan Aßmus		return "";
5530c169aceSStephan Aßmus	return filter->SearchTerms();
5540c169aceSStephan Aßmus}
5550c169aceSStephan Aßmus
5560c169aceSStephan Aßmus
5574fade61fSStephan Aßmusvoid
5584fade61fSStephan AßmusModel::SetShowFeaturedPackages(bool show)
5594fade61fSStephan Aßmus{
5604fade61fSStephan Aßmus	fShowFeaturedPackages = show;
56112107be2SStephan Aßmus	_UpdateIsFeaturedFilter();
5624fade61fSStephan Aßmus}
5634fade61fSStephan Aßmus
5644fade61fSStephan Aßmus
565ab172803SStephan Aßmusvoid
566ab172803SStephan AßmusModel::SetShowAvailablePackages(bool show)
567ab172803SStephan Aßmus{
568ab172803SStephan Aßmus	fShowAvailablePackages = show;
569ab172803SStephan Aßmus}
570ab172803SStephan Aßmus
571ab172803SStephan Aßmus
572ab172803SStephan Aßmusvoid
573ab172803SStephan AßmusModel::SetShowInstalledPackages(bool show)
574ab172803SStephan Aßmus{
575ab172803SStephan Aßmus	fShowInstalledPackages = show;
576ab172803SStephan Aßmus}
577ab172803SStephan Aßmus
578ab172803SStephan Aßmus
579870554acSStephan Aßmusvoid
580870554acSStephan AßmusModel::SetShowSourcePackages(bool show)
581870554acSStephan Aßmus{
582870554acSStephan Aßmus	fShowSourcePackages = show;
583870554acSStephan Aßmus}
584870554acSStephan Aßmus
585870554acSStephan Aßmus
586870554acSStephan Aßmusvoid
587870554acSStephan AßmusModel::SetShowDevelopPackages(bool show)
588870554acSStephan Aßmus{
589870554acSStephan Aßmus	fShowDevelopPackages = show;
590870554acSStephan Aßmus}
591870554acSStephan Aßmus
592870554acSStephan Aßmus
59319c15fecSAndrew Lindesay// #pragma mark - information retrieval
5947eec822dSStephan Aßmus
5957eec822dSStephan Aßmus
596a9edb9bfSAndrew Lindesay/*! Initially only superficial data is loaded from the server into the data
597a9edb9bfSAndrew Lindesay    model of the packages.  When the package is viewed, additional data needs
598a9edb9bfSAndrew Lindesay    to be populated including ratings.  This method takes care of that.
599a9edb9bfSAndrew Lindesay*/
600a9edb9bfSAndrew Lindesay
6017eec822dSStephan Aßmusvoid
602a60a23f8SStephan AßmusModel::PopulatePackage(const PackageInfoRef& package, uint32 flags)
6037eec822dSStephan Aßmus{
6047eec822dSStephan Aßmus	// TODO: There should probably also be a way to "unpopulate" the
6057eec822dSStephan Aßmus	// package information. Maybe a cache of populated packages, so that
6067eec822dSStephan Aßmus	// packages loose their extra information after a certain amount of
6077eec822dSStephan Aßmus	// time when they have not been accessed/displayed in the UI. Otherwise
6087eec822dSStephan Aßmus	// HaikuDepot will consume more and more resources in the packages.
6097eec822dSStephan Aßmus	// Especially screen-shots will be a problem eventually.
61048045f5aSStephan Aßmus	{
61148045f5aSStephan Aßmus		BAutolock locker(&fLock);
61289e4ba17SStephan Aßmus		bool alreadyPopulated = fPopulatedPackages.Contains(package);
61389e4ba17SStephan Aßmus		if ((flags & POPULATE_FORCE) == 0 && alreadyPopulated)
61448045f5aSStephan Aßmus			return;
61589e4ba17SStephan Aßmus		if (!alreadyPopulated)
61689e4ba17SStephan Aßmus			fPopulatedPackages.Add(package);
61748045f5aSStephan Aßmus	}
6187eec822dSStephan Aßmus
61980a272eeSAndrew Lindesay	if ((flags & POPULATE_CHANGELOG) != 0) {
62080a272eeSAndrew Lindesay		_PopulatePackageChangelog(package);
62180a272eeSAndrew Lindesay	}
62280a272eeSAndrew Lindesay
6237ff94fd0SStephan Aßmus	if ((flags & POPULATE_USER_RATINGS) != 0) {
6247ff94fd0SStephan Aßmus		// Retrieve info from web-app
6257ff94fd0SStephan Aßmus		BMessage info;
62648045f5aSStephan Aßmus
62748045f5aSStephan Aßmus		BString packageName;
628051ee9d8SAndrew Lindesay		BString webAppRepositoryCode;
62948045f5aSStephan Aßmus		{
63048045f5aSStephan Aßmus			BAutolock locker(&fLock);
63172992391SStephan Aßmus			packageName = package->Name();
632051ee9d8SAndrew Lindesay			const DepotInfo* depot = DepotForName(package->DepotName());
633051ee9d8SAndrew Lindesay
634051ee9d8SAndrew Lindesay			if (depot != NULL)
635051ee9d8SAndrew Lindesay				webAppRepositoryCode = depot->WebAppRepositoryCode();
63648045f5aSStephan Aßmus		}
63787084745SMichael Lotz
638051ee9d8SAndrew Lindesay		status_t status = fWebAppInterface
639051ee9d8SAndrew Lindesay			.RetreiveUserRatingsForPackageForDisplay(packageName,
640051ee9d8SAndrew Lindesay				webAppRepositoryCode, 0, PACKAGE_INFO_MAX_USER_RATINGS,
641051ee9d8SAndrew Lindesay				info);
6427ff94fd0SStephan Aßmus		if (status == B_OK) {
6437ff94fd0SStephan Aßmus			// Parse message
6447ff94fd0SStephan Aßmus			BMessage result;
6457ff94fd0SStephan Aßmus			BMessage items;
6467ff94fd0SStephan Aßmus			if (info.FindMessage("result", &result) == B_OK
6477ff94fd0SStephan Aßmus				&& result.FindMessage("items", &items) == B_OK) {
64848045f5aSStephan Aßmus
64948045f5aSStephan Aßmus				BAutolock locker(&fLock);
6507eb09bfaSStephan Aßmus				package->ClearUserRatings();
65148045f5aSStephan Aßmus
652a9edb9bfSAndrew Lindesay				int32 index = 0;
6537ff94fd0SStephan Aßmus				while (true) {
6547ff94fd0SStephan Aßmus					BString name;
6557ff94fd0SStephan Aßmus					name << index++;
65687084745SMichael Lotz
6577ff94fd0SStephan Aßmus					BMessage item;
6587ff94fd0SStephan Aßmus					if (items.FindMessage(name, &item) != B_OK)
6597ff94fd0SStephan Aßmus						break;
6607ff94fd0SStephan Aßmus
661a9edb9bfSAndrew Lindesay					BString code;
662a9edb9bfSAndrew Lindesay					if (item.FindString("code", &code) != B_OK) {
663a9edb9bfSAndrew Lindesay						printf("corrupt user rating at index %" B_PRIi32 "\n",
664a9edb9bfSAndrew Lindesay							index);
665a9edb9bfSAndrew Lindesay						continue;
666a9edb9bfSAndrew Lindesay					}
667a9edb9bfSAndrew Lindesay
6687ff94fd0SStephan Aßmus					BString user;
6697ff94fd0SStephan Aßmus					BMessage userInfo;
6707ff94fd0SStephan Aßmus					if (item.FindMessage("user", &userInfo) != B_OK
6717ff94fd0SStephan Aßmus						|| userInfo.FindString("nickname", &user) != B_OK) {
672a9edb9bfSAndrew Lindesay						printf("ignored user rating [%s] without a user "
673a9edb9bfSAndrew Lindesay							"nickname\n", code.String());
6747ff94fd0SStephan Aßmus						continue;
6757ff94fd0SStephan Aßmus					}
6767ff94fd0SStephan Aßmus
67736a9b557SStephan Aßmus					// Extract basic info, all items are optional
6787ff94fd0SStephan Aßmus					BString languageCode;
6797ff94fd0SStephan Aßmus					BString comment;
6807ff94fd0SStephan Aßmus					double rating;
68136a9b557SStephan Aßmus					item.FindString("naturalLanguageCode", &languageCode);
68236a9b557SStephan Aßmus					item.FindString("comment", &comment);
68336a9b557SStephan Aßmus					if (item.FindDouble("rating", &rating) != B_OK)
68436a9b557SStephan Aßmus						rating = -1;
68536a9b557SStephan Aßmus					if (comment.Length() == 0 && rating == -1) {
686a9edb9bfSAndrew Lindesay						printf("rating [%s] has no comment or rating so will be"
687a9edb9bfSAndrew Lindesay							"ignored\n", code.String());
6887ff94fd0SStephan Aßmus						continue;
6897ff94fd0SStephan Aßmus					}
69036a9b557SStephan Aßmus
6917ff94fd0SStephan Aßmus					// For which version of the package was the rating?
6927ff94fd0SStephan Aßmus					BString major = "?";
6937ff94fd0SStephan Aßmus					BString minor = "?";
6947ff94fd0SStephan Aßmus					BString micro = "";
695a9edb9bfSAndrew Lindesay					double revision = -1;
696051ee9d8SAndrew Lindesay					BString architectureCode = "";
6977ff94fd0SStephan Aßmus					BMessage version;
6987ff94fd0SStephan Aßmus					if (item.FindMessage("pkgVersion", &version) == B_OK) {
6997ff94fd0SStephan Aßmus						version.FindString("major", &major);
7007ff94fd0SStephan Aßmus						version.FindString("minor", &minor);
7017ff94fd0SStephan Aßmus						version.FindString("micro", &micro);
702a9edb9bfSAndrew Lindesay						version.FindDouble("revision", &revision);
703051ee9d8SAndrew Lindesay						version.FindString("architectureCode",
704051ee9d8SAndrew Lindesay							&architectureCode);
7057ff94fd0SStephan Aßmus					}
7067ff94fd0SStephan Aßmus					BString versionString = major;
7077ff94fd0SStephan Aßmus					versionString << ".";
7087ff94fd0SStephan Aßmus					versionString << minor;
709051ee9d8SAndrew Lindesay					if (!micro.IsEmpty()) {
7107ff94fd0SStephan Aßmus						versionString << ".";
7117ff94fd0SStephan Aßmus						versionString << micro;
7127ff94fd0SStephan Aßmus					}
713a9edb9bfSAndrew Lindesay					if (revision > 0) {
714a9edb9bfSAndrew Lindesay						versionString << "-";
715a9edb9bfSAndrew Lindesay						versionString << (int) revision;
716a9edb9bfSAndrew Lindesay					}
717a9edb9bfSAndrew Lindesay
718051ee9d8SAndrew Lindesay					if (!architectureCode.IsEmpty()) {
719051ee9d8SAndrew Lindesay						versionString << " " << STR_MDASH << " ";
720051ee9d8SAndrew Lindesay						versionString << architectureCode;
721051ee9d8SAndrew Lindesay					}
722051ee9d8SAndrew Lindesay
723d2d4866dSAndrew Lindesay					double createTimestamp;
724d2d4866dSAndrew Lindesay					item.FindDouble("createTimestamp", &createTimestamp);
725a9edb9bfSAndrew Lindesay
7267ff94fd0SStephan Aßmus					// Add the rating to the PackageInfo
727a9edb9bfSAndrew Lindesay					UserRating userRating = UserRating(UserInfo(user), rating,
728d2d4866dSAndrew Lindesay						comment, languageCode, versionString,
729d2d4866dSAndrew Lindesay						(uint64) createTimestamp);
730a9edb9bfSAndrew Lindesay					package->AddUserRating(userRating);
731a9edb9bfSAndrew Lindesay
732a9edb9bfSAndrew Lindesay					if (Logger::IsDebugEnabled()) {
733a9edb9bfSAndrew Lindesay						printf("rating [%s] retrieved from server\n",
734a9edb9bfSAndrew Lindesay							code.String());
735a9edb9bfSAndrew Lindesay					}
736a9edb9bfSAndrew Lindesay				}
737a9edb9bfSAndrew Lindesay
738a9edb9bfSAndrew Lindesay				if (Logger::IsDebugEnabled()) {
739a9edb9bfSAndrew Lindesay					printf("did retrieve %" B_PRIi32 " user ratings for [%s]\n",
740a9edb9bfSAndrew Lindesay						index - 1, packageName.String());
7417ff94fd0SStephan Aßmus				}
74280a272eeSAndrew Lindesay			} else {
74380a272eeSAndrew Lindesay				_MaybeLogJsonRpcError(info, "retrieve user ratings");
7447ff94fd0SStephan Aßmus			}
745a9edb9bfSAndrew Lindesay		} else {
746a9edb9bfSAndrew Lindesay			printf("unable to retrieve user ratings\n");
747a60a23f8SStephan Aßmus		}
7487eec822dSStephan Aßmus	}
74987038da7SStephan Aßmus
75087038da7SStephan Aßmus	if ((flags & POPULATE_SCREEN_SHOTS) != 0) {
75187038da7SStephan Aßmus		ScreenshotInfoList screenshotInfos;
75287038da7SStephan Aßmus		{
75387038da7SStephan Aßmus			BAutolock locker(&fLock);
75487038da7SStephan Aßmus			screenshotInfos = package->ScreenshotInfos();
7557eb09bfaSStephan Aßmus			package->ClearScreenshots();
75687038da7SStephan Aßmus		}
75787038da7SStephan Aßmus		for (int i = 0; i < screenshotInfos.CountItems(); i++) {
75887038da7SStephan Aßmus			const ScreenshotInfo& info = screenshotInfos.ItemAtFast(i);
759e0881f21SStephan Aßmus			_PopulatePackageScreenshot(package, info, 320, false);
76087038da7SStephan Aßmus		}
76187038da7SStephan Aßmus	}
7627eec822dSStephan Aßmus}
7635bd5f161SStephan Aßmus
7645bd5f161SStephan Aßmus
76580a272eeSAndrew Lindesayvoid
76680a272eeSAndrew LindesayModel::_PopulatePackageChangelog(const PackageInfoRef& package)
76780a272eeSAndrew Lindesay{
76880a272eeSAndrew Lindesay	BMessage info;
76980a272eeSAndrew Lindesay	BString packageName;
77080a272eeSAndrew Lindesay
77180a272eeSAndrew Lindesay	{
77280a272eeSAndrew Lindesay		BAutolock locker(&fLock);
77380a272eeSAndrew Lindesay		packageName = package->Name();
77480a272eeSAndrew Lindesay	}
77580a272eeSAndrew Lindesay
77680a272eeSAndrew Lindesay	status_t status = fWebAppInterface.GetChangelog(packageName, info);
77780a272eeSAndrew Lindesay
77880a272eeSAndrew Lindesay	if (status == B_OK) {
77980a272eeSAndrew Lindesay		// Parse message
78080a272eeSAndrew Lindesay		BMessage result;
78180a272eeSAndrew Lindesay		BString content;
78280a272eeSAndrew Lindesay		if (info.FindMessage("result", &result) == B_OK) {
78380a272eeSAndrew Lindesay			if (result.FindString("content", &content) == B_OK
78480a272eeSAndrew Lindesay				&& 0 != content.Length()) {
78580a272eeSAndrew Lindesay				BAutolock locker(&fLock);
78680a272eeSAndrew Lindesay				package->SetChangelog(content);
78780a272eeSAndrew Lindesay				if (Logger::IsDebugEnabled()) {
78880a272eeSAndrew Lindesay					fprintf(stdout, "changelog populated for [%s]\n",
78980a272eeSAndrew Lindesay						packageName.String());
79080a272eeSAndrew Lindesay				}
79180a272eeSAndrew Lindesay			} else {
79280a272eeSAndrew Lindesay				if (Logger::IsDebugEnabled()) {
79380a272eeSAndrew Lindesay					fprintf(stdout, "no changelog present for [%s]\n",
79480a272eeSAndrew Lindesay						packageName.String());
79580a272eeSAndrew Lindesay				}
79680a272eeSAndrew Lindesay			}
79780a272eeSAndrew Lindesay		} else {
79880a272eeSAndrew Lindesay			_MaybeLogJsonRpcError(info, "populate package changelog");
79980a272eeSAndrew Lindesay		}
80080a272eeSAndrew Lindesay	} else {
80180a272eeSAndrew Lindesay		fprintf(stdout, "unable to obtain the changelog for the package"
802a9edb9bfSAndrew Lindesay			" [%s]\n", packageName.String());
80380a272eeSAndrew Lindesay	}
80480a272eeSAndrew Lindesay}
80580a272eeSAndrew Lindesay
80680a272eeSAndrew Lindesay
80728075ee4SStephan Aßmusvoid
808d2d4866dSAndrew LindesayModel::SetNickname(BString nickname)
80989ec84a4SStephan Aßmus{
8108f03a0f9SStephan Aßmus	BString password;
811d2d4866dSAndrew Lindesay	if (nickname.Length() > 0) {
8128f03a0f9SStephan Aßmus		BPasswordKey key;
8138f03a0f9SStephan Aßmus		BKeyStore keyStore;
814d2d4866dSAndrew Lindesay		if (keyStore.GetKey(kHaikuDepotKeyring, B_KEY_TYPE_PASSWORD, nickname,
8158f03a0f9SStephan Aßmus				key) == B_OK) {
8168f03a0f9SStephan Aßmus			password = key.Password();
8178f03a0f9SStephan Aßmus		} else {
818d2d4866dSAndrew Lindesay			nickname = "";
8198f03a0f9SStephan Aßmus		}
82089ec84a4SStephan Aßmus	}
821d2d4866dSAndrew Lindesay	SetAuthorization(nickname, password, false);
8228f03a0f9SStephan Aßmus}
8238f03a0f9SStephan Aßmus
8248f03a0f9SStephan Aßmus
8258f03a0f9SStephan Aßmusconst BString&
826d2d4866dSAndrew LindesayModel::Nickname() const
8278f03a0f9SStephan Aßmus{
828d2d4866dSAndrew Lindesay	return fWebAppInterface.Nickname();
82989ec84a4SStephan Aßmus}
83089ec84a4SStephan Aßmus
83189ec84a4SStephan Aßmus
83289ec84a4SStephan Aßmusvoid
833d2d4866dSAndrew LindesayModel::SetAuthorization(const BString& nickname, const BString& passwordClear,
83489ec84a4SStephan Aßmus	bool storePassword)
83528075ee4SStephan Aßmus{
836d2d4866dSAndrew Lindesay	if (storePassword && nickname.Length() > 0 && passwordClear.Length() > 0) {
837d2d4866dSAndrew Lindesay		BPasswordKey key(passwordClear, B_KEY_PURPOSE_WEB, nickname);
83889ec84a4SStephan Aßmus		BKeyStore keyStore;
83989ec84a4SStephan Aßmus		keyStore.AddKeyring(kHaikuDepotKeyring);
84089ec84a4SStephan Aßmus		keyStore.AddKey(kHaikuDepotKeyring, key);
84189ec84a4SStephan Aßmus	}
84289ec84a4SStephan Aßmus
84328075ee4SStephan Aßmus	BAutolock locker(&fLock);
844d2d4866dSAndrew Lindesay	fWebAppInterface.SetAuthorization(UserCredentials(nickname, passwordClear));
8458f03a0f9SStephan Aßmus
8468f03a0f9SStephan Aßmus	_NotifyAuthorizationChanged();
84728075ee4SStephan Aßmus}
84828075ee4SStephan Aßmus
84928075ee4SStephan Aßmus
8503d528c4aSAndrew Lindesaystatus_t
851f85e0300SAndrew LindesayModel::_LocalDataPath(const BString leaf, BPath& path) const
852ebb1a01cSAndrew Lindesay{
853041bbff9SAndrew Lindesay	BPath resultPath;
854041bbff9SAndrew Lindesay	status_t result = B_OK;
855041bbff9SAndrew Lindesay
856041bbff9SAndrew Lindesay	if (result == B_OK)
857041bbff9SAndrew Lindesay		result = find_directory(B_USER_CACHE_DIRECTORY, &resultPath);
858041bbff9SAndrew Lindesay
859041bbff9SAndrew Lindesay	if (result == B_OK)
860041bbff9SAndrew Lindesay		result = resultPath.Append("HaikuDepot");
861041bbff9SAndrew Lindesay
862041bbff9SAndrew Lindesay	if (result == B_OK)
863041bbff9SAndrew Lindesay		result = create_directory(resultPath.Path(), 0777);
864f85e0300SAndrew Lindesay
865041bbff9SAndrew Lindesay	if (result == B_OK)
866041bbff9SAndrew Lindesay		result = resultPath.Append(leaf);
867ebb1a01cSAndrew Lindesay
868041bbff9SAndrew Lindesay	if (result == B_OK)
869041bbff9SAndrew Lindesay		path.SetTo(resultPath.Path());
870041bbff9SAndrew Lindesay	else {
871041bbff9SAndrew Lindesay		path.Unset();
872041bbff9SAndrew Lindesay		fprintf(stdout, "unable to find the user cache file for "
873041bbff9SAndrew Lindesay			"[%s] data; %s\n", leaf.String(), strerror(result));
8743d528c4aSAndrew Lindesay	}
875ebb1a01cSAndrew Lindesay
876041bbff9SAndrew Lindesay	return result;
8773d528c4aSAndrew Lindesay}
878ebb1a01cSAndrew Lindesay
879ebb1a01cSAndrew Lindesay
880f85e0300SAndrew Lindesay/*! When bulk repository data comes down from the server, it will
881f85e0300SAndrew Lindesay    arrive as a json.gz payload.  This is stored locally as a cache
882f85e0300SAndrew Lindesay    and this method will provide the on-disk storage location for
883f85e0300SAndrew Lindesay    this file.
884f85e0300SAndrew Lindesay*/
885f85e0300SAndrew Lindesay
886f85e0300SAndrew Lindesaystatus_t
887f85e0300SAndrew LindesayModel::DumpExportRepositoryDataPath(BPath& path) const
888f85e0300SAndrew Lindesay{
889041bbff9SAndrew Lindesay	BString leaf;
890041bbff9SAndrew Lindesay	leaf.SetToFormat("repository-all_%s.json.gz",
891041bbff9SAndrew Lindesay		LanguageModel().PreferredLanguage().Code());
892041bbff9SAndrew Lindesay	return _LocalDataPath(leaf, path);
893f85e0300SAndrew Lindesay}
894f85e0300SAndrew Lindesay
895f85e0300SAndrew Lindesay
896f85e0300SAndrew Lindesay/*! When the system downloads reference data (eg; categories) from the server
897f85e0300SAndrew Lindesay    then the downloaded data is stored and cached at the path defined by this
898f85e0300SAndrew Lindesay    method.
899f85e0300SAndrew Lindesay*/
900f85e0300SAndrew Lindesay
901f85e0300SAndrew Lindesaystatus_t
902f85e0300SAndrew LindesayModel::DumpExportReferenceDataPath(BPath& path) const
903f85e0300SAndrew Lindesay{
904041bbff9SAndrew Lindesay	BString leaf;
905041bbff9SAndrew Lindesay	leaf.SetToFormat("reference-all_%s.json.gz",
906041bbff9SAndrew Lindesay		LanguageModel().PreferredLanguage().Code());
907041bbff9SAndrew Lindesay	return _LocalDataPath(leaf, path);
908f85e0300SAndrew Lindesay}
909f85e0300SAndrew Lindesay
910f85e0300SAndrew Lindesay
911c23b641cSAndrew Lindesaystatus_t
9123094fef3SAndrew LindesayModel::IconStoragePath(BPath& path) const
9133094fef3SAndrew Lindesay{
9143094fef3SAndrew Lindesay	BPath iconStoragePath;
915041bbff9SAndrew Lindesay	status_t result = B_OK;
916041bbff9SAndrew Lindesay
917041bbff9SAndrew Lindesay	if (result == B_OK)
918041bbff9SAndrew Lindesay		result = find_directory(B_USER_CACHE_DIRECTORY, &iconStoragePath);
9193094fef3SAndrew Lindesay
920041bbff9SAndrew Lindesay	if (result == B_OK)
921041bbff9SAndrew Lindesay		result = iconStoragePath.Append("HaikuDepot");
922041bbff9SAndrew Lindesay
923041bbff9SAndrew Lindesay	if (result == B_OK)
924041bbff9SAndrew Lindesay		result = iconStoragePath.Append("__allicons");
925041bbff9SAndrew Lindesay
926041bbff9SAndrew Lindesay	if (result == B_OK)
927041bbff9SAndrew Lindesay		result = create_directory(iconStoragePath.Path(), 0777);
928041bbff9SAndrew Lindesay
929041bbff9SAndrew Lindesay	if (result == B_OK)
9303094fef3SAndrew Lindesay		path.SetTo(iconStoragePath.Path());
931041bbff9SAndrew Lindesay	else {
932041bbff9SAndrew Lindesay		path.Unset();
933041bbff9SAndrew Lindesay		fprintf(stdout, "unable to find the user cache directory for "
934041bbff9SAndrew Lindesay			"icons; %s\n", strerror(result));
9353094fef3SAndrew Lindesay	}
9363094fef3SAndrew Lindesay
937041bbff9SAndrew Lindesay	return result;
9383094fef3SAndrew Lindesay}
9393094fef3SAndrew Lindesay
9403094fef3SAndrew Lindesay
9413094fef3SAndrew Lindesaystatus_t
9423094fef3SAndrew LindesayModel::DumpExportPkgDataPath(BPath& path,
943c23b641cSAndrew Lindesay	const BString& repositorySourceCode) const
944c23b641cSAndrew Lindesay{
945041bbff9SAndrew Lindesay	BString leaf;
946041bbff9SAndrew Lindesay	leaf.SetToFormat("pkg-all-%s-%s.json.gz", repositorySourceCode.String(),
947f85e0300SAndrew Lindesay		LanguageModel().PreferredLanguage().Code());
948041bbff9SAndrew Lindesay	return _LocalDataPath(leaf, path);
949c23b641cSAndrew Lindesay}
950c23b641cSAndrew Lindesay
951c23b641cSAndrew Lindesay
95212107be2SStephan Aßmusvoid
95312107be2SStephan AßmusModel::_UpdateIsFeaturedFilter()
95412107be2SStephan Aßmus{
95512107be2SStephan Aßmus	if (fShowFeaturedPackages && SearchTerms().IsEmpty())
95612107be2SStephan Aßmus		fIsFeaturedFilter = PackageFilterRef(new IsFeaturedFilter(), true);
95787084745SMichael Lotz	else
95812107be2SStephan Aßmus		fIsFeaturedFilter = PackageFilterRef(new AnyFilter(), true);
95912107be2SStephan Aßmus}
96012107be2SStephan Aßmus
96112107be2SStephan Aßmus
96287038da7SStephan Aßmusvoid
96387038da7SStephan AßmusModel::_PopulatePackageScreenshot(const PackageInfoRef& package,
96487038da7SStephan Aßmus	const ScreenshotInfo& info, int32 scaledWidth, bool fromCacheOnly)
96587038da7SStephan Aßmus{
96687038da7SStephan Aßmus	// See if there is a cached screenshot
96787038da7SStephan Aßmus	BFile screenshotFile;
96887038da7SStephan Aßmus	BPath screenshotCachePath;
96987038da7SStephan Aßmus	bool fileExists = false;
97087038da7SStephan Aßmus	BString screenshotName(