Model.cpp revision 39f49f70
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);
44239f49f70SJulian Harnath			if (MatchesFilter(package))
4434abd2b71SStephan Aßmus				resultList.Add(package);
4446d367ba4SStephan Aßmus		}
4456d367ba4SStephan Aßmus	}
4466d367ba4SStephan Aßmus
4476d367ba4SStephan Aßmus	return resultList;
4486d367ba4SStephan Aßmus}
4496d367ba4SStephan Aßmus
4506d367ba4SStephan Aßmus
45139f49f70SJulian Harnathbool
45239f49f70SJulian HarnathModel::MatchesFilter(const PackageInfoRef& package) const
45339f49f70SJulian Harnath{
45439f49f70SJulian Harnath	return fCategoryFilter->AcceptsPackage(package)
45539f49f70SJulian Harnath			&& fSearchTermsFilter->AcceptsPackage(package)
45639f49f70SJulian Harnath			&& fIsFeaturedFilter->AcceptsPackage(package)
45739f49f70SJulian Harnath			&& (fShowAvailablePackages || package->State() != NONE)
45839f49f70SJulian Harnath			&& (fShowInstalledPackages || package->State() != ACTIVATED)
45939f49f70SJulian Harnath			&& (fShowSourcePackages || !is_source_package(package))
46039f49f70SJulian Harnath			&& (fShowDevelopPackages || !is_develop_package(package));
46139f49f70SJulian Harnath}
46239f49f70SJulian Harnath
46339f49f70SJulian Harnath
4646d367ba4SStephan Aßmusbool
4656d367ba4SStephan AßmusModel::AddDepot(const DepotInfo& depot)
4666d367ba4SStephan Aßmus{
4676d367ba4SStephan Aßmus	return fDepots.Add(depot);
4686d367ba4SStephan Aßmus}
4696d367ba4SStephan Aßmus
47042479955SStephan Aßmus
4717b2c59b9SStephan Aßmusbool
4727b2c59b9SStephan AßmusModel::HasDepot(const BString& name) const
473ebb1a01cSAndrew Lindesay{
474ebb1a01cSAndrew Lindesay	return NULL != DepotForName(name);
475ebb1a01cSAndrew Lindesay}
476ebb1a01cSAndrew Lindesay
477ebb1a01cSAndrew Lindesay
478ebb1a01cSAndrew Lindesayconst DepotInfo*
479ebb1a01cSAndrew LindesayModel::DepotForName(const BString& name) const
4807b2c59b9SStephan Aßmus{
4817b2c59b9SStephan Aßmus	for (int32 i = fDepots.CountItems() - 1; i >= 0; i--) {
4827b2c59b9SStephan Aßmus		if (fDepots.ItemAtFast(i).Name() == name)
483ebb1a01cSAndrew Lindesay			return &fDepots.ItemAtFast(i);
4847b2c59b9SStephan Aßmus	}
485ebb1a01cSAndrew Lindesay	return NULL;
4867b2c59b9SStephan Aßmus}
4877b2c59b9SStephan Aßmus
4887b2c59b9SStephan Aßmus
4897b2c59b9SStephan Aßmusbool
4907b2c59b9SStephan AßmusModel::SyncDepot(const DepotInfo& depot)
4917b2c59b9SStephan Aßmus{
4927b2c59b9SStephan Aßmus	for (int32 i = fDepots.CountItems() - 1; i >= 0; i--) {
4937b2c59b9SStephan Aßmus		const DepotInfo& existingDepot = fDepots.ItemAtFast(i);
4947b2c59b9SStephan Aßmus		if (existingDepot.Name() == depot.Name()) {
4957b2c59b9SStephan Aßmus			DepotInfo mergedDepot(existingDepot);
4967b2c59b9SStephan Aßmus			mergedDepot.SyncPackages(depot.Packages());
4977b2c59b9SStephan Aßmus			fDepots.Replace(i, mergedDepot);
4987b2c59b9SStephan Aßmus			return true;
4997b2c59b9SStephan Aßmus		}
5007b2c59b9SStephan Aßmus	}
5017b2c59b9SStephan Aßmus	return false;
5027b2c59b9SStephan Aßmus}
5037b2c59b9SStephan Aßmus
5047b2c59b9SStephan Aßmus
5052c287f5bSRene Gollentvoid
5062c287f5bSRene GollentModel::Clear()
5072c287f5bSRene Gollent{
5082c287f5bSRene Gollent	fDepots.Clear();
5092c287f5bSRene Gollent}
5102c287f5bSRene Gollent
5112c287f5bSRene Gollent
512706edd80SStephan Aßmusvoid
513779d8213SStephan AßmusModel::SetPackageState(const PackageInfoRef& package, PackageState state)
514706edd80SStephan Aßmus{
515706edd80SStephan Aßmus	switch (state) {
516706edd80SStephan Aßmus		default:
517706edd80SStephan Aßmus		case NONE:
518706edd80SStephan Aßmus			fInstalledPackages.Remove(package);
519706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
520706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
521706edd80SStephan Aßmus			break;
522706edd80SStephan Aßmus		case INSTALLED:
523706edd80SStephan Aßmus			if (!fInstalledPackages.Contains(package))
524706edd80SStephan Aßmus				fInstalledPackages.Add(package);
525706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
526706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
527706edd80SStephan Aßmus			break;
528706edd80SStephan Aßmus		case ACTIVATED:
529706edd80SStephan Aßmus			if (!fInstalledPackages.Contains(package))
530706edd80SStephan Aßmus				fInstalledPackages.Add(package);
531706edd80SStephan Aßmus			if (!fActivatedPackages.Contains(package))
532706edd80SStephan Aßmus				fActivatedPackages.Add(package);
533706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
534706edd80SStephan Aßmus			break;
535706edd80SStephan Aßmus		case UNINSTALLED:
536706edd80SStephan Aßmus			fInstalledPackages.Remove(package);
537706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
538ab172803SStephan Aßmus			if (!fUninstalledPackages.Contains(package))
539ab172803SStephan Aßmus				fUninstalledPackages.Add(package);
540706edd80SStephan Aßmus			break;
541706edd80SStephan Aßmus	}
542b4c8d2ffSRene Gollent
543b4c8d2ffSRene Gollent	package->SetState(state);
544706edd80SStephan Aßmus}
545706edd80SStephan Aßmus
546706edd80SStephan Aßmus
54709d44cecSStephan Aßmus// #pragma mark - filters
54809d44cecSStephan Aßmus
54909d44cecSStephan Aßmus
55042479955SStephan Aßmusvoid
55142479955SStephan AßmusModel::SetCategory(const BString& category)
55242479955SStephan Aßmus{
5533a6ccc85SStephan Aßmus	PackageFilter* filter;
5541f67148fSRene Gollent
55542479955SStephan Aßmus	if (category.Length() == 0)
5563a6ccc85SStephan Aßmus		filter = new AnyFilter();
55742479955SStephan Aßmus	else
5583a6ccc85SStephan Aßmus		filter = new CategoryFilter(category);
5593a6ccc85SStephan Aßmus
5603a6ccc85SStephan Aßmus	fCategoryFilter.SetTo(filter, true);
56142479955SStephan Aßmus}
56242479955SStephan Aßmus
56309d44cecSStephan Aßmus
5640c169aceSStephan AßmusBString
5650c169aceSStephan AßmusModel::Category() const
5660c169aceSStephan Aßmus{
5670c169aceSStephan Aßmus	CategoryFilter* filter
5680c169aceSStephan Aßmus		= dynamic_cast<CategoryFilter*>(fCategoryFilter.Get());
5690c169aceSStephan Aßmus	if (filter == NULL)
5700c169aceSStephan Aßmus		return "";
5710c169aceSStephan Aßmus	return filter->Category();
5720c169aceSStephan Aßmus}
5730c169aceSStephan Aßmus
5740c169aceSStephan Aßmus
57509d44cecSStephan Aßmusvoid
57609d44cecSStephan AßmusModel::SetDepot(const BString& depot)
57709d44cecSStephan Aßmus{
57809d44cecSStephan Aßmus	fDepotFilter = depot;
57909d44cecSStephan Aßmus}
58009d44cecSStephan Aßmus
58109d44cecSStephan Aßmus
5820c169aceSStephan AßmusBString
5830c169aceSStephan AßmusModel::Depot() const
5840c169aceSStephan Aßmus{
5850c169aceSStephan Aßmus	return fDepotFilter;
5860c169aceSStephan Aßmus}
5870c169aceSStephan Aßmus
5880c169aceSStephan Aßmus
58909d44cecSStephan Aßmusvoid
59009d44cecSStephan AßmusModel::SetSearchTerms(const BString& searchTerms)
59109d44cecSStephan Aßmus{
59209d44cecSStephan Aßmus	PackageFilter* filter;
5931f67148fSRene Gollent
59409d44cecSStephan Aßmus	if (searchTerms.Length() == 0)
59509d44cecSStephan Aßmus		filter = new AnyFilter();
59609d44cecSStephan Aßmus	else
59709d44cecSStephan Aßmus		filter = new SearchTermsFilter(searchTerms);
59809d44cecSStephan Aßmus
59909d44cecSStephan Aßmus	fSearchTermsFilter.SetTo(filter, true);
60012107be2SStephan Aßmus	_UpdateIsFeaturedFilter();
60109d44cecSStephan Aßmus}
60209d44cecSStephan Aßmus
6037eec822dSStephan Aßmus
6040c169aceSStephan AßmusBString
6050c169aceSStephan AßmusModel::SearchTerms() const
6060c169aceSStephan Aßmus{
6070c169aceSStephan Aßmus	SearchTermsFilter* filter
6080c169aceSStephan Aßmus		= dynamic_cast<SearchTermsFilter*>(fSearchTermsFilter.Get());
6090c169aceSStephan Aßmus	if (filter == NULL)
6100c169aceSStephan Aßmus		return "";
6110c169aceSStephan Aßmus	return filter->SearchTerms();
6120c169aceSStephan Aßmus}
6130c169aceSStephan Aßmus
6140c169aceSStephan Aßmus
6154fade61fSStephan Aßmusvoid
6164fade61fSStephan AßmusModel::SetShowFeaturedPackages(bool show)
6174fade61fSStephan Aßmus{
6184fade61fSStephan Aßmus	fShowFeaturedPackages = show;
61912107be2SStephan Aßmus	_UpdateIsFeaturedFilter();
6204fade61fSStephan Aßmus}
6214fade61fSStephan Aßmus
6224fade61fSStephan Aßmus
623ab172803SStephan Aßmusvoid
624ab172803SStephan AßmusModel::SetShowAvailablePackages(bool show)
625ab172803SStephan Aßmus{
626ab172803SStephan Aßmus	fShowAvailablePackages = show;
627ab172803SStephan Aßmus}
628ab172803SStephan Aßmus
629ab172803SStephan Aßmus
630ab172803SStephan Aßmusvoid
631ab172803SStephan AßmusModel::SetShowInstalledPackages(bool show)
632ab172803SStephan Aßmus{
633ab172803SStephan Aßmus	fShowInstalledPackages = show;
634ab172803SStephan Aßmus}
635ab172803SStephan Aßmus
636ab172803SStephan Aßmus
637870554acSStephan Aßmusvoid
638870554acSStephan AßmusModel::SetShowSourcePackages(bool show)
639870554acSStephan Aßmus{
640870554acSStephan Aßmus	fShowSourcePackages = show;
641870554acSStephan Aßmus}
642870554acSStephan Aßmus
643870554acSStephan Aßmus
644870554acSStephan Aßmusvoid
645870554acSStephan AßmusModel::SetShowDevelopPackages(bool show)
646870554acSStephan Aßmus{
647870554acSStephan Aßmus	fShowDevelopPackages = show;
648870554acSStephan Aßmus}
649870554acSStephan Aßmus
650870554acSStephan Aßmus
65119c15fecSAndrew Lindesay// #pragma mark - information retrieval
6527eec822dSStephan Aßmus
6537eec822dSStephan Aßmus
6547eec822dSStephan Aßmusvoid
655a60a23f8SStephan AßmusModel::PopulatePackage(const PackageInfoRef& package, uint32 flags)
6567eec822dSStephan Aßmus{
6577eec822dSStephan Aßmus	// TODO: There should probably also be a way to "unpopulate" the
6587eec822dSStephan Aßmus	// package information. Maybe a cache of populated packages, so that
6597eec822dSStephan Aßmus	// packages loose their extra information after a certain amount of
6607eec822dSStephan Aßmus	// time when they have not been accessed/displayed in the UI. Otherwise
6617eec822dSStephan Aßmus	// HaikuDepot will consume more and more resources in the packages.
6627eec822dSStephan Aßmus	// Especially screen-shots will be a problem eventually.
66348045f5aSStephan Aßmus	{
66448045f5aSStephan Aßmus		BAutolock locker(&fLock);
66589e4ba17SStephan Aßmus		bool alreadyPopulated = fPopulatedPackages.Contains(package);
66689e4ba17SStephan Aßmus		if ((flags & POPULATE_FORCE) == 0 && alreadyPopulated)
66748045f5aSStephan Aßmus			return;
66889e4ba17SStephan Aßmus		if (!alreadyPopulated)
66989e4ba17SStephan Aßmus			fPopulatedPackages.Add(package);
67048045f5aSStephan Aßmus	}
6717eec822dSStephan Aßmus
6727ff94fd0SStephan Aßmus	if ((flags & POPULATE_USER_RATINGS) != 0) {
6737ff94fd0SStephan Aßmus		// Retrieve info from web-app
6747ff94fd0SStephan Aßmus		BMessage info;
67548045f5aSStephan Aßmus
67648045f5aSStephan Aßmus		BString packageName;
67787084745SMichael Lotz		BString architecture;
67848045f5aSStephan Aßmus		{
67948045f5aSStephan Aßmus			BAutolock locker(&fLock);
68072992391SStephan Aßmus			packageName = package->Name();
68148045f5aSStephan Aßmus			architecture = package->Architecture();
68248045f5aSStephan Aßmus		}
68387084745SMichael Lotz
68428075ee4SStephan Aßmus		status_t status = fWebAppInterface.RetrieveUserRatings(packageName,
68548045f5aSStephan Aßmus			architecture, 0, 50, info);
6867ff94fd0SStephan Aßmus		if (status == B_OK) {
6877ff94fd0SStephan Aßmus			// Parse message
6887ff94fd0SStephan Aßmus			BMessage result;
6897ff94fd0SStephan Aßmus			BMessage items;
6907ff94fd0SStephan Aßmus			if (info.FindMessage("result", &result) == B_OK
6917ff94fd0SStephan Aßmus				&& result.FindMessage("items", &items) == B_OK) {
69248045f5aSStephan Aßmus
69348045f5aSStephan Aßmus				BAutolock locker(&fLock);
6947eb09bfaSStephan Aßmus				package->ClearUserRatings();
69548045f5aSStephan Aßmus
6967ff94fd0SStephan Aßmus				int index = 0;
6977ff94fd0SStephan Aßmus				while (true) {
6987ff94fd0SStephan Aßmus					BString name;
6997ff94fd0SStephan Aßmus					name << index++;
70087084745SMichael Lotz
7017ff94fd0SStephan Aßmus					BMessage item;
7027ff94fd0SStephan Aßmus					if (items.FindMessage(name, &item) != B_OK)
7037ff94fd0SStephan Aßmus						break;
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
77028075ee4SStephan Aßmusvoid
7718f03a0f9SStephan AßmusModel::SetUsername(BString username)
77289ec84a4SStephan Aßmus{
7738f03a0f9SStephan Aßmus	BString password;
7748f03a0f9SStephan Aßmus	if (username.Length() > 0) {
7758f03a0f9SStephan Aßmus		BPasswordKey key;
7768f03a0f9SStephan Aßmus		BKeyStore keyStore;
7778f03a0f9SStephan Aßmus		if (keyStore.GetKey(kHaikuDepotKeyring, B_KEY_TYPE_PASSWORD, username,
7788f03a0f9SStephan Aßmus				key) == B_OK) {
7798f03a0f9SStephan Aßmus			password = key.Password();
7808f03a0f9SStephan Aßmus		} else {
7818f03a0f9SStephan Aßmus			username = "";
7828f03a0f9SStephan Aßmus		}
78389ec84a4SStephan Aßmus	}
7848f03a0f9SStephan Aßmus	SetAuthorization(username, password, false);
7858f03a0f9SStephan Aßmus}
7868f03a0f9SStephan Aßmus
7878f03a0f9SStephan Aßmus
7888f03a0f9SStephan Aßmusconst BString&
7898f03a0f9SStephan AßmusModel::Username() const
7908f03a0f9SStephan Aßmus{
7918f03a0f9SStephan Aßmus	return fWebAppInterface.Username();
79289ec84a4SStephan Aßmus}
79389ec84a4SStephan Aßmus
79489ec84a4SStephan Aßmus
79589ec84a4SStephan Aßmusvoid
79689ec84a4SStephan AßmusModel::SetAuthorization(const BString& username, const BString& password,
79789ec84a4SStephan Aßmus	bool storePassword)
79828075ee4SStephan Aßmus{
7998f03a0f9SStephan Aßmus	if (storePassword && username.Length() > 0 && password.Length() > 0) {
80089ec84a4SStephan Aßmus		BPasswordKey key(password, B_KEY_PURPOSE_WEB, username);
80189ec84a4SStephan Aßmus		BKeyStore keyStore;
80289ec84a4SStephan Aßmus		keyStore.AddKeyring(kHaikuDepotKeyring);
80389ec84a4SStephan Aßmus		keyStore.AddKey(kHaikuDepotKeyring, key);
80489ec84a4SStephan Aßmus	}
80589ec84a4SStephan Aßmus
80628075ee4SStephan Aßmus	BAutolock locker(&fLock);
80728075ee4SStephan Aßmus	fWebAppInterface.SetAuthorization(username, password);
8088f03a0f9SStephan Aßmus
8098f03a0f9SStephan Aßmus	_NotifyAuthorizationChanged();
81028075ee4SStephan Aßmus}
81128075ee4SStephan Aßmus
81228075ee4SStephan Aßmus
8133d528c4aSAndrew Lindesay/*! When bulk repository data comes down from the server, it will
8143d528c4aSAndrew Lindesay    arrive as a json.gz payload.  This is stored locally as a cache
8153d528c4aSAndrew Lindesay    and this method will provide the on-disk storage location for
8163d528c4aSAndrew Lindesay    this file.
8173d528c4aSAndrew Lindesay*/
81828075ee4SStephan Aßmus
8193d528c4aSAndrew Lindesaystatus_t
8203094fef3SAndrew LindesayModel::DumpExportRepositoryDataPath(BPath& path) const
821ebb1a01cSAndrew Lindesay{
8223d528c4aSAndrew Lindesay	BPath repoDataPath;
823ebb1a01cSAndrew Lindesay
8243d528c4aSAndrew Lindesay	if (find_directory(B_USER_CACHE_DIRECTORY, &repoDataPath) == B_OK
8253d528c4aSAndrew Lindesay		&& repoDataPath.Append("HaikuDepot") == B_OK
8263d528c4aSAndrew Lindesay		&& create_directory(repoDataPath.Path(), 0777) == B_OK
8273d528c4aSAndrew Lindesay		&& repoDataPath.Append("repository-all_en.json.gz") == B_OK) {
8283d528c4aSAndrew Lindesay		path.SetTo(repoDataPath.Path());
8293d528c4aSAndrew Lindesay		return B_OK;
8303d528c4aSAndrew Lindesay	}
831ebb1a01cSAndrew Lindesay
8323d528c4aSAndrew Lindesay	path.Unset();
8333d528c4aSAndrew Lindesay	fprintf(stdout, "unable to find the user cache file for repositories'"
8343d528c4aSAndrew Lindesay		" data");
8353d528c4aSAndrew Lindesay	return B_ERROR;
8363d528c4aSAndrew Lindesay}
837ebb1a01cSAndrew Lindesay
838ebb1a01cSAndrew Lindesay
839c23b641cSAndrew Lindesaystatus_t
8403094fef3SAndrew LindesayModel::IconStoragePath(BPath& path) const
8413094fef3SAndrew Lindesay{
8423094fef3SAndrew Lindesay	BPath iconStoragePath;
8433094fef3SAndrew Lindesay
8443094fef3SAndrew Lindesay	if (find_directory(B_USER_CACHE_DIRECTORY, &iconStoragePath) == B_OK
8453094fef3SAndrew Lindesay		&& iconStoragePath.Append("HaikuDepot") == B_OK
8463094fef3SAndrew Lindesay		&& iconStoragePath.Append("__allicons") == B_OK
8473094fef3SAndrew Lindesay		&& create_directory(iconStoragePath.Path(), 0777) == B_OK) {
8483094fef3SAndrew Lindesay		path.SetTo(iconStoragePath.Path());
8493094fef3SAndrew Lindesay		return B_OK;
8503094fef3SAndrew Lindesay	}
8513094fef3SAndrew Lindesay
8523094fef3SAndrew Lindesay	path.Unset();
8533094fef3SAndrew Lindesay	fprintf(stdout, "unable to find the user cache directory for icons");
8543094fef3SAndrew Lindesay	return B_ERROR;
8553094fef3SAndrew Lindesay}
8563094fef3SAndrew Lindesay
8573094fef3SAndrew Lindesay
8583094fef3SAndrew Lindesaystatus_t
8593094fef3SAndrew LindesayModel::DumpExportPkgDataPath(BPath& path,
860c23b641cSAndrew Lindesay	const BString& repositorySourceCode) const
861c23b641cSAndrew Lindesay{
862c23b641cSAndrew Lindesay	BPath repoDataPath;
863c23b641cSAndrew Lindesay	BString leafName;
864c23b641cSAndrew Lindesay
865c23b641cSAndrew Lindesay	leafName.SetToFormat("pkg-all-%s-%s.json.gz", repositorySourceCode.String(),
866c23b641cSAndrew Lindesay		fPreferredLanguage.String());
867c23b641cSAndrew Lindesay
868c23b641cSAndrew Lindesay	if (find_directory(B_USER_CACHE_DIRECTORY, &repoDataPath) == B_OK
869c23b641cSAndrew Lindesay		&& repoDataPath.Append("HaikuDepot") == B_OK
870c23b641cSAndrew Lindesay		&& create_directory(repoDataPath.Path(), 0777) == B_OK
871c23b641cSAndrew Lindesay		&& repoDataPath.Append(leafName.String()) == B_OK) {
872c23b641cSAndrew Lindesay		path.SetTo(repoDataPath.Path());
873c23b641cSAndrew Lindesay		return B_OK;
874c23b641cSAndrew Lindesay	}
875c23b641cSAndrew Lindesay
876c23b641cSAndrew Lindesay	path.Unset();
877c23b641cSAndrew Lindesay	fprintf(stdout, "unable to find the user cache file for pkgs' data");
878c23b641cSAndrew Lindesay	return B_ERROR;
879c23b641cSAndrew Lindesay}
880c23b641cSAndrew Lindesay
881c23b641cSAndrew Lindesay
88212107be2SStephan Aßmusvoid
88312107be2SStephan AßmusModel::_UpdateIsFeaturedFilter()
88412107be2SStephan Aßmus{
88512107be2SStephan Aßmus	if (fShowFeaturedPackages && SearchTerms().IsEmpty())
88612107be2SStephan Aßmus		fIsFeaturedFilter = PackageFilterRef(new IsFeaturedFilter(), true);
88787084745SMichael Lotz	else
88812107be2SStephan Aßmus		fIsFeaturedFilter = PackageFilterRef(new AnyFilter(), true);
88912107be2SStephan Aßmus}
89012107be2SStephan Aßmus
89112107be2SStephan Aßmus
89287038da7SStephan Aßmusvoid
89387038da7SStephan AßmusModel::_PopulatePackageScreenshot(const PackageInfoRef& package,
89487038da7SStephan Aßmus	const ScreenshotInfo& info, int32 scaledWidth, bool fromCacheOnly)
89587038da7SStephan Aßmus{
89687038da7SStephan Aßmus	// See if there is a cached screenshot
89787038da7SStephan Aßmus	BFile screenshotFile;
89887038da7SStephan Aßmus	BPath screenshotCachePath;
89987038da7SStephan Aßmus	bool fileExists = false;
90087038da7SStephan Aßmus	BString screenshotName(info.Code());
90187038da7SStephan Aßmus	screenshotName << "@" << scaledWidth;
90287038da7SStephan Aßmus	screenshotName << ".png";
90387038da7SStephan Aßmus	time_t modifiedTime;
90487038da7SStephan Aßmus	if (find_directory(B_USER_CACHE_DIRECTORY, &screenshotCachePath) == B_OK
90587038da7SStephan Aßmus		&& screenshotCachePath.Append("HaikuDepot/Screenshots") == B_OK
90687038da7SStephan Aßmus		&& create_directory(screenshotCachePath.Path(), 0777) == B_OK
90787038da7SStephan Aßmus		&& screenshotCachePath.Append(screenshotName) == B_OK) {
90887038da7SStephan Aßmus		// Try opening the file in read-only mode, which will fail if its
90987038da7SStephan Aßmus		// not a file or does not exist.
91087038da7SStephan Aßmus		fileExists = screenshotFile.SetTo(screenshotCachePath.Path(),
91187038da7SStephan Aßmus			B_READ_ONLY) == B_OK;
91287038da7SStephan Aßmus		if (fileExists)
91387038da7SStephan Aßmus			screenshotFile.GetModificationTime(&modifiedTime);
91487038da7SStephan Aßmus	}
91587038da7SStephan Aßmus
91687038da7SStephan Aßmus	if (fileExists) {
91787038da7SStephan Aßmus		time_t now;
91887038da7SStephan Aßmus		time(&now);
91987038da7SStephan Aßmus		if (fromCacheOnly || now - modifiedTime < 60 * 60) {
92087038da7SStephan Aßmus			// Cache file is recent enough, just use it and return.
92187038da7SStephan Aßmus			BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(screenshotFile),
92287038da7SStephan Aßmus				true);
92387038da7SStephan Aßmus			BAutolock locker(&fLock);
92487038da7SStephan Aßmus			package->AddScreenshot(bitmapRef);
92587038da7SStephan Aßmus			return;
92687038da7SStephan Aßmus		}
92787038da7SStephan Aßmus	}
92887038da7SStephan Aßmus
92987038da7SStephan Aßmus	if (fromCacheOnly)
93087038da7SStephan Aßmus		return;
93187038da7SStephan Aßmus
93287038da7SStephan Aßmus	// Retrieve screenshot from web-app
93387038da7SStephan Aßmus	BMallocIO buffer;
93487038da7SStephan Aßmus
93587038da7SStephan Aßmus	int32 scaledHeight = scaledWidth * info.Height() / info.Width();
93687038da7SStephan Aßmus
93728075ee4SStephan Aßmus	status_t status = fWebAppInterface.RetrieveScreenshot(info.Code(),
93887038da7SStephan Aßmus		scaledWidth, scaledHeight, &buffer);
93987038da7SStephan Aßmus	if (status == B_OK) {
94087038da7SStephan Aßmus		BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(buffer), true);
94187038da7SStephan Aßmus		BAutolock locker(&fLock);
94287038da7SStephan Aßmus		package->AddScreenshot(bitmapRef);
94387038da7SStephan Aßmus		locker.Unlock();
94487038da7SStephan Aßmus		if (screenshotFile.SetTo(screenshotCachePath.Path(),
94587038da7SStephan Aßmus				B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE) == B_OK) {
94687038da7SStephan Aßmus			screenshotFile.Write(buffer.Buffer(), buffer.BufferLength());
94787038da7SStephan Aßmus		}
948e0881f21SStephan Aßmus	} else {
949e0881f21SStephan Aßmus		fprintf(stderr, "Failed to retrieve screenshot for code '%s' "
950e0881f21SStephan Aßmus			"at %" B_PRIi32 "x%" B_PRIi32 ".\n", info.Code().String(),
951e0881f21SStephan Aßmus			scaledWidth, scaledHeight);
95287038da7SStephan Aßmus	}
95387038da7SStephan Aßmus}
95487038da7SStephan Aßmus
95587038da7SStephan Aßmus
9568f03a0f9SStephan Aßmus// #pragma mark - listener notification methods
9578f03a0f9SStephan Aßmus
9588f03a0f9SStephan Aßmus
9598f03a0f9SStephan Aßmusvoid
9608f03a0f9SStephan AßmusModel::_NotifyAuthorizationChanged()
9618f03a0f9SStephan Aßmus{
9628f03a0f9SStephan Aßmus	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
9638f03a0f9SStephan Aßmus		const ModelListenerRef& listener = fListeners.ItemAtFast(i);
9648f03a0f9SStephan Aßmus		if (listener.Get() != NULL)
9658f03a0f9SStephan Aßmus			listener->AuthorizationChanged();
9668f03a0f9SStephan Aßmus	}
9678f03a0f9SStephan Aßmus}
9683094fef3SAndrew Lindesay
9693094fef3SAndrew Lindesay
9703094fef3SAndrew Lindesay// temporary - should not be required once the repo info url is used.
9713094fef3SAndrew Lindesaystatic void
9723094fef3SAndrew Lindesaynormalize_repository_base_url(BUrl& url)
9733094fef3SAndrew Lindesay{
9743094fef3SAndrew Lindesay	if (url.Protocol() == "https")
9753094fef3SAndrew Lindesay		url.SetProtocol("http");
9763094fef3SAndrew Lindesay
9773094fef3SAndrew Lindesay	BString path(url.Path());
9783094fef3SAndrew Lindesay
9793094fef3SAndrew Lindesay	if (path.EndsWith("/"))
9803094fef3SAndrew Lindesay		url.SetPath(path.Truncate(path.Length() - 1));
9813094fef3SAndrew Lindesay}
9823094fef3SAndrew Lindesay
9833094fef3SAndrew Lindesay
9843094fef3SAndrew Lindesayvoid
9853094fef3SAndrew LindesayModel::ForAllDepots(void (*func)(const DepotInfo& depot, void* context),
9863094fef3SAndrew Lindesay	void* context)
9873094fef3SAndrew Lindesay{
9883094fef3SAndrew Lindesay	for (int32 i = 0; i < fDepots.CountItems(); i++) {
9893094fef3SAndrew Lindesay		DepotInfo depotInfo = fDepots.ItemAtFast(i);
9903094fef3SAndrew Lindesay		func(depotInfo, context);
9913094fef3SAndrew Lindesay	}
9923094fef3SAndrew Lindesay}
9933094fef3SAndrew Lindesay
9943094fef3SAndrew Lindesay
9953094fef3SAndrew Lindesay// TODO; should use the repo.info url and not the base url.
9963094fef3SAndrew Lindesay
9973094fef3SAndrew Lindesayvoid
9983094fef3SAndrew LindesayModel::ReplaceDepotByUrl(const BString& url,
9993094fef3SAndrew Lindesay	DepotMapper* depotMapper, void* context)
10003094fef3SAndrew Lindesay{
10013094fef3SAndrew Lindesay	for (int32 i = 0; i < fDepots.CountItems(); i++) {
10023094fef3SAndrew Lindesay		DepotInfo depotInfo = fDepots.ItemAtFast(i);
10033094fef3SAndrew Lindesay
10043094fef3SAndrew Lindesay		BUrl url(url);
10053094fef3SAndrew Lindesay		BUrl depotUrlNormalized(depotInfo.BaseURL());
10063094fef3SAndrew Lindesay
10073094fef3SAndrew Lindesay		normalize_repository_base_url(url);
10083094fef3SAndrew Lindesay		normalize_repository_base_url(depotUrlNormalized);
10093094fef3SAndrew Lindesay
10103094fef3SAndrew Lindesay		if (url == depotUrlNormalized) {
10113094fef3SAndrew Lindesay			BAutolock locker(&fLock);
10123094fef3SAndrew Lindesay			fDepots.Replace(i, depotMapper->MapDepot(depotInfo, context));
10133094fef3SAndrew Lindesay		}
10143094fef3SAndrew Lindesay	}
10153094fef3SAndrew Lindesay}
10163094fef3SAndrew Lindesay
10173094fef3SAndrew Lindesay
10183094fef3SAndrew Lindesayvoid
10193094fef3SAndrew LindesayModel::ForAllPackages(PackageConsumer* packageConsumer, void* context)
10203094fef3SAndrew Lindesay{
10213094fef3SAndrew Lindesay	for (int32 i = 0; i < fDepots.CountItems(); i++) {
10223094fef3SAndrew Lindesay		DepotInfo depotInfo = fDepots.ItemAtFast(i);
10233094fef3SAndrew Lindesay		PackageList packages = depotInfo.Packages();
10243094fef3SAndrew Lindesay		for(int32 j = 0; j < packages.CountItems(); j++) {
10253094fef3SAndrew Lindesay			const PackageInfoRef& packageInfoRef = packages.ItemAtFast(j);
10263094fef3SAndrew Lindesay
10273094fef3SAndrew Lindesay			if (packageInfoRef != NULL) {
10283094fef3SAndrew Lindesay				BAutolock locker(&fLock);
10293094fef3SAndrew Lindesay				if (!packageConsumer->ConsumePackage(packageInfoRef, context))
10303094fef3SAndrew Lindesay					return;
10313094fef3SAndrew Lindesay			}
10323094fef3SAndrew Lindesay		}
10333094fef3SAndrew Lindesay	}
10343094fef3SAndrew Lindesay}
1035