Model.cpp revision 3094fef3
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"
26f0665db4SAndrew Lindesay#include "StorageUtils.h"
27f0665db4SAndrew Lindesay
28f75ec10dSStephan Aßmus
29f75ec10dSStephan Aßmus#undef B_TRANSLATION_CONTEXT
30f75ec10dSStephan Aßmus#define B_TRANSLATION_CONTEXT "Model"
31f75ec10dSStephan Aßmus
326d367ba4SStephan Aßmus
3389ec84a4SStephan Aßmusstatic const char* kHaikuDepotKeyring = "HaikuDepot";
3489ec84a4SStephan Aßmus
3589ec84a4SStephan Aßmus
368f03a0f9SStephan AßmusPackageFilter::~PackageFilter()
378f03a0f9SStephan Aßmus{
388f03a0f9SStephan Aßmus}
394abd2b71SStephan Aßmus
404abd2b71SStephan Aßmus
418f03a0f9SStephan AßmusModelListener::~ModelListener()
424abd2b71SStephan Aßmus{
434abd2b71SStephan Aßmus}
444abd2b71SStephan Aßmus
454abd2b71SStephan Aßmus
468f03a0f9SStephan Aßmus// #pragma mark - PackageFilters
478f03a0f9SStephan Aßmus
488f03a0f9SStephan Aßmus
494abd2b71SStephan Aßmusclass AnyFilter : public PackageFilter {
504abd2b71SStephan Aßmuspublic:
51779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
524abd2b71SStephan Aßmus	{
534abd2b71SStephan Aßmus		return true;
544abd2b71SStephan Aßmus	}
554abd2b71SStephan Aßmus};
564abd2b71SStephan Aßmus
574abd2b71SStephan Aßmus
584abd2b71SStephan Aßmusclass DepotFilter : public PackageFilter {
594abd2b71SStephan Aßmuspublic:
604abd2b71SStephan Aßmus	DepotFilter(const DepotInfo& depot)
614abd2b71SStephan Aßmus		:
624abd2b71SStephan Aßmus		fDepot(depot)
634abd2b71SStephan Aßmus	{
644abd2b71SStephan Aßmus	}
651f67148fSRene Gollent
66779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
674abd2b71SStephan Aßmus	{
684abd2b71SStephan Aßmus		// TODO: Maybe a PackageInfo ought to know the Depot it came from?
694abd2b71SStephan Aßmus		// But right now the same package could theoretically be provided
704abd2b71SStephan Aßmus		// from different depots and the filter would work correctly.
714abd2b71SStephan Aßmus		// Also the PackageList could actually contain references to packages
724abd2b71SStephan Aßmus		// instead of the packages as objects. The equal operator is quite
734abd2b71SStephan Aßmus		// expensive as is.
74634c2c68SAxel Dörfler		return fDepot.Packages().Contains(package);
754abd2b71SStephan Aßmus	}
761f67148fSRene Gollent
770c169aceSStephan Aßmus	const BString& Depot() const
780c169aceSStephan Aßmus	{
790c169aceSStephan Aßmus		return fDepot.Name();
800c169aceSStephan Aßmus	}
810c169aceSStephan Aßmus
824abd2b71SStephan Aßmusprivate:
834abd2b71SStephan Aßmus	DepotInfo	fDepot;
844abd2b71SStephan Aßmus};
854abd2b71SStephan Aßmus
864abd2b71SStephan Aßmus
8742479955SStephan Aßmusclass CategoryFilter : public PackageFilter {
8842479955SStephan Aßmuspublic:
8942479955SStephan Aßmus	CategoryFilter(const BString& category)
9042479955SStephan Aßmus		:
9142479955SStephan Aßmus		fCategory(category)
9242479955SStephan Aßmus	{
9342479955SStephan Aßmus	}
941f67148fSRene Gollent
95779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
9642479955SStephan Aßmus	{
97779d8213SStephan Aßmus		if (package.Get() == NULL)
98779d8213SStephan Aßmus			return false;
99634c2c68SAxel Dörfler
100779d8213SStephan Aßmus		const CategoryList& categories = package->Categories();
10142479955SStephan Aßmus		for (int i = categories.CountItems() - 1; i >= 0; i--) {
10242479955SStephan Aßmus			const CategoryRef& category = categories.ItemAtFast(i);
10342479955SStephan Aßmus			if (category.Get() == NULL)
10442479955SStephan Aßmus				continue;
10542479955SStephan Aßmus			if (category->Name() == fCategory)
10642479955SStephan Aßmus				return true;
10742479955SStephan Aßmus		}
10842479955SStephan Aßmus		return false;
10942479955SStephan Aßmus	}
11042479955SStephan Aßmus
1110c169aceSStephan Aßmus	const BString& Category() const
1120c169aceSStephan Aßmus	{
1130c169aceSStephan Aßmus		return fCategory;
1140c169aceSStephan Aßmus	}
1150c169aceSStephan Aßmus
11642479955SStephan Aßmusprivate:
11742479955SStephan Aßmus	BString		fCategory;
11842479955SStephan Aßmus};
11942479955SStephan Aßmus
12042479955SStephan Aßmus
1213a6ccc85SStephan Aßmusclass ContainedInFilter : public PackageFilter {
1223a6ccc85SStephan Aßmuspublic:
1233a6ccc85SStephan Aßmus	ContainedInFilter(const PackageList& packageList)
1243a6ccc85SStephan Aßmus		:
1253a6ccc85SStephan Aßmus		fPackageList(packageList)
1263a6ccc85SStephan Aßmus	{
1273a6ccc85SStephan Aßmus	}
1281f67148fSRene Gollent
129779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
1303a6ccc85SStephan Aßmus	{
1313a6ccc85SStephan Aßmus		return fPackageList.Contains(package);
1323a6ccc85SStephan Aßmus	}
1333a6ccc85SStephan Aßmus
1343a6ccc85SStephan Aßmusprivate:
1353a6ccc85SStephan Aßmus	const PackageList&	fPackageList;
1363a6ccc85SStephan Aßmus};
1373a6ccc85SStephan Aßmus
1383a6ccc85SStephan Aßmus
1393a6ccc85SStephan Aßmusclass ContainedInEitherFilter : public PackageFilter {
1403a6ccc85SStephan Aßmuspublic:
1413a6ccc85SStephan Aßmus	ContainedInEitherFilter(const PackageList& packageListA,
1423a6ccc85SStephan Aßmus		const PackageList& packageListB)
1433a6ccc85SStephan Aßmus		:
1443a6ccc85SStephan Aßmus		fPackageListA(packageListA),
1453a6ccc85SStephan Aßmus		fPackageListB(packageListB)
1463a6ccc85SStephan Aßmus	{
1473a6ccc85SStephan Aßmus	}
1481f67148fSRene Gollent
149779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
1503a6ccc85SStephan Aßmus	{
1513a6ccc85SStephan Aßmus		return fPackageListA.Contains(package)
1523a6ccc85SStephan Aßmus			|| fPackageListB.Contains(package);
1533a6ccc85SStephan Aßmus	}
1543a6ccc85SStephan Aßmus
1553a6ccc85SStephan Aßmusprivate:
1563a6ccc85SStephan Aßmus	const PackageList&	fPackageListA;
1573a6ccc85SStephan Aßmus	const PackageList&	fPackageListB;
1583a6ccc85SStephan Aßmus};
1593a6ccc85SStephan Aßmus
1603a6ccc85SStephan Aßmus
161634c2c68SAxel Dörflerclass NotContainedInFilter : public PackageFilter {
162634c2c68SAxel Dörflerpublic:
163634c2c68SAxel Dörfler	NotContainedInFilter(const PackageList* packageList, ...)
164634c2c68SAxel Dörfler	{
165634c2c68SAxel Dörfler		va_list args;
166634c2c68SAxel Dörfler		va_start(args, packageList);
167634c2c68SAxel Dörfler		while (true) {
168634c2c68SAxel Dörfler			const PackageList* packageList = va_arg(args, const PackageList*);
169634c2c68SAxel Dörfler			if (packageList == NULL)
170634c2c68SAxel Dörfler				break;
171634c2c68SAxel Dörfler			fPackageLists.Add(packageList);
172634c2c68SAxel Dörfler		}
173634c2c68SAxel Dörfler		va_end(args);
174634c2c68SAxel Dörfler	}
175634c2c68SAxel Dörfler
176634c2c68SAxel Dörfler	virtual bool AcceptsPackage(const PackageInfoRef& package) const
177634c2c68SAxel Dörfler	{
178be298474SAndrew Lindesay		if (package.Get() == NULL)
179634c2c68SAxel Dörfler			return false;
180a10ad54cSJonathan Schleifer
18172992391SStephan Aßmus		printf("TEST %s\n", package->Name().String());
182a10ad54cSJonathan Schleifer
183634c2c68SAxel Dörfler		for (int32 i = 0; i < fPackageLists.CountItems(); i++) {
184634c2c68SAxel Dörfler			if (fPackageLists.ItemAtFast(i)->Contains(package)) {
185998f4043SJonathan Schleifer				printf("  contained in %" B_PRId32 "\n", i);
186634c2c68SAxel Dörfler				return false;
187634c2c68SAxel Dörfler			}
188634c2c68SAxel Dörfler		}
189634c2c68SAxel Dörfler		return true;
190634c2c68SAxel Dörfler	}
191634c2c68SAxel Dörfler
192634c2c68SAxel Dörflerprivate:
193634c2c68SAxel Dörfler	List<const PackageList*, true>	fPackageLists;
194634c2c68SAxel Dörfler};
195634c2c68SAxel Dörfler
196634c2c68SAxel Dörfler
197634c2c68SAxel Dörflerclass StateFilter : public PackageFilter {
198634c2c68SAxel Dörflerpublic:
199634c2c68SAxel Dörfler	StateFilter(PackageState state)
200634c2c68SAxel Dörfler		:
201634c2c68SAxel Dörfler		fState(state)
202634c2c68SAxel Dörfler	{
203634c2c68SAxel Dörfler	}
204634c2c68SAxel Dörfler
205634c2c68SAxel Dörfler	virtual bool AcceptsPackage(const PackageInfoRef& package) const
206634c2c68SAxel Dörfler	{
207634c2c68SAxel Dörfler		return package->State() == NONE;
208634c2c68SAxel Dörfler	}
209634c2c68SAxel Dörfler
210634c2c68SAxel Dörflerprivate:
211634c2c68SAxel Dörfler	PackageState	fState;
212634c2c68SAxel Dörfler};
213634c2c68SAxel Dörfler
214634c2c68SAxel Dörfler
21509d44cecSStephan Aßmusclass SearchTermsFilter : public PackageFilter {
21609d44cecSStephan Aßmuspublic:
21709d44cecSStephan Aßmus	SearchTermsFilter(const BString& searchTerms)
21809d44cecSStephan Aßmus	{
21909d44cecSStephan Aßmus		// Separate the string into terms at spaces
22009d44cecSStephan Aßmus		int32 index = 0;
22109d44cecSStephan Aßmus		while (index < searchTerms.Length()) {
22209d44cecSStephan Aßmus			int32 nextSpace = searchTerms.FindFirst(" ", index);
22309d44cecSStephan Aßmus			if (nextSpace < 0)
22409d44cecSStephan Aßmus				nextSpace = searchTerms.Length();
22509d44cecSStephan Aßmus			if (nextSpace > index) {
22609d44cecSStephan Aßmus				BString term;
22709d44cecSStephan Aßmus				searchTerms.CopyInto(term, index, nextSpace - index);
22809d44cecSStephan Aßmus				term.ToLower();
22909d44cecSStephan Aßmus				fSearchTerms.Add(term);
23009d44cecSStephan Aßmus			}
23109d44cecSStephan Aßmus			index = nextSpace + 1;
23209d44cecSStephan Aßmus		}
23309d44cecSStephan Aßmus	}
2341f67148fSRene Gollent
235779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
23609d44cecSStephan Aßmus	{
237779d8213SStephan Aßmus		if (package.Get() == NULL)
238779d8213SStephan Aßmus			return false;
23909d44cecSStephan Aßmus		// Every search term must be found in one of the package texts
24009d44cecSStephan Aßmus		for (int32 i = fSearchTerms.CountItems() - 1; i >= 0; i--) {
24109d44cecSStephan Aßmus			const BString& term = fSearchTerms.ItemAtFast(i);
24272992391SStephan Aßmus			if (!_TextContains(package->Name(), term)
24372992391SStephan Aßmus				&& !_TextContains(package->Title(), term)
244779d8213SStephan Aßmus				&& !_TextContains(package->Publisher().Name(), term)
245779d8213SStephan Aßmus				&& !_TextContains(package->ShortDescription(), term)
246779d8213SStephan Aßmus				&& !_TextContains(package->FullDescription(), term)) {
24709d44cecSStephan Aßmus				return false;
24809d44cecSStephan Aßmus			}
24909d44cecSStephan Aßmus		}
25009d44cecSStephan Aßmus		return true;
25109d44cecSStephan Aßmus	}
25209d44cecSStephan Aßmus
2530c169aceSStephan Aßmus	BString SearchTerms() const
2540c169aceSStephan Aßmus	{
2550c169aceSStephan Aßmus		BString searchTerms;
2560c169aceSStephan Aßmus		for (int32 i = 0; i < fSearchTerms.CountItems(); i++) {
2570c169aceSStephan Aßmus			const BString& term = fSearchTerms.ItemAtFast(i);
2580c169aceSStephan Aßmus			if (term.IsEmpty())
2590c169aceSStephan Aßmus				continue;
2600c169aceSStephan Aßmus			if (!searchTerms.IsEmpty())
2610c169aceSStephan Aßmus				searchTerms.Append(" ");
2620c169aceSStephan Aßmus			searchTerms.Append(term);
2630c169aceSStephan Aßmus		}
2640c169aceSStephan Aßmus		return searchTerms;
2650c169aceSStephan Aßmus	}
2660c169aceSStephan Aßmus
26709d44cecSStephan Aßmusprivate:
26809d44cecSStephan Aßmus	bool _TextContains(BString text, const BString& string) const
26909d44cecSStephan Aßmus	{
27009d44cecSStephan Aßmus		text.ToLower();
27109d44cecSStephan Aßmus		int32 index = text.FindFirst(string);
27209d44cecSStephan Aßmus		return index >= 0;
27309d44cecSStephan Aßmus	}
27409d44cecSStephan Aßmus
27509d44cecSStephan Aßmusprivate:
2760c169aceSStephan Aßmus	StringList fSearchTerms;
27709d44cecSStephan Aßmus};
27809d44cecSStephan Aßmus
27909d44cecSStephan Aßmus
28012107be2SStephan Aßmusclass IsFeaturedFilter : public PackageFilter {
28112107be2SStephan Aßmuspublic:
28212107be2SStephan Aßmus	IsFeaturedFilter()
28312107be2SStephan Aßmus	{
28412107be2SStephan Aßmus	}
28512107be2SStephan Aßmus
28612107be2SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
28712107be2SStephan Aßmus	{
28812107be2SStephan Aßmus		return package.Get() != NULL && package->IsProminent();
28912107be2SStephan Aßmus	}
29012107be2SStephan Aßmus};
29112107be2SStephan Aßmus
29212107be2SStephan Aßmus
293870554acSStephan Aßmusstatic inline bool
294870554acSStephan Aßmusis_source_package(const PackageInfoRef& package)
295870554acSStephan Aßmus{
29672992391SStephan Aßmus	const BString& packageName = package->Name();
297870554acSStephan Aßmus	return packageName.EndsWith("_source");
298870554acSStephan Aßmus}
299870554acSStephan Aßmus
300870554acSStephan Aßmus
301870554acSStephan Aßmusstatic inline bool
302870554acSStephan Aßmusis_develop_package(const PackageInfoRef& package)
303870554acSStephan Aßmus{
30472992391SStephan Aßmus	const BString& packageName = package->Name();
30574e41611SJulian Harnath	return packageName.EndsWith("_devel")
30674e41611SJulian Harnath		|| packageName.EndsWith("_debuginfo");
307870554acSStephan Aßmus}
308870554acSStephan Aßmus
309870554acSStephan Aßmus
3104abd2b71SStephan Aßmus// #pragma mark - Model
3114abd2b71SStephan Aßmus
3124abd2b71SStephan Aßmus
3136d367ba4SStephan AßmusModel::Model()
3146d367ba4SStephan Aßmus	:
315f75ec10dSStephan Aßmus	fDepots(),
316f75ec10dSStephan Aßmus
317f75ec10dSStephan Aßmus	fCategoryAudio(new PackageCategory(
318f75ec10dSStephan Aßmus		BitmapRef(),
319f75ec10dSStephan Aßmus		B_TRANSLATE("Audio"), "audio"), true),
3204a02fc4fSStephan Aßmus	fCategoryBusiness(new PackageCategory(
321f75ec10dSStephan Aßmus		BitmapRef(),
3224a02fc4fSStephan Aßmus		B_TRANSLATE("Business"), "business"), true),
3234a02fc4fSStephan Aßmus	fCategoryDevelopment(new PackageCategory(
3244a02fc4fSStephan Aßmus		BitmapRef(),
3254a02fc4fSStephan Aßmus		B_TRANSLATE("Development"), "development"), true),
3264a02fc4fSStephan Aßmus	fCategoryEducation(new PackageCategory(
3274a02fc4fSStephan Aßmus		BitmapRef(),
3284a02fc4fSStephan Aßmus		B_TRANSLATE("Education"), "education"), true),
3294a02fc4fSStephan Aßmus	fCategoryGames(new PackageCategory(
3304a02fc4fSStephan Aßmus		BitmapRef(),
3314a02fc4fSStephan Aßmus		B_TRANSLATE("Games"), "games"), true),
332f75ec10dSStephan Aßmus	fCategoryGraphics(new PackageCategory(
333f75ec10dSStephan Aßmus		BitmapRef(),
334f75ec10dSStephan Aßmus		B_TRANSLATE("Graphics"), "graphics"), true),
3354a02fc4fSStephan Aßmus	fCategoryInternetAndNetwork(new PackageCategory(
3364a02fc4fSStephan Aßmus		BitmapRef(),
3374a02fc4fSStephan Aßmus		B_TRANSLATE("Internet & Network"), "internetandnetwork"), true),
338f75ec10dSStephan Aßmus	fCategoryProductivity(new PackageCategory(
339f75ec10dSStephan Aßmus		BitmapRef(),
340f75ec10dSStephan Aßmus		B_TRANSLATE("Productivity"), "productivity"), true),
3414a02fc4fSStephan Aßmus	fCategoryScienceAndMathematics(new PackageCategory(
342f75ec10dSStephan Aßmus		BitmapRef(),
3434a02fc4fSStephan Aßmus		B_TRANSLATE("Science & Mathematics"), "scienceandmathematics"), true),
3444a02fc4fSStephan Aßmus	fCategorySystemAndUtilities(new PackageCategory(
345f75ec10dSStephan Aßmus		BitmapRef(),
3464a02fc4fSStephan Aßmus		B_TRANSLATE("System & Utilities"), "systemandutilities"), true),
3474a02fc4fSStephan Aßmus	fCategoryVideo(new PackageCategory(
3484abd2b71SStephan Aßmus		BitmapRef(),
3494a02fc4fSStephan Aßmus		B_TRANSLATE("Video"), "video"), true),
3504abd2b71SStephan Aßmus
3514abd2b71SStephan Aßmus	fCategoryFilter(PackageFilterRef(new AnyFilter(), true)),
35209d44cecSStephan Aßmus	fDepotFilter(""),
353870554acSStephan Aßmus	fSearchTermsFilter(PackageFilterRef(new AnyFilter(), true)),
35412107be2SStephan Aßmus	fIsFeaturedFilter(),
355634c2c68SAxel Dörfler
3564fade61fSStephan Aßmus	fShowFeaturedPackages(true),
357ab172803SStephan Aßmus	fShowAvailablePackages(true),
3584fade61fSStephan Aßmus	fShowInstalledPackages(true),
359870554acSStephan Aßmus	fShowSourcePackages(false),
3603094fef3SAndrew Lindesay	fShowDevelopPackages(false)
3616d367ba4SStephan Aßmus{
36212107be2SStephan Aßmus	_UpdateIsFeaturedFilter();
36312107be2SStephan Aßmus
364633ed4c4SStephan Aßmus	// Don't forget to add new categories to this list:
3654a02fc4fSStephan Aßmus	fCategories.Add(fCategoryGames);
3664a02fc4fSStephan Aßmus	fCategories.Add(fCategoryBusiness);
367633ed4c4SStephan Aßmus	fCategories.Add(fCategoryAudio);
368633ed4c4SStephan Aßmus	fCategories.Add(fCategoryVideo);
369633ed4c4SStephan Aßmus	fCategories.Add(fCategoryGraphics);
3704a02fc4fSStephan Aßmus	fCategories.Add(fCategoryEducation);
371633ed4c4SStephan Aßmus	fCategories.Add(fCategoryProductivity);
3724a02fc4fSStephan Aßmus	fCategories.Add(fCategorySystemAndUtilities);
3734a02fc4fSStephan Aßmus	fCategories.Add(fCategoryInternetAndNetwork);
374633ed4c4SStephan Aßmus	fCategories.Add(fCategoryDevelopment);
3754a02fc4fSStephan Aßmus	fCategories.Add(fCategoryScienceAndMathematics);
3762b411c02SStephan Aßmus	// TODO: The server will eventually support an API to
3772b411c02SStephan Aßmus	// get the defined categories and their translated names.
3782b411c02SStephan Aßmus	// This should then be used instead of hard-coded
3792b411c02SStephan Aßmus	// categories and translations in the app.
380d07ca069SStephan Aßmus
381d07ca069SStephan Aßmus	fPreferredLanguage = "en";
382d07ca069SStephan Aßmus	BLocaleRoster* localeRoster = BLocaleRoster::Default();
383d07ca069SStephan Aßmus	if (localeRoster != NULL) {
384d07ca069SStephan Aßmus		BMessage preferredLanguages;
385d07ca069SStephan Aßmus		if (localeRoster->GetPreferredLanguages(&preferredLanguages) == B_OK) {
386d07ca069SStephan Aßmus			BString language;
387d07ca069SStephan Aßmus			if (preferredLanguages.FindString("language", 0, &language) == B_OK)
388d07ca069SStephan Aßmus				language.CopyInto(fPreferredLanguage, 0, 2);
389d07ca069SStephan Aßmus		}
390d07ca069SStephan Aßmus	}
3914ff3d17aSStephan Aßmus
3924ff3d17aSStephan Aßmus	// TODO: Fetch this from the web-app.
3934ff3d17aSStephan Aßmus	fSupportedLanguages.Add("en");
3944ff3d17aSStephan Aßmus	fSupportedLanguages.Add("de");
3954ff3d17aSStephan Aßmus	fSupportedLanguages.Add("fr");
3964ff3d17aSStephan Aßmus	fSupportedLanguages.Add("ja");
3974ff3d17aSStephan Aßmus	fSupportedLanguages.Add("es");
3984ff3d17aSStephan Aßmus	fSupportedLanguages.Add("zh");
3994ff3d17aSStephan Aßmus	fSupportedLanguages.Add("pt");
4004ff3d17aSStephan Aßmus	fSupportedLanguages.Add("ru");
4014ff3d17aSStephan Aßmus
4024ff3d17aSStephan Aßmus	if (!fSupportedLanguages.Contains(fPreferredLanguage)) {
40312f29d44SStephan Aßmus		// Force the preferred language to one of the currently supported
40412f29d44SStephan Aßmus		// ones, until the web application supports all ISO language codes.
4054ff3d17aSStephan Aßmus		printf("User preferred language '%s' not currently supported, "
4064ff3d17aSStephan Aßmus			"defaulting to 'en'.", fPreferredLanguage.String());
40712f29d44SStephan Aßmus		fPreferredLanguage = "en";
40812f29d44SStephan Aßmus	}
40928075ee4SStephan Aßmus	fWebAppInterface.SetPreferredLanguage(fPreferredLanguage);
4106d367ba4SStephan Aßmus}
4116d367ba4SStephan Aßmus
4126d367ba4SStephan Aßmus
4135bd5f161SStephan AßmusModel::~Model()
4145bd5f161SStephan Aßmus{
4155bd5f161SStephan Aßmus}
4165bd5f161SStephan Aßmus
4175bd5f161SStephan Aßmus
4188f03a0f9SStephan Aßmusbool
4198f03a0f9SStephan AßmusModel::AddListener(const ModelListenerRef& listener)
4208f03a0f9SStephan Aßmus{
4218f03a0f9SStephan Aßmus	return fListeners.Add(listener);
4228f03a0f9SStephan Aßmus}
4238f03a0f9SStephan Aßmus
4248f03a0f9SStephan Aßmus
425706edd80SStephan AßmusPackageList
4266d367ba4SStephan AßmusModel::CreatePackageList() const
4276d367ba4SStephan Aßmus{
428ab172803SStephan Aßmus	// Iterate all packages from all depots.
429ab172803SStephan Aßmus	// If configured, restrict depot, filter by search terms, status, name ...
430706edd80SStephan Aßmus	PackageList resultList;
4316d367ba4SStephan Aßmus
4326d367ba4SStephan Aßmus	for (int32 i = 0; i < fDepots.CountItems(); i++) {
43309d44cecSStephan Aßmus		const DepotInfo& depot = fDepots.ItemAtFast(i);
4341f67148fSRene Gollent
43509d44cecSStephan Aßmus		if (fDepotFilter.Length() > 0 && fDepotFilter != depot.Name())
43609d44cecSStephan Aßmus			continue;
4371f67148fSRene Gollent
43809d44cecSStephan Aßmus		const PackageList& packages = depot.Packages();
4396d367ba4SStephan Aßmus
440706edd80SStephan Aßmus		for (int32 j = 0; j < packages.CountItems(); j++) {
441779d8213SStephan Aßmus			const PackageInfoRef& package = packages.ItemAtFast(j);
4424abd2b71SStephan Aßmus			if (fCategoryFilter->AcceptsPackage(package)
443870554acSStephan Aßmus				&& fSearchTermsFilter->AcceptsPackage(package)
44412107be2SStephan Aßmus				&& fIsFeaturedFilter->AcceptsPackage(package)
445ab172803SStephan Aßmus				&& (fShowAvailablePackages || package->State() != NONE)
446ab172803SStephan Aßmus				&& (fShowInstalledPackages || package->State() != ACTIVATED)
447870554acSStephan Aßmus				&& (fShowSourcePackages || !is_source_package(package))
448870554acSStephan Aßmus				&& (fShowDevelopPackages || !is_develop_package(package))) {
4494abd2b71SStephan Aßmus				resultList.Add(package);
4504abd2b71SStephan Aßmus			}
4516d367ba4SStephan Aßmus		}
4526d367ba4SStephan Aßmus	}
4536d367ba4SStephan Aßmus
4546d367ba4SStephan Aßmus	return resultList;
4556d367ba4SStephan Aßmus}
4566d367ba4SStephan Aßmus
4576d367ba4SStephan Aßmus
4586d367ba4SStephan Aßmusbool
4596d367ba4SStephan AßmusModel::AddDepot(const DepotInfo& depot)
4606d367ba4SStephan Aßmus{
4616d367ba4SStephan Aßmus	return fDepots.Add(depot);
4626d367ba4SStephan Aßmus}
4636d367ba4SStephan Aßmus
46442479955SStephan Aßmus
4657b2c59b9SStephan Aßmusbool
4667b2c59b9SStephan AßmusModel::HasDepot(const BString& name) const
467ebb1a01cSAndrew Lindesay{
468ebb1a01cSAndrew Lindesay	return NULL != DepotForName(name);
469ebb1a01cSAndrew Lindesay}
470ebb1a01cSAndrew Lindesay
471ebb1a01cSAndrew Lindesay
472ebb1a01cSAndrew Lindesayconst DepotInfo*
473ebb1a01cSAndrew LindesayModel::DepotForName(const BString& name) const
4747b2c59b9SStephan Aßmus{
4757b2c59b9SStephan Aßmus	for (int32 i = fDepots.CountItems() - 1; i >= 0; i--) {
4767b2c59b9SStephan Aßmus		if (fDepots.ItemAtFast(i).Name() == name)
477ebb1a01cSAndrew Lindesay			return &fDepots.ItemAtFast(i);
4787b2c59b9SStephan Aßmus	}
479ebb1a01cSAndrew Lindesay	return NULL;
4807b2c59b9SStephan Aßmus}
4817b2c59b9SStephan Aßmus
4827b2c59b9SStephan Aßmus
4837b2c59b9SStephan Aßmusbool
4847b2c59b9SStephan AßmusModel::SyncDepot(const DepotInfo& depot)
4857b2c59b9SStephan Aßmus{
4867b2c59b9SStephan Aßmus	for (int32 i = fDepots.CountItems() - 1; i >= 0; i--) {
4877b2c59b9SStephan Aßmus		const DepotInfo& existingDepot = fDepots.ItemAtFast(i);
4887b2c59b9SStephan Aßmus		if (existingDepot.Name() == depot.Name()) {
4897b2c59b9SStephan Aßmus			DepotInfo mergedDepot(existingDepot);
4907b2c59b9SStephan Aßmus			mergedDepot.SyncPackages(depot.Packages());
4917b2c59b9SStephan Aßmus			fDepots.Replace(i, mergedDepot);
4927b2c59b9SStephan Aßmus			return true;
4937b2c59b9SStephan Aßmus		}
4947b2c59b9SStephan Aßmus	}
4957b2c59b9SStephan Aßmus	return false;
4967b2c59b9SStephan Aßmus}
4977b2c59b9SStephan Aßmus
4987b2c59b9SStephan Aßmus
4992c287f5bSRene Gollentvoid
5002c287f5bSRene GollentModel::Clear()
5012c287f5bSRene Gollent{
5022c287f5bSRene Gollent	fDepots.Clear();
5032c287f5bSRene Gollent}
5042c287f5bSRene Gollent
5052c287f5bSRene Gollent
506706edd80SStephan Aßmusvoid
507779d8213SStephan AßmusModel::SetPackageState(const PackageInfoRef& package, PackageState state)
508706edd80SStephan Aßmus{
509706edd80SStephan Aßmus	switch (state) {
510706edd80SStephan Aßmus		default:
511706edd80SStephan Aßmus		case NONE:
512706edd80SStephan Aßmus			fInstalledPackages.Remove(package);
513706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
514706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
515706edd80SStephan Aßmus			break;
516706edd80SStephan Aßmus		case INSTALLED:
517706edd80SStephan Aßmus			if (!fInstalledPackages.Contains(package))
518706edd80SStephan Aßmus				fInstalledPackages.Add(package);
519706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
520706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
521706edd80SStephan Aßmus			break;
522706edd80SStephan Aßmus		case ACTIVATED:
523706edd80SStephan Aßmus			if (!fInstalledPackages.Contains(package))
524706edd80SStephan Aßmus				fInstalledPackages.Add(package);
525706edd80SStephan Aßmus			if (!fActivatedPackages.Contains(package))
526706edd80SStephan Aßmus				fActivatedPackages.Add(package);
527706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
528706edd80SStephan Aßmus			break;
529706edd80SStephan Aßmus		case UNINSTALLED:
530706edd80SStephan Aßmus			fInstalledPackages.Remove(package);
531706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
532ab172803SStephan Aßmus			if (!fUninstalledPackages.Contains(package))
533ab172803SStephan Aßmus				fUninstalledPackages.Add(package);
534706edd80SStephan Aßmus			break;
535706edd80SStephan Aßmus	}
536b4c8d2ffSRene Gollent
537b4c8d2ffSRene Gollent	package->SetState(state);
538706edd80SStephan Aßmus}
539706edd80SStephan Aßmus
540706edd80SStephan Aßmus
54109d44cecSStephan Aßmus// #pragma mark - filters
54209d44cecSStephan Aßmus
54309d44cecSStephan Aßmus
54442479955SStephan Aßmusvoid
54542479955SStephan AßmusModel::SetCategory(const BString& category)
54642479955SStephan Aßmus{
5473a6ccc85SStephan Aßmus	PackageFilter* filter;
5481f67148fSRene Gollent
54942479955SStephan Aßmus	if (category.Length() == 0)
5503a6ccc85SStephan Aßmus		filter = new AnyFilter();
55142479955SStephan Aßmus	else
5523a6ccc85SStephan Aßmus		filter = new CategoryFilter(category);
5533a6ccc85SStephan Aßmus
5543a6ccc85SStephan Aßmus	fCategoryFilter.SetTo(filter, true);
55542479955SStephan Aßmus}
55642479955SStephan Aßmus
55709d44cecSStephan Aßmus
5580c169aceSStephan AßmusBString
5590c169aceSStephan AßmusModel::Category() const
5600c169aceSStephan Aßmus{
5610c169aceSStephan Aßmus	CategoryFilter* filter
5620c169aceSStephan Aßmus		= dynamic_cast<CategoryFilter*>(fCategoryFilter.Get());
5630c169aceSStephan Aßmus	if (filter == NULL)
5640c169aceSStephan Aßmus		return "";
5650c169aceSStephan Aßmus	return filter->Category();
5660c169aceSStephan Aßmus}
5670c169aceSStephan Aßmus
5680c169aceSStephan Aßmus
56909d44cecSStephan Aßmusvoid
57009d44cecSStephan AßmusModel::SetDepot(const BString& depot)
57109d44cecSStephan Aßmus{
57209d44cecSStephan Aßmus	fDepotFilter = depot;
57309d44cecSStephan Aßmus}
57409d44cecSStephan Aßmus
57509d44cecSStephan Aßmus
5760c169aceSStephan AßmusBString
5770c169aceSStephan AßmusModel::Depot() const
5780c169aceSStephan Aßmus{
5790c169aceSStephan Aßmus	return fDepotFilter;
5800c169aceSStephan Aßmus}
5810c169aceSStephan Aßmus
5820c169aceSStephan Aßmus
58309d44cecSStephan Aßmusvoid
58409d44cecSStephan AßmusModel::SetSearchTerms(const BString& searchTerms)
58509d44cecSStephan Aßmus{
58609d44cecSStephan Aßmus	PackageFilter* filter;
5871f67148fSRene Gollent
58809d44cecSStephan Aßmus	if (searchTerms.Length() == 0)
58909d44cecSStephan Aßmus		filter = new AnyFilter();
59009d44cecSStephan Aßmus	else
59109d44cecSStephan Aßmus		filter = new SearchTermsFilter(searchTerms);
59209d44cecSStephan Aßmus
59309d44cecSStephan Aßmus	fSearchTermsFilter.SetTo(filter, true);
59412107be2SStephan Aßmus	_UpdateIsFeaturedFilter();
59509d44cecSStephan Aßmus}
59609d44cecSStephan Aßmus
5977eec822dSStephan Aßmus
5980c169aceSStephan AßmusBString
5990c169aceSStephan AßmusModel::SearchTerms() const
6000c169aceSStephan Aßmus{
6010c169aceSStephan Aßmus	SearchTermsFilter* filter
6020c169aceSStephan Aßmus		= dynamic_cast<SearchTermsFilter*>(fSearchTermsFilter.Get());
6030c169aceSStephan Aßmus	if (filter == NULL)
6040c169aceSStephan Aßmus		return "";
6050c169aceSStephan Aßmus	return filter->SearchTerms();
6060c169aceSStephan Aßmus}
6070c169aceSStephan Aßmus
6080c169aceSStephan Aßmus
6094fade61fSStephan Aßmusvoid
6104fade61fSStephan AßmusModel::SetShowFeaturedPackages(bool show)
6114fade61fSStephan Aßmus{
6124fade61fSStephan Aßmus	fShowFeaturedPackages = show;
61312107be2SStephan Aßmus	_UpdateIsFeaturedFilter();
6144fade61fSStephan Aßmus}
6154fade61fSStephan Aßmus
6164fade61fSStephan Aßmus
617ab172803SStephan Aßmusvoid
618ab172803SStephan AßmusModel::SetShowAvailablePackages(bool show)
619ab172803SStephan Aßmus{
620ab172803SStephan Aßmus	fShowAvailablePackages = show;
621ab172803SStephan Aßmus}
622ab172803SStephan Aßmus
623ab172803SStephan Aßmus
624ab172803SStephan Aßmusvoid
625ab172803SStephan AßmusModel::SetShowInstalledPackages(bool show)
626ab172803SStephan Aßmus{
627ab172803SStephan Aßmus	fShowInstalledPackages = show;
628ab172803SStephan Aßmus}
629ab172803SStephan Aßmus
630ab172803SStephan Aßmus
631870554acSStephan Aßmusvoid
632870554acSStephan AßmusModel::SetShowSourcePackages(bool show)
633870554acSStephan Aßmus{
634870554acSStephan Aßmus	fShowSourcePackages = show;
635870554acSStephan Aßmus}
636870554acSStephan Aßmus
637870554acSStephan Aßmus
638870554acSStephan Aßmusvoid
639870554acSStephan AßmusModel::SetShowDevelopPackages(bool show)
640870554acSStephan Aßmus{
641870554acSStephan Aßmus	fShowDevelopPackages = show;
642870554acSStephan Aßmus}
643870554acSStephan Aßmus
644870554acSStephan Aßmus
64519c15fecSAndrew Lindesay// #pragma mark - information retrieval
6467eec822dSStephan Aßmus
6477eec822dSStephan Aßmus
6487eec822dSStephan Aßmusvoid
649a60a23f8SStephan AßmusModel::PopulatePackage(const PackageInfoRef& package, uint32 flags)
6507eec822dSStephan Aßmus{
6517eec822dSStephan Aßmus	// TODO: There should probably also be a way to "unpopulate" the
6527eec822dSStephan Aßmus	// package information. Maybe a cache of populated packages, so that
6537eec822dSStephan Aßmus	// packages loose their extra information after a certain amount of
6547eec822dSStephan Aßmus	// time when they have not been accessed/displayed in the UI. Otherwise
6557eec822dSStephan Aßmus	// HaikuDepot will consume more and more resources in the packages.
6567eec822dSStephan Aßmus	// Especially screen-shots will be a problem eventually.
65748045f5aSStephan Aßmus	{
65848045f5aSStephan Aßmus		BAutolock locker(&fLock);
65989e4ba17SStephan Aßmus		bool alreadyPopulated = fPopulatedPackages.Contains(package);
66089e4ba17SStephan Aßmus		if ((flags & POPULATE_FORCE) == 0 && alreadyPopulated)
66148045f5aSStephan Aßmus			return;
66289e4ba17SStephan Aßmus		if (!alreadyPopulated)
66389e4ba17SStephan Aßmus			fPopulatedPackages.Add(package);
66448045f5aSStephan Aßmus	}
6657eec822dSStephan Aßmus
6667ff94fd0SStephan Aßmus	if ((flags & POPULATE_USER_RATINGS) != 0) {
6677ff94fd0SStephan Aßmus		// Retrieve info from web-app
6687ff94fd0SStephan Aßmus		BMessage info;
66948045f5aSStephan Aßmus
67048045f5aSStephan Aßmus		BString packageName;
67187084745SMichael Lotz		BString architecture;
67248045f5aSStephan Aßmus		{
67348045f5aSStephan Aßmus			BAutolock locker(&fLock);
67472992391SStephan Aßmus			packageName = package->Name();
67548045f5aSStephan Aßmus			architecture = package->Architecture();
67648045f5aSStephan Aßmus		}
67787084745SMichael Lotz
67828075ee4SStephan Aßmus		status_t status = fWebAppInterface.RetrieveUserRatings(packageName,
67948045f5aSStephan Aßmus			architecture, 0, 50, info);
6807ff94fd0SStephan Aßmus		if (status == B_OK) {
6817ff94fd0SStephan Aßmus			// Parse message
6827ff94fd0SStephan Aßmus			BMessage result;
6837ff94fd0SStephan Aßmus			BMessage items;
6847ff94fd0SStephan Aßmus			if (info.FindMessage("result", &result) == B_OK
6857ff94fd0SStephan Aßmus				&& result.FindMessage("items", &items) == B_OK) {
68648045f5aSStephan Aßmus
68748045f5aSStephan Aßmus				BAutolock locker(&fLock);
6887eb09bfaSStephan Aßmus				package->ClearUserRatings();
68948045f5aSStephan Aßmus
6907ff94fd0SStephan Aßmus				int index = 0;
6917ff94fd0SStephan Aßmus				while (true) {
6927ff94fd0SStephan Aßmus					BString name;
6937ff94fd0SStephan Aßmus					name << index++;
69487084745SMichael Lotz
6957ff94fd0SStephan Aßmus					BMessage item;
6967ff94fd0SStephan Aßmus					if (items.FindMessage(name, &item) != B_OK)
6977ff94fd0SStephan Aßmus						break;
6987ff94fd0SStephan Aßmus
6997ff94fd0SStephan Aßmus					BString user;
7007ff94fd0SStephan Aßmus					BMessage userInfo;
7017ff94fd0SStephan Aßmus					if (item.FindMessage("user", &userInfo) != B_OK
7027ff94fd0SStephan Aßmus						|| userInfo.FindString("nickname", &user) != B_OK) {
7037ff94fd0SStephan Aßmus						// Ignore, we need the user name
7047ff94fd0SStephan Aßmus						continue;
7057ff94fd0SStephan Aßmus					}
7067ff94fd0SStephan Aßmus
70736a9b557SStephan Aßmus					// Extract basic info, all items are optional
7087ff94fd0SStephan Aßmus					BString languageCode;
7097ff94fd0SStephan Aßmus					BString comment;
7107ff94fd0SStephan Aßmus					double rating;
71136a9b557SStephan Aßmus					item.FindString("naturalLanguageCode", &languageCode);
71236a9b557SStephan Aßmus					item.FindString("comment", &comment);
71336a9b557SStephan Aßmus					if (item.FindDouble("rating", &rating) != B_OK)
71436a9b557SStephan Aßmus						rating = -1;
71536a9b557SStephan Aßmus					if (comment.Length() == 0 && rating == -1) {
71636a9b557SStephan Aßmus						// No useful information given.
7177ff94fd0SStephan Aßmus						continue;
7187ff94fd0SStephan Aßmus					}
71936a9b557SStephan Aßmus
7207ff94fd0SStephan Aßmus					// For which version of the package was the rating?
7217ff94fd0SStephan Aßmus					BString major = "?";
7227ff94fd0SStephan Aßmus					BString minor = "?";
7237ff94fd0SStephan Aßmus					BString micro = "";
7247ff94fd0SStephan Aßmus					BMessage version;
7257ff94fd0SStephan Aßmus					if (item.FindMessage("pkgVersion", &version) == B_OK) {
7267ff94fd0SStephan Aßmus						version.FindString("major", &major);
7277ff94fd0SStephan Aßmus						version.FindString("minor", &minor);
7287ff94fd0SStephan Aßmus						version.FindString("micro", &micro);
7297ff94fd0SStephan Aßmus					}
7307ff94fd0SStephan Aßmus					BString versionString = major;
7317ff94fd0SStephan Aßmus					versionString << ".";
7327ff94fd0SStephan Aßmus					versionString << minor;
7337ff94fd0SStephan Aßmus					if (micro.Length() > 0) {
7347ff94fd0SStephan Aßmus						versionString << ".";
7357ff94fd0SStephan Aßmus						versionString << micro;
7367ff94fd0SStephan Aßmus					}
7377ff94fd0SStephan Aßmus					// Add the rating to the PackageInfo
7387ff94fd0SStephan Aßmus					package->AddUserRating(
7397ff94fd0SStephan Aßmus						UserRating(UserInfo(user), rating,
7407ff94fd0SStephan Aßmus							comment, languageCode, versionString, 0, 0)
7417ff94fd0SStephan Aßmus					);
7427ff94fd0SStephan Aßmus				}
7437ff94fd0SStephan Aßmus			} else if (info.FindMessage("error", &result) == B_OK) {
7447ff94fd0SStephan Aßmus				result.PrintToStream();
7457ff94fd0SStephan Aßmus			}
746a60a23f8SStephan Aßmus		}
7477eec822dSStephan Aßmus	}
74887038da7SStephan Aßmus
74987038da7SStephan Aßmus	if ((flags & POPULATE_SCREEN_SHOTS) != 0) {
75087038da7SStephan Aßmus		ScreenshotInfoList screenshotInfos;
75187038da7SStephan Aßmus		{
75287038da7SStephan Aßmus			BAutolock locker(&fLock);
75387038da7SStephan Aßmus			screenshotInfos = package->ScreenshotInfos();
7547eb09bfaSStephan Aßmus			package->ClearScreenshots();
75587038da7SStephan Aßmus		}
75687038da7SStephan Aßmus		for (int i = 0; i < screenshotInfos.CountItems(); i++) {
75787038da7SStephan Aßmus			const ScreenshotInfo& info = screenshotInfos.ItemAtFast(i);
758e0881f21SStephan Aßmus			_PopulatePackageScreenshot(package, info, 320, false);
75987038da7SStephan Aßmus		}
76087038da7SStephan Aßmus	}
7617eec822dSStephan Aßmus}
7625bd5f161SStephan Aßmus
7635bd5f161SStephan Aßmus
76428075ee4SStephan Aßmusvoid
7658f03a0f9SStephan AßmusModel::SetUsername(BString username)
76689ec84a4SStephan Aßmus{
7678f03a0f9SStephan Aßmus	BString password;
7688f03a0f9SStephan Aßmus	if (username.Length() > 0) {
7698f03a0f9SStephan Aßmus		BPasswordKey key;
7708f03a0f9SStephan Aßmus		BKeyStore keyStore;
7718f03a0f9SStephan Aßmus		if (keyStore.GetKey(kHaikuDepotKeyring, B_KEY_TYPE_PASSWORD, username,
7728f03a0f9SStephan Aßmus				key) == B_OK) {
7738f03a0f9SStephan Aßmus			password = key.Password();
7748f03a0f9SStephan Aßmus		} else {
7758f03a0f9SStephan Aßmus			username = "";
7768f03a0f9SStephan Aßmus		}
77789ec84a4SStephan Aßmus	}
7788f03a0f9SStephan Aßmus	SetAuthorization(username, password, false);
7798f03a0f9SStephan Aßmus}
7808f03a0f9SStephan Aßmus
7818f03a0f9SStephan Aßmus
7828f03a0f9SStephan Aßmusconst BString&
7838f03a0f9SStephan AßmusModel::Username() const
7848f03a0f9SStephan Aßmus{
7858f03a0f9SStephan Aßmus	return fWebAppInterface.Username();
78689ec84a4SStephan Aßmus}
78789ec84a4SStephan Aßmus
78889ec84a4SStephan Aßmus
78989ec84a4SStephan Aßmusvoid
79089ec84a4SStephan AßmusModel::SetAuthorization(const BString& username, const BString& password,
79189ec84a4SStephan Aßmus	bool storePassword)
79228075ee4SStephan Aßmus{
7938f03a0f9SStephan Aßmus	if (storePassword && username.Length() > 0 && password.Length() > 0) {
79489ec84a4SStephan Aßmus		BPasswordKey key(password, B_KEY_PURPOSE_WEB, username);
79589ec84a4SStephan Aßmus		BKeyStore keyStore;
79689ec84a4SStephan Aßmus		keyStore.AddKeyring(kHaikuDepotKeyring);
79789ec84a4SStephan Aßmus		keyStore.AddKey(kHaikuDepotKeyring, key);
79889ec84a4SStephan Aßmus	}
79989ec84a4SStephan Aßmus
80028075ee4SStephan Aßmus	BAutolock locker(&fLock);
80128075ee4SStephan Aßmus	fWebAppInterface.SetAuthorization(username, password);
8028f03a0f9SStephan Aßmus
8038f03a0f9SStephan Aßmus	_NotifyAuthorizationChanged();
80428075ee4SStephan Aßmus}
80528075ee4SStephan Aßmus
80628075ee4SStephan Aßmus
8073d528c4aSAndrew Lindesay/*! When bulk repository data comes down from the server, it will
8083d528c4aSAndrew Lindesay    arrive as a json.gz payload.  This is stored locally as a cache
8093d528c4aSAndrew Lindesay    and this method will provide the on-disk storage location for
8103d528c4aSAndrew Lindesay    this file.
8113d528c4aSAndrew Lindesay*/
81228075ee4SStephan Aßmus
8133d528c4aSAndrew Lindesaystatus_t
8143094fef3SAndrew LindesayModel::DumpExportRepositoryDataPath(BPath& path) const
815ebb1a01cSAndrew Lindesay{
8163d528c4aSAndrew Lindesay	BPath repoDataPath;
817ebb1a01cSAndrew Lindesay
8183d528c4aSAndrew Lindesay	if (find_directory(B_USER_CACHE_DIRECTORY, &repoDataPath) == B_OK
8193d528c4aSAndrew Lindesay		&& repoDataPath.Append("HaikuDepot") == B_OK
8203d528c4aSAndrew Lindesay		&& create_directory(repoDataPath.Path(), 0777) == B_OK
8213d528c4aSAndrew Lindesay		&& repoDataPath.Append("repository-all_en.json.gz") == B_OK) {
8223d528c4aSAndrew Lindesay		path.SetTo(repoDataPath.Path());
8233d528c4aSAndrew Lindesay		return B_OK;
8243d528c4aSAndrew Lindesay	}
825ebb1a01cSAndrew Lindesay
8263d528c4aSAndrew Lindesay	path.Unset();
8273d528c4aSAndrew Lindesay	fprintf(stdout, "unable to find the user cache file for repositories'"
8283d528c4aSAndrew Lindesay		" data");
8293d528c4aSAndrew Lindesay	return B_ERROR;
8303d528c4aSAndrew Lindesay}
831ebb1a01cSAndrew Lindesay
832ebb1a01cSAndrew Lindesay
833c23b641cSAndrew Lindesaystatus_t
8343094fef3SAndrew LindesayModel::IconStoragePath(BPath& path) const
8353094fef3SAndrew Lindesay{
8363094fef3SAndrew Lindesay	BPath iconStoragePath;
8373094fef3SAndrew Lindesay
8383094fef3SAndrew Lindesay	if (find_directory(B_USER_CACHE_DIRECTORY, &iconStoragePath) == B_OK
8393094fef3SAndrew Lindesay		&& iconStoragePath.Append("HaikuDepot") == B_OK
8403094fef3SAndrew Lindesay		&& iconStoragePath.Append("__allicons") == B_OK
8413094fef3SAndrew Lindesay		&& create_directory(iconStoragePath.Path(), 0777) == B_OK) {
8423094fef3SAndrew Lindesay		path.SetTo(iconStoragePath.Path());
8433094fef3SAndrew Lindesay		return B_OK;
8443094fef3SAndrew Lindesay	}
8453094fef3SAndrew Lindesay
8463094fef3SAndrew Lindesay	path.Unset();
8473094fef3SAndrew Lindesay	fprintf(stdout, "unable to find the user cache directory for icons");
8483094fef3SAndrew Lindesay	return B_ERROR;
8493094fef3SAndrew Lindesay}
8503094fef3SAndrew Lindesay
8513094fef3SAndrew Lindesay
8523094fef3SAndrew Lindesaystatus_t
8533094fef3SAndrew LindesayModel::DumpExportPkgDataPath(BPath& path,
854c23b641cSAndrew Lindesay	const BString& repositorySourceCode) const
855c23b641cSAndrew Lindesay{
856c23b641cSAndrew Lindesay	BPath repoDataPath;
857c23b641cSAndrew Lindesay	BString leafName;
858c23b641cSAndrew Lindesay
859c23b641cSAndrew Lindesay	leafName.SetToFormat("pkg-all-%s-%s.json.gz", repositorySourceCode.String(),
860c23b641cSAndrew Lindesay		fPreferredLanguage.String());
861c23b641cSAndrew Lindesay
862c23b641cSAndrew Lindesay	if (find_directory(B_USER_CACHE_DIRECTORY, &repoDataPath) == B_OK
863c23b641cSAndrew Lindesay		&& repoDataPath.Append("HaikuDepot") == B_OK
864c23b641cSAndrew Lindesay		&& create_directory(repoDataPath.Path(), 0777) == B_OK
865c23b641cSAndrew Lindesay		&& repoDataPath.Append(leafName.String()) == B_OK) {
866c23b641cSAndrew Lindesay		path.SetTo(repoDataPath.Path());
867c23b641cSAndrew Lindesay		return B_OK;
868c23b641cSAndrew Lindesay	}
869c23b641cSAndrew Lindesay
870c23b641cSAndrew Lindesay	path.Unset();
871c23b641cSAndrew Lindesay	fprintf(stdout, "unable to find the user cache file for pkgs' data");
872c23b641cSAndrew Lindesay	return B_ERROR;
873c23b641cSAndrew Lindesay}
874c23b641cSAndrew Lindesay
875c23b641cSAndrew Lindesay
87612107be2SStephan Aßmusvoid
87712107be2SStephan AßmusModel::_UpdateIsFeaturedFilter()
87812107be2SStephan Aßmus{
87912107be2SStephan Aßmus	if (fShowFeaturedPackages && SearchTerms().IsEmpty())
88012107be2SStephan Aßmus		fIsFeaturedFilter = PackageFilterRef(new IsFeaturedFilter(), true);
88187084745SMichael Lotz	else
88212107be2SStephan Aßmus		fIsFeaturedFilter = PackageFilterRef(new AnyFilter(), true);
88312107be2SStephan Aßmus}
88412107be2SStephan Aßmus
88512107be2SStephan Aßmus
88687038da7SStephan Aßmusvoid
88787038da7SStephan AßmusModel::_PopulatePackageScreenshot(const PackageInfoRef& package,
88887038da7SStephan Aßmus	const ScreenshotInfo& info, int32 scaledWidth, bool fromCacheOnly)
88987038da7SStephan Aßmus{
89087038da7SStephan Aßmus	// See if there is a cached screenshot
89187038da7SStephan Aßmus	BFile screenshotFile;
89287038da7SStephan Aßmus	BPath screenshotCachePath;
89387038da7SStephan Aßmus	bool fileExists = false;
89487038da7SStephan Aßmus	BString screenshotName(info.Code());
89587038da7SStephan Aßmus	screenshotName << "@" << scaledWidth;
89687038da7SStephan Aßmus	screenshotName << ".png";
89787038da7SStephan Aßmus	time_t modifiedTime;
89887038da7SStephan Aßmus	if (find_directory(B_USER_CACHE_DIRECTORY, &screenshotCachePath) == B_OK
89987038da7SStephan Aßmus		&& screenshotCachePath.Append("HaikuDepot/Screenshots") == B_OK
90087038da7SStephan Aßmus		&& create_directory(screenshotCachePath.Path(), 0777) == B_OK
90187038da7SStephan Aßmus		&& screenshotCachePath.Append(screenshotName) == B_OK) {
90287038da7SStephan Aßmus		// Try opening the file in read-only mode, which will fail if its
90387038da7SStephan Aßmus		// not a file or does not exist.
90487038da7SStephan Aßmus		fileExists = screenshotFile.SetTo(screenshotCachePath.Path(),
90587038da7SStephan Aßmus			B_READ_ONLY) == B_OK;
90687038da7SStephan Aßmus		if (fileExists)
90787038da7SStephan Aßmus			screenshotFile.GetModificationTime(&modifiedTime);
90887038da7SStephan Aßmus	}
90987038da7SStephan Aßmus
91087038da7SStephan Aßmus	if (fileExists) {
91187038da7SStephan Aßmus		time_t now;
91287038da7SStephan Aßmus		time(&now);
91387038da7SStephan Aßmus		if (fromCacheOnly || now - modifiedTime < 60 * 60) {
91487038da7SStephan Aßmus			// Cache file is recent enough, just use it and return.
91587038da7SStephan Aßmus			BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(screenshotFile),
91687038da7SStephan Aßmus				true);
91787038da7SStephan Aßmus			BAutolock locker(&fLock);
91887038da7SStephan Aßmus			package->AddScreenshot(bitmapRef);
91987038da7SStephan Aßmus			return;
92087038da7SStephan Aßmus		}
92187038da7SStephan Aßmus	}
92287038da7SStephan Aßmus
92387038da7SStephan Aßmus	if (fromCacheOnly)
92487038da7SStephan Aßmus		return;
92587038da7SStephan Aßmus
92687038da7SStephan Aßmus	// Retrieve screenshot from web-app
92787038da7SStephan Aßmus	BMallocIO buffer;
92887038da7SStephan Aßmus
92987038da7SStephan Aßmus	int32 scaledHeight = scaledWidth * info.Height() / info.Width();
93087038da7SStephan Aßmus
93128075ee4SStephan Aßmus	status_t status = fWebAppInterface.RetrieveScreenshot(info.Code(),
93287038da7SStephan Aßmus		scaledWidth, scaledHeight, &buffer);
93387038da7SStephan Aßmus	if (status == B_OK) {
93487038da7SStephan Aßmus		BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(buffer), true);
93587038da7SStephan Aßmus		BAutolock locker(&fLock);
93687038da7SStephan Aßmus		package->AddScreenshot(bitmapRef);
93787038da7SStephan Aßmus		locker.Unlock();
93887038da7SStephan Aßmus		if (screenshotFile.SetTo(screenshotCachePath.Path(),
93987038da7SStephan Aßmus				B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE) == B_OK) {
94087038da7SStephan Aßmus			screenshotFile.Write(buffer.Buffer(), buffer.BufferLength());
94187038da7SStephan Aßmus		}
942e0881f21SStephan Aßmus	} else {
943e0881f21SStephan Aßmus		fprintf(stderr, "Failed to retrieve screenshot for code '%s' "
944e0881f21SStephan Aßmus			"at %" B_PRIi32 "x%" B_PRIi32 ".\n", info.Code().String(),
945e0881f21SStephan Aßmus			scaledWidth, scaledHeight);
94687038da7SStephan Aßmus	}
94787038da7SStephan Aßmus}
94887038da7SStephan Aßmus
94987038da7SStephan Aßmus
9508f03a0f9SStephan Aßmus// #pragma mark - listener notification methods
9518f03a0f9SStephan Aßmus
9528f03a0f9SStephan Aßmus
9538f03a0f9SStephan Aßmusvoid
9548f03a0f9SStephan AßmusModel::_NotifyAuthorizationChanged()
9558f03a0f9SStephan Aßmus{
9568f03a0f9SStephan Aßmus	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
9578f03a0f9SStephan Aßmus		const ModelListenerRef& listener = fListeners.ItemAtFast(i);
9588f03a0f9SStephan Aßmus		if (listener.Get() != NULL)
9598f03a0f9SStephan Aßmus			listener->AuthorizationChanged();
9608f03a0f9SStephan Aßmus	}
9618f03a0f9SStephan Aßmus}
9623094fef3SAndrew Lindesay
9633094fef3SAndrew Lindesay
9643094fef3SAndrew Lindesay// temporary - should not be required once the repo info url is used.
9653094fef3SAndrew Lindesaystatic void
9663094fef3SAndrew Lindesaynormalize_repository_base_url(BUrl& url)
9673094fef3SAndrew Lindesay{
9683094fef3SAndrew Lindesay	if (url.Protocol() == "https")
9693094fef3SAndrew Lindesay		url.SetProtocol("http");
9703094fef3SAndrew Lindesay
9713094fef3SAndrew Lindesay	BString path(url.Path());
9723094fef3SAndrew Lindesay
9733094fef3SAndrew Lindesay	if (path.EndsWith("/"))
9743094fef3SAndrew Lindesay		url.SetPath(path.Truncate(path.Length() - 1));
9753094fef3SAndrew Lindesay}
9763094fef3SAndrew Lindesay
9773094fef3SAndrew Lindesay
9783094fef3SAndrew Lindesayvoid
9793094fef3SAndrew LindesayModel::ForAllDepots(void (*func)(const DepotInfo& depot, void* context),
9803094fef3SAndrew Lindesay	void* context)
9813094fef3SAndrew Lindesay{
9823094fef3SAndrew Lindesay	for (int32 i = 0; i < fDepots.CountItems(); i++) {
9833094fef3SAndrew Lindesay		DepotInfo depotInfo = fDepots.ItemAtFast(i);
9843094fef3SAndrew Lindesay		func(depotInfo, context);
9853094fef3SAndrew Lindesay	}
9863094fef3SAndrew Lindesay}
9873094fef3SAndrew Lindesay
9883094fef3SAndrew Lindesay
9893094fef3SAndrew Lindesay// TODO; should use the repo.info url and not the base url.
9903094fef3SAndrew Lindesay
9913094fef3SAndrew Lindesayvoid
9923094fef3SAndrew LindesayModel::ReplaceDepotByUrl(const BString& url,
9933094fef3SAndrew Lindesay	DepotMapper* depotMapper, void* context)
9943094fef3SAndrew Lindesay{
9953094fef3SAndrew Lindesay	for (int32 i = 0; i < fDepots.CountItems(); i++) {
9963094fef3SAndrew Lindesay		DepotInfo depotInfo = fDepots.ItemAtFast(i);
9973094fef3SAndrew Lindesay
9983094fef3SAndrew Lindesay		BUrl url(url);
9993094fef3SAndrew Lindesay		BUrl depotUrlNormalized(depotInfo.BaseURL());
10003094fef3SAndrew Lindesay
10013094fef3SAndrew Lindesay		normalize_repository_base_url(url);
10023094fef3SAndrew Lindesay		normalize_repository_base_url(depotUrlNormalized);
10033094fef3SAndrew Lindesay
10043094fef3SAndrew Lindesay		if (url == depotUrlNormalized) {
10053094fef3SAndrew Lindesay			BAutolock locker(&fLock);
10063094fef3SAndrew Lindesay			fDepots.Replace(i, depotMapper->MapDepot(depotInfo, context));
10073094fef3SAndrew Lindesay		}
10083094fef3SAndrew Lindesay	}
10093094fef3SAndrew Lindesay}
10103094fef3SAndrew Lindesay
10113094fef3SAndrew Lindesay
10123094fef3SAndrew Lindesayvoid
10133094fef3SAndrew LindesayModel::ForAllPackages(PackageConsumer* packageConsumer, void* context)
10143094fef3SAndrew Lindesay{
10153094fef3SAndrew Lindesay	for (int32 i = 0; i < fDepots.CountItems(); i++) {
10163094fef3SAndrew Lindesay		DepotInfo depotInfo = fDepots.ItemAtFast(i);
10173094fef3SAndrew Lindesay		PackageList packages = depotInfo.Packages();
10183094fef3SAndrew Lindesay		for(int32 j = 0; j < packages.CountItems(); j++) {
10193094fef3SAndrew Lindesay			const PackageInfoRef& packageInfoRef = packages.ItemAtFast(j);
10203094fef3SAndrew Lindesay
10213094fef3SAndrew Lindesay			if (packageInfoRef != NULL) {
10223094fef3SAndrew Lindesay				BAutolock locker(&fLock);
10233094fef3SAndrew Lindesay				if (!packageConsumer->ConsumePackage(packageInfoRef, context))
10243094fef3SAndrew Lindesay					return;
10253094fef3SAndrew Lindesay			}
10263094fef3SAndrew Lindesay		}
10273094fef3SAndrew Lindesay	}
10283094fef3SAndrew Lindesay}
10293094fef3SAndrew Lindesay
10303094fef3SAndrew Lindesay
10313094fef3SAndrew Lindesayvoid
10323094fef3SAndrew LindesayModel::ForPackageByNameInDepot(const BString& depotName,
10333094fef3SAndrew Lindesay	const BString& packageName, PackageConsumer* packageConsumer, void* context)
10343094fef3SAndrew Lindesay{
10353094fef3SAndrew Lindesay	int32 depotCount = fDepots.CountItems();
10363094fef3SAndrew Lindesay
10373094fef3SAndrew Lindesay	for (int32 i = 0; i < depotCount; i++) {
10383094fef3SAndrew Lindesay		DepotInfo depotInfo = fDepots.ItemAtFast(i);
10393094fef3SAndrew Lindesay
10403094fef3SAndrew Lindesay		if (depotInfo.Name() == depotName) {
10413094fef3SAndrew Lindesay			int32 packageIndex = depotInfo.PackageIndexByName(packageName);
10423094fef3SAndrew Lindesay
1043