Model.cpp revision 7b2c59b9
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>.
46d367ba4SStephan Aßmus * All rights reserved. Distributed under the terms of the MIT License.
56d367ba4SStephan Aßmus */
66d367ba4SStephan Aßmus
76d367ba4SStephan Aßmus#include "Model.h"
86d367ba4SStephan Aßmus
9726db742SStephan Aßmus#include <ctime>
10634c2c68SAxel Dörfler#include <stdarg.h>
116d367ba4SStephan Aßmus#include <stdio.h>
12726db742SStephan Aßmus#include <time.h>
136d367ba4SStephan Aßmus
147eec822dSStephan Aßmus#include <Autolock.h>
15f75ec10dSStephan Aßmus#include <Catalog.h>
16726db742SStephan Aßmus#include <Directory.h>
17726db742SStephan Aßmus#include <Entry.h>
18726db742SStephan Aßmus#include <File.h>
1989ec84a4SStephan Aßmus#include <KeyStore.h>
206e48c81bSStephan Aßmus#include <LocaleRoster.h>
21e3fdc894SStephan Aßmus#include <Message.h>
22726db742SStephan Aßmus#include <Path.h>
23f75ec10dSStephan Aßmus
24f75ec10dSStephan Aßmus
25f75ec10dSStephan Aßmus#undef B_TRANSLATION_CONTEXT
26f75ec10dSStephan Aßmus#define B_TRANSLATION_CONTEXT "Model"
27f75ec10dSStephan Aßmus
286d367ba4SStephan Aßmus
2989ec84a4SStephan Aßmusstatic const char* kHaikuDepotKeyring = "HaikuDepot";
3089ec84a4SStephan Aßmus
3189ec84a4SStephan Aßmus
328f03a0f9SStephan AßmusPackageFilter::~PackageFilter()
338f03a0f9SStephan Aßmus{
348f03a0f9SStephan Aßmus}
354abd2b71SStephan Aßmus
364abd2b71SStephan Aßmus
378f03a0f9SStephan AßmusModelListener::~ModelListener()
384abd2b71SStephan Aßmus{
394abd2b71SStephan Aßmus}
404abd2b71SStephan Aßmus
414abd2b71SStephan Aßmus
428f03a0f9SStephan Aßmus// #pragma mark - PackageFilters
438f03a0f9SStephan Aßmus
448f03a0f9SStephan Aßmus
454abd2b71SStephan Aßmusclass AnyFilter : public PackageFilter {
464abd2b71SStephan Aßmuspublic:
47779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
484abd2b71SStephan Aßmus	{
494abd2b71SStephan Aßmus		return true;
504abd2b71SStephan Aßmus	}
514abd2b71SStephan Aßmus};
524abd2b71SStephan Aßmus
534abd2b71SStephan Aßmus
544abd2b71SStephan Aßmusclass DepotFilter : public PackageFilter {
554abd2b71SStephan Aßmuspublic:
564abd2b71SStephan Aßmus	DepotFilter(const DepotInfo& depot)
574abd2b71SStephan Aßmus		:
584abd2b71SStephan Aßmus		fDepot(depot)
594abd2b71SStephan Aßmus	{
604abd2b71SStephan Aßmus	}
611f67148fSRene Gollent
62779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
634abd2b71SStephan Aßmus	{
644abd2b71SStephan Aßmus		// TODO: Maybe a PackageInfo ought to know the Depot it came from?
654abd2b71SStephan Aßmus		// But right now the same package could theoretically be provided
664abd2b71SStephan Aßmus		// from different depots and the filter would work correctly.
674abd2b71SStephan Aßmus		// Also the PackageList could actually contain references to packages
684abd2b71SStephan Aßmus		// instead of the packages as objects. The equal operator is quite
694abd2b71SStephan Aßmus		// expensive as is.
70634c2c68SAxel Dörfler		return fDepot.Packages().Contains(package);
714abd2b71SStephan Aßmus	}
721f67148fSRene Gollent
730c169aceSStephan Aßmus	const BString& Depot() const
740c169aceSStephan Aßmus	{
750c169aceSStephan Aßmus		return fDepot.Name();
760c169aceSStephan Aßmus	}
770c169aceSStephan Aßmus
784abd2b71SStephan Aßmusprivate:
794abd2b71SStephan Aßmus	DepotInfo	fDepot;
804abd2b71SStephan Aßmus};
814abd2b71SStephan Aßmus
824abd2b71SStephan Aßmus
8342479955SStephan Aßmusclass CategoryFilter : public PackageFilter {
8442479955SStephan Aßmuspublic:
8542479955SStephan Aßmus	CategoryFilter(const BString& category)
8642479955SStephan Aßmus		:
8742479955SStephan Aßmus		fCategory(category)
8842479955SStephan Aßmus	{
8942479955SStephan Aßmus	}
901f67148fSRene Gollent
91779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
9242479955SStephan Aßmus	{
93779d8213SStephan Aßmus		if (package.Get() == NULL)
94779d8213SStephan Aßmus			return false;
95634c2c68SAxel Dörfler
96779d8213SStephan Aßmus		const CategoryList& categories = package->Categories();
9742479955SStephan Aßmus		for (int i = categories.CountItems() - 1; i >= 0; i--) {
9842479955SStephan Aßmus			const CategoryRef& category = categories.ItemAtFast(i);
9942479955SStephan Aßmus			if (category.Get() == NULL)
10042479955SStephan Aßmus				continue;
10142479955SStephan Aßmus			if (category->Name() == fCategory)
10242479955SStephan Aßmus				return true;
10342479955SStephan Aßmus		}
10442479955SStephan Aßmus		return false;
10542479955SStephan Aßmus	}
10642479955SStephan Aßmus
1070c169aceSStephan Aßmus	const BString& Category() const
1080c169aceSStephan Aßmus	{
1090c169aceSStephan Aßmus		return fCategory;
1100c169aceSStephan Aßmus	}
1110c169aceSStephan Aßmus
11242479955SStephan Aßmusprivate:
11342479955SStephan Aßmus	BString		fCategory;
11442479955SStephan Aßmus};
11542479955SStephan Aßmus
11642479955SStephan Aßmus
1173a6ccc85SStephan Aßmusclass ContainedInFilter : public PackageFilter {
1183a6ccc85SStephan Aßmuspublic:
1193a6ccc85SStephan Aßmus	ContainedInFilter(const PackageList& packageList)
1203a6ccc85SStephan Aßmus		:
1213a6ccc85SStephan Aßmus		fPackageList(packageList)
1223a6ccc85SStephan Aßmus	{
1233a6ccc85SStephan Aßmus	}
1241f67148fSRene Gollent
125779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
1263a6ccc85SStephan Aßmus	{
1273a6ccc85SStephan Aßmus		return fPackageList.Contains(package);
1283a6ccc85SStephan Aßmus	}
1293a6ccc85SStephan Aßmus
1303a6ccc85SStephan Aßmusprivate:
1313a6ccc85SStephan Aßmus	const PackageList&	fPackageList;
1323a6ccc85SStephan Aßmus};
1333a6ccc85SStephan Aßmus
1343a6ccc85SStephan Aßmus
1353a6ccc85SStephan Aßmusclass ContainedInEitherFilter : public PackageFilter {
1363a6ccc85SStephan Aßmuspublic:
1373a6ccc85SStephan Aßmus	ContainedInEitherFilter(const PackageList& packageListA,
1383a6ccc85SStephan Aßmus		const PackageList& packageListB)
1393a6ccc85SStephan Aßmus		:
1403a6ccc85SStephan Aßmus		fPackageListA(packageListA),
1413a6ccc85SStephan Aßmus		fPackageListB(packageListB)
1423a6ccc85SStephan Aßmus	{
1433a6ccc85SStephan Aßmus	}
1441f67148fSRene Gollent
145779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
1463a6ccc85SStephan Aßmus	{
1473a6ccc85SStephan Aßmus		return fPackageListA.Contains(package)
1483a6ccc85SStephan Aßmus			|| fPackageListB.Contains(package);
1493a6ccc85SStephan Aßmus	}
1503a6ccc85SStephan Aßmus
1513a6ccc85SStephan Aßmusprivate:
1523a6ccc85SStephan Aßmus	const PackageList&	fPackageListA;
1533a6ccc85SStephan Aßmus	const PackageList&	fPackageListB;
1543a6ccc85SStephan Aßmus};
1553a6ccc85SStephan Aßmus
1563a6ccc85SStephan Aßmus
157634c2c68SAxel Dörflerclass NotContainedInFilter : public PackageFilter {
158634c2c68SAxel Dörflerpublic:
159634c2c68SAxel Dörfler	NotContainedInFilter(const PackageList* packageList, ...)
160634c2c68SAxel Dörfler	{
161634c2c68SAxel Dörfler		va_list args;
162634c2c68SAxel Dörfler		va_start(args, packageList);
163634c2c68SAxel Dörfler		while (true) {
164634c2c68SAxel Dörfler			const PackageList* packageList = va_arg(args, const PackageList*);
165634c2c68SAxel Dörfler			if (packageList == NULL)
166634c2c68SAxel Dörfler				break;
167634c2c68SAxel Dörfler			fPackageLists.Add(packageList);
168634c2c68SAxel Dörfler		}
169634c2c68SAxel Dörfler		va_end(args);
170634c2c68SAxel Dörfler	}
171634c2c68SAxel Dörfler
172634c2c68SAxel Dörfler	virtual bool AcceptsPackage(const PackageInfoRef& package) const
173634c2c68SAxel Dörfler	{
174634c2c68SAxel Dörfler		if (package.Get()==NULL)
175634c2c68SAxel Dörfler			return false;
176a10ad54cSJonathan Schleifer
177a10ad54cSJonathan Schleifer		printf("TEST %s\n", package->Title().String());
178a10ad54cSJonathan Schleifer
179634c2c68SAxel Dörfler		for (int32 i = 0; i < fPackageLists.CountItems(); i++) {
180634c2c68SAxel Dörfler			if (fPackageLists.ItemAtFast(i)->Contains(package)) {
181998f4043SJonathan Schleifer				printf("  contained in %" B_PRId32 "\n", i);
182634c2c68SAxel Dörfler				return false;
183634c2c68SAxel Dörfler			}
184634c2c68SAxel Dörfler		}
185634c2c68SAxel Dörfler		return true;
186634c2c68SAxel Dörfler	}
187634c2c68SAxel Dörfler
188634c2c68SAxel Dörflerprivate:
189634c2c68SAxel Dörfler	List<const PackageList*, true>	fPackageLists;
190634c2c68SAxel Dörfler};
191634c2c68SAxel Dörfler
192634c2c68SAxel Dörfler
193634c2c68SAxel Dörflerclass StateFilter : public PackageFilter {
194634c2c68SAxel Dörflerpublic:
195634c2c68SAxel Dörfler	StateFilter(PackageState state)
196634c2c68SAxel Dörfler		:
197634c2c68SAxel Dörfler		fState(state)
198634c2c68SAxel Dörfler	{
199634c2c68SAxel Dörfler	}
200634c2c68SAxel Dörfler
201634c2c68SAxel Dörfler	virtual bool AcceptsPackage(const PackageInfoRef& package) const
202634c2c68SAxel Dörfler	{
203634c2c68SAxel Dörfler		return package->State() == NONE;
204634c2c68SAxel Dörfler	}
205634c2c68SAxel Dörfler
206634c2c68SAxel Dörflerprivate:
207634c2c68SAxel Dörfler	PackageState	fState;
208634c2c68SAxel Dörfler};
209634c2c68SAxel Dörfler
210634c2c68SAxel Dörfler
21109d44cecSStephan Aßmusclass SearchTermsFilter : public PackageFilter {
21209d44cecSStephan Aßmuspublic:
21309d44cecSStephan Aßmus	SearchTermsFilter(const BString& searchTerms)
21409d44cecSStephan Aßmus	{
21509d44cecSStephan Aßmus		// Separate the string into terms at spaces
21609d44cecSStephan Aßmus		int32 index = 0;
21709d44cecSStephan Aßmus		while (index < searchTerms.Length()) {
21809d44cecSStephan Aßmus			int32 nextSpace = searchTerms.FindFirst(" ", index);
21909d44cecSStephan Aßmus			if (nextSpace < 0)
22009d44cecSStephan Aßmus				nextSpace = searchTerms.Length();
22109d44cecSStephan Aßmus			if (nextSpace > index) {
22209d44cecSStephan Aßmus				BString term;
22309d44cecSStephan Aßmus				searchTerms.CopyInto(term, index, nextSpace - index);
22409d44cecSStephan Aßmus				term.ToLower();
22509d44cecSStephan Aßmus				fSearchTerms.Add(term);
22609d44cecSStephan Aßmus			}
22709d44cecSStephan Aßmus			index = nextSpace + 1;
22809d44cecSStephan Aßmus		}
22909d44cecSStephan Aßmus	}
2301f67148fSRene Gollent
231779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
23209d44cecSStephan Aßmus	{
233779d8213SStephan Aßmus		if (package.Get() == NULL)
234779d8213SStephan Aßmus			return false;
23509d44cecSStephan Aßmus		// Every search term must be found in one of the package texts
23609d44cecSStephan Aßmus		for (int32 i = fSearchTerms.CountItems() - 1; i >= 0; i--) {
23709d44cecSStephan Aßmus			const BString& term = fSearchTerms.ItemAtFast(i);
238779d8213SStephan Aßmus			if (!_TextContains(package->Title(), term)
239779d8213SStephan Aßmus				&& !_TextContains(package->Publisher().Name(), term)
240779d8213SStephan Aßmus				&& !_TextContains(package->ShortDescription(), term)
241779d8213SStephan Aßmus				&& !_TextContains(package->FullDescription(), term)) {
24209d44cecSStephan Aßmus				return false;
24309d44cecSStephan Aßmus			}
24409d44cecSStephan Aßmus		}
24509d44cecSStephan Aßmus		return true;
24609d44cecSStephan Aßmus	}
24709d44cecSStephan Aßmus
2480c169aceSStephan Aßmus	BString SearchTerms() const
2490c169aceSStephan Aßmus	{
2500c169aceSStephan Aßmus		BString searchTerms;
2510c169aceSStephan Aßmus		for (int32 i = 0; i < fSearchTerms.CountItems(); i++) {
2520c169aceSStephan Aßmus			const BString& term = fSearchTerms.ItemAtFast(i);
2530c169aceSStephan Aßmus			if (term.IsEmpty())
2540c169aceSStephan Aßmus				continue;
2550c169aceSStephan Aßmus			if (!searchTerms.IsEmpty())
2560c169aceSStephan Aßmus				searchTerms.Append(" ");
2570c169aceSStephan Aßmus			searchTerms.Append(term);
2580c169aceSStephan Aßmus		}
2590c169aceSStephan Aßmus		return searchTerms;
2600c169aceSStephan Aßmus	}
2610c169aceSStephan Aßmus
26209d44cecSStephan Aßmusprivate:
26309d44cecSStephan Aßmus	bool _TextContains(BString text, const BString& string) const
26409d44cecSStephan Aßmus	{
26509d44cecSStephan Aßmus		text.ToLower();
26609d44cecSStephan Aßmus		int32 index = text.FindFirst(string);
26709d44cecSStephan Aßmus		return index >= 0;
26809d44cecSStephan Aßmus	}
26909d44cecSStephan Aßmus
27009d44cecSStephan Aßmusprivate:
2710c169aceSStephan Aßmus	StringList fSearchTerms;
27209d44cecSStephan Aßmus};
27309d44cecSStephan Aßmus
27409d44cecSStephan Aßmus
27512107be2SStephan Aßmusclass IsFeaturedFilter : public PackageFilter {
27612107be2SStephan Aßmuspublic:
27712107be2SStephan Aßmus	IsFeaturedFilter()
27812107be2SStephan Aßmus	{
27912107be2SStephan Aßmus	}
28012107be2SStephan Aßmus
28112107be2SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
28212107be2SStephan Aßmus	{
28312107be2SStephan Aßmus		return package.Get() != NULL && package->IsProminent();
28412107be2SStephan Aßmus	}
28512107be2SStephan Aßmus};
28612107be2SStephan Aßmus
28712107be2SStephan Aßmus
288870554acSStephan Aßmusstatic inline bool
289870554acSStephan Aßmusis_source_package(const PackageInfoRef& package)
290870554acSStephan Aßmus{
291870554acSStephan Aßmus	const BString& packageName = package->Title();
292870554acSStephan Aßmus	return packageName.EndsWith("_source");
293870554acSStephan Aßmus}
294870554acSStephan Aßmus
295870554acSStephan Aßmus
296870554acSStephan Aßmusstatic inline bool
297870554acSStephan Aßmusis_develop_package(const PackageInfoRef& package)
298870554acSStephan Aßmus{
299870554acSStephan Aßmus	const BString& packageName = package->Title();
300870554acSStephan Aßmus	return packageName.EndsWith("_devel");
301870554acSStephan Aßmus}
302870554acSStephan Aßmus
303870554acSStephan Aßmus
3044abd2b71SStephan Aßmus// #pragma mark - Model
3054abd2b71SStephan Aßmus
3064abd2b71SStephan Aßmus
3076d367ba4SStephan AßmusModel::Model()
3086d367ba4SStephan Aßmus	:
309f75ec10dSStephan Aßmus	fDepots(),
310f75ec10dSStephan Aßmus
311f75ec10dSStephan Aßmus	fCategoryAudio(new PackageCategory(
312f75ec10dSStephan Aßmus		BitmapRef(),
313f75ec10dSStephan Aßmus		B_TRANSLATE("Audio"), "audio"), true),
3144a02fc4fSStephan Aßmus	fCategoryBusiness(new PackageCategory(
315f75ec10dSStephan Aßmus		BitmapRef(),
3164a02fc4fSStephan Aßmus		B_TRANSLATE("Business"), "business"), true),
3174a02fc4fSStephan Aßmus	fCategoryDevelopment(new PackageCategory(
3184a02fc4fSStephan Aßmus		BitmapRef(),
3194a02fc4fSStephan Aßmus		B_TRANSLATE("Development"), "development"), true),
3204a02fc4fSStephan Aßmus	fCategoryEducation(new PackageCategory(
3214a02fc4fSStephan Aßmus		BitmapRef(),
3224a02fc4fSStephan Aßmus		B_TRANSLATE("Education"), "education"), true),
3234a02fc4fSStephan Aßmus	fCategoryGames(new PackageCategory(
3244a02fc4fSStephan Aßmus		BitmapRef(),
3254a02fc4fSStephan Aßmus		B_TRANSLATE("Games"), "games"), true),
326f75ec10dSStephan Aßmus	fCategoryGraphics(new PackageCategory(
327f75ec10dSStephan Aßmus		BitmapRef(),
328f75ec10dSStephan Aßmus		B_TRANSLATE("Graphics"), "graphics"), true),
3294a02fc4fSStephan Aßmus	fCategoryInternetAndNetwork(new PackageCategory(
3304a02fc4fSStephan Aßmus		BitmapRef(),
3314a02fc4fSStephan Aßmus		B_TRANSLATE("Internet & Network"), "internetandnetwork"), true),
332f75ec10dSStephan Aßmus	fCategoryProductivity(new PackageCategory(
333f75ec10dSStephan Aßmus		BitmapRef(),
334f75ec10dSStephan Aßmus		B_TRANSLATE("Productivity"), "productivity"), true),
3354a02fc4fSStephan Aßmus	fCategoryScienceAndMathematics(new PackageCategory(
336f75ec10dSStephan Aßmus		BitmapRef(),
3374a02fc4fSStephan Aßmus		B_TRANSLATE("Science & Mathematics"), "scienceandmathematics"), true),
3384a02fc4fSStephan Aßmus	fCategorySystemAndUtilities(new PackageCategory(
339f75ec10dSStephan Aßmus		BitmapRef(),
3404a02fc4fSStephan Aßmus		B_TRANSLATE("System & Utilities"), "systemandutilities"), true),
3414a02fc4fSStephan Aßmus	fCategoryVideo(new PackageCategory(
3424abd2b71SStephan Aßmus		BitmapRef(),
3434a02fc4fSStephan Aßmus		B_TRANSLATE("Video"), "video"), true),
3444abd2b71SStephan Aßmus
3454abd2b71SStephan Aßmus	fCategoryFilter(PackageFilterRef(new AnyFilter(), true)),
34609d44cecSStephan Aßmus	fDepotFilter(""),
347870554acSStephan Aßmus	fSearchTermsFilter(PackageFilterRef(new AnyFilter(), true)),
34812107be2SStephan Aßmus	fIsFeaturedFilter(),
349634c2c68SAxel Dörfler
3504fade61fSStephan Aßmus	fShowFeaturedPackages(true),
351ab172803SStephan Aßmus	fShowAvailablePackages(true),
3524fade61fSStephan Aßmus	fShowInstalledPackages(true),
353870554acSStephan Aßmus	fShowSourcePackages(false),
3545bd5f161SStephan Aßmus	fShowDevelopPackages(false),
3555bd5f161SStephan Aßmus
3565bd5f161SStephan Aßmus	fPopulateAllPackagesThread(-1),
3575bd5f161SStephan Aßmus	fStopPopulatingAllPackages(false)
3586d367ba4SStephan Aßmus{
35912107be2SStephan Aßmus	_UpdateIsFeaturedFilter();
36012107be2SStephan Aßmus
361633ed4c4SStephan Aßmus	// Don't forget to add new categories to this list:
3624a02fc4fSStephan Aßmus	fCategories.Add(fCategoryGames);
3634a02fc4fSStephan Aßmus	fCategories.Add(fCategoryBusiness);
364633ed4c4SStephan Aßmus	fCategories.Add(fCategoryAudio);
365633ed4c4SStephan Aßmus	fCategories.Add(fCategoryVideo);
366633ed4c4SStephan Aßmus	fCategories.Add(fCategoryGraphics);
3674a02fc4fSStephan Aßmus	fCategories.Add(fCategoryEducation);
368633ed4c4SStephan Aßmus	fCategories.Add(fCategoryProductivity);
3694a02fc4fSStephan Aßmus	fCategories.Add(fCategorySystemAndUtilities);
3704a02fc4fSStephan Aßmus	fCategories.Add(fCategoryInternetAndNetwork);
371633ed4c4SStephan Aßmus	fCategories.Add(fCategoryDevelopment);
3724a02fc4fSStephan Aßmus	fCategories.Add(fCategoryScienceAndMathematics);
3732b411c02SStephan Aßmus	// TODO: The server will eventually support an API to
3742b411c02SStephan Aßmus	// get the defined categories and their translated names.
3752b411c02SStephan Aßmus	// This should then be used instead of hard-coded
3762b411c02SStephan Aßmus	// categories and translations in the app.
377d07ca069SStephan Aßmus
378d07ca069SStephan Aßmus	fPreferredLanguage = "en";
379d07ca069SStephan Aßmus	BLocaleRoster* localeRoster = BLocaleRoster::Default();
380d07ca069SStephan Aßmus	if (localeRoster != NULL) {
381d07ca069SStephan Aßmus		BMessage preferredLanguages;
382d07ca069SStephan Aßmus		if (localeRoster->GetPreferredLanguages(&preferredLanguages) == B_OK) {
383d07ca069SStephan Aßmus			BString language;
384d07ca069SStephan Aßmus			if (preferredLanguages.FindString("language", 0, &language) == B_OK)
385d07ca069SStephan Aßmus				language.CopyInto(fPreferredLanguage, 0, 2);
386d07ca069SStephan Aßmus		}
387d07ca069SStephan Aßmus	}
3884ff3d17aSStephan Aßmus
3894ff3d17aSStephan Aßmus	// TODO: Fetch this from the web-app.
3904ff3d17aSStephan Aßmus	fSupportedLanguages.Add("en");
3914ff3d17aSStephan Aßmus	fSupportedLanguages.Add("de");
3924ff3d17aSStephan Aßmus	fSupportedLanguages.Add("fr");
3934ff3d17aSStephan Aßmus	fSupportedLanguages.Add("ja");
3944ff3d17aSStephan Aßmus	fSupportedLanguages.Add("es");
3954ff3d17aSStephan Aßmus	fSupportedLanguages.Add("zh");
3964ff3d17aSStephan Aßmus	fSupportedLanguages.Add("pt");
3974ff3d17aSStephan Aßmus	fSupportedLanguages.Add("ru");
3984ff3d17aSStephan Aßmus
3994ff3d17aSStephan Aßmus	if (!fSupportedLanguages.Contains(fPreferredLanguage)) {
40012f29d44SStephan Aßmus		// Force the preferred language to one of the currently supported
40112f29d44SStephan Aßmus		// ones, until the web application supports all ISO language codes.
4024ff3d17aSStephan Aßmus		printf("User preferred language '%s' not currently supported, "
4034ff3d17aSStephan Aßmus			"defaulting to 'en'.", fPreferredLanguage.String());
40412f29d44SStephan Aßmus		fPreferredLanguage = "en";
40512f29d44SStephan Aßmus	}
40628075ee4SStephan Aßmus	fWebAppInterface.SetPreferredLanguage(fPreferredLanguage);
4076d367ba4SStephan Aßmus}
4086d367ba4SStephan Aßmus
4096d367ba4SStephan Aßmus
4105bd5f161SStephan AßmusModel::~Model()
4115bd5f161SStephan Aßmus{
4125bd5f161SStephan Aßmus	StopPopulatingAllPackages();
4135bd5f161SStephan Aßmus}
4145bd5f161SStephan Aßmus
4155bd5f161SStephan Aßmus
4168f03a0f9SStephan Aßmusbool
4178f03a0f9SStephan AßmusModel::AddListener(const ModelListenerRef& listener)
4188f03a0f9SStephan Aßmus{
4198f03a0f9SStephan Aßmus	return fListeners.Add(listener);
4208f03a0f9SStephan Aßmus}
4218f03a0f9SStephan Aßmus
4228f03a0f9SStephan Aßmus
423706edd80SStephan AßmusPackageList
4246d367ba4SStephan AßmusModel::CreatePackageList() const
4256d367ba4SStephan Aßmus{
426ab172803SStephan Aßmus	// Iterate all packages from all depots.
427ab172803SStephan Aßmus	// If configured, restrict depot, filter by search terms, status, name ...
428706edd80SStephan Aßmus	PackageList resultList;
4296d367ba4SStephan Aßmus
4306d367ba4SStephan Aßmus	for (int32 i = 0; i < fDepots.CountItems(); i++) {
43109d44cecSStephan Aßmus		const DepotInfo& depot = fDepots.ItemAtFast(i);
4321f67148fSRene Gollent
43309d44cecSStephan Aßmus		if (fDepotFilter.Length() > 0 && fDepotFilter != depot.Name())
43409d44cecSStephan Aßmus			continue;
4351f67148fSRene Gollent
43609d44cecSStephan Aßmus		const PackageList& packages = depot.Packages();
4376d367ba4SStephan Aßmus
438706edd80SStephan Aßmus		for (int32 j = 0; j < packages.CountItems(); j++) {
439779d8213SStephan Aßmus			const PackageInfoRef& package = packages.ItemAtFast(j);
4404abd2b71SStephan Aßmus			if (fCategoryFilter->AcceptsPackage(package)
441870554acSStephan Aßmus				&& fSearchTermsFilter->AcceptsPackage(package)
44212107be2SStephan Aßmus				&& fIsFeaturedFilter->AcceptsPackage(package)
443ab172803SStephan Aßmus				&& (fShowAvailablePackages || package->State() != NONE)
444ab172803SStephan Aßmus				&& (fShowInstalledPackages || package->State() != ACTIVATED)
445870554acSStephan Aßmus				&& (fShowSourcePackages || !is_source_package(package))
446870554acSStephan Aßmus				&& (fShowDevelopPackages || !is_develop_package(package))) {
4474abd2b71SStephan Aßmus				resultList.Add(package);
4484abd2b71SStephan Aßmus			}
4496d367ba4SStephan Aßmus		}
4506d367ba4SStephan Aßmus	}
4516d367ba4SStephan Aßmus
4526d367ba4SStephan Aßmus	return resultList;
4536d367ba4SStephan Aßmus}
4546d367ba4SStephan Aßmus
4556d367ba4SStephan Aßmus
4566d367ba4SStephan Aßmusbool
4576d367ba4SStephan AßmusModel::AddDepot(const DepotInfo& depot)
4586d367ba4SStephan Aßmus{
4596d367ba4SStephan Aßmus	return fDepots.Add(depot);
4606d367ba4SStephan Aßmus}
4616d367ba4SStephan Aßmus
46242479955SStephan Aßmus
4637b2c59b9SStephan Aßmusbool
4647b2c59b9SStephan AßmusModel::HasDepot(const BString& name) const
4657b2c59b9SStephan Aßmus{
4667b2c59b9SStephan Aßmus	for (int32 i = fDepots.CountItems() - 1; i >= 0; i--) {
4677b2c59b9SStephan Aßmus		if (fDepots.ItemAtFast(i).Name() == name)
4687b2c59b9SStephan Aßmus			return true;
4697b2c59b9SStephan Aßmus	}
4707b2c59b9SStephan Aßmus	return false;
4717b2c59b9SStephan Aßmus}
4727b2c59b9SStephan Aßmus
4737b2c59b9SStephan Aßmus
4747b2c59b9SStephan Aßmusbool
4757b2c59b9SStephan AßmusModel::SyncDepot(const DepotInfo& depot)
4767b2c59b9SStephan Aßmus{
4777b2c59b9SStephan Aßmus	for (int32 i = fDepots.CountItems() - 1; i >= 0; i--) {
4787b2c59b9SStephan Aßmus		const DepotInfo& existingDepot = fDepots.ItemAtFast(i);
4797b2c59b9SStephan Aßmus		if (existingDepot.Name() == depot.Name()) {
4807b2c59b9SStephan Aßmus			DepotInfo mergedDepot(existingDepot);
4817b2c59b9SStephan Aßmus			mergedDepot.SyncPackages(depot.Packages());
4827b2c59b9SStephan Aßmus			fDepots.Replace(i, mergedDepot);
4837b2c59b9SStephan Aßmus			return true;
4847b2c59b9SStephan Aßmus		}
4857b2c59b9SStephan Aßmus	}
4867b2c59b9SStephan Aßmus	return false;
4877b2c59b9SStephan Aßmus}
4887b2c59b9SStephan Aßmus
4897b2c59b9SStephan Aßmus
4902c287f5bSRene Gollentvoid
4912c287f5bSRene GollentModel::Clear()
4922c287f5bSRene Gollent{
4932c287f5bSRene Gollent	fDepots.Clear();
4942c287f5bSRene Gollent}
4952c287f5bSRene Gollent
4962c287f5bSRene Gollent
497706edd80SStephan Aßmusvoid
498779d8213SStephan AßmusModel::SetPackageState(const PackageInfoRef& package, PackageState state)
499706edd80SStephan Aßmus{
500706edd80SStephan Aßmus	switch (state) {
501706edd80SStephan Aßmus		default:
502706edd80SStephan Aßmus		case NONE:
503706edd80SStephan Aßmus			fInstalledPackages.Remove(package);
504706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
505706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
506706edd80SStephan Aßmus			break;
507706edd80SStephan Aßmus		case INSTALLED:
508706edd80SStephan Aßmus			if (!fInstalledPackages.Contains(package))
509706edd80SStephan Aßmus				fInstalledPackages.Add(package);
510706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
511706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
512706edd80SStephan Aßmus			break;
513706edd80SStephan Aßmus		case ACTIVATED:
514706edd80SStephan Aßmus			if (!fInstalledPackages.Contains(package))
515706edd80SStephan Aßmus				fInstalledPackages.Add(package);
516706edd80SStephan Aßmus			if (!fActivatedPackages.Contains(package))
517706edd80SStephan Aßmus				fActivatedPackages.Add(package);
518706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
519706edd80SStephan Aßmus			break;
520706edd80SStephan Aßmus		case UNINSTALLED:
521706edd80SStephan Aßmus			fInstalledPackages.Remove(package);
522706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
523ab172803SStephan Aßmus			if (!fUninstalledPackages.Contains(package))
524ab172803SStephan Aßmus				fUninstalledPackages.Add(package);
525706edd80SStephan Aßmus			break;
526706edd80SStephan Aßmus	}
527b4c8d2ffSRene Gollent
528b4c8d2ffSRene Gollent	package->SetState(state);
529706edd80SStephan Aßmus}
530706edd80SStephan Aßmus
531706edd80SStephan Aßmus
53209d44cecSStephan Aßmus// #pragma mark - filters
53309d44cecSStephan Aßmus
53409d44cecSStephan Aßmus
53542479955SStephan Aßmusvoid
53642479955SStephan AßmusModel::SetCategory(const BString& category)
53742479955SStephan Aßmus{
5383a6ccc85SStephan Aßmus	PackageFilter* filter;
5391f67148fSRene Gollent
54042479955SStephan Aßmus	if (category.Length() == 0)
5413a6ccc85SStephan Aßmus		filter = new AnyFilter();
54242479955SStephan Aßmus	else
5433a6ccc85SStephan Aßmus		filter = new CategoryFilter(category);
5443a6ccc85SStephan Aßmus
5453a6ccc85SStephan Aßmus	fCategoryFilter.SetTo(filter, true);
54642479955SStephan Aßmus}
54742479955SStephan Aßmus
54809d44cecSStephan Aßmus
5490c169aceSStephan AßmusBString
5500c169aceSStephan AßmusModel::Category() const
5510c169aceSStephan Aßmus{
5520c169aceSStephan Aßmus	CategoryFilter* filter
5530c169aceSStephan Aßmus		= dynamic_cast<CategoryFilter*>(fCategoryFilter.Get());
5540c169aceSStephan Aßmus	if (filter == NULL)
5550c169aceSStephan Aßmus		return "";
5560c169aceSStephan Aßmus	return filter->Category();
5570c169aceSStephan Aßmus}
5580c169aceSStephan Aßmus
5590c169aceSStephan Aßmus
56009d44cecSStephan Aßmusvoid
56109d44cecSStephan AßmusModel::SetDepot(const BString& depot)
56209d44cecSStephan Aßmus{
56309d44cecSStephan Aßmus	fDepotFilter = depot;
56409d44cecSStephan Aßmus}
56509d44cecSStephan Aßmus
56609d44cecSStephan Aßmus
5670c169aceSStephan AßmusBString
5680c169aceSStephan AßmusModel::Depot() const
5690c169aceSStephan Aßmus{
5700c169aceSStephan Aßmus	return fDepotFilter;
5710c169aceSStephan Aßmus}
5720c169aceSStephan Aßmus
5730c169aceSStephan Aßmus
57409d44cecSStephan Aßmusvoid
57509d44cecSStephan AßmusModel::SetSearchTerms(const BString& searchTerms)
57609d44cecSStephan Aßmus{
57709d44cecSStephan Aßmus	PackageFilter* filter;
5781f67148fSRene Gollent
57909d44cecSStephan Aßmus	if (searchTerms.Length() == 0)
58009d44cecSStephan Aßmus		filter = new AnyFilter();
58109d44cecSStephan Aßmus	else
58209d44cecSStephan Aßmus		filter = new SearchTermsFilter(searchTerms);
58309d44cecSStephan Aßmus
58409d44cecSStephan Aßmus	fSearchTermsFilter.SetTo(filter, true);
58512107be2SStephan Aßmus	_UpdateIsFeaturedFilter();
58609d44cecSStephan Aßmus}
58709d44cecSStephan Aßmus
5887eec822dSStephan Aßmus
5890c169aceSStephan AßmusBString
5900c169aceSStephan AßmusModel::SearchTerms() const
5910c169aceSStephan Aßmus{
5920c169aceSStephan Aßmus	SearchTermsFilter* filter
5930c169aceSStephan Aßmus		= dynamic_cast<SearchTermsFilter*>(fSearchTermsFilter.Get());
5940c169aceSStephan Aßmus	if (filter == NULL)
5950c169aceSStephan Aßmus		return "";
5960c169aceSStephan Aßmus	return filter->SearchTerms();
5970c169aceSStephan Aßmus}
5980c169aceSStephan Aßmus
5990c169aceSStephan Aßmus
6004fade61fSStephan Aßmusvoid
6014fade61fSStephan AßmusModel::SetShowFeaturedPackages(bool show)
6024fade61fSStephan Aßmus{
6034fade61fSStephan Aßmus	fShowFeaturedPackages = show;
60412107be2SStephan Aßmus	_UpdateIsFeaturedFilter();
6054fade61fSStephan Aßmus}
6064fade61fSStephan Aßmus
6074fade61fSStephan Aßmus
608ab172803SStephan Aßmusvoid
609ab172803SStephan AßmusModel::SetShowAvailablePackages(bool show)
610ab172803SStephan Aßmus{
611ab172803SStephan Aßmus	fShowAvailablePackages = show;
612ab172803SStephan Aßmus}
613ab172803SStephan Aßmus
614ab172803SStephan Aßmus
615ab172803SStephan Aßmusvoid
616ab172803SStephan AßmusModel::SetShowInstalledPackages(bool show)
617ab172803SStephan Aßmus{
618ab172803SStephan Aßmus	fShowInstalledPackages = show;
619ab172803SStephan Aßmus}
620ab172803SStephan Aßmus
621ab172803SStephan Aßmus
622870554acSStephan Aßmusvoid
623870554acSStephan AßmusModel::SetShowSourcePackages(bool show)
624870554acSStephan Aßmus{
625870554acSStephan Aßmus	fShowSourcePackages = show;
626870554acSStephan Aßmus}
627870554acSStephan Aßmus
628870554acSStephan Aßmus
629870554acSStephan Aßmusvoid
630870554acSStephan AßmusModel::SetShowDevelopPackages(bool show)
631870554acSStephan Aßmus{
632870554acSStephan Aßmus	fShowDevelopPackages = show;
633870554acSStephan Aßmus}
634870554acSStephan Aßmus
635870554acSStephan Aßmus
6367eec822dSStephan Aßmus// #pragma mark - information retrival
6377eec822dSStephan Aßmus
6387eec822dSStephan Aßmus
6397eec822dSStephan Aßmusvoid
640a60a23f8SStephan AßmusModel::PopulatePackage(const PackageInfoRef& package, uint32 flags)
6417eec822dSStephan Aßmus{
6427eec822dSStephan Aßmus	// TODO: There should probably also be a way to "unpopulate" the
6437eec822dSStephan Aßmus	// package information. Maybe a cache of populated packages, so that
6447eec822dSStephan Aßmus	// packages loose their extra information after a certain amount of
6457eec822dSStephan Aßmus	// time when they have not been accessed/displayed in the UI. Otherwise
6467eec822dSStephan Aßmus	// HaikuDepot will consume more and more resources in the packages.
6477eec822dSStephan Aßmus	// Especially screen-shots will be a problem eventually.
64848045f5aSStephan Aßmus	{
64948045f5aSStephan Aßmus		BAutolock locker(&fLock);
65089e4ba17SStephan Aßmus		bool alreadyPopulated = fPopulatedPackages.Contains(package);
65189e4ba17SStephan Aßmus		if ((flags & POPULATE_FORCE) == 0 && alreadyPopulated)
65248045f5aSStephan Aßmus			return;
65389e4ba17SStephan Aßmus		if (!alreadyPopulated)
65489e4ba17SStephan Aßmus			fPopulatedPackages.Add(package);
65548045f5aSStephan Aßmus	}
6567eec822dSStephan Aßmus
6577ff94fd0SStephan Aßmus	if ((flags & POPULATE_USER_RATINGS) != 0) {
6587ff94fd0SStephan Aßmus		// Retrieve info from web-app
6597ff94fd0SStephan Aßmus		BMessage info;
66048045f5aSStephan Aßmus
66148045f5aSStephan Aßmus		BString packageName;
66248045f5aSStephan Aßmus		BString architecture;
66348045f5aSStephan Aßmus		{
66448045f5aSStephan Aßmus			BAutolock locker(&fLock);
66548045f5aSStephan Aßmus			packageName = package->Title();
66648045f5aSStephan Aßmus			architecture = package->Architecture();
66748045f5aSStephan Aßmus		}
6687ff94fd0SStephan Aßmus
66928075ee4SStephan Aßmus		status_t status = fWebAppInterface.RetrieveUserRatings(packageName,
67048045f5aSStephan Aßmus			architecture, 0, 50, info);
6717ff94fd0SStephan Aßmus		if (status == B_OK) {
6727ff94fd0SStephan Aßmus			// Parse message
6737ff94fd0SStephan Aßmus			BMessage result;
6747ff94fd0SStephan Aßmus			BMessage items;
6757ff94fd0SStephan Aßmus			if (info.FindMessage("result", &result) == B_OK
6767ff94fd0SStephan Aßmus				&& result.FindMessage("items", &items) == B_OK) {
67748045f5aSStephan Aßmus
67848045f5aSStephan Aßmus				BAutolock locker(&fLock);
6797eb09bfaSStephan Aßmus				package->ClearUserRatings();
68048045f5aSStephan Aßmus
6817ff94fd0SStephan Aßmus				int index = 0;
6827ff94fd0SStephan Aßmus				while (true) {
6837ff94fd0SStephan Aßmus					BString name;
6847ff94fd0SStephan Aßmus					name << index++;
6857ff94fd0SStephan Aßmus
6867ff94fd0SStephan Aßmus					BMessage item;
6877ff94fd0SStephan Aßmus					if (items.FindMessage(name, &item) != B_OK)
6887ff94fd0SStephan Aßmus						break;
6897ff94fd0SStephan Aßmus//					item.PrintToStream();
6907ff94fd0SStephan Aßmus
6917ff94fd0SStephan Aßmus					BString user;
6927ff94fd0SStephan Aßmus					BMessage userInfo;
6937ff94fd0SStephan Aßmus					if (item.FindMessage("user", &userInfo) != B_OK
6947ff94fd0SStephan Aßmus						|| userInfo.FindString("nickname", &user) != B_OK) {
6957ff94fd0SStephan Aßmus						// Ignore, we need the user name
6967ff94fd0SStephan Aßmus						continue;
6977ff94fd0SStephan Aßmus					}
6987ff94fd0SStephan Aßmus
69936a9b557SStephan Aßmus					// Extract basic info, all items are optional
7007ff94fd0SStephan Aßmus					BString languageCode;
7017ff94fd0SStephan Aßmus					BString comment;
7027ff94fd0SStephan Aßmus					double rating;
70336a9b557SStephan Aßmus					item.FindString("naturalLanguageCode", &languageCode);
70436a9b557SStephan Aßmus					item.FindString("comment", &comment);
70536a9b557SStephan Aßmus					if (item.FindDouble("rating", &rating) != B_OK)
70636a9b557SStephan Aßmus						rating = -1;
70736a9b557SStephan Aßmus					if (comment.Length() == 0 && rating == -1) {
70836a9b557SStephan Aßmus						// No useful information given.
7097ff94fd0SStephan Aßmus						continue;
7107ff94fd0SStephan Aßmus					}
71136a9b557SStephan Aßmus
7127ff94fd0SStephan Aßmus					// For which version of the package was the rating?
7137ff94fd0SStephan Aßmus					BString major = "?";
7147ff94fd0SStephan Aßmus					BString minor = "?";
7157ff94fd0SStephan Aßmus					BString micro = "";
7167ff94fd0SStephan Aßmus					BMessage version;
7177ff94fd0SStephan Aßmus					if (item.FindMessage("pkgVersion", &version) == B_OK) {
7187ff94fd0SStephan Aßmus						version.FindString("major", &major);
7197ff94fd0SStephan Aßmus						version.FindString("minor", &minor);
7207ff94fd0SStephan Aßmus						version.FindString("micro", &micro);
7217ff94fd0SStephan Aßmus					}
7227ff94fd0SStephan Aßmus					BString versionString = major;
7237ff94fd0SStephan Aßmus					versionString << ".";
7247ff94fd0SStephan Aßmus					versionString << minor;
7257ff94fd0SStephan Aßmus					if (micro.Length() > 0) {
7267ff94fd0SStephan Aßmus						versionString << ".";
7277ff94fd0SStephan Aßmus						versionString << micro;
7287ff94fd0SStephan Aßmus					}
7297ff94fd0SStephan Aßmus					// Add the rating to the PackageInfo
7307ff94fd0SStephan Aßmus					package->AddUserRating(
7317ff94fd0SStephan Aßmus						UserRating(UserInfo(user), rating,
7327ff94fd0SStephan Aßmus							comment, languageCode, versionString, 0, 0)
7337ff94fd0SStephan Aßmus					);
7347ff94fd0SStephan Aßmus				}
7357ff94fd0SStephan Aßmus			} else if (info.FindMessage("error", &result) == B_OK) {
7367ff94fd0SStephan Aßmus				result.PrintToStream();
7377ff94fd0SStephan Aßmus			}
738a60a23f8SStephan Aßmus		}
7397eec822dSStephan Aßmus	}
74087038da7SStephan Aßmus
74187038da7SStephan Aßmus	if ((flags & POPULATE_SCREEN_SHOTS) != 0) {
74287038da7SStephan Aßmus		ScreenshotInfoList screenshotInfos;
74387038da7SStephan Aßmus		{
74487038da7SStephan Aßmus			BAutolock locker(&fLock);
74587038da7SStephan Aßmus			screenshotInfos = package->ScreenshotInfos();
7467eb09bfaSStephan Aßmus			package->ClearScreenshots();
74787038da7SStephan Aßmus		}
74887038da7SStephan Aßmus		for (int i = 0; i < screenshotInfos.CountItems(); i++) {
74987038da7SStephan Aßmus			const ScreenshotInfo& info = screenshotInfos.ItemAtFast(i);
750e0881f21SStephan Aßmus			_PopulatePackageScreenshot(package, info, 320, false);
75187038da7SStephan Aßmus		}
75287038da7SStephan Aßmus	}
7537eec822dSStephan Aßmus}
7545bd5f161SStephan Aßmus
7555bd5f161SStephan Aßmus
7565bd5f161SStephan Aßmusvoid
7575bd5f161SStephan AßmusModel::PopulateAllPackages()
7585bd5f161SStephan Aßmus{
7595bd5f161SStephan Aßmus	StopPopulatingAllPackages();
7605bd5f161SStephan Aßmus
7615bd5f161SStephan Aßmus	fStopPopulatingAllPackages = false;
7625bd5f161SStephan Aßmus
7635bd5f161SStephan Aßmus	fPopulateAllPackagesThread = spawn_thread(&_PopulateAllPackagesEntry,
7645bd5f161SStephan Aßmus		"Package populator", B_NORMAL_PRIORITY, this);
7655bd5f161SStephan Aßmus	if (fPopulateAllPackagesThread >= 0)
7665bd5f161SStephan Aßmus		resume_thread(fPopulateAllPackagesThread);
7675bd5f161SStephan Aßmus}
7685bd5f161SStephan Aßmus
7695bd5f161SStephan Aßmus
7705bd5f161SStephan Aßmusvoid
7715bd5f161SStephan AßmusModel::StopPopulatingAllPackages()
7725bd5f161SStephan Aßmus{
7735bd5f161SStephan Aßmus	if (fPopulateAllPackagesThread < 0)
7745bd5f161SStephan Aßmus		return;
7755bd5f161SStephan Aßmus
7765bd5f161SStephan Aßmus	fStopPopulatingAllPackages = true;
7775bd5f161SStephan Aßmus	wait_for_thread(fPopulateAllPackagesThread, NULL);
7785bd5f161SStephan Aßmus	fPopulateAllPackagesThread = -1;
7795bd5f161SStephan Aßmus}
7805bd5f161SStephan Aßmus
7815bd5f161SStephan Aßmus
78228075ee4SStephan Aßmusvoid
7838f03a0f9SStephan AßmusModel::SetUsername(BString username)
78489ec84a4SStephan Aßmus{
7858f03a0f9SStephan Aßmus	BString password;
7868f03a0f9SStephan Aßmus	if (username.Length() > 0) {
7878f03a0f9SStephan Aßmus		BPasswordKey key;
7888f03a0f9SStephan Aßmus		BKeyStore keyStore;
7898f03a0f9SStephan Aßmus		if (keyStore.GetKey(kHaikuDepotKeyring, B_KEY_TYPE_PASSWORD, username,
7908f03a0f9SStephan Aßmus				key) == B_OK) {
7918f03a0f9SStephan Aßmus			password = key.Password();
7928f03a0f9SStephan Aßmus		} else {
7938f03a0f9SStephan Aßmus			username = "";
7948f03a0f9SStephan Aßmus		}
79589ec84a4SStephan Aßmus	}
7968f03a0f9SStephan Aßmus	SetAuthorization(username, password, false);
7978f03a0f9SStephan Aßmus}
7988f03a0f9SStephan Aßmus
7998f03a0f9SStephan Aßmus
8008f03a0f9SStephan Aßmusconst BString&
8018f03a0f9SStephan AßmusModel::Username() const
8028f03a0f9SStephan Aßmus{
8038f03a0f9SStephan Aßmus	return fWebAppInterface.Username();
80489ec84a4SStephan Aßmus}
80589ec84a4SStephan Aßmus
80689ec84a4SStephan Aßmus
80789ec84a4SStephan Aßmusvoid
80889ec84a4SStephan AßmusModel::SetAuthorization(const BString& username, const BString& password,
80989ec84a4SStephan Aßmus	bool storePassword)
81028075ee4SStephan Aßmus{
8118f03a0f9SStephan Aßmus	if (storePassword && username.Length() > 0 && password.Length() > 0) {
81289ec84a4SStephan Aßmus		BPasswordKey key(password, B_KEY_PURPOSE_WEB, username);
81389ec84a4SStephan Aßmus		BKeyStore keyStore;
81489ec84a4SStephan Aßmus		keyStore.AddKeyring(kHaikuDepotKeyring);
81589ec84a4SStephan Aßmus		keyStore.AddKey(kHaikuDepotKeyring, key);
81689ec84a4SStephan Aßmus	}
81789ec84a4SStephan Aßmus
81828075ee4SStephan Aßmus	BAutolock locker(&fLock);
81928075ee4SStephan Aßmus	fWebAppInterface.SetAuthorization(username, password);
8208f03a0f9SStephan Aßmus
8218f03a0f9SStephan Aßmus	_NotifyAuthorizationChanged();
82228075ee4SStephan Aßmus}
82328075ee4SStephan Aßmus
82428075ee4SStephan Aßmus
82528075ee4SStephan Aßmus// #pragma mark - private
82628075ee4SStephan Aßmus
82728075ee4SStephan Aßmus
82812107be2SStephan Aßmusvoid
82912107be2SStephan AßmusModel::_UpdateIsFeaturedFilter()
83012107be2SStephan Aßmus{
83112107be2SStephan Aßmus	if (fShowFeaturedPackages && SearchTerms().IsEmpty())
83212107be2SStephan Aßmus		fIsFeaturedFilter = PackageFilterRef(new IsFeaturedFilter(), true);
83312107be2SStephan Aßmus	else
83412107be2SStephan Aßmus		fIsFeaturedFilter = PackageFilterRef(new AnyFilter(), true);
83512107be2SStephan Aßmus}
83612107be2SStephan Aßmus
83712107be2SStephan Aßmus
8385bd5f161SStephan Aßmusint32
8395bd5f161SStephan AßmusModel::_PopulateAllPackagesEntry(void* cookie)
8405bd5f161SStephan Aßmus{
8415bd5f161SStephan Aßmus	Model* model = static_cast<Model*>(cookie);
842726db742SStephan Aßmus	model->_PopulateAllPackagesThread(true);
843726db742SStephan Aßmus	model->_PopulateAllPackagesThread(false);
8445bd5f161SStephan Aßmus	return 0;
8455bd5f161SStephan Aßmus}
8465bd5f161SStephan Aßmus
8475bd5f161SStephan Aßmus
8485bd5f161SStephan Aßmusvoid
849726db742SStephan AßmusModel::_PopulateAllPackagesThread(bool fromCacheOnly)
8505bd5f161SStephan Aßmus{
8515bd5f161SStephan Aßmus	int32 depotIndex = 0;
8525bd5f161SStephan Aßmus	int32 packageIndex = 0;
853e3fdc894SStephan Aßmus	PackageList bulkPackageList;
854e3fdc894SStephan Aßmus	PackageList packagesWithIconsList;
8555bd5f161SStephan Aßmus
8565bd5f161SStephan Aßmus	while (!fStopPopulatingAllPackages) {
8575bd5f161SStephan Aßmus		// Obtain PackageInfoRef while keeping the depot and package lists
8585bd5f161SStephan Aßmus		// locked.
8595bd5f161SStephan Aßmus		PackageInfoRef package;
8605bd5f161SStephan Aßmus		{
8615bd5f161SStephan Aßmus			BAutolock locker(&fLock);
8625bd5f161SStephan Aßmus
8635bd5f161SStephan Aßmus			if (depotIndex >= fDepots.CountItems())
8645bd5f161SStephan Aßmus				break;
8655bd5f161SStephan Aßmus			const DepotInfo& depot = fDepots.ItemAt(depotIndex);
8665bd5f161SStephan Aßmus
8675bd5f161SStephan Aßmus			const PackageList& packages = depot.Packages();
8685bd5f161SStephan Aßmus			if (packageIndex >= packages.CountItems()) {
8695bd5f161SStephan Aßmus				// Need the next depot
8705bd5f161SStephan Aßmus				packageIndex = 0;
8715bd5f161SStephan Aßmus				depotIndex++;
8725bd5f161SStephan Aßmus				continue;
8735bd5f161SStephan Aßmus			}
8745bd5f161SStephan Aßmus
8755bd5f161SStephan Aßmus			package = packages.ItemAt(packageIndex);
8765bd5f161SStephan Aßmus			packageIndex++;
8775bd5f161SStephan Aßmus		}
8785bd5f161SStephan Aßmus
8795bd5f161SStephan Aßmus		if (package.Get() == NULL)
8805bd5f161SStephan Aßmus			continue;
8815bd5f161SStephan Aßmus
882e3fdc894SStephan Aßmus		//_PopulatePackageInfo(package, fromCacheOnly);
883e3fdc894SStephan Aßmus		bulkPackageList.Add(package);
884e3fdc894SStephan Aßmus		if (bulkPackageList.CountItems() == 50) {
885e3fdc894SStephan Aßmus			_PopulatePackageInfos(bulkPackageList, fromCacheOnly,
886e3fdc894SStephan Aßmus				packagesWithIconsList);
887e3fdc894SStephan Aßmus			bulkPackageList.Clear();
888e3fdc894SStephan Aßmus		}
889e3fdc894SStephan Aßmus		if (fromCacheOnly)
890e3fdc894SStephan Aßmus			_PopulatePackageIcon(package, fromCacheOnly);
8915bd5f161SStephan Aßmus		// TODO: Average user rating. It needs to be shown in the
8925bd5f161SStephan Aßmus		// list view, so without the user clicking the package.
8935bd5f161SStephan Aßmus	}
894e3fdc894SStephan Aßmus
8956e48c81bSStephan Aßmus	if (bulkPackageList.CountItems() > 0) {
896e3fdc894SStephan Aßmus		_PopulatePackageInfos(bulkPackageList, fromCacheOnly,
897e3fdc894SStephan Aßmus			packagesWithIconsList);
898e3fdc894SStephan Aßmus	}
899e3fdc894SStephan Aßmus
900e3fdc894SStephan Aßmus	if (!fromCacheOnly) {
901e3fdc894SStephan Aßmus		for (int i = packagesWithIconsList.CountItems() - 1; i >= 0; i--) {
902e3fdc894SStephan Aßmus			if (fStopPopulatingAllPackages)
903e3fdc894SStephan Aßmus				break;
904e3fdc894SStephan Aßmus			const PackageInfoRef& package = packagesWithIconsList.ItemAtFast(i);
905e3fdc894SStephan Aßmus			printf("Getting/Updating native icon for %s\n",
906e3fdc894SStephan Aßmus				package->Title().String());
907e3fdc894SStephan Aßmus			_PopulatePackageIcon(package, fromCacheOnly);
908e3fdc894SStephan Aßmus		}
909e3fdc894SStephan Aßmus	}
9105bd5f161SStephan Aßmus}
9115bd5f161SStephan Aßmus
9125bd5f161SStephan Aßmus
913a3dfc57dSStephan Aßmusbool
914a3dfc57dSStephan AßmusModel::_GetCacheFile(BPath& path, BFile& file, directory_which directory,
915a3dfc57dSStephan Aßmus	const char* relativeLocation, const char* fileName, uint32 openMode) const
916a3dfc57dSStephan Aßmus{
917a3dfc57dSStephan Aßmus	if (find_directory(directory, &path) == B_OK
918a3dfc57dSStephan Aßmus		&& path.Append(relativeLocation) == B_OK
919a3dfc57dSStephan Aßmus		&& create_directory(path.Path(), 0777) == B_OK
920a3dfc57dSStephan Aßmus		&& path.Append(fileName) == B_OK) {
921a3dfc57dSStephan Aßmus		// Try opening the file which will fail if its
922a3dfc57dSStephan Aßmus		// not a file or does not exist.
923a3dfc57dSStephan Aßmus		return file.SetTo(path.Path(), openMode) == B_OK;
924a3dfc57dSStephan Aßmus	}
925a3dfc57dSStephan Aßmus	return false;
926a3dfc57dSStephan Aßmus}
927a3dfc57dSStephan Aßmus
928a3dfc57dSStephan Aßmus
929a3dfc57dSStephan Aßmusbool
930a3dfc57dSStephan AßmusModel::_GetCacheFile(BPath& path, BFile& file, directory_which directory,
931a3dfc57dSStephan Aßmus	const char* relativeLocation, const char* fileName,
932a3dfc57dSStephan Aßmus	bool ignoreAge, time_t maxAge) const
933a3dfc57dSStephan Aßmus{
934a3dfc57dSStephan Aßmus	if (!_GetCacheFile(path, file, directory, relativeLocation, fileName,
935a3dfc57dSStephan Aßmus			B_READ_ONLY)) {
936a3dfc57dSStephan Aßmus		return false;
937a3dfc57dSStephan Aßmus	}
938a3dfc57dSStephan Aßmus
939a3dfc57dSStephan Aßmus	if (ignoreAge)
940a3dfc57dSStephan Aßmus		return true;
941a3dfc57dSStephan Aßmus
942a3dfc57dSStephan Aßmus	time_t modifiedTime;
943a3dfc57dSStephan Aßmus	file.GetModificationTime(&modifiedTime);
944a3dfc57dSStephan Aßmus	time_t now;
945a3dfc57dSStephan Aßmus	time(&now);
946a3dfc57dSStephan Aßmus	return now - modifiedTime < maxAge;
947a3dfc57dSStephan Aßmus}
948a3dfc57dSStephan Aßmus
949a3dfc57dSStephan Aßmus
950e08beebeSStephan Aßmusvoid
951e3fdc894SStephan AßmusModel::_PopulatePackageInfos(PackageList& packages, bool fromCacheOnly,
952e3fdc894SStephan Aßmus	PackageList& packagesWithIcons)
953e08beebeSStephan Aßmus{
9546e48c81bSStephan Aßmus	if (fStopPopulatingAllPackages)
9556e48c81bSStephan Aßmus		return;
9566e48c81bSStephan Aßmus
957a3dfc57dSStephan Aßmus	// See if there are cached info files
958a3dfc57dSStephan Aßmus	for (int i = packages.CountItems() - 1; i >= 0; i--) {
959a3dfc57dSStephan Aßmus		if (fStopPopulatingAllPackages)
960a3dfc57dSStephan Aßmus			return;
961a3dfc57dSStephan Aßmus
962a3dfc57dSStephan Aßmus		const PackageInfoRef& package = packages.ItemAtFast(i);
963a3dfc57dSStephan Aßmus
964a3dfc57dSStephan Aßmus		BFile file;
965a3dfc57dSStephan Aßmus		BPath path;
966a3dfc57dSStephan Aßmus		BString name(package->Title());
967a3dfc57dSStephan Aßmus		name << ".info";
968a3dfc57dSStephan Aßmus		if (_GetCacheFile(path, file, B_USER_CACHE_DIRECTORY,
969a3dfc57dSStephan Aßmus			"HaikuDepot", name, fromCacheOnly, 60 * 60)) {
970a3dfc57dSStephan Aßmus			// Cache file is recent enough, just use it and return.
971a3dfc57dSStephan Aßmus			BMessage pkgInfo;
972a3dfc57dSStephan Aßmus			if (pkgInfo.Unflatten(&file) == B_OK) {
973a3dfc57dSStephan Aßmus				_PopulatePackageInfo(package, pkgInfo);
974a3dfc57dSStephan Aßmus				if (_HasNativeIcon(pkgInfo))
975a3dfc57dSStephan Aßmus					packagesWithIcons.Add(package);
976a3dfc57dSStephan Aßmus				packages.Remove(i);
977a3dfc57dSStephan Aßmus			}
978a3dfc57dSStephan Aßmus		}
979a3dfc57dSStephan Aßmus	}
980a3dfc57dSStephan Aßmus
981a3dfc57dSStephan Aßmus	if (fromCacheOnly || packages.IsEmpty())
982e08beebeSStephan Aßmus		return;
983e08beebeSStephan Aßmus
984e08beebeSStephan Aßmus	// Retrieve info from web-app
985e08beebeSStephan Aßmus	BMessage info;
986e08beebeSStephan Aßmus
987e3fdc894SStephan Aßmus	StringList packageNames;
9882b411c02SStephan Aßmus	StringList packageArchitectures;
989e3fdc894SStephan Aßmus	for (int i = 0; i < packages.CountItems(); i++) {
990e3fdc894SStephan Aßmus		const PackageInfoRef& package = packages.ItemAtFast(i);
991e3fdc894SStephan Aßmus		packageNames.Add(package->Title());
9922b411c02SStephan Aßmus		packageArchitectures.Add(package->Architecture());
993e3fdc894SStephan Aßmus	}
994e3fdc894SStephan Aßmus
99528075ee4SStephan Aßmus	status_t status = fWebAppInterface.RetrieveBulkPackageInfo(packageNames,
9962b411c02SStephan Aßmus		packageArchitectures, info);
997e08beebeSStephan Aßmus	if (status == B_OK) {
99895e4ca3dSStephan Aßmus		// Parse message
999e3fdc894SStephan Aßmus//		info.PrintToStream();
100095e4ca3dSStephan Aßmus		BMessage result;
1001e3fdc894SStephan Aßmus		BMessage pkgs;
1002e3fdc894SStephan Aßmus		if (info.FindMessage("result", &result) == B_OK
1003e3fdc894SStephan Aßmus			&& result.FindMessage("pkgs", &pkgs) == B_OK) {
1004e3fdc894SStephan Aßmus			int32 index = 0;
1005e3fdc894SStephan Aßmus			while (true) {
10066e48c81bSStephan Aßmus				if (fStopPopulatingAllPackages)
10076e48c81bSStephan Aßmus					return;
1008e3fdc894SStephan Aßmus				BString name;
1009e3fdc894SStephan Aßmus				name << index++;
1010e3fdc894SStephan Aßmus				BMessage pkgInfo;
1011e3fdc894SStephan Aßmus				if (pkgs.FindMessage(name, &pkgInfo) != B_OK)
1012e3fdc894SStephan Aßmus					break;
1013e3fdc894SStephan Aßmus
1014e3fdc894SStephan Aßmus				BString pkgName;
1015e3fdc894SStephan Aßmus				if (pkgInfo.FindString("name", &pkgName) != B_OK)
1016e3fdc894SStephan Aßmus					continue;
1017e3fdc894SStephan Aßmus
1018e3fdc894SStephan Aßmus				// Find the PackageInfoRef
1019e3fdc894SStephan Aßmus				bool found = false;
1020e3fdc894SStephan Aßmus				for (int i = 0; i < packages.CountItems(); i++) {
1021e3fdc894SStephan Aßmus					const PackageInfoRef& package = packages.ItemAtFast(i);
1022e3fdc894SStephan Aßmus					if (pkgName == package->Title()) {
1023e3fdc894SStephan Aßmus						_PopulatePackageInfo(package, pkgInfo);
1024e3fdc894SStephan Aßmus						if (_HasNativeIcon(pkgInfo))
1025e3fdc894SStephan Aßmus							packagesWithIcons.Add(package);
1026a3dfc57dSStephan Aßmus
1027a3dfc57dSStephan Aßmus						// Store in cache
1028a3dfc57dSStephan Aßmus						BFile file;
1029a3dfc57dSStephan Aßmus						BPath path;
1030a3dfc57dSStephan Aßmus						BString fileName(package->Title());
1031a3dfc57dSStephan Aßmus						fileName << ".info";
1032a3dfc57dSStephan Aßmus						if (_GetCacheFile(path, file, B_USER_CACHE_DIRECTORY,
1033a3dfc57dSStephan Aßmus								"HaikuDepot", fileName,
1034a3dfc57dSStephan Aßmus								B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE)) {
1035a3dfc57dSStephan Aßmus							pkgInfo.Flatten(&file);
1036a3dfc57dSStephan Aßmus						}
1037a3dfc57dSStephan Aßmus
1038e3fdc894SStephan Aßmus						packages.Remove(i);
1039e3fdc894SStephan Aßmus						found = true;
104095e4ca3dSStephan Aßmus						break;
1041e3fdc894SStephan Aßmus					}
104295e4ca3dSStephan Aßmus				}
1043e3fdc894SStephan Aßmus				if (!found)
1044e3fdc894SStephan Aßmus					printf("No matching package for %s\n", pkgName.String());
104595e4ca3dSStephan Aßmus			}
1046e3fdc894SStephan Aßmus		}
1047e3fdc894SStephan Aßmus	} else {
1048e3fdc894SStephan Aßmus		printf("Error sending request: %s\n", strerror(status));
1049e3fdc894SStephan Aßmus		int count = packages.CountItems();
1050e3fdc894SStephan Aßmus		if (count >= 4) {
1051e3fdc894SStephan Aßmus			// Retry in smaller chunks
1052e3fdc894SStephan Aßmus			PackageList firstHalf;
1053e3fdc894SStephan Aßmus			PackageList secondHalf;
1054e3fdc894SStephan Aßmus			for (int i = 0; i < count / 2; i++)
1055e3fdc894SStephan Aßmus				firstHalf.Add(packages.ItemAtFast(i));
1056e3fdc894SStephan Aßmus			for (int i = count / 2; i < count; i++)
1057e3fdc894SStephan Aßmus				secondHalf.Add(packages.ItemAtFast(i));
1058e3fdc894SStephan Aßmus			packages.Clear();
1059e3fdc894SStephan Aßmus			_PopulatePackageInfos(firstHalf, fromCacheOnly, packagesWithIcons);
1060e3fdc894SStephan Aßmus			_PopulatePackageInfos(secondHalf, fromCacheOnly, packagesWithIcons);
1061e3fdc894SStephan Aßmus		} else {
1062e3fdc894SStephan Aßmus			while (packages.CountItems() > 0) {
1063e3fdc894SStephan Aßmus				const PackageInfoRef& package = packages.ItemAtFast(0);
1064e3fdc894SStephan Aßmus				_PopulatePackageInfo(package, fromCacheOnly);
1065e3fdc894SStephan Aßmus				packages.Remove(0);
106695e4ca3dSStephan Aßmus			}
106795e4ca3dSStephan Aßmus		}
1068e08beebeSStephan Aßmus	}
1069e3fdc894SStephan Aßmus
1070e3fdc894SStephan Aßmus	if (packages.CountItems() > 0) {
1071e3fdc894SStephan Aßmus		for (int i = 0; i < packages.CountItems(); i++) {
1072e3fdc894SStephan Aßmus			const PackageInfoRef& package = packages.ItemAtFast(i);
1073e3fdc894SStephan Aßmus			printf("No package info for %s\n", package->Title().String());
1074e3fdc894SStephan Aßmus		}
1075e3fdc894SStephan Aßmus	}
1076e3fdc894SStephan Aßmus}
1077e3fdc894SStephan Aßmus
1078e3fdc894SStephan Aßmus
1079e3fdc894SStephan Aßmusvoid
1080e3fdc894SStephan AßmusModel::_PopulatePackageInfo(const PackageInfoRef& package, bool fromCacheOnly)
1081e3fdc894SStephan Aßmus{
1082e3fdc894SStephan Aßmus	if (fromCacheOnly)
1083e3fdc894SStephan Aßmus		return;
1084e3fdc894SStephan Aßmus
1085e3fdc894SStephan Aßmus	// Retrieve info from web-app
1086e3fdc894SStephan Aßmus	BMessage info;
1087e3fdc894SStephan Aßmus
108828075ee4SStephan Aßmus	status_t status = fWebAppInterface.RetrievePackageInfo(package->Title(),
10896e48c81bSStephan Aßmus		package->Architecture(), info);
1090e3fdc894SStephan Aßmus	if (status == B_OK) {
1091e3fdc894SStephan Aßmus		// Parse message
1092e3fdc894SStephan Aßmus//		info.PrintToStream();
1093e3fdc894SStephan Aßmus		BMessage result;
1094e3fdc894SStephan Aßmus		if (info.FindMessage("result", &result) == B_OK)
1095e3fdc894SStephan Aßmus			_PopulatePackageInfo(package, result);
1096e3fdc894SStephan Aßmus	}
1097e3fdc894SStephan Aßmus}
1098e3fdc894SStephan Aßmus
1099e3fdc894SStephan Aßmus
11006e48c81bSStephan Aßmusstatic void
11016e48c81bSStephan Aßmusappend_word_list(BString& words, const char* word)
11026e48c81bSStephan Aßmus{
11036e48c81bSStephan Aßmus	if (words.Length() > 0)
11046e48c81bSStephan Aßmus		words << ", ";
11056e48c81bSStephan Aßmus	words << word;
11066e48c81bSStephan Aßmus}
11076e48c81bSStephan Aßmus
11086e48c81bSStephan Aßmus
1109e3fdc894SStephan Aßmusvoid
11106e48c81bSStephan AßmusModel::_PopulatePackageInfo(const PackageInfoRef& package, const BMessage& data)
1111e3fdc894SStephan Aßmus{
11126e48c81bSStephan Aßmus	BAutolock locker(&fLock);
11136e48c81bSStephan Aßmus
11146e48c81bSStephan Aßmus	BString foundInfo;
11156e48c81bSStephan Aßmus
11166e48c81bSStephan Aßmus	BMessage versions;
1117ab172803SStephan Aßmus	if (data.FindMessage("versions", &versions) == B_OK) {
1118ab172803SStephan Aßmus		// Search a summary and description in the preferred language
1119ab172803SStephan Aßmus		int32 index = 0;
1120ab172803SStephan Aßmus		while (true) {
1121ab172803SStephan Aßmus			BString name;
1122ab172803SStephan Aßmus			name << index++;
1123ab172803SStephan Aßmus			BMessage version;
1124ab172803SStephan Aßmus			if (versions.FindMessage(name, &version) != B_OK)
1125ab172803SStephan Aßmus				break;
1126ab172803SStephan Aßmus			BString languageCode;
1127ab172803SStephan Aßmus			if (version.FindString("naturalLanguageCode",
1128ab172803SStephan Aßmus					&languageCode) != B_OK
1129ab172803SStephan Aßmus				|| languageCode != fPreferredLanguage) {
1130ab172803SStephan Aßmus				continue;
11316e48c81bSStephan Aßmus			}
1132ab172803SStephan Aßmus
1133ab172803SStephan Aßmus			BString summary;
1134ab172803SStephan Aßmus			if (version.FindString("summary", &summary) == B_OK) {
1135ab172803SStephan Aßmus				package->SetShortDescription(summary);
1136ab172803SStephan Aßmus				append_word_list(foundInfo, "summary");
1137ab172803SStephan Aßmus			}
1138ab172803SStephan Aßmus			BString description;
1139ab172803SStephan Aßmus			if (version.FindString("description", &description) == B_OK) {
1140ab172803SStephan Aßmus				package->SetFullDescription(description);
1141ab172803SStephan Aßmus				append_word_list(foundInfo, "description");
1142ab172803SStephan Aßmus			}
1143ab172803SStephan Aßmus			break;
11446e48c81bSStephan Aßmus		}
11456e48c81bSStephan Aßmus	}
1146e3fdc894SStephan Aßmus
1147e3fdc894SStephan Aßmus	BMessage categories;
1148e3fdc894SStephan Aßmus	if (data.FindMessage("pkgCategoryCodes", &categories) == B_OK) {
11496e48c81bSStephan Aßmus		bool foundCategory = false;
1150e3fdc894SStephan Aßmus		int32 index = 0;
1151e3fdc894SStephan Aßmus		while (true) {
1152e3fdc894SStephan Aßmus			BString name;
1153e3fdc894SStephan Aßmus			name << index++;
1154e3fdc894SStephan Aßmus			BString category;
1155e3fdc894SStephan Aßmus			if (categories.FindString(name, &category) != B_OK)
1156e3fdc894SStephan Aßmus				break;
1157e3fdc894SStephan Aßmus
11587eb09bfaSStephan Aßmus			package->ClearCategories();
11592b411c02SStephan Aßmus			for (int i = fCategories.CountItems() - 1; i >= 0; i--) {
11602b411c02SStephan Aßmus				const CategoryRef& categoryRef = fCategories.ItemAtFast(i);
11612b411c02SStephan Aßmus				if (categoryRef->Name() == category) {
11622b411c02SStephan Aßmus					package->AddCategory(categoryRef);
11632b411c02SStephan Aßmus					foundCategory = true;
11642b411c02SStephan Aßmus					break;
11652b411c02SStephan Aßmus				}
11662b411c02SStephan Aßmus			}
1167e3fdc894SStephan Aßmus		}
11686e48c81bSStephan Aßmus		if (foundCategory)
11696e48c81bSStephan Aßmus			append_word_list(foundInfo, "categories");
1170e3fdc894SStephan Aßmus	}
11711d9d4873SStephan Aßmus
1172e3fdc894SStephan Aßmus	double derivedRating;
117336a9b557SStephan Aßmus	if (data.FindDouble("derivedRating", &derivedRating) == B_OK) {
117436a9b557SStephan Aßmus		RatingSummary summary;
117536a9b557SStephan Aßmus		summary.averageRating = derivedRating;
117636a9b557SStephan Aßmus		package->SetRatingSummary(summary);
117736a9b557SStephan Aßmus
117836a9b557SStephan Aßmus		append_word_list(foundInfo, "rating");
1179e3fdc894SStephan Aßmus	}
11801d9d4873SStephan Aßmus
11819f40cd41SStephan Aßmus	double prominenceOrdering;
11829f40cd41SStephan Aßmus	if (data.FindDouble("prominenceOrdering", &prominenceOrdering) == B_OK) {
11839f40cd41SStephan Aßmus		package->SetProminence(prominenceOrdering);
11849f40cd41SStephan Aßmus
11859f40cd41SStephan Aßmus		append_word_list(foundInfo, "prominence");
11869f40cd41SStephan Aßmus	}
11879f40cd41SStephan Aßmus
11881d9d4873SStephan Aßmus	BMessage screenshots;
11891d9d4873SStephan Aßmus	if (data.FindMessage("pkgScreenshots", &screenshots) == B_OK) {
11907eb09bfaSStephan Aßmus		package->ClearScreenshotInfos();
11911d9d4873SStephan Aßmus		bool foundScreenshot = false;
11921d9d4873SStephan Aßmus		int32 index = 0;
11931d9d4873SStephan Aßmus		while (true) {
11941d9d4873SStephan Aßmus			BString name;
11951d9d4873SStephan Aßmus			name << index++;
11961d9d4873SStephan Aßmus
11971d9d4873SStephan Aßmus			BMessage screenshot;
11981d9d4873SStephan Aßmus			if (screenshots.FindMessage(name, &screenshot) != B_OK)
11991d9d4873SStephan Aßmus				break;
12001d9d4873SStephan Aßmus
12011d9d4873SStephan Aßmus			BString code;
12021d9d4873SStephan Aßmus			double width;
12031d9d4873SStephan Aßmus			double height;
12041d9d4873SStephan Aßmus			double dataSize;
12051d9d4873SStephan Aßmus			if (screenshot.FindString("code", &code) == B_OK
12061d9d4873SStephan Aßmus				&& screenshot.FindDouble("width", &width) == B_OK
12071d9d4873SStephan Aßmus				&& screenshot.FindDouble("height", &height) == B_OK
12081d9d4873SStephan Aßmus				&& screenshot.FindDouble("length", &dataSize) == B_OK) {
12091d9d4873SStephan Aßmus				package->AddScreenshotInfo(ScreenshotInfo(code, (int32)width,
12101d9d4873SStephan Aßmus					(int32)height, (int32)dataSize));
12111d9d4873SStephan Aßmus				foundScreenshot = true;
12121d9d4873SStephan Aßmus			}
12131d9d4873SStephan Aßmus		}
12141d9d4873SStephan Aßmus		if (foundScreenshot)
12151d9d4873SStephan Aßmus			append_word_list(foundInfo, "screenshots");
12161d9d4873SStephan Aßmus	}
12171d9d4873SStephan Aßmus
12186e48c81bSStephan Aßmus	if (foundInfo.Length() > 0) {
12196e48c81bSStephan Aßmus		printf("Populated package info for %s: %s\n",
12206e48c81bSStephan Aßmus			package->Title().String(), foundInfo.String());
12216e48c81bSStephan Aßmus	}
122287038da7SStephan Aßmus
122387038da7SStephan Aßmus	// If the user already clicked this package, remove it from the
122487038da7SStephan Aßmus	// list of populated packages, so that clicking it again will
122587038da7SStephan Aßmus	// populate any additional information.
122687038da7SStephan Aßmus	// TODO: Trigger re-populating if the package is currently showing.
122787038da7SStephan Aßmus	fPopulatedPackages.Remove(package);
1228e08beebeSStephan Aßmus}
1229e08beebeSStephan Aßmus
1230e08beebeSStephan Aßmus
12315bd5f161SStephan Aßmusvoid
1232726db742SStephan AßmusModel::_PopulatePackageIcon(const PackageInfoRef& package, bool fromCacheOnly)
12335bd5f161SStephan Aßmus{
1234726db742SStephan Aßmus	// See if there is a cached icon file
1235726db742SStephan Aßmus	BFile iconFile;
1236726db742SStephan Aßmus	BPath iconCachePath;
1237726db742SStephan Aßmus	BString iconName(package->Title());
1238726db742SStephan Aßmus	iconName << ".hvif";
1239a3dfc57dSStephan Aßmus	if (_GetCacheFile(iconCachePath, iconFile, B_USER_CACHE_DIRECTORY,
1240a3dfc57dSStephan Aßmus		"HaikuDepot", iconName, fromCacheOnly, 60 * 60)) {
1241a3dfc57dSStephan Aßmus		// Cache file is recent enough, just use it and return.
1242a3dfc57dSStephan Aßmus		BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(iconFile), true);
1243a3dfc57dSStephan Aßmus		BAutolock locker(&fLock);
1244a3dfc57dSStephan Aßmus		package->SetIcon(bitmapRef);
1245a3dfc57dSStephan Aßmus		return;
1246726db742SStephan Aßmus	}
1247726db742SStephan Aßmus
1248726db742SStephan Aßmus	if (fromCacheOnly)
1249726db742SStephan Aßmus		return;
1250726db742SStephan Aßmus
1251726db742SStephan Aßmus	// Retrieve icon from web-app
12525bd5f161SStephan Aßmus	BMallocIO buffer;
12535bd5f161SStephan Aßmus
125428075ee4SStephan Aßmus	status_t status = fWebAppInterface.RetrievePackageIcon(package->Title(),
125528075ee4SStephan Aßmus		&buffer);
12565bd5f161SStephan Aßmus	if (status == B_OK) {
12575bd5f161SStephan Aßmus		BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(buffer), true);
12586e48c81bSStephan Aßmus		BAutolock locker(&fLock);
12595bd5f161SStephan Aßmus		package->SetIcon(bitmapRef);
12606e48c81bSStephan Aßmus		locker.Unlock();
1261726db742SStephan Aßmus		if (iconFile.SetTo(iconCachePath.Path(),
1262726db742SStephan Aßmus				B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE) == B_OK) {
1263726db742SStephan Aßmus			iconFile.Write(buffer.Buffer(), buffer.BufferLength());
1264726db742SStephan Aßmus		}
12655bd5f161SStephan Aßmus	}
12665bd5f161SStephan Aßmus}
1267e3fdc894SStephan Aßmus
1268e3fdc894SStephan Aßmus
126987038da7SStephan Aßmusvoid
127087038da7SStephan AßmusModel::_PopulatePackageScreenshot(const PackageInfoRef& package,
127187038da7SStephan Aßmus	const ScreenshotInfo& info, int32 scaledWidth, bool fromCacheOnly)
127287038da7SStephan Aßmus{
127387038da7SStephan Aßmus	// See if there is a cached screenshot
127487038da7SStephan Aßmus	BFile screenshotFile;
127587038da7SStephan Aßmus	BPath screenshotCachePath;
127687038da7SStephan Aßmus	bool fileExists = false;
127787038da7SStephan Aßmus	BString screenshotName(info.Code());
127887038da7SStephan Aßmus	screenshotName << "@" << scaledWidth;
127987038da7SStephan Aßmus	screenshotName << ".png";
128087038da7SStephan Aßmus	time_t modifiedTime;
128187038da7SStephan Aßmus	if (find_directory(B_USER_CACHE_DIRECTORY, &screenshotCachePath) == B_OK
128287038da7SStephan Aßmus		&& screenshotCachePath.Append("HaikuDepot/Screenshots") == B_OK
128387038da7SStephan Aßmus		&& create_directory(screenshotCachePath.Path(), 0777) == B_OK
128487038da7SStephan Aßmus		&& screenshotCachePath.Append(screenshotName) == B_OK) {
128587038da7SStephan Aßmus		// Try opening the file in read-only mode, which will fail if its
128687038da7SStephan Aßmus		// not a file or does not exist.
128787038da7SStephan Aßmus		fileExists = screenshotFile.SetTo(screenshotCachePath.Path(),
128887038da7SStephan Aßmus			B_READ_ONLY) == B_OK;
128987038da7SStephan Aßmus		if (fileExists)
129087038da7SStephan Aßmus			screenshotFile.GetModificationTime(&modifiedTime);
129187038da7SStephan Aßmus	}
129287038da7SStephan Aßmus
129387038da7SStephan Aßmus	if (fileExists) {
129487038da7SStephan Aßmus		time_t now;
129587038da7SStephan Aßmus		time(&now);
129687038da7SStephan Aßmus		if (fromCacheOnly || now - modifiedTime < 60 * 60) {
129787038da7SStephan Aßmus			// Cache file is recent enough, just use it and return.
129887038da7SStephan Aßmus			BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(screenshotFile),
129987038da7SStephan Aßmus				true);
130087038da7SStephan Aßmus			BAutolock locker(&fLock);
130187038da7SStephan Aßmus			package->AddScreenshot(bitmapRef);
130287038da7SStephan Aßmus			return;
130387038da7SStephan Aßmus		}
130487038da7SStephan Aßmus	}
130587038da7SStephan Aßmus
130687038da7SStephan Aßmus	if (fromCacheOnly)
130787038da7SStephan Aßmus		return;
130887038da7SStephan Aßmus
130987038da7SStephan Aßmus	// Retrieve screenshot from web-app
131087038da7SStephan Aßmus	BMallocIO buffer;
131187038da7SStephan Aßmus
131287038da7SStephan Aßmus	int32 scaledHeight = scaledWidth * info.Height() / info.Width();
131387038da7SStephan Aßmus
131428075ee4SStephan Aßmus	status_t status = fWebAppInterface.RetrieveScreenshot(info.Code(),
131587038da7SStephan Aßmus		scaledWidth, scaledHeight, &buffer);
131687038da7SStephan Aßmus	if (status == B_OK) {
131787038da7SStephan Aßmus		BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(buffer), true);
131887038da7SStephan Aßmus		BAutolock locker(&fLock);
131987038da7SStephan Aßmus		package->AddScreenshot(bitmapRef);
132087038da7SStephan Aßmus		locker.Unlock();
132187038da7SStephan Aßmus		if (screenshotFile.SetTo(screenshotCachePath.Path(),
132287038da7SStephan Aßmus				B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE) == B_OK) {
132387038da7SStephan Aßmus			screenshotFile.Write(buffer.Buffer(), buffer.BufferLength());
132487038da7SStephan Aßmus		}
1325e0881f21SStephan Aßmus	} else {
1326e0881f21SStephan Aßmus		fprintf(stderr, "Failed to retrieve screenshot for code '%s' "
1327e0881f21SStephan Aßmus			"at %" B_PRIi32 "x%" B_PRIi32 ".\n", info.Code().String(),
1328e0881f21SStephan Aßmus			scaledWidth, scaledHeight);
132987038da7SStephan Aßmus	}
133087038da7SStephan Aßmus}
133187038da7SStephan Aßmus
133287038da7SStephan Aßmus
1333e3fdc894SStephan Aßmusbool
1334e3fdc894SStephan AßmusModel::_HasNativeIcon(const BMessage& message) const
1335e3fdc894SStephan Aßmus{
1336e3fdc894SStephan Aßmus	BMessage pkgIcons;
1337e3fdc894SStephan Aßmus	if (message.FindMessage("pkgIcons", &pkgIcons) != B_OK)
1338e3fdc894SStephan Aßmus		return false;
1339e3fdc894SStephan Aßmus
1340e3fdc894SStephan Aßmus	int32 index = 0;
1341e3fdc894SStephan Aßmus	while (true) {
1342e3fdc894SStephan Aßmus		BString name;
1343e3fdc894SStephan Aßmus		name << index++;
1344e3fdc894SStephan Aßmus
1345e3fdc894SStephan Aßmus		BMessage typeCodeInfo;
1346e3fdc894SStephan Aßmus		if (pkgIcons.FindMessage(name, &typeCodeInfo) != B_OK)
1347e3fdc894SStephan Aßmus			break;
1348e3fdc894SStephan Aßmus
1349e3fdc894SStephan Aßmus		BString mediaTypeCode;
1350e3fdc894SStephan Aßmus		if (typeCodeInfo.FindString("mediaTypeCode", &mediaTypeCode) == B_OK
1351e3fdc894SStephan Aßmus			&& mediaTypeCode == "application/x-vnd.haiku-icon") {
1352e3fdc894SStephan Aßmus			return true;
1353e3fdc894SStephan Aßmus		}
1354e3fdc894SStephan Aßmus	}
1355e3fdc894SStephan Aßmus	return false;
1356e3fdc894SStephan Aßmus}
13578f03a0f9SStephan Aßmus
13588f03a0f9SStephan Aßmus
13598f03a0f9SStephan Aßmus// #pragma mark - listener notification methods
13608f03a0f9SStephan Aßmus
13618f03a0f9SStephan Aßmus
13628f03a0f9SStephan Aßmusvoid
13638f03a0f9SStephan AßmusModel::_NotifyAuthorizationChanged()
13648f03a0f9SStephan Aßmus{
13658f03a0f9SStephan Aßmus	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
13668f03a0f9SStephan Aßmus		const ModelListenerRef& listener = fListeners.ItemAtFast(i);
13678f03a0f9SStephan Aßmus		if (listener.Get() != NULL)
13688f03a0f9SStephan Aßmus			listener->AuthorizationChanged();
13698f03a0f9SStephan Aßmus	}
13708f03a0f9SStephan Aßmus}
13718f03a0f9SStephan Aßmus
1372