Model.cpp revision c23b641c
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>.
419c15fecSAndrew Lindesay * Copyright 2016-2017, 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>
17726db742SStephan Aßmus#include <Directory.h>
18726db742SStephan Aßmus#include <Entry.h>
19726db742SStephan Aßmus#include <File.h>
2089ec84a4SStephan Aßmus#include <KeyStore.h>
216e48c81bSStephan Aßmus#include <LocaleRoster.h>
22e3fdc894SStephan Aßmus#include <Message.h>
23726db742SStephan Aßmus#include <Path.h>
24f75ec10dSStephan Aßmus
25f0665db4SAndrew Lindesay#include "Logger.h"
26c23b641cSAndrew Lindesay#include "PkgDataUpdateProcess.h"
27f0665db4SAndrew Lindesay#include "RepositoryDataUpdateProcess.h"
28f0665db4SAndrew Lindesay#include "StorageUtils.h"
29f0665db4SAndrew Lindesay
30f75ec10dSStephan Aßmus
31f75ec10dSStephan Aßmus#undef B_TRANSLATION_CONTEXT
32f75ec10dSStephan Aßmus#define B_TRANSLATION_CONTEXT "Model"
33f75ec10dSStephan Aßmus
346d367ba4SStephan Aßmus
3589ec84a4SStephan Aßmusstatic const char* kHaikuDepotKeyring = "HaikuDepot";
3689ec84a4SStephan Aßmus
3789ec84a4SStephan Aßmus
388f03a0f9SStephan AßmusPackageFilter::~PackageFilter()
398f03a0f9SStephan Aßmus{
408f03a0f9SStephan Aßmus}
414abd2b71SStephan Aßmus
424abd2b71SStephan Aßmus
438f03a0f9SStephan AßmusModelListener::~ModelListener()
444abd2b71SStephan Aßmus{
454abd2b71SStephan Aßmus}
464abd2b71SStephan Aßmus
474abd2b71SStephan Aßmus
488f03a0f9SStephan Aßmus// #pragma mark - PackageFilters
498f03a0f9SStephan Aßmus
508f03a0f9SStephan Aßmus
514abd2b71SStephan Aßmusclass AnyFilter : public PackageFilter {
524abd2b71SStephan Aßmuspublic:
53779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
544abd2b71SStephan Aßmus	{
554abd2b71SStephan Aßmus		return true;
564abd2b71SStephan Aßmus	}
574abd2b71SStephan Aßmus};
584abd2b71SStephan Aßmus
594abd2b71SStephan Aßmus
604abd2b71SStephan Aßmusclass DepotFilter : public PackageFilter {
614abd2b71SStephan Aßmuspublic:
624abd2b71SStephan Aßmus	DepotFilter(const DepotInfo& depot)
634abd2b71SStephan Aßmus		:
644abd2b71SStephan Aßmus		fDepot(depot)
654abd2b71SStephan Aßmus	{
664abd2b71SStephan Aßmus	}
671f67148fSRene Gollent
68779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
694abd2b71SStephan Aßmus	{
704abd2b71SStephan Aßmus		// TODO: Maybe a PackageInfo ought to know the Depot it came from?
714abd2b71SStephan Aßmus		// But right now the same package could theoretically be provided
724abd2b71SStephan Aßmus		// from different depots and the filter would work correctly.
734abd2b71SStephan Aßmus		// Also the PackageList could actually contain references to packages
744abd2b71SStephan Aßmus		// instead of the packages as objects. The equal operator is quite
754abd2b71SStephan Aßmus		// expensive as is.
76634c2c68SAxel Dörfler		return fDepot.Packages().Contains(package);
774abd2b71SStephan Aßmus	}
781f67148fSRene Gollent
790c169aceSStephan Aßmus	const BString& Depot() const
800c169aceSStephan Aßmus	{
810c169aceSStephan Aßmus		return fDepot.Name();
820c169aceSStephan Aßmus	}
830c169aceSStephan Aßmus
844abd2b71SStephan Aßmusprivate:
854abd2b71SStephan Aßmus	DepotInfo	fDepot;
864abd2b71SStephan Aßmus};
874abd2b71SStephan Aßmus
884abd2b71SStephan Aßmus
8942479955SStephan Aßmusclass CategoryFilter : public PackageFilter {
9042479955SStephan Aßmuspublic:
9142479955SStephan Aßmus	CategoryFilter(const BString& category)
9242479955SStephan Aßmus		:
9342479955SStephan Aßmus		fCategory(category)
9442479955SStephan Aßmus	{
9542479955SStephan Aßmus	}
961f67148fSRene Gollent
97779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
9842479955SStephan Aßmus	{
99779d8213SStephan Aßmus		if (package.Get() == NULL)
100779d8213SStephan Aßmus			return false;
101634c2c68SAxel Dörfler
102779d8213SStephan Aßmus		const CategoryList& categories = package->Categories();
10342479955SStephan Aßmus		for (int i = categories.CountItems() - 1; i >= 0; i--) {
10442479955SStephan Aßmus			const CategoryRef& category = categories.ItemAtFast(i);
10542479955SStephan Aßmus			if (category.Get() == NULL)
10642479955SStephan Aßmus				continue;
10742479955SStephan Aßmus			if (category->Name() == fCategory)
10842479955SStephan Aßmus				return true;
10942479955SStephan Aßmus		}
11042479955SStephan Aßmus		return false;
11142479955SStephan Aßmus	}
11242479955SStephan Aßmus
1130c169aceSStephan Aßmus	const BString& Category() const
1140c169aceSStephan Aßmus	{
1150c169aceSStephan Aßmus		return fCategory;
1160c169aceSStephan Aßmus	}
1170c169aceSStephan Aßmus
11842479955SStephan Aßmusprivate:
11942479955SStephan Aßmus	BString		fCategory;
12042479955SStephan Aßmus};
12142479955SStephan Aßmus
12242479955SStephan Aßmus
1233a6ccc85SStephan Aßmusclass ContainedInFilter : public PackageFilter {
1243a6ccc85SStephan Aßmuspublic:
1253a6ccc85SStephan Aßmus	ContainedInFilter(const PackageList& packageList)
1263a6ccc85SStephan Aßmus		:
1273a6ccc85SStephan Aßmus		fPackageList(packageList)
1283a6ccc85SStephan Aßmus	{
1293a6ccc85SStephan Aßmus	}
1301f67148fSRene Gollent
131779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
1323a6ccc85SStephan Aßmus	{
1333a6ccc85SStephan Aßmus		return fPackageList.Contains(package);
1343a6ccc85SStephan Aßmus	}
1353a6ccc85SStephan Aßmus
1363a6ccc85SStephan Aßmusprivate:
1373a6ccc85SStephan Aßmus	const PackageList&	fPackageList;
1383a6ccc85SStephan Aßmus};
1393a6ccc85SStephan Aßmus
1403a6ccc85SStephan Aßmus
1413a6ccc85SStephan Aßmusclass ContainedInEitherFilter : public PackageFilter {
1423a6ccc85SStephan Aßmuspublic:
1433a6ccc85SStephan Aßmus	ContainedInEitherFilter(const PackageList& packageListA,
1443a6ccc85SStephan Aßmus		const PackageList& packageListB)
1453a6ccc85SStephan Aßmus		:
1463a6ccc85SStephan Aßmus		fPackageListA(packageListA),
1473a6ccc85SStephan Aßmus		fPackageListB(packageListB)
1483a6ccc85SStephan Aßmus	{
1493a6ccc85SStephan Aßmus	}
1501f67148fSRene Gollent
151779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
1523a6ccc85SStephan Aßmus	{
1533a6ccc85SStephan Aßmus		return fPackageListA.Contains(package)
1543a6ccc85SStephan Aßmus			|| fPackageListB.Contains(package);
1553a6ccc85SStephan Aßmus	}
1563a6ccc85SStephan Aßmus
1573a6ccc85SStephan Aßmusprivate:
1583a6ccc85SStephan Aßmus	const PackageList&	fPackageListA;
1593a6ccc85SStephan Aßmus	const PackageList&	fPackageListB;
1603a6ccc85SStephan Aßmus};
1613a6ccc85SStephan Aßmus
1623a6ccc85SStephan Aßmus
163634c2c68SAxel Dörflerclass NotContainedInFilter : public PackageFilter {
164634c2c68SAxel Dörflerpublic:
165634c2c68SAxel Dörfler	NotContainedInFilter(const PackageList* packageList, ...)
166634c2c68SAxel Dörfler	{
167634c2c68SAxel Dörfler		va_list args;
168634c2c68SAxel Dörfler		va_start(args, packageList);
169634c2c68SAxel Dörfler		while (true) {
170634c2c68SAxel Dörfler			const PackageList* packageList = va_arg(args, const PackageList*);
171634c2c68SAxel Dörfler			if (packageList == NULL)
172634c2c68SAxel Dörfler				break;
173634c2c68SAxel Dörfler			fPackageLists.Add(packageList);
174634c2c68SAxel Dörfler		}
175634c2c68SAxel Dörfler		va_end(args);
176634c2c68SAxel Dörfler	}
177634c2c68SAxel Dörfler
178634c2c68SAxel Dörfler	virtual bool AcceptsPackage(const PackageInfoRef& package) const
179634c2c68SAxel Dörfler	{
180be298474SAndrew Lindesay		if (package.Get() == NULL)
181634c2c68SAxel Dörfler			return false;
182a10ad54cSJonathan Schleifer
18372992391SStephan Aßmus		printf("TEST %s\n", package->Name().String());
184a10ad54cSJonathan Schleifer
185634c2c68SAxel Dörfler		for (int32 i = 0; i < fPackageLists.CountItems(); i++) {
186634c2c68SAxel Dörfler			if (fPackageLists.ItemAtFast(i)->Contains(package)) {
187998f4043SJonathan Schleifer				printf("  contained in %" B_PRId32 "\n", i);
188634c2c68SAxel Dörfler				return false;
189634c2c68SAxel Dörfler			}
190634c2c68SAxel Dörfler		}
191634c2c68SAxel Dörfler		return true;
192634c2c68SAxel Dörfler	}
193634c2c68SAxel Dörfler
194634c2c68SAxel Dörflerprivate:
195634c2c68SAxel Dörfler	List<const PackageList*, true>	fPackageLists;
196634c2c68SAxel Dörfler};
197634c2c68SAxel Dörfler
198634c2c68SAxel Dörfler
199634c2c68SAxel Dörflerclass StateFilter : public PackageFilter {
200634c2c68SAxel Dörflerpublic:
201634c2c68SAxel Dörfler	StateFilter(PackageState state)
202634c2c68SAxel Dörfler		:
203634c2c68SAxel Dörfler		fState(state)
204634c2c68SAxel Dörfler	{
205634c2c68SAxel Dörfler	}
206634c2c68SAxel Dörfler
207634c2c68SAxel Dörfler	virtual bool AcceptsPackage(const PackageInfoRef& package) const
208634c2c68SAxel Dörfler	{
209634c2c68SAxel Dörfler		return package->State() == NONE;
210634c2c68SAxel Dörfler	}
211634c2c68SAxel Dörfler
212634c2c68SAxel Dörflerprivate:
213634c2c68SAxel Dörfler	PackageState	fState;
214634c2c68SAxel Dörfler};
215634c2c68SAxel Dörfler
216634c2c68SAxel Dörfler
21709d44cecSStephan Aßmusclass SearchTermsFilter : public PackageFilter {
21809d44cecSStephan Aßmuspublic:
21909d44cecSStephan Aßmus	SearchTermsFilter(const BString& searchTerms)
22009d44cecSStephan Aßmus	{
22109d44cecSStephan Aßmus		// Separate the string into terms at spaces
22209d44cecSStephan Aßmus		int32 index = 0;
22309d44cecSStephan Aßmus		while (index < searchTerms.Length()) {
22409d44cecSStephan Aßmus			int32 nextSpace = searchTerms.FindFirst(" ", index);
22509d44cecSStephan Aßmus			if (nextSpace < 0)
22609d44cecSStephan Aßmus				nextSpace = searchTerms.Length();
22709d44cecSStephan Aßmus			if (nextSpace > index) {
22809d44cecSStephan Aßmus				BString term;
22909d44cecSStephan Aßmus				searchTerms.CopyInto(term, index, nextSpace - index);
23009d44cecSStephan Aßmus				term.ToLower();
23109d44cecSStephan Aßmus				fSearchTerms.Add(term);
23209d44cecSStephan Aßmus			}
23309d44cecSStephan Aßmus			index = nextSpace + 1;
23409d44cecSStephan Aßmus		}
23509d44cecSStephan Aßmus	}
2361f67148fSRene Gollent
237779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
23809d44cecSStephan Aßmus	{
239779d8213SStephan Aßmus		if (package.Get() == NULL)
240779d8213SStephan Aßmus			return false;
24109d44cecSStephan Aßmus		// Every search term must be found in one of the package texts
24209d44cecSStephan Aßmus		for (int32 i = fSearchTerms.CountItems() - 1; i >= 0; i--) {
24309d44cecSStephan Aßmus			const BString& term = fSearchTerms.ItemAtFast(i);
24472992391SStephan Aßmus			if (!_TextContains(package->Name(), term)
24572992391SStephan Aßmus				&& !_TextContains(package->Title(), term)
246779d8213SStephan Aßmus				&& !_TextContains(package->Publisher().Name(), term)
247779d8213SStephan Aßmus				&& !_TextContains(package->ShortDescription(), term)
248779d8213SStephan Aßmus				&& !_TextContains(package->FullDescription(), term)) {
24909d44cecSStephan Aßmus				return false;
25009d44cecSStephan Aßmus			}
25109d44cecSStephan Aßmus		}
25209d44cecSStephan Aßmus		return true;
25309d44cecSStephan Aßmus	}
25409d44cecSStephan Aßmus
2550c169aceSStephan Aßmus	BString SearchTerms() const
2560c169aceSStephan Aßmus	{
2570c169aceSStephan Aßmus		BString searchTerms;
2580c169aceSStephan Aßmus		for (int32 i = 0; i < fSearchTerms.CountItems(); i++) {
2590c169aceSStephan Aßmus			const BString& term = fSearchTerms.ItemAtFast(i);
2600c169aceSStephan Aßmus			if (term.IsEmpty())
2610c169aceSStephan Aßmus				continue;
2620c169aceSStephan Aßmus			if (!searchTerms.IsEmpty())
2630c169aceSStephan Aßmus				searchTerms.Append(" ");
2640c169aceSStephan Aßmus			searchTerms.Append(term);
2650c169aceSStephan Aßmus		}
2660c169aceSStephan Aßmus		return searchTerms;
2670c169aceSStephan Aßmus	}
2680c169aceSStephan Aßmus
26909d44cecSStephan Aßmusprivate:
27009d44cecSStephan Aßmus	bool _TextContains(BString text, const BString& string) const
27109d44cecSStephan Aßmus	{
27209d44cecSStephan Aßmus		text.ToLower();
27309d44cecSStephan Aßmus		int32 index = text.FindFirst(string);
27409d44cecSStephan Aßmus		return index >= 0;
27509d44cecSStephan Aßmus	}
27609d44cecSStephan Aßmus
27709d44cecSStephan Aßmusprivate:
2780c169aceSStephan Aßmus	StringList fSearchTerms;
27909d44cecSStephan Aßmus};
28009d44cecSStephan Aßmus
28109d44cecSStephan Aßmus
28212107be2SStephan Aßmusclass IsFeaturedFilter : public PackageFilter {
28312107be2SStephan Aßmuspublic:
28412107be2SStephan Aßmus	IsFeaturedFilter()
28512107be2SStephan Aßmus	{
28612107be2SStephan Aßmus	}
28712107be2SStephan Aßmus
28812107be2SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
28912107be2SStephan Aßmus	{
29012107be2SStephan Aßmus		return package.Get() != NULL && package->IsProminent();
29112107be2SStephan Aßmus	}
29212107be2SStephan Aßmus};
29312107be2SStephan Aßmus
29412107be2SStephan Aßmus
295870554acSStephan Aßmusstatic inline bool
296870554acSStephan Aßmusis_source_package(const PackageInfoRef& package)
297870554acSStephan Aßmus{
29872992391SStephan Aßmus	const BString& packageName = package->Name();
299870554acSStephan Aßmus	return packageName.EndsWith("_source");
300870554acSStephan Aßmus}
301870554acSStephan Aßmus
302870554acSStephan Aßmus
303870554acSStephan Aßmusstatic inline bool
304870554acSStephan Aßmusis_develop_package(const PackageInfoRef& package)
305870554acSStephan Aßmus{
30672992391SStephan Aßmus	const BString& packageName = package->Name();
307870554acSStephan Aßmus	return packageName.EndsWith("_devel");
308870554acSStephan Aßmus}
309870554acSStephan Aßmus
310870554acSStephan Aßmus
3114abd2b71SStephan Aßmus// #pragma mark - Model
3124abd2b71SStephan Aßmus
3134abd2b71SStephan Aßmus
3146d367ba4SStephan AßmusModel::Model()
3156d367ba4SStephan Aßmus	:
316f75ec10dSStephan Aßmus	fDepots(),
317f75ec10dSStephan Aßmus
318f75ec10dSStephan Aßmus	fCategoryAudio(new PackageCategory(
319f75ec10dSStephan Aßmus		BitmapRef(),
320f75ec10dSStephan Aßmus		B_TRANSLATE("Audio"), "audio"), true),
3214a02fc4fSStephan Aßmus	fCategoryBusiness(new PackageCategory(
322f75ec10dSStephan Aßmus		BitmapRef(),
3234a02fc4fSStephan Aßmus		B_TRANSLATE("Business"), "business"), true),
3244a02fc4fSStephan Aßmus	fCategoryDevelopment(new PackageCategory(
3254a02fc4fSStephan Aßmus		BitmapRef(),
3264a02fc4fSStephan Aßmus		B_TRANSLATE("Development"), "development"), true),
3274a02fc4fSStephan Aßmus	fCategoryEducation(new PackageCategory(
3284a02fc4fSStephan Aßmus		BitmapRef(),
3294a02fc4fSStephan Aßmus		B_TRANSLATE("Education"), "education"), true),
3304a02fc4fSStephan Aßmus	fCategoryGames(new PackageCategory(
3314a02fc4fSStephan Aßmus		BitmapRef(),
3324a02fc4fSStephan Aßmus		B_TRANSLATE("Games"), "games"), true),
333f75ec10dSStephan Aßmus	fCategoryGraphics(new PackageCategory(
334f75ec10dSStephan Aßmus		BitmapRef(),
335f75ec10dSStephan Aßmus		B_TRANSLATE("Graphics"), "graphics"), true),
3364a02fc4fSStephan Aßmus	fCategoryInternetAndNetwork(new PackageCategory(
3374a02fc4fSStephan Aßmus		BitmapRef(),
3384a02fc4fSStephan Aßmus		B_TRANSLATE("Internet & Network"), "internetandnetwork"), true),
339f75ec10dSStephan Aßmus	fCategoryProductivity(new PackageCategory(
340f75ec10dSStephan Aßmus		BitmapRef(),
341f75ec10dSStephan Aßmus		B_TRANSLATE("Productivity"), "productivity"), true),
3424a02fc4fSStephan Aßmus	fCategoryScienceAndMathematics(new PackageCategory(
343f75ec10dSStephan Aßmus		BitmapRef(),
3444a02fc4fSStephan Aßmus		B_TRANSLATE("Science & Mathematics"), "scienceandmathematics"), true),
3454a02fc4fSStephan Aßmus	fCategorySystemAndUtilities(new PackageCategory(
346f75ec10dSStephan Aßmus		BitmapRef(),
3474a02fc4fSStephan Aßmus		B_TRANSLATE("System & Utilities"), "systemandutilities"), true),
3484a02fc4fSStephan Aßmus	fCategoryVideo(new PackageCategory(
3494abd2b71SStephan Aßmus		BitmapRef(),
3504a02fc4fSStephan Aßmus		B_TRANSLATE("Video"), "video"), true),
3514abd2b71SStephan Aßmus
3524abd2b71SStephan Aßmus	fCategoryFilter(PackageFilterRef(new AnyFilter(), true)),
35309d44cecSStephan Aßmus	fDepotFilter(""),
354870554acSStephan Aßmus	fSearchTermsFilter(PackageFilterRef(new AnyFilter(), true)),
35512107be2SStephan Aßmus	fIsFeaturedFilter(),
356634c2c68SAxel Dörfler
3574fade61fSStephan Aßmus	fShowFeaturedPackages(true),
358ab172803SStephan Aßmus	fShowAvailablePackages(true),
3594fade61fSStephan Aßmus	fShowInstalledPackages(true),
360870554acSStephan Aßmus	fShowSourcePackages(false),
3615bd5f161SStephan Aßmus	fShowDevelopPackages(false),
3625bd5f161SStephan Aßmus
3635bd5f161SStephan Aßmus	fPopulateAllPackagesThread(-1),
3645bd5f161SStephan Aßmus	fStopPopulatingAllPackages(false)
3656d367ba4SStephan Aßmus{
36612107be2SStephan Aßmus	_UpdateIsFeaturedFilter();
36712107be2SStephan Aßmus
368633ed4c4SStephan Aßmus	// Don't forget to add new categories to this list:
3694a02fc4fSStephan Aßmus	fCategories.Add(fCategoryGames);
3704a02fc4fSStephan Aßmus	fCategories.Add(fCategoryBusiness);
371633ed4c4SStephan Aßmus	fCategories.Add(fCategoryAudio);
372633ed4c4SStephan Aßmus	fCategories.Add(fCategoryVideo);
373633ed4c4SStephan Aßmus	fCategories.Add(fCategoryGraphics);
3744a02fc4fSStephan Aßmus	fCategories.Add(fCategoryEducation);
375633ed4c4SStephan Aßmus	fCategories.Add(fCategoryProductivity);
3764a02fc4fSStephan Aßmus	fCategories.Add(fCategorySystemAndUtilities);
3774a02fc4fSStephan Aßmus	fCategories.Add(fCategoryInternetAndNetwork);
378633ed4c4SStephan Aßmus	fCategories.Add(fCategoryDevelopment);
3794a02fc4fSStephan Aßmus	fCategories.Add(fCategoryScienceAndMathematics);
3802b411c02SStephan Aßmus	// TODO: The server will eventually support an API to
3812b411c02SStephan Aßmus	// get the defined categories and their translated names.
3822b411c02SStephan Aßmus	// This should then be used instead of hard-coded
3832b411c02SStephan Aßmus	// categories and translations in the app.
384d07ca069SStephan Aßmus
385d07ca069SStephan Aßmus	fPreferredLanguage = "en";
386d07ca069SStephan Aßmus	BLocaleRoster* localeRoster = BLocaleRoster::Default();
387d07ca069SStephan Aßmus	if (localeRoster != NULL) {
388d07ca069SStephan Aßmus		BMessage preferredLanguages;
389d07ca069SStephan Aßmus		if (localeRoster->GetPreferredLanguages(&preferredLanguages) == B_OK) {
390d07ca069SStephan Aßmus			BString language;
391d07ca069SStephan Aßmus			if (preferredLanguages.FindString("language", 0, &language) == B_OK)
392d07ca069SStephan Aßmus				language.CopyInto(fPreferredLanguage, 0, 2);
393d07ca069SStephan Aßmus		}
394d07ca069SStephan Aßmus	}
3954ff3d17aSStephan Aßmus
3964ff3d17aSStephan Aßmus	// TODO: Fetch this from the web-app.
3974ff3d17aSStephan Aßmus	fSupportedLanguages.Add("en");
3984ff3d17aSStephan Aßmus	fSupportedLanguages.Add("de");
3994ff3d17aSStephan Aßmus	fSupportedLanguages.Add("fr");
4004ff3d17aSStephan Aßmus	fSupportedLanguages.Add("ja");
4014ff3d17aSStephan Aßmus	fSupportedLanguages.Add("es");
4024ff3d17aSStephan Aßmus	fSupportedLanguages.Add("zh");
4034ff3d17aSStephan Aßmus	fSupportedLanguages.Add("pt");
4044ff3d17aSStephan Aßmus	fSupportedLanguages.Add("ru");
4054ff3d17aSStephan Aßmus
4064ff3d17aSStephan Aßmus	if (!fSupportedLanguages.Contains(fPreferredLanguage)) {
40712f29d44SStephan Aßmus		// Force the preferred language to one of the currently supported
40812f29d44SStephan Aßmus		// ones, until the web application supports all ISO language codes.
4094ff3d17aSStephan Aßmus		printf("User preferred language '%s' not currently supported, "
4104ff3d17aSStephan Aßmus			"defaulting to 'en'.", fPreferredLanguage.String());
41112f29d44SStephan Aßmus		fPreferredLanguage = "en";
41212f29d44SStephan Aßmus	}
41328075ee4SStephan Aßmus	fWebAppInterface.SetPreferredLanguage(fPreferredLanguage);
4146d367ba4SStephan Aßmus}
4156d367ba4SStephan Aßmus
4166d367ba4SStephan Aßmus
4175bd5f161SStephan AßmusModel::~Model()
4185bd5f161SStephan Aßmus{
4195bd5f161SStephan Aßmus	StopPopulatingAllPackages();
4205bd5f161SStephan Aßmus}
4215bd5f161SStephan Aßmus
4225bd5f161SStephan Aßmus
4238f03a0f9SStephan Aßmusbool
4248f03a0f9SStephan AßmusModel::AddListener(const ModelListenerRef& listener)
4258f03a0f9SStephan Aßmus{
4268f03a0f9SStephan Aßmus	return fListeners.Add(listener);
4278f03a0f9SStephan Aßmus}
4288f03a0f9SStephan Aßmus
4298f03a0f9SStephan Aßmus
430706edd80SStephan AßmusPackageList
4316d367ba4SStephan AßmusModel::CreatePackageList() const
4326d367ba4SStephan Aßmus{
433ab172803SStephan Aßmus	// Iterate all packages from all depots.
434ab172803SStephan Aßmus	// If configured, restrict depot, filter by search terms, status, name ...
435706edd80SStephan Aßmus	PackageList resultList;
4366d367ba4SStephan Aßmus
4376d367ba4SStephan Aßmus	for (int32 i = 0; i < fDepots.CountItems(); i++) {
43809d44cecSStephan Aßmus		const DepotInfo& depot = fDepots.ItemAtFast(i);
4391f67148fSRene Gollent
44009d44cecSStephan Aßmus		if (fDepotFilter.Length() > 0 && fDepotFilter != depot.Name())
44109d44cecSStephan Aßmus			continue;
4421f67148fSRene Gollent
44309d44cecSStephan Aßmus		const PackageList& packages = depot.Packages();
4446d367ba4SStephan Aßmus
445706edd80SStephan Aßmus		for (int32 j = 0; j < packages.CountItems(); j++) {
446779d8213SStephan Aßmus			const PackageInfoRef& package = packages.ItemAtFast(j);
4474abd2b71SStephan Aßmus			if (fCategoryFilter->AcceptsPackage(package)
448870554acSStephan Aßmus				&& fSearchTermsFilter->AcceptsPackage(package)
44912107be2SStephan Aßmus				&& fIsFeaturedFilter->AcceptsPackage(package)
450ab172803SStephan Aßmus				&& (fShowAvailablePackages || package->State() != NONE)
451ab172803SStephan Aßmus				&& (fShowInstalledPackages || package->State() != ACTIVATED)
452870554acSStephan Aßmus				&& (fShowSourcePackages || !is_source_package(package))
453870554acSStephan Aßmus				&& (fShowDevelopPackages || !is_develop_package(package))) {
4544abd2b71SStephan Aßmus				resultList.Add(package);
4554abd2b71SStephan Aßmus			}
4566d367ba4SStephan Aßmus		}
4576d367ba4SStephan Aßmus	}
4586d367ba4SStephan Aßmus
4596d367ba4SStephan Aßmus	return resultList;
4606d367ba4SStephan Aßmus}
4616d367ba4SStephan Aßmus
4626d367ba4SStephan Aßmus
4636d367ba4SStephan Aßmusbool
4646d367ba4SStephan AßmusModel::AddDepot(const DepotInfo& depot)
4656d367ba4SStephan Aßmus{
4666d367ba4SStephan Aßmus	return fDepots.Add(depot);
4676d367ba4SStephan Aßmus}
4686d367ba4SStephan Aßmus
46942479955SStephan Aßmus
4707b2c59b9SStephan Aßmusbool
4717b2c59b9SStephan AßmusModel::HasDepot(const BString& name) const
472ebb1a01cSAndrew Lindesay{
473ebb1a01cSAndrew Lindesay	return NULL != DepotForName(name);
474ebb1a01cSAndrew Lindesay}
475ebb1a01cSAndrew Lindesay
476ebb1a01cSAndrew Lindesay
477ebb1a01cSAndrew Lindesayconst DepotInfo*
478ebb1a01cSAndrew LindesayModel::DepotForName(const BString& name) const
4797b2c59b9SStephan Aßmus{
4807b2c59b9SStephan Aßmus	for (int32 i = fDepots.CountItems() - 1; i >= 0; i--) {
4817b2c59b9SStephan Aßmus		if (fDepots.ItemAtFast(i).Name() == name)
482ebb1a01cSAndrew Lindesay			return &fDepots.ItemAtFast(i);
4837b2c59b9SStephan Aßmus	}
484ebb1a01cSAndrew Lindesay	return NULL;
4857b2c59b9SStephan Aßmus}
4867b2c59b9SStephan Aßmus
4877b2c59b9SStephan Aßmus
4887b2c59b9SStephan Aßmusbool
4897b2c59b9SStephan AßmusModel::SyncDepot(const DepotInfo& depot)
4907b2c59b9SStephan Aßmus{
4917b2c59b9SStephan Aßmus	for (int32 i = fDepots.CountItems() - 1; i >= 0; i--) {
4927b2c59b9SStephan Aßmus		const DepotInfo& existingDepot = fDepots.ItemAtFast(i);
4937b2c59b9SStephan Aßmus		if (existingDepot.Name() == depot.Name()) {
4947b2c59b9SStephan Aßmus			DepotInfo mergedDepot(existingDepot);
4957b2c59b9SStephan Aßmus			mergedDepot.SyncPackages(depot.Packages());
4967b2c59b9SStephan Aßmus			fDepots.Replace(i, mergedDepot);
4977b2c59b9SStephan Aßmus			return true;
4987b2c59b9SStephan Aßmus		}
4997b2c59b9SStephan Aßmus	}
5007b2c59b9SStephan Aßmus	return false;
5017b2c59b9SStephan Aßmus}
5027b2c59b9SStephan Aßmus
5037b2c59b9SStephan Aßmus
5042c287f5bSRene Gollentvoid
5052c287f5bSRene GollentModel::Clear()
5062c287f5bSRene Gollent{
5072c287f5bSRene Gollent	fDepots.Clear();
5082c287f5bSRene Gollent}
5092c287f5bSRene Gollent
5102c287f5bSRene Gollent
511706edd80SStephan Aßmusvoid
512779d8213SStephan AßmusModel::SetPackageState(const PackageInfoRef& package, PackageState state)
513706edd80SStephan Aßmus{
514706edd80SStephan Aßmus	switch (state) {
515706edd80SStephan Aßmus		default:
516706edd80SStephan Aßmus		case NONE:
517706edd80SStephan Aßmus			fInstalledPackages.Remove(package);
518706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
519706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
520706edd80SStephan Aßmus			break;
521706edd80SStephan Aßmus		case INSTALLED:
522706edd80SStephan Aßmus			if (!fInstalledPackages.Contains(package))
523706edd80SStephan Aßmus				fInstalledPackages.Add(package);
524706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
525706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
526706edd80SStephan Aßmus			break;
527706edd80SStephan Aßmus		case ACTIVATED:
528706edd80SStephan Aßmus			if (!fInstalledPackages.Contains(package))
529706edd80SStephan Aßmus				fInstalledPackages.Add(package);
530706edd80SStephan Aßmus			if (!fActivatedPackages.Contains(package))
531706edd80SStephan Aßmus				fActivatedPackages.Add(package);
532706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
533706edd80SStephan Aßmus			break;
534706edd80SStephan Aßmus		case UNINSTALLED:
535706edd80SStephan Aßmus			fInstalledPackages.Remove(package);
536706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
537ab172803SStephan Aßmus			if (!fUninstalledPackages.Contains(package))
538ab172803SStephan Aßmus				fUninstalledPackages.Add(package);
539706edd80SStephan Aßmus			break;
540706edd80SStephan Aßmus	}
541b4c8d2ffSRene Gollent
542b4c8d2ffSRene Gollent	package->SetState(state);
543706edd80SStephan Aßmus}
544706edd80SStephan Aßmus
545706edd80SStephan Aßmus
54609d44cecSStephan Aßmus// #pragma mark - filters
54709d44cecSStephan Aßmus
54809d44cecSStephan Aßmus
54942479955SStephan Aßmusvoid
55042479955SStephan AßmusModel::SetCategory(const BString& category)
55142479955SStephan Aßmus{
5523a6ccc85SStephan Aßmus	PackageFilter* filter;
5531f67148fSRene Gollent
55442479955SStephan Aßmus	if (category.Length() == 0)
5553a6ccc85SStephan Aßmus		filter = new AnyFilter();
55642479955SStephan Aßmus	else
5573a6ccc85SStephan Aßmus		filter = new CategoryFilter(category);
5583a6ccc85SStephan Aßmus
5593a6ccc85SStephan Aßmus	fCategoryFilter.SetTo(filter, true);
56042479955SStephan Aßmus}
56142479955SStephan Aßmus
56209d44cecSStephan Aßmus
5630c169aceSStephan AßmusBString
5640c169aceSStephan AßmusModel::Category() const
5650c169aceSStephan Aßmus{
5660c169aceSStephan Aßmus	CategoryFilter* filter
5670c169aceSStephan Aßmus		= dynamic_cast<CategoryFilter*>(fCategoryFilter.Get());
5680c169aceSStephan Aßmus	if (filter == NULL)
5690c169aceSStephan Aßmus		return "";
5700c169aceSStephan Aßmus	return filter->Category();
5710c169aceSStephan Aßmus}
5720c169aceSStephan Aßmus
5730c169aceSStephan Aßmus
57409d44cecSStephan Aßmusvoid
57509d44cecSStephan AßmusModel::SetDepot(const BString& depot)
57609d44cecSStephan Aßmus{
57709d44cecSStephan Aßmus	fDepotFilter = depot;
57809d44cecSStephan Aßmus}
57909d44cecSStephan Aßmus
58009d44cecSStephan Aßmus
5810c169aceSStephan AßmusBString
5820c169aceSStephan AßmusModel::Depot() const
5830c169aceSStephan Aßmus{
5840c169aceSStephan Aßmus	return fDepotFilter;
5850c169aceSStephan Aßmus}
5860c169aceSStephan Aßmus
5870c169aceSStephan Aßmus
58809d44cecSStephan Aßmusvoid
58909d44cecSStephan AßmusModel::SetSearchTerms(const BString& searchTerms)
59009d44cecSStephan Aßmus{
59109d44cecSStephan Aßmus	PackageFilter* filter;
5921f67148fSRene Gollent
59309d44cecSStephan Aßmus	if (searchTerms.Length() == 0)
59409d44cecSStephan Aßmus		filter = new AnyFilter();
59509d44cecSStephan Aßmus	else
59609d44cecSStephan Aßmus		filter = new SearchTermsFilter(searchTerms);
59709d44cecSStephan Aßmus
59809d44cecSStephan Aßmus	fSearchTermsFilter.SetTo(filter, true);
59912107be2SStephan Aßmus	_UpdateIsFeaturedFilter();
60009d44cecSStephan Aßmus}
60109d44cecSStephan Aßmus
6027eec822dSStephan Aßmus
6030c169aceSStephan AßmusBString
6040c169aceSStephan AßmusModel::SearchTerms() const
6050c169aceSStephan Aßmus{
6060c169aceSStephan Aßmus	SearchTermsFilter* filter
6070c169aceSStephan Aßmus		= dynamic_cast<SearchTermsFilter*>(fSearchTermsFilter.Get());
6080c169aceSStephan Aßmus	if (filter == NULL)
6090c169aceSStephan Aßmus		return "";
6100c169aceSStephan Aßmus	return filter->SearchTerms();
6110c169aceSStephan Aßmus}
6120c169aceSStephan Aßmus
6130c169aceSStephan Aßmus
6144fade61fSStephan Aßmusvoid
6154fade61fSStephan AßmusModel::SetShowFeaturedPackages(bool show)
6164fade61fSStephan Aßmus{
6174fade61fSStephan Aßmus	fShowFeaturedPackages = show;
61812107be2SStephan Aßmus	_UpdateIsFeaturedFilter();
6194fade61fSStephan Aßmus}
6204fade61fSStephan Aßmus
6214fade61fSStephan Aßmus
622ab172803SStephan Aßmusvoid
623ab172803SStephan AßmusModel::SetShowAvailablePackages(bool show)
624ab172803SStephan Aßmus{
625ab172803SStephan Aßmus	fShowAvailablePackages = show;
626ab172803SStephan Aßmus}
627ab172803SStephan Aßmus
628ab172803SStephan Aßmus
629ab172803SStephan Aßmusvoid
630ab172803SStephan AßmusModel::SetShowInstalledPackages(bool show)
631ab172803SStephan Aßmus{
632ab172803SStephan Aßmus	fShowInstalledPackages = show;
633ab172803SStephan Aßmus}
634ab172803SStephan Aßmus
635ab172803SStephan Aßmus
636870554acSStephan Aßmusvoid
637870554acSStephan AßmusModel::SetShowSourcePackages(bool show)
638870554acSStephan Aßmus{
639870554acSStephan Aßmus	fShowSourcePackages = show;
640870554acSStephan Aßmus}
641870554acSStephan Aßmus
642870554acSStephan Aßmus
643870554acSStephan Aßmusvoid
644870554acSStephan AßmusModel::SetShowDevelopPackages(bool show)
645870554acSStephan Aßmus{
646870554acSStephan Aßmus	fShowDevelopPackages = show;
647870554acSStephan Aßmus}
648870554acSStephan Aßmus
649870554acSStephan Aßmus
65019c15fecSAndrew Lindesay// #pragma mark - information retrieval
6517eec822dSStephan Aßmus
6527eec822dSStephan Aßmus
6537eec822dSStephan Aßmusvoid
654a60a23f8SStephan AßmusModel::PopulatePackage(const PackageInfoRef& package, uint32 flags)
6557eec822dSStephan Aßmus{
6567eec822dSStephan Aßmus	// TODO: There should probably also be a way to "unpopulate" the
6577eec822dSStephan Aßmus	// package information. Maybe a cache of populated packages, so that
6587eec822dSStephan Aßmus	// packages loose their extra information after a certain amount of
6597eec822dSStephan Aßmus	// time when they have not been accessed/displayed in the UI. Otherwise
6607eec822dSStephan Aßmus	// HaikuDepot will consume more and more resources in the packages.
6617eec822dSStephan Aßmus	// Especially screen-shots will be a problem eventually.
66248045f5aSStephan Aßmus	{
66348045f5aSStephan Aßmus		BAutolock locker(&fLock);
66489e4ba17SStephan Aßmus		bool alreadyPopulated = fPopulatedPackages.Contains(package);
66589e4ba17SStephan Aßmus		if ((flags & POPULATE_FORCE) == 0 && alreadyPopulated)
66648045f5aSStephan Aßmus			return;
66789e4ba17SStephan Aßmus		if (!alreadyPopulated)
66889e4ba17SStephan Aßmus			fPopulatedPackages.Add(package);
66948045f5aSStephan Aßmus	}
6707eec822dSStephan Aßmus
6717ff94fd0SStephan Aßmus	if ((flags & POPULATE_USER_RATINGS) != 0) {
6727ff94fd0SStephan Aßmus		// Retrieve info from web-app
6737ff94fd0SStephan Aßmus		BMessage info;
67448045f5aSStephan Aßmus
67548045f5aSStephan Aßmus		BString packageName;
67687084745SMichael Lotz		BString architecture;
67748045f5aSStephan Aßmus		{
67848045f5aSStephan Aßmus			BAutolock locker(&fLock);
67972992391SStephan Aßmus			packageName = package->Name();
68048045f5aSStephan Aßmus			architecture = package->Architecture();
68148045f5aSStephan Aßmus		}
68287084745SMichael Lotz
68328075ee4SStephan Aßmus		status_t status = fWebAppInterface.RetrieveUserRatings(packageName,
68448045f5aSStephan Aßmus			architecture, 0, 50, info);
6857ff94fd0SStephan Aßmus		if (status == B_OK) {
6867ff94fd0SStephan Aßmus			// Parse message
6877ff94fd0SStephan Aßmus			BMessage result;
6887ff94fd0SStephan Aßmus			BMessage items;
6897ff94fd0SStephan Aßmus			if (info.FindMessage("result", &result) == B_OK
6907ff94fd0SStephan Aßmus				&& result.FindMessage("items", &items) == B_OK) {
69148045f5aSStephan Aßmus
69248045f5aSStephan Aßmus				BAutolock locker(&fLock);
6937eb09bfaSStephan Aßmus				package->ClearUserRatings();
69448045f5aSStephan Aßmus
6957ff94fd0SStephan Aßmus				int index = 0;
6967ff94fd0SStephan Aßmus				while (true) {
6977ff94fd0SStephan Aßmus					BString name;
6987ff94fd0SStephan Aßmus					name << index++;
69987084745SMichael Lotz
7007ff94fd0SStephan Aßmus					BMessage item;
7017ff94fd0SStephan Aßmus					if (items.FindMessage(name, &item) != B_OK)
7027ff94fd0SStephan Aßmus						break;
7037ff94fd0SStephan Aßmus//					item.PrintToStream();
7047ff94fd0SStephan Aßmus
7057ff94fd0SStephan Aßmus					BString user;
7067ff94fd0SStephan Aßmus					BMessage userInfo;
7077ff94fd0SStephan Aßmus					if (item.FindMessage("user", &userInfo) != B_OK
7087ff94fd0SStephan Aßmus						|| userInfo.FindString("nickname", &user) != B_OK) {
7097ff94fd0SStephan Aßmus						// Ignore, we need the user name
7107ff94fd0SStephan Aßmus						continue;
7117ff94fd0SStephan Aßmus					}
7127ff94fd0SStephan Aßmus
71336a9b557SStephan Aßmus					// Extract basic info, all items are optional
7147ff94fd0SStephan Aßmus					BString languageCode;
7157ff94fd0SStephan Aßmus					BString comment;
7167ff94fd0SStephan Aßmus					double rating;
71736a9b557SStephan Aßmus					item.FindString("naturalLanguageCode", &languageCode);
71836a9b557SStephan Aßmus					item.FindString("comment", &comment);
71936a9b557SStephan Aßmus					if (item.FindDouble("rating", &rating) != B_OK)
72036a9b557SStephan Aßmus						rating = -1;
72136a9b557SStephan Aßmus					if (comment.Length() == 0 && rating == -1) {
72236a9b557SStephan Aßmus						// No useful information given.
7237ff94fd0SStephan Aßmus						continue;
7247ff94fd0SStephan Aßmus					}
72536a9b557SStephan Aßmus
7267ff94fd0SStephan Aßmus					// For which version of the package was the rating?
7277ff94fd0SStephan Aßmus					BString major = "?";
7287ff94fd0SStephan Aßmus					BString minor = "?";
7297ff94fd0SStephan Aßmus					BString micro = "";
7307ff94fd0SStephan Aßmus					BMessage version;
7317ff94fd0SStephan Aßmus					if (item.FindMessage("pkgVersion", &version) == B_OK) {
7327ff94fd0SStephan Aßmus						version.FindString("major", &major);
7337ff94fd0SStephan Aßmus						version.FindString("minor", &minor);
7347ff94fd0SStephan Aßmus						version.FindString("micro", &micro);
7357ff94fd0SStephan Aßmus					}
7367ff94fd0SStephan Aßmus					BString versionString = major;
7377ff94fd0SStephan Aßmus					versionString << ".";
7387ff94fd0SStephan Aßmus					versionString << minor;
7397ff94fd0SStephan Aßmus					if (micro.Length() > 0) {
7407ff94fd0SStephan Aßmus						versionString << ".";
7417ff94fd0SStephan Aßmus						versionString << micro;
7427ff94fd0SStephan Aßmus					}
7437ff94fd0SStephan Aßmus					// Add the rating to the PackageInfo
7447ff94fd0SStephan Aßmus					package->AddUserRating(
7457ff94fd0SStephan Aßmus						UserRating(UserInfo(user), rating,
7467ff94fd0SStephan Aßmus							comment, languageCode, versionString, 0, 0)
7477ff94fd0SStephan Aßmus					);
7487ff94fd0SStephan Aßmus				}
7497ff94fd0SStephan Aßmus			} else if (info.FindMessage("error", &result) == B_OK) {
7507ff94fd0SStephan Aßmus				result.PrintToStream();
7517ff94fd0SStephan Aßmus			}
752a60a23f8SStephan Aßmus		}
7537eec822dSStephan Aßmus	}
75487038da7SStephan Aßmus
75587038da7SStephan Aßmus	if ((flags & POPULATE_SCREEN_SHOTS) != 0) {
75687038da7SStephan Aßmus		ScreenshotInfoList screenshotInfos;
75787038da7SStephan Aßmus		{
75887038da7SStephan Aßmus			BAutolock locker(&fLock);
75987038da7SStephan Aßmus			screenshotInfos = package->ScreenshotInfos();
7607eb09bfaSStephan Aßmus			package->ClearScreenshots();
76187038da7SStephan Aßmus		}
76287038da7SStephan Aßmus		for (int i = 0; i < screenshotInfos.CountItems(); i++) {
76387038da7SStephan Aßmus			const ScreenshotInfo& info = screenshotInfos.ItemAtFast(i);
764e0881f21SStephan Aßmus			_PopulatePackageScreenshot(package, info, 320, false);
76587038da7SStephan Aßmus		}
76687038da7SStephan Aßmus	}
7677eec822dSStephan Aßmus}
7685bd5f161SStephan Aßmus
7695bd5f161SStephan Aßmus
7705bd5f161SStephan Aßmusvoid
7715bd5f161SStephan AßmusModel::PopulateAllPackages()
7725bd5f161SStephan Aßmus{
7735bd5f161SStephan Aßmus	StopPopulatingAllPackages();
7745bd5f161SStephan Aßmus
7755bd5f161SStephan Aßmus	fStopPopulatingAllPackages = false;
7765bd5f161SStephan Aßmus
7775bd5f161SStephan Aßmus	fPopulateAllPackagesThread = spawn_thread(&_PopulateAllPackagesEntry,
7785bd5f161SStephan Aßmus		"Package populator", B_NORMAL_PRIORITY, this);
7795bd5f161SStephan Aßmus	if (fPopulateAllPackagesThread >= 0)
7805bd5f161SStephan Aßmus		resume_thread(fPopulateAllPackagesThread);
7815bd5f161SStephan Aßmus}
7825bd5f161SStephan Aßmus
7835bd5f161SStephan Aßmus
7845bd5f161SStephan Aßmusvoid
7855bd5f161SStephan AßmusModel::StopPopulatingAllPackages()
7865bd5f161SStephan Aßmus{
7875bd5f161SStephan Aßmus	if (fPopulateAllPackagesThread < 0)
7885bd5f161SStephan Aßmus		return;
7895bd5f161SStephan Aßmus
7905bd5f161SStephan Aßmus	fStopPopulatingAllPackages = true;
7915bd5f161SStephan Aßmus	wait_for_thread(fPopulateAllPackagesThread, NULL);
7925bd5f161SStephan Aßmus	fPopulateAllPackagesThread = -1;
7935bd5f161SStephan Aßmus}
7945bd5f161SStephan Aßmus
7955bd5f161SStephan Aßmus
79628075ee4SStephan Aßmusvoid
7978f03a0f9SStephan AßmusModel::SetUsername(BString username)
79889ec84a4SStephan Aßmus{
7998f03a0f9SStephan Aßmus	BString password;
8008f03a0f9SStephan Aßmus	if (username.Length() > 0) {
8018f03a0f9SStephan Aßmus		BPasswordKey key;
8028f03a0f9SStephan Aßmus		BKeyStore keyStore;
8038f03a0f9SStephan Aßmus		if (keyStore.GetKey(kHaikuDepotKeyring, B_KEY_TYPE_PASSWORD, username,
8048f03a0f9SStephan Aßmus				key) == B_OK) {
8058f03a0f9SStephan Aßmus			password = key.Password();
8068f03a0f9SStephan Aßmus		} else {
8078f03a0f9SStephan Aßmus			username = "";
8088f03a0f9SStephan Aßmus		}
80989ec84a4SStephan Aßmus	}
8108f03a0f9SStephan Aßmus	SetAuthorization(username, password, false);
8118f03a0f9SStephan Aßmus}
8128f03a0f9SStephan Aßmus
8138f03a0f9SStephan Aßmus
8148f03a0f9SStephan Aßmusconst BString&
8158f03a0f9SStephan AßmusModel::Username() const
8168f03a0f9SStephan Aßmus{
8178f03a0f9SStephan Aßmus	return fWebAppInterface.Username();
81889ec84a4SStephan Aßmus}
81989ec84a4SStephan Aßmus
82089ec84a4SStephan Aßmus
82189ec84a4SStephan Aßmusvoid
82289ec84a4SStephan AßmusModel::SetAuthorization(const BString& username, const BString& password,
82389ec84a4SStephan Aßmus	bool storePassword)
82428075ee4SStephan Aßmus{
8258f03a0f9SStephan Aßmus	if (storePassword && username.Length() > 0 && password.Length() > 0) {
82689ec84a4SStephan Aßmus		BPasswordKey key(password, B_KEY_PURPOSE_WEB, username);
82789ec84a4SStephan Aßmus		BKeyStore keyStore;
82889ec84a4SStephan Aßmus		keyStore.AddKeyring(kHaikuDepotKeyring);
82989ec84a4SStephan Aßmus		keyStore.AddKey(kHaikuDepotKeyring, key);
83089ec84a4SStephan Aßmus	}
83189ec84a4SStephan Aßmus
83228075ee4SStephan Aßmus	BAutolock locker(&fLock);
83328075ee4SStephan Aßmus	fWebAppInterface.SetAuthorization(username, password);
8348f03a0f9SStephan Aßmus
8358f03a0f9SStephan Aßmus	_NotifyAuthorizationChanged();
83628075ee4SStephan Aßmus}
83728075ee4SStephan Aßmus
83828075ee4SStephan Aßmus
83928075ee4SStephan Aßmus// #pragma mark - private
84028075ee4SStephan Aßmus
8413d528c4aSAndrew Lindesay/*! When bulk repository data comes down from the server, it will
8423d528c4aSAndrew Lindesay    arrive as a json.gz payload.  This is stored locally as a cache
8433d528c4aSAndrew Lindesay    and this method will provide the on-disk storage location for
8443d528c4aSAndrew Lindesay    this file.
8453d528c4aSAndrew Lindesay*/
84628075ee4SStephan Aßmus
8473d528c4aSAndrew Lindesaystatus_t
8483d528c4aSAndrew LindesayModel::_DumpExportRepositoryDataPath(BPath& path) const
849ebb1a01cSAndrew Lindesay{
8503d528c4aSAndrew Lindesay	BPath repoDataPath;
851ebb1a01cSAndrew Lindesay
8523d528c4aSAndrew Lindesay	if (find_directory(B_USER_CACHE_DIRECTORY, &repoDataPath) == B_OK
8533d528c4aSAndrew Lindesay		&& repoDataPath.Append("HaikuDepot") == B_OK
8543d528c4aSAndrew Lindesay		&& create_directory(repoDataPath.Path(), 0777) == B_OK
8553d528c4aSAndrew Lindesay		&& repoDataPath.Append("repository-all_en.json.gz") == B_OK) {
8563d528c4aSAndrew Lindesay		path.SetTo(repoDataPath.Path());
8573d528c4aSAndrew Lindesay		return B_OK;
8583d528c4aSAndrew Lindesay	}
859ebb1a01cSAndrew Lindesay
8603d528c4aSAndrew Lindesay	path.Unset();
8613d528c4aSAndrew Lindesay	fprintf(stdout, "unable to find the user cache file for repositories'"
8623d528c4aSAndrew Lindesay		" data");
8633d528c4aSAndrew Lindesay	return B_ERROR;
8643d528c4aSAndrew Lindesay}
865ebb1a01cSAndrew Lindesay
866ebb1a01cSAndrew Lindesay
867c23b641cSAndrew Lindesaystatus_t
868c23b641cSAndrew LindesayModel::_DumpExportPkgDataPath(BPath& path,
869c23b641cSAndrew Lindesay	const BString& repositorySourceCode) const
870c23b641cSAndrew Lindesay{
871c23b641cSAndrew Lindesay	BPath repoDataPath;
872c23b641cSAndrew Lindesay	BString leafName;
873c23b641cSAndrew Lindesay
874c23b641cSAndrew Lindesay	leafName.SetToFormat("pkg-all-%s-%s.json.gz", repositorySourceCode.String(),
875c23b641cSAndrew Lindesay		fPreferredLanguage.String());
876c23b641cSAndrew Lindesay
877c23b641cSAndrew Lindesay	if (find_directory(B_USER_CACHE_DIRECTORY, &repoDataPath) == B_OK
878c23b641cSAndrew Lindesay		&& repoDataPath.Append("HaikuDepot") == B_OK
879c23b641cSAndrew Lindesay		&& create_directory(repoDataPath.Path(), 0777) == B_OK
880c23b641cSAndrew Lindesay		&& repoDataPath.Append(leafName.String()) == B_OK) {
881c23b641cSAndrew Lindesay		path.SetTo(repoDataPath.Path());
882c23b641cSAndrew Lindesay		return B_OK;
883c23b641cSAndrew Lindesay	}
884c23b641cSAndrew Lindesay
885c23b641cSAndrew Lindesay	path.Unset();
886c23b641cSAndrew Lindesay	fprintf(stdout, "unable to find the user cache file for pkgs' data");
887c23b641cSAndrew Lindesay	return B_ERROR;
888c23b641cSAndrew Lindesay}
889c23b641cSAndrew Lindesay
890c23b641cSAndrew Lindesay
8913d528c4aSAndrew Lindesaystatus_t
8923d528c4aSAndrew LindesayModel::PopulateWebAppRepositoryCodes()
8933d528c4aSAndrew Lindesay{
8943d528c4aSAndrew Lindesay	status_t result = B_OK;
8953d528c4aSAndrew Lindesay	BPath dataPath;
896ebb1a01cSAndrew Lindesay
8973d528c4aSAndrew Lindesay	result = _DumpExportRepositoryDataPath(dataPath);
898ebb1a01cSAndrew Lindesay
8993d528c4aSAndrew Lindesay	if (result != B_OK)
9003d528c4aSAndrew Lindesay		return result;
901ebb1a01cSAndrew Lindesay
9023d528c4aSAndrew Lindesay	RepositoryDataUpdateProcess process(dataPath, &fDepots);
9033d528c4aSAndrew Lindesay	result = process.Run();
904ebb1a01cSAndrew Lindesay
9053d528c4aSAndrew Lindesay	return result;
906ebb1a01cSAndrew Lindesay}
907ebb1a01cSAndrew Lindesay
908ebb1a01cSAndrew Lindesay
90912107be2SStephan Aßmusvoid
91012107be2SStephan AßmusModel::_UpdateIsFeaturedFilter()
91112107be2SStephan Aßmus{
91212107be2SStephan Aßmus	if (fShowFeaturedPackages && SearchTerms().IsEmpty())
91312107be2SStephan Aßmus		fIsFeaturedFilter = PackageFilterRef(new IsFeaturedFilter(), true);
91487084745SMichael Lotz	else
91512107be2SStephan Aßmus		fIsFeaturedFilter = PackageFilterRef(new AnyFilter(), true);
91612107be2SStephan Aßmus}
91712107be2SStephan Aßmus
91812107be2SStephan Aßmus
9195bd5f161SStephan Aßmusint32
9205bd5f161SStephan AßmusModel::_PopulateAllPackagesEntry(void* cookie)
9215bd5f161SStephan Aßmus{
9225bd5f161SStephan Aßmus	Model* model = static_cast<Model*>(cookie);
923c23b641cSAndrew Lindesay	model->_PopulateAllPackagesThread();
92419c15fecSAndrew Lindesay	model->_PopulateAllPackagesIcons();
9255bd5f161SStephan Aßmus	return 0;
9265bd5f161SStephan Aßmus}
9275bd5f161SStephan Aßmus
9285bd5f161SStephan Aßmus
92919c15fecSAndrew Lindesayvoid
93019c15fecSAndrew LindesayModel::_PopulateAllPackagesIcons()
93119c15fecSAndrew Lindesay{
93219c15fecSAndrew Lindesay	fLocalIconStore.UpdateFromServerIfNecessary();
93319c15fecSAndrew Lindesay
93419c15fecSAndrew Lindesay	int32 depotIndex = 0;
93519c15fecSAndrew Lindesay	int32 packageIndex = 0;
93619c15fecSAndrew Lindesay	int32 countIconsSet = 0;
93719c15fecSAndrew Lindesay
93819c15fecSAndrew Lindesay	fprintf(stdout, "will populate all packages' icons\n");
93919c15fecSAndrew Lindesay
94019c15fecSAndrew Lindesay	while (true) {
94119c15fecSAndrew Lindesay		PackageInfoRef package;
94219c15fecSAndrew Lindesay		BAutolock locker(&fLock);
94319c15fecSAndrew Lindesay
94419c15fecSAndrew Lindesay		if (depotIndex > fDepots.CountItems()) {
9450d51483fSJessica Hamilton			fprintf(stdout, "did populate %" B_PRId32 " packages' icons\n",
94619c15fecSAndrew Lindesay				countIconsSet);
94719c15fecSAndrew Lindesay			return;
94819c15fecSAndrew Lindesay		}
94919c15fecSAndrew Lindesay
95019c15fecSAndrew Lindesay		const DepotInfo& depot = fDepots.ItemAt(depotIndex);
95119c15fecSAndrew Lindesay		const PackageList& packages = depot.Packages();
95219c15fecSAndrew Lindesay
95319c15fecSAndrew Lindesay		if (packageIndex >= packages.CountItems()) {
95419c15fecSAndrew Lindesay			// Need the next depot
95519c15fecSAndrew Lindesay			packageIndex = 0;
95619c15fecSAndrew Lindesay			depotIndex++;
95719c15fecSAndrew Lindesay		} else {
95819c15fecSAndrew Lindesay			package = packages.ItemAt(packageIndex);
959f0665db4SAndrew Lindesay
960f0665db4SAndrew Lindesay			if (Logger::IsDebugEnabled()) {
961f0665db4SAndrew Lindesay				fprintf(stdout, "will populate package icon for [%s]\n",
962f0665db4SAndrew Lindesay					package->Name().String());
963f0665db4SAndrew Lindesay			}
964f0665db4SAndrew Lindesay
96519c15fecSAndrew Lindesay			if (_PopulatePackageIcon(package) == B_OK)
96619c15fecSAndrew Lindesay				countIconsSet++;
96719c15fecSAndrew Lindesay
96819c15fecSAndrew Lindesay			packageIndex++;
96919c15fecSAndrew Lindesay		}
97019c15fecSAndrew Lindesay	}
97119c15fecSAndrew Lindesay}
97219c15fecSAndrew Lindesay
97319c15fecSAndrew Lindesay
9745bd5f161SStephan Aßmusvoid
975c23b641cSAndrew LindesayModel::_PopulatePackagesForDepot(const DepotInfo& depotInfo)
9765bd5f161SStephan Aßmus{
977c23b641cSAndrew Lindesay	BString repositorySourceCode = depotInfo.WebAppRepositorySourceCode();
978c23b641cSAndrew Lindesay	BPath repositorySourcePkgDataPath;
9795bd5f161SStephan Aßmus
980c23b641cSAndrew Lindesay	if (B_OK != _DumpExportPkgDataPath(repositorySourcePkgDataPath,
981c23b641cSAndrew Lindesay		repositorySourceCode)) {
982c23b641cSAndrew Lindesay		printf("unable to obtain the path for storing data for [%s]\n",
983c23b641cSAndrew Lindesay			repositorySourceCode.String());
984c23b641cSAndrew Lindesay	} else {
98587084745SMichael Lotz
986c23b641cSAndrew Lindesay		printf("will fetch and process data for repository source [%s]\n",
987c23b641cSAndrew Lindesay			repositorySourceCode.String());
988e3fdc894SStephan Aßmus
989c23b641cSAndrew Lindesay		PkgDataUpdateProcess process(
990c23b641cSAndrew Lindesay			repositorySourcePkgDataPath,
991c23b641cSAndrew Lindesay			fLock, repositorySourceCode, fPreferredLanguage,
992c23b641cSAndrew Lindesay			depotInfo.Packages(),
993c23b641cSAndrew Lindesay			fCategories);
9945bd5f161SStephan Aßmus
995c23b641cSAndrew Lindesay		process.Run();
9965bd5f161SStephan Aßmus
997c23b641cSAndrew Lindesay		printf("did fetch and process data for repository source [%s]\n",
998c23b641cSAndrew Lindesay			repositorySourceCode.String());
999a3dfc57dSStephan Aßmus	}
1000a3dfc57dSStephan Aßmus}
1001a3dfc57dSStephan Aßmus
1002a3dfc57dSStephan Aßmus
1003e08beebeSStephan Aßmusvoid
1004c23b641cSAndrew LindesayModel::_PopulateAllPackagesThread()
1005e08beebeSStephan Aßmus{
1006c23b641cSAndrew Lindesay	int32 depotIndex = 0;
1007c23b641cSAndrew Lindesay	int32 count = 0;
1008e3fdc894SStephan Aßmus
1009c23b641cSAndrew Lindesay	for (depotIndex = 0;
1010c23b641cSAndrew Lindesay		depotIndex < fDepots.CountItems() && !fStopPopulatingAllPackages;
1011c23b641cSAndrew Lindesay		depotIndex++) {
1012c23b641cSAndrew Lindesay		const DepotInfo& depot = fDepots.ItemAt(depotIndex);
1013f0665db4SAndrew Lindesay
1014c23b641cSAndrew Lindesay		if (depot.WebAppRepositorySourceCode().Length() > 0) {
1015c23b641cSAndrew Lindesay			_PopulatePackagesForDepot(depot);
1016f0665db4SAndrew Lindesay			count++;
1017e3fdc894SStephan Aßmus		}
10186e48c81bSStephan Aßmus	}
101987038da7SStephan Aßmus
1020c23b641cSAndrew Lindesay	printf("did populate package data for %" B_PRIi32 " depots\n", count);
1021e08beebeSStephan Aßmus}
1022e08beebeSStephan Aßmus
1023e08beebeSStephan Aßmus
102419c15fecSAndrew Lindesaystatus_t
102519c15fecSAndrew LindesayModel::_PopulatePackageIcon(const PackageInfoRef& package)
10265bd5f161SStephan Aßmus{
102719c15fecSAndrew Lindesay	BPath bestIconPath;
1028726db742SStephan Aßmus
102919c15fecSAndrew Lindesay	if ( fLocalIconStore.TryFindIconPath(
103019c15fecSAndrew Lindesay		package->Name(), bestIconPath) == B_OK) {
10315bd5f161SStephan Aßmus
103219c15fecSAndrew Lindesay		BFile bestIconFile(bestIconPath.Path(), O_RDONLY);
103319c15fecSAndrew Lindesay		BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(bestIconFile), true);
10346e48c81bSStephan Aßmus		BAutolock locker(&fLock);
10355bd5f161SStephan Aßmus		package->SetIcon(bitmapRef);
103619c15fecSAndrew Lindesay
1037f0665db4SAndrew Lindesay		if (Logger::IsDebugEnabled()) {
1038f0665db4SAndrew Lindesay			fprintf(stdout, "have set the package icon for [%s] from [%s]\n",
1039f0665db4SAndrew Lindesay				package->Name().String(), bestIconPath.Path());
1040f0665db4SAndrew Lindesay		}
104119c15fecSAndrew Lindesay
104219c15fecSAndrew Lindesay		return B_OK;
10435bd5f161SStephan Aßmus	}
104419c15fecSAndrew Lindesay
1045f0665db4SAndrew Lindesay	if (Logger::IsDebugEnabled()) {
1046f0665db4SAndrew Lindesay		fprintf(stdout, "did not set the package icon for [%s]; no data\n",
1047f0665db4SAndrew Lindesay			package->Name().String());
1048f0665db4SAndrew Lindesay	}
104919c15fecSAndrew Lindesay
105019c15fecSAndrew Lindesay	return B_FILE_NOT_FOUND;
10515bd5f161SStephan Aßmus}
1052e3fdc894SStephan Aßmus
1053e3fdc894SStephan Aßmus
105487038da7SStephan Aßmusvoid
105587038da7SStephan AßmusModel::_PopulatePackageScreenshot(const PackageInfoRef& package,
105687038da7SStephan Aßmus	const ScreenshotInfo& info, int32 scaledWidth, bool fromCacheOnly)
105787038da7SStephan Aßmus{
105887038da7SStephan Aßmus	// See if there is a cached screenshot
105987038da7SStephan Aßmus	BFile screenshotFile;
106087038da7SStephan Aßmus	BPath screenshotCachePath;
106187038da7SStephan Aßmus	bool fileExists = false;
106287038da7SStephan Aßmus	BString screenshotName(info.Code());
106387038da7SStephan Aßmus	screenshotName << "@" << scaledWidth;
106487038da7SStephan Aßmus	screenshotName << ".png";
106587038da7SStephan Aßmus	time_t modifiedTime;
106687038da7SStephan Aßmus	if (find_directory(B_USER_CACHE_DIRECTORY, &screenshotCachePath) == B_OK
106787038da7SStephan Aßmus		&& screenshotCachePath.Append("HaikuDepot/Screenshots") == B_OK
106887038da7SStephan Aßmus		&& create_directory(screenshotCachePath.Path(), 0777) == B_OK
106987038da7SStephan Aßmus		&& screenshotCachePath.Append(screenshotName) == B_OK) {
107087038da7SStephan Aßmus		// Try opening the file in read-only mode, which will fail if its
107187038da7SStephan Aßmus		// not a file or does not exist.
107287038da7SStephan Aßmus		fileExists = screenshotFile.SetTo(screenshotCachePath.Path(),
107387038da7SStephan Aßmus			B_READ_ONLY) == B_OK;
107487038da7SStephan Aßmus		if (fileExists)
107587038da7SStephan Aßmus			screenshotFile.GetModificationTime(&modifiedTime);
107687038da7SStephan Aßmus	}
107787038da7SStephan Aßmus
107887038da7SStephan Aßmus	if (fileExists) {
107987038da7SStephan Aßmus		time_t now;
108087038da7SStephan Aßmus		time(&now);
108187038da7SStephan Aßmus		if (fromCacheOnly || now - modifiedTime < 60 * 60) {
108287038da7SStephan Aßmus			// Cache file is recent enough, just use it and return.
108387038da7SStephan Aßmus			BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(screenshotFile),
108487038da7SStephan Aßmus				true);
108587038da7SStephan Aßmus			BAutolock locker(&fLock);
108687038da7SStephan Aßmus			package->AddScreenshot(bitmapRef);
108787038da7SStephan Aßmus			return;
108887038da7SStephan Aßmus		}
108987038da7SStephan Aßmus	}
109087038da7SStephan Aßmus
109187038da7SStephan Aßmus	if (fromCacheOnly)
109287038da7SStephan Aßmus		return;
109387038da7SStephan Aßmus
109487038da7SStephan Aßmus	// Retrieve screenshot from web-app
109587038da7SStephan Aßmus	BMallocIO buffer;
109687038da7SStephan Aßmus
109787038da7SStephan Aßmus	int32 scaledHeight = scaledWidth * info.Height() / info.Width();
109887038da7SStephan Aßmus
109928075ee4SStephan Aßmus	status_t status = fWebAppInterface.RetrieveScreenshot(info.Code(),
110087038da7SStephan Aßmus		scaledWidth, scaledHeight, &buffer);
110187038da7SStephan Aßmus	if (status == B_OK) {
110287038da7SStephan Aßmus		BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(buffer), true);
110387038da7SStephan Aßmus		BAutolock locker(&fLock);
110487038da7SStephan Aßmus		package->AddScreenshot(bitmapRef);
110587038da7SStephan Aßmus		locker.Unlock();
110687038da7SStephan Aßmus		if (screenshotFile.SetTo(screenshotCachePath.Path(),
110787038da7SStephan Aßmus				B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE) == B_OK) {
110887038da7SStephan Aßmus			screenshotFile.Write(buffer.Buffer(), buffer.BufferLength());
110987038da7SStephan Aßmus		}
1110e0881f21SStephan Aßmus	} else {
1111e0881f21SStephan Aßmus		fprintf(stderr, "Failed to retrieve screenshot for code '%s' "
1112e0881f21SStephan Aßmus			"at %" B_PRIi32 "x%" B_PRIi32 ".\n", info.Code().String(),
1113e0881f21SStephan Aßmus			scaledWidth, scaledHeight);
111487038da7SStephan Aßmus	}
111587038da7SStephan Aßmus}
111687038da7SStephan Aßmus
111787038da7SStephan Aßmus
11188f03a0f9SStephan Aßmus// #pragma mark - listener notification methods
11198f03a0f9SStephan Aßmus
11208f03a0f9SStephan Aßmus
11218f03a0f9SStephan Aßmusvoid
11228f03a0f9SStephan AßmusModel::_NotifyAuthorizationChanged()
11238f03a0f9SStephan Aßmus{
11248f03a0f9SStephan Aßmus	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
11258f03a0f9SStephan Aßmus		const ModelListenerRef& listener = fListeners.ItemAtFast(i);
11268f03a0f9SStephan Aßmus		if (listener.Get() != NULL)
11278f03a0f9SStephan Aßmus			listener->AuthorizationChanged();
11288f03a0f9SStephan Aßmus	}
11298f03a0f9SStephan Aßmus}
1130