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
27f0665db4SAndrew Lindesay#include "Logger.h"
2889b16bb4SAndrew Lindesay#include "LocaleUtils.h"
29f0665db4SAndrew Lindesay#include "StorageUtils.h"
303b17d8ddSAndrew Lindesay#include "RepositoryUrlUtils.h"
31f0665db4SAndrew Lindesay
32f75ec10dSStephan Aßmus
33f75ec10dSStephan Aßmus#undef B_TRANSLATION_CONTEXT
34f75ec10dSStephan Aßmus#define B_TRANSLATION_CONTEXT "Model"
35f75ec10dSStephan Aßmus
366d367ba4SStephan Aßmus
3789ec84a4SStephan Aßmusstatic const char* kHaikuDepotKeyring = "HaikuDepot";
3889ec84a4SStephan Aßmus
3989ec84a4SStephan Aßmus
408f03a0f9SStephan AßmusPackageFilter::~PackageFilter()
418f03a0f9SStephan Aßmus{
428f03a0f9SStephan Aßmus}
434abd2b71SStephan Aßmus
444abd2b71SStephan Aßmus
458f03a0f9SStephan AßmusModelListener::~ModelListener()
464abd2b71SStephan Aßmus{
474abd2b71SStephan Aßmus}
484abd2b71SStephan Aßmus
494abd2b71SStephan Aßmus
508f03a0f9SStephan Aßmus// #pragma mark - PackageFilters
518f03a0f9SStephan Aßmus
528f03a0f9SStephan Aßmus
534abd2b71SStephan Aßmusclass AnyFilter : public PackageFilter {
544abd2b71SStephan Aßmuspublic:
55779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
564abd2b71SStephan Aßmus	{
574abd2b71SStephan Aßmus		return true;
584abd2b71SStephan Aßmus	}
594abd2b71SStephan Aßmus};
604abd2b71SStephan Aßmus
614abd2b71SStephan Aßmus
624abd2b71SStephan Aßmusclass DepotFilter : public PackageFilter {
634abd2b71SStephan Aßmuspublic:
644abd2b71SStephan Aßmus	DepotFilter(const DepotInfo& depot)
654abd2b71SStephan Aßmus		:
664abd2b71SStephan Aßmus		fDepot(depot)
674abd2b71SStephan Aßmus	{
684abd2b71SStephan Aßmus	}
691f67148fSRene Gollent
70779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
714abd2b71SStephan Aßmus	{
724abd2b71SStephan Aßmus		// TODO: Maybe a PackageInfo ought to know the Depot it came from?
734abd2b71SStephan Aßmus		// But right now the same package could theoretically be provided
744abd2b71SStephan Aßmus		// from different depots and the filter would work correctly.
754abd2b71SStephan Aßmus		// Also the PackageList could actually contain references to packages
764abd2b71SStephan Aßmus		// instead of the packages as objects. The equal operator is quite
774abd2b71SStephan Aßmus		// expensive as is.
78634c2c68SAxel Dörfler		return fDepot.Packages().Contains(package);
794abd2b71SStephan Aßmus	}
801f67148fSRene Gollent
810c169aceSStephan Aßmus	const BString& Depot() const
820c169aceSStephan Aßmus	{
830c169aceSStephan Aßmus		return fDepot.Name();
840c169aceSStephan Aßmus	}
850c169aceSStephan Aßmus
864abd2b71SStephan Aßmusprivate:
874abd2b71SStephan Aßmus	DepotInfo	fDepot;
884abd2b71SStephan Aßmus};
894abd2b71SStephan Aßmus
904abd2b71SStephan Aßmus
9142479955SStephan Aßmusclass CategoryFilter : public PackageFilter {
9242479955SStephan Aßmuspublic:
9342479955SStephan Aßmus	CategoryFilter(const BString& category)
9442479955SStephan Aßmus		:
9542479955SStephan Aßmus		fCategory(category)
9642479955SStephan Aßmus	{
9742479955SStephan Aßmus	}
981f67148fSRene Gollent
99779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
10042479955SStephan Aßmus	{
101779d8213SStephan Aßmus		if (package.Get() == NULL)
102779d8213SStephan Aßmus			return false;
103634c2c68SAxel Dörfler
104779d8213SStephan Aßmus		const CategoryList& categories = package->Categories();
10542479955SStephan Aßmus		for (int i = categories.CountItems() - 1; i >= 0; i--) {
10642479955SStephan Aßmus			const CategoryRef& category = categories.ItemAtFast(i);
10742479955SStephan Aßmus			if (category.Get() == NULL)
10842479955SStephan Aßmus				continue;
10989b16bb4SAndrew Lindesay			if (category->Code() == fCategory)
11042479955SStephan Aßmus				return true;
11142479955SStephan Aßmus		}
11242479955SStephan Aßmus		return false;
11342479955SStephan Aßmus	}
11442479955SStephan Aßmus
1150c169aceSStephan Aßmus	const BString& Category() const
1160c169aceSStephan Aßmus	{
1170c169aceSStephan Aßmus		return fCategory;
1180c169aceSStephan Aßmus	}
1190c169aceSStephan Aßmus
12042479955SStephan Aßmusprivate:
12142479955SStephan Aßmus	BString		fCategory;
12242479955SStephan Aßmus};
12342479955SStephan Aßmus
12442479955SStephan Aßmus
1253a6ccc85SStephan Aßmusclass ContainedInFilter : public PackageFilter {
1263a6ccc85SStephan Aßmuspublic:
1273a6ccc85SStephan Aßmus	ContainedInFilter(const PackageList& packageList)
1283a6ccc85SStephan Aßmus		:
1293a6ccc85SStephan Aßmus		fPackageList(packageList)
1303a6ccc85SStephan Aßmus	{
1313a6ccc85SStephan Aßmus	}
1321f67148fSRene Gollent
133779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
1343a6ccc85SStephan Aßmus	{
1353a6ccc85SStephan Aßmus		return fPackageList.Contains(package);
1363a6ccc85SStephan Aßmus	}
1373a6ccc85SStephan Aßmus
1383a6ccc85SStephan Aßmusprivate:
1393a6ccc85SStephan Aßmus	const PackageList&	fPackageList;
1403a6ccc85SStephan Aßmus};
1413a6ccc85SStephan Aßmus
1423a6ccc85SStephan Aßmus
1433a6ccc85SStephan Aßmusclass ContainedInEitherFilter : public PackageFilter {
1443a6ccc85SStephan Aßmuspublic:
1453a6ccc85SStephan Aßmus	ContainedInEitherFilter(const PackageList& packageListA,
1463a6ccc85SStephan Aßmus		const PackageList& packageListB)
1473a6ccc85SStephan Aßmus		:
1483a6ccc85SStephan Aßmus		fPackageListA(packageListA),
1493a6ccc85SStephan Aßmus		fPackageListB(packageListB)
1503a6ccc85SStephan Aßmus	{
1513a6ccc85SStephan Aßmus	}
1521f67148fSRene Gollent
153779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
1543a6ccc85SStephan Aßmus	{
1553a6ccc85SStephan Aßmus		return fPackageListA.Contains(package)
1563a6ccc85SStephan Aßmus			|| fPackageListB.Contains(package);
1573a6ccc85SStephan Aßmus	}
1583a6ccc85SStephan Aßmus
1593a6ccc85SStephan Aßmusprivate:
1603a6ccc85SStephan Aßmus	const PackageList&	fPackageListA;
1613a6ccc85SStephan Aßmus	const PackageList&	fPackageListB;
1623a6ccc85SStephan Aßmus};
1633a6ccc85SStephan Aßmus
1643a6ccc85SStephan Aßmus
165634c2c68SAxel Dörflerclass NotContainedInFilter : public PackageFilter {
166634c2c68SAxel Dörflerpublic:
167634c2c68SAxel Dörfler	NotContainedInFilter(const PackageList* packageList, ...)
168634c2c68SAxel Dörfler	{
169634c2c68SAxel Dörfler		va_list args;
170634c2c68SAxel Dörfler		va_start(args, packageList);
171634c2c68SAxel Dörfler		while (true) {
172634c2c68SAxel Dörfler			const PackageList* packageList = va_arg(args, const PackageList*);
173634c2c68SAxel Dörfler			if (packageList == NULL)
174634c2c68SAxel Dörfler				break;
175634c2c68SAxel Dörfler			fPackageLists.Add(packageList);
176634c2c68SAxel Dörfler		}
177634c2c68SAxel Dörfler		va_end(args);
178634c2c68SAxel Dörfler	}
179634c2c68SAxel Dörfler
180634c2c68SAxel Dörfler	virtual bool AcceptsPackage(const PackageInfoRef& package) const
181634c2c68SAxel Dörfler	{
182be298474SAndrew Lindesay		if (package.Get() == NULL)
183634c2c68SAxel Dörfler			return false;
184a10ad54cSJonathan Schleifer
18572992391SStephan Aßmus		printf("TEST %s\n", package->Name().String());
186a10ad54cSJonathan Schleifer
187634c2c68SAxel Dörfler		for (int32 i = 0; i < fPackageLists.CountItems(); i++) {
188634c2c68SAxel Dörfler			if (fPackageLists.ItemAtFast(i)->Contains(package)) {
189998f4043SJonathan Schleifer				printf("  contained in %" B_PRId32 "\n", i);
190634c2c68SAxel Dörfler				return false;
191634c2c68SAxel Dörfler			}
192634c2c68SAxel Dörfler		}
193634c2c68SAxel Dörfler		return true;
194634c2c68SAxel Dörfler	}
195634c2c68SAxel Dörfler
196634c2c68SAxel Dörflerprivate:
197634c2c68SAxel Dörfler	List<const PackageList*, true>	fPackageLists;
198634c2c68SAxel Dörfler};
199634c2c68SAxel Dörfler
200634c2c68SAxel Dörfler
201634c2c68SAxel Dörflerclass StateFilter : public PackageFilter {
202634c2c68SAxel Dörflerpublic:
203634c2c68SAxel Dörfler	StateFilter(PackageState state)
204634c2c68SAxel Dörfler		:
205634c2c68SAxel Dörfler		fState(state)
206634c2c68SAxel Dörfler	{
207634c2c68SAxel Dörfler	}
208634c2c68SAxel Dörfler
209634c2c68SAxel Dörfler	virtual bool AcceptsPackage(const PackageInfoRef& package) const
210634c2c68SAxel Dörfler	{
211634c2c68SAxel Dörfler		return package->State() == NONE;
212634c2c68SAxel Dörfler	}
213634c2c68SAxel Dörfler
214634c2c68SAxel Dörflerprivate:
215634c2c68SAxel Dörfler	PackageState	fState;
216634c2c68SAxel Dörfler};
217634c2c68SAxel Dörfler
218634c2c68SAxel Dörfler
21909d44cecSStephan Aßmusclass SearchTermsFilter : public PackageFilter {
22009d44cecSStephan Aßmuspublic:
22109d44cecSStephan Aßmus	SearchTermsFilter(const BString& searchTerms)
22209d44cecSStephan Aßmus	{
22309d44cecSStephan Aßmus		// Separate the string into terms at spaces
22409d44cecSStephan Aßmus		int32 index = 0;
22509d44cecSStephan Aßmus		while (index < searchTerms.Length()) {
22609d44cecSStephan Aßmus			int32 nextSpace = searchTerms.FindFirst(" ", index);
22709d44cecSStephan Aßmus			if (nextSpace < 0)
22809d44cecSStephan Aßmus				nextSpace = searchTerms.Length();
22909d44cecSStephan Aßmus			if (nextSpace > index) {
23009d44cecSStephan Aßmus				BString term;
23109d44cecSStephan Aßmus				searchTerms.CopyInto(term, index, nextSpace - index);
23209d44cecSStephan Aßmus				term.ToLower();
23309d44cecSStephan Aßmus				fSearchTerms.Add(term);
23409d44cecSStephan Aßmus			}
23509d44cecSStephan Aßmus			index = nextSpace + 1;
23609d44cecSStephan Aßmus		}
23709d44cecSStephan Aßmus	}
2381f67148fSRene Gollent
239779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
24009d44cecSStephan Aßmus	{
241779d8213SStephan Aßmus		if (package.Get() == NULL)
242779d8213SStephan Aßmus			return false;
24309d44cecSStephan Aßmus		// Every search term must be found in one of the package texts
24409d44cecSStephan Aßmus		for (int32 i = fSearchTerms.CountItems() - 1; i >= 0; i--) {
24509d44cecSStephan Aßmus			const BString& term = fSearchTerms.ItemAtFast(i);
24672992391SStephan Aßmus			if (!_TextContains(package->Name(), term)
24772992391SStephan Aßmus				&& !_TextContains(package->Title(), term)
248779d8213SStephan Aßmus				&& !_TextContains(package->Publisher().Name(), term)
249779d8213SStephan Aßmus				&& !_TextContains(package->ShortDescription(), term)
250779d8213SStephan Aßmus				&& !_TextContains(package->FullDescription(), term)) {
25109d44cecSStephan Aßmus				return false;
25209d44cecSStephan Aßmus			}
25309d44cecSStephan Aßmus		}
25409d44cecSStephan Aßmus		return true;
25509d44cecSStephan Aßmus	}
25609d44cecSStephan Aßmus
2570c169aceSStephan Aßmus	BString SearchTerms() const
2580c169aceSStephan Aßmus	{
2590c169aceSStephan Aßmus		BString searchTerms;
2600c169aceSStephan Aßmus		for (int32 i = 0; i < fSearchTerms.CountItems(); i++) {
2610c169aceSStephan Aßmus			const BString& term = fSearchTerms.ItemAtFast(i);
2620c169aceSStephan Aßmus			if (term.IsEmpty())
2630c169aceSStephan Aßmus				continue;
2640c169aceSStephan Aßmus			if (!searchTerms.IsEmpty())
2650c169aceSStephan Aßmus				searchTerms.Append(" ");
2660c169aceSStephan Aßmus			searchTerms.Append(term);
2670c169aceSStephan Aßmus		}
2680c169aceSStephan Aßmus		return searchTerms;
2690c169aceSStephan Aßmus	}
2700c169aceSStephan Aßmus
27109d44cecSStephan Aßmusprivate:
27209d44cecSStephan Aßmus	bool _TextContains(BString text, const BString& string) const
27309d44cecSStephan Aßmus	{
27409d44cecSStephan Aßmus		text.ToLower();
27509d44cecSStephan Aßmus		int32 index = text.FindFirst(string);
27609d44cecSStephan Aßmus		return index >= 0;
27709d44cecSStephan Aßmus	}
27809d44cecSStephan Aßmus
27909d44cecSStephan Aßmusprivate:
2800c169aceSStephan Aßmus	StringList fSearchTerms;
28109d44cecSStephan Aßmus};
28209d44cecSStephan Aßmus
28309d44cecSStephan Aßmus
28412107be2SStephan Aßmusclass IsFeaturedFilter : public PackageFilter {
28512107be2SStephan Aßmuspublic:
28612107be2SStephan Aßmus	IsFeaturedFilter()
28712107be2SStephan Aßmus	{
28812107be2SStephan Aßmus	}
28912107be2SStephan Aßmus
29012107be2SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
29112107be2SStephan Aßmus	{
29212107be2SStephan Aßmus		return package.Get() != NULL && package->IsProminent();
29312107be2SStephan Aßmus	}
29412107be2SStephan Aßmus};
29512107be2SStephan Aßmus
29612107be2SStephan Aßmus
297870554acSStephan Aßmusstatic inline bool
298870554acSStephan Aßmusis_source_package(const PackageInfoRef& package)
299870554acSStephan Aßmus{
30072992391SStephan Aßmus	const BString& packageName = package->Name();
301870554acSStephan Aßmus	return packageName.EndsWith("_source");
302870554acSStephan Aßmus}
303870554acSStephan Aßmus
304870554acSStephan Aßmus
305870554acSStephan Aßmusstatic inline bool
306870554acSStephan Aßmusis_develop_package(const PackageInfoRef& package)
307870554acSStephan Aßmus{
30872992391SStephan Aßmus	const BString& packageName = package->Name();
30974e41611SJulian Harnath	return packageName.EndsWith("_devel")
31074e41611SJulian Harnath		|| packageName.EndsWith("_debuginfo");
311870554acSStephan Aßmus}
312870554acSStephan Aßmus
313870554acSStephan Aßmus
3144abd2b71SStephan Aßmus// #pragma mark - Model
3154abd2b71SStephan Aßmus
3164abd2b71SStephan Aßmus
31789b16bb4SAndrew Lindesaystatic int32
31889b16bb4SAndrew LindesayPackageCategoryCompareFn(const CategoryRef& c1, const CategoryRef& c2)
31989b16bb4SAndrew Lindesay{
32089b16bb4SAndrew Lindesay	BCollator* collator = LocaleUtils::GetSharedCollator();
32189b16bb4SAndrew Lindesay	int32 result = collator->Compare(c1->Name().String(),
32289b16bb4SAndrew Lindesay		c2->Name().String());
32389b16bb4SAndrew Lindesay	if (result == 0)
32489b16bb4SAndrew Lindesay		result = c1->Code().Compare(c2->Code());
32589b16bb4SAndrew Lindesay	return result;
32689b16bb4SAndrew Lindesay}
32789b16bb4SAndrew Lindesay
32889b16bb4SAndrew Lindesay
3296d367ba4SStephan AßmusModel::Model()
3306d367ba4SStephan Aßmus	:
331f75ec10dSStephan Aßmus	fDepots(),
33289b16bb4SAndrew Lindesay	fCategories(&PackageCategoryCompareFn, NULL),
3334abd2b71SStephan Aßmus	fCategoryFilter(PackageFilterRef(new AnyFilter(), true)),
33409d44cecSStephan Aßmus	fDepotFilter(""),
335870554acSStephan Aßmus	fSearchTermsFilter(PackageFilterRef(new AnyFilter(), true)),
33612107be2SStephan Aßmus	fIsFeaturedFilter(),
3374fade61fSStephan Aßmus	fShowFeaturedPackages(true),
338ab172803SStephan Aßmus	fShowAvailablePackages(true),
3394fade61fSStephan Aßmus	fShowInstalledPackages(true),
340870554acSStephan Aßmus	fShowSourcePackages(false),
3413094fef3SAndrew Lindesay	fShowDevelopPackages(false)
3426d367ba4SStephan Aßmus{
34312107be2SStephan Aßmus	_UpdateIsFeaturedFilter();
344f85e0300SAndrew Lindesay}
345d07ca069SStephan Aßmus
3464ff3d17aSStephan Aßmus
347f85e0300SAndrew LindesayModel::~Model()
348f85e0300SAndrew Lindesay{
3496d367ba4SStephan Aßmus}
3506d367ba4SStephan Aßmus
3516d367ba4SStephan Aßmus
352f85e0300SAndrew LindesayLanguageModel&
353f85e0300SAndrew LindesayModel::Language()
3545bd5f161SStephan Aßmus{
355f85e0300SAndrew Lindesay	return fLanguageModel;
3565bd5f161SStephan Aßmus}
3575bd5f161SStephan Aßmus
3585bd5f161SStephan Aßmus
3598f03a0f9SStephan Aßmusbool
3608f03a0f9SStephan AßmusModel::AddListener(const ModelListenerRef& listener)
3618f03a0f9SStephan Aßmus{
3628f03a0f9SStephan Aßmus	return fListeners.Add(listener);
3638f03a0f9SStephan Aßmus}
3648f03a0f9SStephan Aßmus
3658f03a0f9SStephan Aßmus
366706edd80SStephan AßmusPackageList
3676d367ba4SStephan AßmusModel::CreatePackageList() const
3686d367ba4SStephan Aßmus{
369ab172803SStephan Aßmus	// Iterate all packages from all depots.
370ab172803SStephan Aßmus	// If configured, restrict depot, filter by search terms, status, name ...
371706edd80SStephan Aßmus	PackageList resultList;
3726d367ba4SStephan Aßmus
3736d367ba4SStephan Aßmus	for (int32 i = 0; i < fDepots.CountItems(); i++) {
37409d44cecSStephan Aßmus		const DepotInfo& depot = fDepots.ItemAtFast(i);
3751f67148fSRene Gollent
37609d44cecSStephan Aßmus		if (fDepotFilter.Length() > 0 && fDepotFilter != depot.Name())
37709d44cecSStephan Aßmus			continue;
3781f67148fSRene Gollent
37909d44cecSStephan Aßmus		const PackageList& packages = depot.Packages();
3806d367ba4SStephan Aßmus
381706edd80SStephan Aßmus		for (int32 j = 0; j < packages.CountItems(); j++) {
382779d8213SStephan Aßmus			const PackageInfoRef& package = packages.ItemAtFast(j);
38339f49f70SJulian Harnath			if (MatchesFilter(package))
3844abd2b71SStephan Aßmus				resultList.Add(package);
3856d367ba4SStephan Aßmus		}
3866d367ba4SStephan Aßmus	}
3876d367ba4SStephan Aßmus
3886d367ba4SStephan Aßmus	return resultList;
3896d367ba4SStephan Aßmus}
3906d367ba4SStephan Aßmus
3916d367ba4SStephan Aßmus
39239f49f70SJulian Harnathbool
39339f49f70SJulian HarnathModel::MatchesFilter(const PackageInfoRef& package) const
39439f49f70SJulian Harnath{
39539f49f70SJulian Harnath	return fCategoryFilter->AcceptsPackage(package)
39639f49f70SJulian Harnath			&& fSearchTermsFilter->AcceptsPackage(package)
39739f49f70SJulian Harnath			&& fIsFeaturedFilter->AcceptsPackage(package)
39839f49f70SJulian Harnath			&& (fShowAvailablePackages || package->State() != NONE)
39939f49f70SJulian Harnath			&& (fShowInstalledPackages || package->State() != ACTIVATED)
40039f49f70SJulian Harnath			&& (fShowSourcePackages || !is_source_package(package))
40139f49f70SJulian Harnath			&& (fShowDevelopPackages || !is_develop_package(package));
40239f49f70SJulian Harnath}
40339f49f70SJulian Harnath
40439f49f70SJulian Harnath
4056d367ba4SStephan Aßmusbool
4066d367ba4SStephan AßmusModel::AddDepot(const DepotInfo& depot)
4076d367ba4SStephan Aßmus{
4086d367ba4SStephan Aßmus	return fDepots.Add(depot);
4096d367ba4SStephan Aßmus}
4106d367ba4SStephan Aßmus
41142479955SStephan Aßmus
4127b2c59b9SStephan Aßmusbool
4137b2c59b9SStephan AßmusModel::HasDepot(const BString& name) const
414ebb1a01cSAndrew Lindesay{
415ebb1a01cSAndrew Lindesay	return NULL != DepotForName(name);
416ebb1a01cSAndrew Lindesay}
417ebb1a01cSAndrew Lindesay
418ebb1a01cSAndrew Lindesay
419ebb1a01cSAndrew Lindesayconst DepotInfo*
420ebb1a01cSAndrew LindesayModel::DepotForName(const BString& name) const
4217b2c59b9SStephan Aßmus{
4227b2c59b9SStephan Aßmus	for (int32 i = fDepots.CountItems() - 1; i >= 0; i--) {
4237b2c59b9SStephan Aßmus		if (fDepots.ItemAtFast(i).Name() == name)
424ebb1a01cSAndrew Lindesay			return &fDepots.ItemAtFast(i);
4257b2c59b9SStephan Aßmus	}
426ebb1a01cSAndrew Lindesay	return NULL;
4277b2c59b9SStephan Aßmus}
4287b2c59b9SStephan Aßmus
4297b2c59b9SStephan Aßmus
4307b2c59b9SStephan Aßmusbool
4317b2c59b9SStephan AßmusModel::SyncDepot(const DepotInfo& depot)
4327b2c59b9SStephan Aßmus{
4337b2c59b9SStephan Aßmus	for (int32 i = fDepots.CountItems() - 1; i >= 0; i--) {
4347b2c59b9SStephan Aßmus		const DepotInfo& existingDepot = fDepots.ItemAtFast(i);
4357b2c59b9SStephan Aßmus		if (existingDepot.Name() == depot.Name()) {
4367b2c59b9SStephan Aßmus			DepotInfo mergedDepot(existingDepot);
4377b2c59b9SStephan Aßmus			mergedDepot.SyncPackages(depot.Packages());
4387b2c59b9SStephan Aßmus			fDepots.Replace(i, mergedDepot);
4397b2c59b9SStephan Aßmus			return true;
4407b2c59b9SStephan Aßmus		}
4417b2c59b9SStephan Aßmus	}
4427b2c59b9SStephan Aßmus	return false;
4437b2c59b9SStephan Aßmus}
4447b2c59b9SStephan Aßmus
4457b2c59b9SStephan Aßmus
4462c287f5bSRene Gollentvoid
4472c287f5bSRene GollentModel::Clear()
4482c287f5bSRene Gollent{
4492c287f5bSRene Gollent	fDepots.Clear();
4502c287f5bSRene Gollent}
4512c287f5bSRene Gollent
4522c287f5bSRene Gollent
453706edd80SStephan Aßmusvoid
454779d8213SStephan AßmusModel::SetPackageState(const PackageInfoRef& package, PackageState state)
455706edd80SStephan Aßmus{
456706edd80SStephan Aßmus	switch (state) {
457706edd80SStephan Aßmus		default:
458706edd80SStephan Aßmus		case NONE:
459706edd80SStephan Aßmus			fInstalledPackages.Remove(package);
460706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
461706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
462706edd80SStephan Aßmus			break;
463706edd80SStephan Aßmus		case INSTALLED:
464706edd80SStephan Aßmus			if (!fInstalledPackages.Contains(package))
465706edd80SStephan Aßmus				fInstalledPackages.Add(package);
466706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
467706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
468706edd80SStephan Aßmus			break;
469706edd80SStephan Aßmus		case ACTIVATED:
470706edd80SStephan Aßmus			if (!fInstalledPackages.Contains(package))
471706edd80SStephan Aßmus				fInstalledPackages.Add(package);
472706edd80SStephan Aßmus			if (!fActivatedPackages.Contains(package))
473706edd80SStephan Aßmus				fActivatedPackages.Add(package);
474706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
475706edd80SStephan Aßmus			break;
476706edd80SStephan Aßmus		case UNINSTALLED:
477706edd80SStephan Aßmus			fInstalledPackages.Remove(package);
478706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
479ab172803SStephan Aßmus			if (!fUninstalledPackages.Contains(package))
480ab172803SStephan Aßmus				fUninstalledPackages.Add(package);
481706edd80SStephan Aßmus			break;
482706edd80SStephan Aßmus	}
483b4c8d2ffSRene Gollent
484b4c8d2ffSRene Gollent	package->SetState(state);
485706edd80SStephan Aßmus}
486706edd80SStephan Aßmus
487706edd80SStephan Aßmus
48809d44cecSStephan Aßmus// #pragma mark - filters
48909d44cecSStephan Aßmus
49009d44cecSStephan Aßmus
49142479955SStephan Aßmusvoid
49242479955SStephan AßmusModel::SetCategory(const BString& category)
49342479955SStephan Aßmus{
4943a6ccc85SStephan Aßmus	PackageFilter* filter;
4951f67148fSRene Gollent
49642479955SStephan Aßmus	if (category.Length() == 0)
4973a6ccc85SStephan Aßmus		filter = new AnyFilter();
49842479955SStephan Aßmus	else
4993a6ccc85SStephan Aßmus		filter = new CategoryFilter(category);
5003a6ccc85SStephan Aßmus
5013a6ccc85SStephan Aßmus	fCategoryFilter.SetTo(filter, true);
50242479955SStephan Aßmus}
50342479955SStephan Aßmus
50409d44cecSStephan Aßmus
5050c169aceSStephan AßmusBString
5060c169aceSStephan AßmusModel::Category() const
5070c169aceSStephan Aßmus{
5080c169aceSStephan Aßmus	CategoryFilter* filter
5090c169aceSStephan Aßmus		= dynamic_cast<CategoryFilter*>(fCategoryFilter.Get());
5100c169aceSStephan Aßmus	if (filter == NULL)
5110c169aceSStephan Aßmus		return "";
5120c169aceSStephan Aßmus	return filter->Category();
5130c169aceSStephan Aßmus}
5140c169aceSStephan Aßmus
5150c169aceSStephan Aßmus
51609d44cecSStephan Aßmusvoid
51709d44cecSStephan AßmusModel::SetDepot(const BString& depot)
51809d44cecSStephan Aßmus{
51909d44cecSStephan Aßmus	fDepotFilter = depot;
52009d44cecSStephan Aßmus}
52109d44cecSStephan Aßmus
52209d44cecSStephan Aßmus
5230c169aceSStephan AßmusBString
5240c169aceSStephan AßmusModel::Depot() const
5250c169aceSStephan Aßmus{
5260c169aceSStephan Aßmus	return fDepotFilter;
5270c169aceSStephan Aßmus}
5280c169aceSStephan Aßmus
5290c169aceSStephan Aßmus
53009d44cecSStephan Aßmusvoid
53109d44cecSStephan AßmusModel::SetSearchTerms(const BString& searchTerms)
53209d44cecSStephan Aßmus{
53309d44cecSStephan Aßmus	PackageFilter* filter;
5341f67148fSRene Gollent
53509d44cecSStephan Aßmus	if (searchTerms.Length() == 0)
53609d44cecSStephan Aßmus		filter = new AnyFilter();
53709d44cecSStephan Aßmus	else
53809d44cecSStephan Aßmus		filter = new SearchTermsFilter(searchTerms);
53909d44cecSStephan Aßmus
54009d44cecSStephan Aßmus	fSearchTermsFilter.SetTo(filter, true);
54112107be2SStephan Aßmus	_UpdateIsFeaturedFilter();
54209d44cecSStephan Aßmus}
54309d44cecSStephan Aßmus
5447eec822dSStephan Aßmus
5450c169aceSStephan AßmusBString
5460c169aceSStephan AßmusModel::SearchTerms() const
5470c169aceSStephan Aßmus{
5480c169aceSStephan Aßmus	SearchTermsFilter* filter
5490c169aceSStephan Aßmus		= dynamic_cast<SearchTermsFilter*>(fSearchTermsFilter.Get());
5500c169aceSStephan Aßmus	if (filter == NULL)
5510c169aceSStephan Aßmus		return "";
5520c169aceSStephan Aßmus	return filter->SearchTerms();
5530c169aceSStephan Aßmus}
5540c169aceSStephan Aßmus
5550c169aceSStephan Aßmus
5564fade61fSStephan Aßmusvoid
5574fade61fSStephan AßmusModel::SetShowFeaturedPackages(bool show)
5584fade61fSStephan Aßmus{
5594fade61fSStephan Aßmus	fShowFeaturedPackages = show;
56012107be2SStephan Aßmus	_UpdateIsFeaturedFilter();
5614fade61fSStephan Aßmus}
5624fade61fSStephan Aßmus
5634fade61fSStephan Aßmus
564ab172803SStephan Aßmusvoid
565ab172803SStephan AßmusModel::SetShowAvailablePackages(bool show)
566ab172803SStephan Aßmus{
567ab172803SStephan Aßmus	fShowAvailablePackages = show;
568ab172803SStephan Aßmus}
569ab172803SStephan Aßmus
570ab172803SStephan Aßmus
571ab172803SStephan Aßmusvoid
572ab172803SStephan AßmusModel::SetShowInstalledPackages(bool show)
573ab172803SStephan Aßmus{
574ab172803SStephan Aßmus	fShowInstalledPackages = show;
575ab172803SStephan Aßmus}
576ab172803SStephan Aßmus
577ab172803SStephan Aßmus
578870554acSStephan Aßmusvoid
579870554acSStephan AßmusModel::SetShowSourcePackages(bool show)
580870554acSStephan Aßmus{
581870554acSStephan Aßmus	fShowSourcePackages = show;
582870554acSStephan Aßmus}
583870554acSStephan Aßmus
584870554acSStephan Aßmus
585870554acSStephan Aßmusvoid
586870554acSStephan AßmusModel::SetShowDevelopPackages(bool show)
587870554acSStephan Aßmus{
588870554acSStephan Aßmus	fShowDevelopPackages = show;
589870554acSStephan Aßmus}
590870554acSStephan Aßmus
591870554acSStephan Aßmus
59219c15fecSAndrew Lindesay// #pragma mark - information retrieval
5937eec822dSStephan Aßmus
5947eec822dSStephan Aßmus
595a9edb9bfSAndrew Lindesay/*! Initially only superficial data is loaded from the server into the data
596a9edb9bfSAndrew Lindesay    model of the packages.  When the package is viewed, additional data needs
597a9edb9bfSAndrew Lindesay    to be populated including ratings.  This method takes care of that.
598a9edb9bfSAndrew Lindesay*/
599a9edb9bfSAndrew Lindesay
6007eec822dSStephan Aßmusvoid
601a60a23f8SStephan AßmusModel::PopulatePackage(const PackageInfoRef& package, uint32 flags)
6027eec822dSStephan Aßmus{
6037eec822dSStephan Aßmus	// TODO: There should probably also be a way to "unpopulate" the
6047eec822dSStephan Aßmus	// package information. Maybe a cache of populated packages, so that
6057eec822dSStephan Aßmus	// packages loose their extra information after a certain amount of
6067eec822dSStephan Aßmus	// time when they have not been accessed/displayed in the UI. Otherwise
6077eec822dSStephan Aßmus	// HaikuDepot will consume more and more resources in the packages.
6087eec822dSStephan Aßmus	// Especially screen-shots will be a problem eventually.
60948045f5aSStephan Aßmus	{
61048045f5aSStephan Aßmus		BAutolock locker(&fLock);
61189e4ba17SStephan Aßmus		bool alreadyPopulated = fPopulatedPackages.Contains(package);
61289e4ba17SStephan Aßmus		if ((flags & POPULATE_FORCE) == 0 && alreadyPopulated)
61348045f5aSStephan Aßmus			return;
61489e4ba17SStephan Aßmus		if (!alreadyPopulated)
61589e4ba17SStephan Aßmus			fPopulatedPackages.Add(package);
61648045f5aSStephan Aßmus	}
6177eec822dSStephan Aßmus
61880a272eeSAndrew Lindesay	if ((flags & POPULATE_CHANGELOG) != 0) {
61980a272eeSAndrew Lindesay		_PopulatePackageChangelog(package);
62080a272eeSAndrew Lindesay	}
62180a272eeSAndrew Lindesay
6227ff94fd0SStephan Aßmus	if ((flags & POPULATE_USER_RATINGS) != 0) {
6237ff94fd0SStephan Aßmus		// Retrieve info from web-app
6247ff94fd0SStephan Aßmus		BMessage info;
62548045f5aSStephan Aßmus
62648045f5aSStephan Aßmus		BString packageName;
62787084745SMichael Lotz		BString architecture;
62848045f5aSStephan Aßmus		{
62948045f5aSStephan Aßmus			BAutolock locker(&fLock);
63072992391SStephan Aßmus			packageName = package->Name();
63148045f5aSStephan Aßmus			architecture = package->Architecture();
63248045f5aSStephan Aßmus		}
63387084745SMichael Lotz
63428075ee4SStephan Aßmus		status_t status = fWebAppInterface.RetrieveUserRatings(packageName,
63548045f5aSStephan Aßmus			architecture, 0, 50, info);
6367ff94fd0SStephan Aßmus		if (status == B_OK) {
6377ff94fd0SStephan Aßmus			// Parse message
6387ff94fd0SStephan Aßmus			BMessage result;
6397ff94fd0SStephan Aßmus			BMessage items;
6407ff94fd0SStephan Aßmus			if (info.FindMessage("result", &result) == B_OK
6417ff94fd0SStephan Aßmus				&& result.FindMessage("items", &items) == B_OK) {
64248045f5aSStephan Aßmus
64348045f5aSStephan Aßmus				BAutolock locker(&fLock);
6447eb09bfaSStephan Aßmus				package->ClearUserRatings();
64548045f5aSStephan Aßmus
646a9edb9bfSAndrew Lindesay				int32 index = 0;
6477ff94fd0SStephan Aßmus				while (true) {
6487ff94fd0SStephan Aßmus					BString name;
6497ff94fd0SStephan Aßmus					name << index++;
65087084745SMichael Lotz
6517ff94fd0SStephan Aßmus					BMessage item;
6527ff94fd0SStephan Aßmus					if (items.FindMessage(name, &item) != B_OK)
6537ff94fd0SStephan Aßmus						break;
6547ff94fd0SStephan Aßmus
655a9edb9bfSAndrew Lindesay					BString code;
656a9edb9bfSAndrew Lindesay					if (item.FindString("code", &code) != B_OK) {
657a9edb9bfSAndrew Lindesay						printf("corrupt user rating at index %" B_PRIi32 "\n",
658a9edb9bfSAndrew Lindesay							index);
659a9edb9bfSAndrew Lindesay						continue;
660a9edb9bfSAndrew Lindesay					}
661a9edb9bfSAndrew Lindesay
6627ff94fd0SStephan Aßmus					BString user;
6637ff94fd0SStephan Aßmus					BMessage userInfo;
6647ff94fd0SStephan Aßmus					if (item.FindMessage("user", &userInfo) != B_OK
6657ff94fd0SStephan Aßmus						|| userInfo.FindString("nickname", &user) != B_OK) {
666a9edb9bfSAndrew Lindesay						printf("ignored user rating [%s] without a user "
667a9edb9bfSAndrew Lindesay							"nickname\n", code.String());
6687ff94fd0SStephan Aßmus						continue;
6697ff94fd0SStephan Aßmus					}
6707ff94fd0SStephan Aßmus
67136a9b557SStephan Aßmus					// Extract basic info, all items are optional
6727ff94fd0SStephan Aßmus					BString languageCode;
6737ff94fd0SStephan Aßmus					BString comment;
6747ff94fd0SStephan Aßmus					double rating;
67536a9b557SStephan Aßmus					item.FindString("naturalLanguageCode", &languageCode);
67636a9b557SStephan Aßmus					item.FindString("comment", &comment);
67736a9b557SStephan Aßmus					if (item.FindDouble("rating", &rating) != B_OK)
67836a9b557SStephan Aßmus						rating = -1;
67936a9b557SStephan Aßmus					if (comment.Length() == 0 && rating == -1) {
680a9edb9bfSAndrew Lindesay						printf("rating [%s] has no comment or rating so will be"
681a9edb9bfSAndrew Lindesay							"ignored\n", code.String());
6827ff94fd0SStephan Aßmus						continue;
6837ff94fd0SStephan Aßmus					}
68436a9b557SStephan Aßmus
6857ff94fd0SStephan Aßmus					// For which version of the package was the rating?
6867ff94fd0SStephan Aßmus					BString major = "?";
6877ff94fd0SStephan Aßmus					BString minor = "?";
6887ff94fd0SStephan Aßmus					BString micro = "";
689a9edb9bfSAndrew Lindesay					double revision = -1;
6907ff94fd0SStephan Aßmus					BMessage version;
6917ff94fd0SStephan Aßmus					if (item.FindMessage("pkgVersion", &version) == B_OK) {
6927ff94fd0SStephan Aßmus						version.FindString("major", &major);
6937ff94fd0SStephan Aßmus						version.FindString("minor", &minor);
6947ff94fd0SStephan Aßmus						version.FindString("micro", &micro);
695a9edb9bfSAndrew Lindesay						version.FindDouble("revision", &revision);
6967ff94fd0SStephan Aßmus					}
6977ff94fd0SStephan Aßmus					BString versionString = major;
6987ff94fd0SStephan Aßmus					versionString << ".";
6997ff94fd0SStephan Aßmus					versionString << minor;
7007ff94fd0SStephan Aßmus					if (micro.Length() > 0) {
7017ff94fd0SStephan Aßmus						versionString << ".";
7027ff94fd0SStephan Aßmus						versionString << micro;
7037ff94fd0SStephan Aßmus					}
704a9edb9bfSAndrew Lindesay					if (revision > 0) {
705a9edb9bfSAndrew Lindesay						versionString << "-";
706a9edb9bfSAndrew Lindesay						versionString << (int) revision;
707a9edb9bfSAndrew Lindesay					}
708a9edb9bfSAndrew Lindesay
709a9edb9bfSAndrew Lindesay					BDateTime createTimestamp;
710a9edb9bfSAndrew Lindesay					double createTimestampMillisF;
711a9edb9bfSAndrew Lindesay					if (item.FindDouble("createTimestamp",
712a9edb9bfSAndrew Lindesay						&createTimestampMillisF) == B_OK) {
713a9edb9bfSAndrew Lindesay						double createTimestampSecsF =
714a9edb9bfSAndrew Lindesay							createTimestampMillisF / 1000.0;
715a9edb9bfSAndrew Lindesay						time_t createTimestampSecs =
716a9edb9bfSAndrew Lindesay							(time_t) createTimestampSecsF;
717a9edb9bfSAndrew Lindesay						createTimestamp.SetTime_t(createTimestampSecs);
718a9edb9bfSAndrew Lindesay					}
719a9edb9bfSAndrew Lindesay
7207ff94fd0SStephan Aßmus					// Add the rating to the PackageInfo
721a9edb9bfSAndrew Lindesay					UserRating userRating = UserRating(UserInfo(user), rating,
722a9edb9bfSAndrew Lindesay						comment, languageCode, versionString, 0, 0,
723a9edb9bfSAndrew Lindesay						createTimestamp);
724a9edb9bfSAndrew Lindesay					package->AddUserRating(userRating);
725a9edb9bfSAndrew Lindesay
726a9edb9bfSAndrew Lindesay					if (Logger::IsDebugEnabled()) {
727a9edb9bfSAndrew Lindesay						printf("rating [%s] retrieved from server\n",
728a9edb9bfSAndrew Lindesay							code.String());
729a9edb9bfSAndrew Lindesay					}
730a9edb9bfSAndrew Lindesay				}
731a9edb9bfSAndrew Lindesay
732a9edb9bfSAndrew Lindesay				if (Logger::IsDebugEnabled()) {
733a9edb9bfSAndrew Lindesay					printf("did retrieve %" B_PRIi32 " user ratings for [%s]\n",
734a9edb9bfSAndrew Lindesay						index - 1, packageName.String());
7357ff94fd0SStephan Aßmus				}
73680a272eeSAndrew Lindesay			} else {
73780a272eeSAndrew Lindesay				_MaybeLogJsonRpcError(info, "retrieve user ratings");
7387ff94fd0SStephan Aßmus			}
739a9edb9bfSAndrew Lindesay		} else {
740a9edb9bfSAndrew Lindesay			printf("unable to retrieve user ratings\n");
741a60a23f8SStephan Aßmus		}
7427eec822dSStephan Aßmus	}
74387038da7SStephan Aßmus
74487038da7SStephan Aßmus	if ((flags & POPULATE_SCREEN_SHOTS) != 0) {
74587038da7SStephan Aßmus		ScreenshotInfoList screenshotInfos;
74687038da7SStephan Aßmus		{
74787038da7SStephan Aßmus			BAutolock locker(&fLock);
74887038da7SStephan Aßmus			screenshotInfos = package->ScreenshotInfos();
7497eb09bfaSStephan Aßmus			package->ClearScreenshots();
75087038da7SStephan Aßmus		}
75187038da7SStephan Aßmus		for (int i = 0; i < screenshotInfos.CountItems(); i++) {
75287038da7SStephan Aßmus			const ScreenshotInfo& info = screenshotInfos.ItemAtFast(i);
753e0881f21SStephan Aßmus			_PopulatePackageScreenshot(package, info, 320, false);
75487038da7SStephan Aßmus		}
75587038da7SStephan Aßmus	}
7567eec822dSStephan Aßmus}
7575bd5f161SStephan Aßmus
7585bd5f161SStephan Aßmus
75980a272eeSAndrew Lindesayvoid
76080a272eeSAndrew LindesayModel::_PopulatePackageChangelog(const PackageInfoRef& package)
76180a272eeSAndrew Lindesay{
76280a272eeSAndrew Lindesay	BMessage info;
76380a272eeSAndrew Lindesay	BString packageName;
76480a272eeSAndrew Lindesay
76580a272eeSAndrew Lindesay	{
76680a272eeSAndrew Lindesay		BAutolock locker(&fLock);
76780a272eeSAndrew Lindesay		packageName = package->Name();
76880a272eeSAndrew Lindesay	}
76980a272eeSAndrew Lindesay
77080a272eeSAndrew Lindesay	status_t status = fWebAppInterface.GetChangelog(packageName, info);
77180a272eeSAndrew Lindesay
77280a272eeSAndrew Lindesay	if (status == B_OK) {
77380a272eeSAndrew Lindesay		// Parse message
77480a272eeSAndrew Lindesay		BMessage result;
77580a272eeSAndrew Lindesay		BString content;
77680a272eeSAndrew Lindesay		if (info.FindMessage("result", &result) == B_OK) {
77780a272eeSAndrew Lindesay			if (result.FindString("content", &content) == B_OK
77880a272eeSAndrew Lindesay				&& 0 != content.Length()) {
77980a272eeSAndrew Lindesay				BAutolock locker(&fLock);
78080a272eeSAndrew Lindesay				package->SetChangelog(content);
78180a272eeSAndrew Lindesay				if (Logger::IsDebugEnabled()) {
78280a272eeSAndrew Lindesay					fprintf(stdout, "changelog populated for [%s]\n",
78380a272eeSAndrew Lindesay						packageName.String());
78480a272eeSAndrew Lindesay				}
78580a272eeSAndrew Lindesay			} else {
78680a272eeSAndrew Lindesay				if (Logger::IsDebugEnabled()) {
78780a272eeSAndrew Lindesay					fprintf(stdout, "no changelog present for [%s]\n",
78880a272eeSAndrew Lindesay						packageName.String());
78980a272eeSAndrew Lindesay				}
79080a272eeSAndrew Lindesay			}
79180a272eeSAndrew Lindesay		} else {
79280a272eeSAndrew Lindesay			_MaybeLogJsonRpcError(info, "populate package changelog");
79380a272eeSAndrew Lindesay		}
79480a272eeSAndrew Lindesay	} else {
79580a272eeSAndrew Lindesay		fprintf(stdout, "unable to obtain the changelog for the package"
796a9edb9bfSAndrew Lindesay			" [%s]\n", packageName.String());
79780a272eeSAndrew Lindesay	}
79880a272eeSAndrew Lindesay}
79980a272eeSAndrew Lindesay
80080a272eeSAndrew Lindesay
80128075ee4SStephan Aßmusvoid
8028f03a0f9SStephan AßmusModel::SetUsername(BString username)
80389ec84a4SStephan Aßmus{
8048f03a0f9SStephan Aßmus	BString password;
8058f03a0f9SStephan Aßmus	if (username.Length() > 0) {
8068f03a0f9SStephan Aßmus		BPasswordKey key;
8078f03a0f9SStephan Aßmus		BKeyStore keyStore;
8088f03a0f9SStephan Aßmus		if (keyStore.GetKey(kHaikuDepotKeyring, B_KEY_TYPE_PASSWORD, username,
8098f03a0f9SStephan Aßmus				key) == B_OK) {
8108f03a0f9SStephan Aßmus			password = key.Password();
8118f03a0f9SStephan Aßmus		} else {
8128f03a0f9SStephan Aßmus			username = "";
8138f03a0f9SStephan Aßmus		}
81489ec84a4SStephan Aßmus	}
8158f03a0f9SStephan Aßmus	SetAuthorization(username, password, false);
8168f03a0f9SStephan Aßmus}
8178f03a0f9SStephan Aßmus
8188f03a0f9SStephan Aßmus
8198f03a0f9SStephan Aßmusconst BString&
8208f03a0f9SStephan AßmusModel::Username() const
8218f03a0f9SStephan Aßmus{
8228f03a0f9SStephan Aßmus	return fWebAppInterface.Username();
82389ec84a4SStephan Aßmus}
82489ec84a4SStephan Aßmus
82589ec84a4SStephan Aßmus
82689ec84a4SStephan Aßmusvoid
82789ec84a4SStephan AßmusModel::SetAuthorization(const BString& username, const BString& password,
82889ec84a4SStephan Aßmus	bool storePassword)
82928075ee4SStephan Aßmus{
8308f03a0f9SStephan Aßmus	if (storePassword && username.Length() > 0 && password.Length() > 0) {
83189ec84a4SStephan Aßmus		BPasswordKey key(password, B_KEY_PURPOSE_WEB, username);
83289ec84a4SStephan Aßmus		BKeyStore keyStore;
83389ec84a4SStephan Aßmus		keyStore.AddKeyring(kHaikuDepotKeyring);
83489ec84a4SStephan Aßmus		keyStore.AddKey(kHaikuDepotKeyring, key);
83589ec84a4SStephan Aßmus	}
83689ec84a4SStephan Aßmus
83728075ee4SStephan Aßmus	BAutolock locker(&fLock);
83828075ee4SStephan Aßmus	fWebAppInterface.SetAuthorization(username, password);
8398f03a0f9SStephan Aßmus
8408f03a0f9SStephan Aßmus	_NotifyAuthorizationChanged();
84128075ee4SStephan Aßmus}
84228075ee4SStephan Aßmus
84328075ee4SStephan Aßmus
8443d528c4aSAndrew Lindesaystatus_t
845f85e0300SAndrew LindesayModel::_LocalDataPath(const BString leaf, BPath& path) const
846ebb1a01cSAndrew Lindesay{
847f85e0300SAndrew Lindesay	BString leafAssembled(leaf);
848f85e0300SAndrew Lindesay	leafAssembled.ReplaceAll("%languageCode%",
849f85e0300SAndrew Lindesay		LanguageModel().PreferredLanguage().Code());
850f85e0300SAndrew Lindesay
8513d528c4aSAndrew Lindesay	BPath repoDataPath;
852ebb1a01cSAndrew Lindesay
8533d528c4aSAndrew Lindesay	if (find_directory(B_USER_CACHE_DIRECTORY, &repoDataPath) == B_OK
8543d528c4aSAndrew Lindesay		&& repoDataPath.Append("HaikuDepot") == B_OK
8553d528c4aSAndrew Lindesay		&& create_directory(repoDataPath.Path(), 0777) == B_OK
856f85e0300SAndrew Lindesay		&& repoDataPath.Append(leafAssembled) == B_OK) {
8573d528c4aSAndrew Lindesay		path.SetTo(repoDataPath.Path());
8583d528c4aSAndrew Lindesay		return B_OK;
8593d528c4aSAndrew Lindesay	}
860ebb1a01cSAndrew Lindesay
8613d528c4aSAndrew Lindesay	path.Unset();
862f85e0300SAndrew Lindesay	fprintf(stdout, "unable to find the user cache file for [%s] data",
863f85e0300SAndrew Lindesay		leaf.String());
8643d528c4aSAndrew Lindesay	return B_ERROR;
8653d528c4aSAndrew Lindesay}
866ebb1a01cSAndrew Lindesay
867ebb1a01cSAndrew Lindesay
868f85e0300SAndrew Lindesay/*! When bulk repository data comes down from the server, it will
869f85e0300SAndrew Lindesay    arrive as a json.gz payload.  This is stored locally as a cache
870f85e0300SAndrew Lindesay    and this method will provide the on-disk storage location for
871f85e0300SAndrew Lindesay    this file.
872f85e0300SAndrew Lindesay*/
873f85e0300SAndrew Lindesay
874f85e0300SAndrew Lindesaystatus_t
875f85e0300SAndrew LindesayModel::DumpExportRepositoryDataPath(BPath& path) const
876f85e0300SAndrew Lindesay{
877f85e0300SAndrew Lindesay	return _LocalDataPath("repository-all_%languageCode%.json.gz", path);
878f85e0300SAndrew Lindesay}
879f85e0300SAndrew Lindesay
880f85e0300SAndrew Lindesay
881f85e0300SAndrew Lindesay/*! When the system downloads reference data (eg; categories) from the server
882f85e0300SAndrew Lindesay    then the downloaded data is stored and cached at the path defined by this
883f85e0300SAndrew Lindesay    method.
884f85e0300SAndrew Lindesay*/
885f85e0300SAndrew Lindesay
886f85e0300SAndrew Lindesaystatus_t
887f85e0300SAndrew LindesayModel::DumpExportReferenceDataPath(BPath& path) const
888f85e0300SAndrew Lindesay{
889f85e0300SAndrew Lindesay	return _LocalDataPath("reference-all_%languageCode%.json.gz", path);
890f85e0300SAndrew Lindesay}
891f85e0300SAndrew Lindesay
892f85e0300SAndrew Lindesay
893c23b641cSAndrew Lindesaystatus_t
8943094fef3SAndrew LindesayModel::IconStoragePath(BPath& path) const
8953094fef3SAndrew Lindesay{
8963094fef3SAndrew Lindesay	BPath iconStoragePath;
8973094fef3SAndrew Lindesay
8983094fef3SAndrew Lindesay	if (find_directory(B_USER_CACHE_DIRECTORY, &iconStoragePath) == B_OK
8993094fef3SAndrew Lindesay		&& iconStoragePath.Append("HaikuDepot") == B_OK
9003094fef3SAndrew Lindesay		&& iconStoragePath.Append("__allicons") == B_OK
9013094fef3SAndrew Lindesay		&& create_directory(iconStoragePath.Path(), 0777) == B_OK) {
9023094fef3SAndrew Lindesay		path.SetTo(iconStoragePath.Path());
9033094fef3SAndrew Lindesay		return B_OK;
9043094fef3SAndrew Lindesay	}
9053094fef3SAndrew Lindesay
9063094fef3SAndrew Lindesay	path.Unset();
9073094fef3SAndrew Lindesay	fprintf(stdout, "unable to find the user cache directory for icons");
9083094fef3SAndrew Lindesay	return B_ERROR;
9093094fef3SAndrew Lindesay}
9103094fef3SAndrew Lindesay
9113094fef3SAndrew Lindesay
9123094fef3SAndrew Lindesaystatus_t
9133094fef3SAndrew LindesayModel::DumpExportPkgDataPath(BPath& path,
914c23b641cSAndrew Lindesay	const BString& repositorySourceCode) const
915c23b641cSAndrew Lindesay{
916c23b641cSAndrew Lindesay	BPath repoDataPath;
917c23b641cSAndrew Lindesay	BString leafName;
918c23b641cSAndrew Lindesay
919c23b641cSAndrew Lindesay	leafName.SetToFormat("pkg-all-%s-%s.json.gz", repositorySourceCode.String(),
920f85e0300SAndrew Lindesay		LanguageModel().PreferredLanguage().Code());
921c23b641cSAndrew Lindesay
922c23b641cSAndrew Lindesay	if (find_directory(B_USER_CACHE_DIRECTORY, &repoDataPath) == B_OK
923c23b641cSAndrew Lindesay		&& repoDataPath.Append("HaikuDepot") == B_OK
924c23b641cSAndrew Lindesay		&& create_directory(repoDataPath.Path(), 0777) == B_OK
925c23b641cSAndrew Lindesay		&& repoDataPath.Append(leafName.String()) == B_OK) {
926c23b641cSAndrew Lindesay		path.SetTo(repoDataPath.Path());
927c23b641cSAndrew Lindesay		return B_OK;
928c23b641cSAndrew Lindesay	}
929c23b641cSAndrew Lindesay
930c23b641cSAndrew Lindesay	path.Unset();
931c23b641cSAndrew Lindesay	fprintf(stdout, "unable to find the user cache file for pkgs' data");
932c23b641cSAndrew Lindesay	return B_ERROR;
933c23b641cSAndrew Lindesay}
934c23b641cSAndrew Lindesay
935c23b641cSAndrew Lindesay
93612107be2SStephan Aßmusvoid
93712107be2SStephan AßmusModel::_UpdateIsFeaturedFilter()
93812107be2SStephan Aßmus{
93912107be2SStephan Aßmus	if (fShowFeaturedPackages && SearchTerms().IsEmpty())
94012107be2SStephan Aßmus		fIsFeaturedFilter = PackageFilterRef(new IsFeaturedFilter(), true);
94187084745SMichael Lotz	else
94212107be2SStephan Aßmus		fIsFeaturedFilter = PackageFilterRef(new AnyFilter(), true);
94312107be2SStephan Aßmus}
94412107be2SStephan Aßmus
94512107be2SStephan Aßmus
94687038da7SStephan Aßmusvoid
94787038da7SStephan AßmusModel::_PopulatePackageScreenshot(const PackageInfoRef& package,
94887038da7SStephan Aßmus	const ScreenshotInfo& info, int32 scaledWidth, bool fromCacheOnly)
94987038da7SStephan Aßmus{
95087038da7SStephan Aßmus	// See if there is a cached screenshot
95187038da7SStephan Aßmus	BFile screenshotFile;
95287038da7SStephan Aßmus	BPath screenshotCachePath;
95387038da7SStephan Aßmus	bool fileExists = false;
95487038da7SStephan Aßmus	BString screenshotName(info.Code());
95587038da7SStephan Aßmus	screenshotName << "@" << scaledWidth;
95687038da7SStephan Aßmus	screenshotName << ".png";
95787038da7SStephan Aßmus	time_t modifiedTime;
95887038da7SStephan Aßmus	if (find_directory(B_USER_CACHE_DIRECTORY, &screenshotCachePath) == B_OK
95987038da7SStephan Aßmus		&& screenshotCachePath.Append("HaikuDepot/Screenshots") == B_OK
96087038da7SStephan Aßmus		&& create_directory(screenshotCachePath.Path(), 0777) == B_OK
96187038da7SStephan Aßmus		&& screenshotCachePath.Append(screenshotName) == B_OK) {
96287038da7SStephan Aßmus		// Try opening the file in read-only mode, which will fail if its
96387038da7SStephan Aßmus		// not a file or does not exist.
96487038da7SStephan Aßmus		fileExists = screenshotFile.SetTo(screenshotCachePath.Path(),
96587038da7SStephan Aßmus			B_READ_ONLY) == B_OK;
96687038da7SStephan Aßmus		if (fileExists)
96787038da7SStephan Aßmus			screenshotFile.GetModificationTime(&modifiedTime);
96887038da7SStephan Aßmus	}
96987038da7SStephan Aßmus
97087038da7SStephan Aßmus	if (fileExists) {
97187038da7SStephan Aßmus		time_t now;
97287038da7SStephan Aßmus		time(&now);
97387038da7SStephan Aßmus		if (fromCacheOnly || now - modifiedTime < 60 * 60) {
97487038da7SStephan Aßmus			// Cache file is recent enough, just use it and return.
97587038da7SStephan Aßmus			BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(screenshotFile),
97687038da7SStephan Aßmus				true);
97787038da7SStephan Aßmus			BAutolock locker(&fLock);
97887038da7SStephan Aßmus			package->AddScreenshot(bitmapRef);
97987038da7SStephan Aßmus			return;
98087038da7SStephan Aßmus		}
98187038da7SStephan Aßmus	}
98287038da7SStephan Aßmus
98387038da7SStephan Aßmus	if (fromCacheOnly)
98487038da7SStephan Aßmus		return;
98587038da7SStephan Aßmus
98687038da7SStephan Aßmus	// Retrieve screenshot from web-app
98787038da7SStephan Aßmus	BMallocIO buffer;
98887038da7SStephan Aßmus
98987038da7SStephan Aßmus	int32 scaledHeight = scaledWidth * info.Height() / info.Width();
99087038da7SStephan Aßmus
99128075ee4SStephan Aßmus	status_t status = fWebAppInterface.RetrieveScreenshot(info.Code(),
99287038da7SStephan Aßmus		scaledWidth, scaledHeight, &buffer);
99387038da7SStephan Aßmus	if (status == B_OK) {
99487038da7SStephan Aßmus		BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(buffer), true);
99587038da7SStephan Aßmus		BAutolock locker(&fLock);
99687038da7SStephan Aßmus		package->AddScreenshot(bitmapRef);
99787038da7SStephan Aßmus		locker.Unlock();
99887038da7SStephan Aßmus		if (screenshotFile.SetTo(screenshotCachePath.Path(),
99987038da7SStephan Aßmus				B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE) == B_OK) {
100087038da7SStephan Aßmus			screenshotFile.Write(buffer.Buffer(), buffer.BufferLength());
100187038da7SStephan Aßmus		}
1002e0881f21SStephan Aßmus	} else {
1003e0881f21SStephan Aßmus		fprintf(stderr, "Failed to retrieve screenshot for code '%s' "
1004e0881f21SStephan Aßmus			"at %" B_PRIi32 "x%" B_PRIi32 ".\n", info.Code().String(),
1005e0881f21SStephan Aßmus			scaledWidth, scaledHeight);
100687038da7SStephan Aßmus	}
100787038da7SStephan Aßmus}
100887038da7SStephan Aßmus
100987038da7SStephan Aßmus
10108f03a0f9SStephan Aßmus// #pragma mark - listener notification methods
10118f03a0f9SStephan Aßmus
10128f03a0f9SStephan Aßmus
10138f03a0f9SStephan Aßmusvoid
10148f03a0f9SStephan AßmusModel::_NotifyAuthorizationChanged()
10158f03a0f9SStephan Aßmus{
10168f03a0f9SStephan Aßmus	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
10178f03a0f9SStephan Aßmus		const ModelListenerRef& listener = fListeners.ItemAtFast(i);
10188f03a0f9SStephan Aßmus		if (listener.Get() != NULL)
10198f03a0f9SStephan Aßmus			listener->AuthorizationChanged();
1020