Model.cpp revision f85e0300
16d367ba4SStephan Aßmus/*
25bd5f161SStephan Aßmus * Copyright 2013-2014, Stephan A��mus <superstippi@gmx.de>.
3634c2c68SAxel Dörfler * Copyright 2014, Axel D��rfler <axeld@pinc-software.de>.
4f85e0300SAndrew Lindesay * Copyright 2016-2019, Andrew Lindesay <apl@lindesay.co.nz>.
56d367ba4SStephan Aßmus * All rights reserved. Distributed under the terms of the MIT License.
66d367ba4SStephan Aßmus */
76d367ba4SStephan Aßmus
86d367ba4SStephan Aßmus#include "Model.h"
96d367ba4SStephan Aßmus
10726db742SStephan Aßmus#include <ctime>
11634c2c68SAxel Dörfler#include <stdarg.h>
126d367ba4SStephan Aßmus#include <stdio.h>
13726db742SStephan Aßmus#include <time.h>
146d367ba4SStephan Aßmus
157eec822dSStephan Aßmus#include <Autolock.h>
16f75ec10dSStephan Aßmus#include <Catalog.h>
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"
273b17d8ddSAndrew Lindesay#include "RepositoryUrlUtils.h"
28f0665db4SAndrew Lindesay
29f75ec10dSStephan Aßmus
30f75ec10dSStephan Aßmus#undef B_TRANSLATION_CONTEXT
31f75ec10dSStephan Aßmus#define B_TRANSLATION_CONTEXT "Model"
32f75ec10dSStephan Aßmus
336d367ba4SStephan Aßmus
3489ec84a4SStephan Aßmusstatic const char* kHaikuDepotKeyring = "HaikuDepot";
3589ec84a4SStephan Aßmus
3689ec84a4SStephan Aßmus
378f03a0f9SStephan AßmusPackageFilter::~PackageFilter()
388f03a0f9SStephan Aßmus{
398f03a0f9SStephan Aßmus}
404abd2b71SStephan Aßmus
414abd2b71SStephan Aßmus
428f03a0f9SStephan AßmusModelListener::~ModelListener()
434abd2b71SStephan Aßmus{
444abd2b71SStephan Aßmus}
454abd2b71SStephan Aßmus
464abd2b71SStephan Aßmus
478f03a0f9SStephan Aßmus// #pragma mark - PackageFilters
488f03a0f9SStephan Aßmus
498f03a0f9SStephan Aßmus
504abd2b71SStephan Aßmusclass AnyFilter : public PackageFilter {
514abd2b71SStephan Aßmuspublic:
52779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
534abd2b71SStephan Aßmus	{
544abd2b71SStephan Aßmus		return true;
554abd2b71SStephan Aßmus	}
564abd2b71SStephan Aßmus};
574abd2b71SStephan Aßmus
584abd2b71SStephan Aßmus
594abd2b71SStephan Aßmusclass DepotFilter : public PackageFilter {
604abd2b71SStephan Aßmuspublic:
614abd2b71SStephan Aßmus	DepotFilter(const DepotInfo& depot)
624abd2b71SStephan Aßmus		:
634abd2b71SStephan Aßmus		fDepot(depot)
644abd2b71SStephan Aßmus	{
654abd2b71SStephan Aßmus	}
661f67148fSRene Gollent
67779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
684abd2b71SStephan Aßmus	{
694abd2b71SStephan Aßmus		// TODO: Maybe a PackageInfo ought to know the Depot it came from?
704abd2b71SStephan Aßmus		// But right now the same package could theoretically be provided
714abd2b71SStephan Aßmus		// from different depots and the filter would work correctly.
724abd2b71SStephan Aßmus		// Also the PackageList could actually contain references to packages
734abd2b71SStephan Aßmus		// instead of the packages as objects. The equal operator is quite
744abd2b71SStephan Aßmus		// expensive as is.
75634c2c68SAxel Dörfler		return fDepot.Packages().Contains(package);
764abd2b71SStephan Aßmus	}
771f67148fSRene Gollent
780c169aceSStephan Aßmus	const BString& Depot() const
790c169aceSStephan Aßmus	{
800c169aceSStephan Aßmus		return fDepot.Name();
810c169aceSStephan Aßmus	}
820c169aceSStephan Aßmus
834abd2b71SStephan Aßmusprivate:
844abd2b71SStephan Aßmus	DepotInfo	fDepot;
854abd2b71SStephan Aßmus};
864abd2b71SStephan Aßmus
874abd2b71SStephan Aßmus
8842479955SStephan Aßmusclass CategoryFilter : public PackageFilter {
8942479955SStephan Aßmuspublic:
9042479955SStephan Aßmus	CategoryFilter(const BString& category)
9142479955SStephan Aßmus		:
9242479955SStephan Aßmus		fCategory(category)
9342479955SStephan Aßmus	{
9442479955SStephan Aßmus	}
951f67148fSRene Gollent
96779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
9742479955SStephan Aßmus	{
98779d8213SStephan Aßmus		if (package.Get() == NULL)
99779d8213SStephan Aßmus			return false;
100634c2c68SAxel Dörfler
101779d8213SStephan Aßmus		const CategoryList& categories = package->Categories();
10242479955SStephan Aßmus		for (int i = categories.CountItems() - 1; i >= 0; i--) {
10342479955SStephan Aßmus			const CategoryRef& category = categories.ItemAtFast(i);
10442479955SStephan Aßmus			if (category.Get() == NULL)
10542479955SStephan Aßmus				continue;
10642479955SStephan Aßmus			if (category->Name() == fCategory)
10742479955SStephan Aßmus				return true;
10842479955SStephan Aßmus		}
10942479955SStephan Aßmus		return false;
11042479955SStephan Aßmus	}
11142479955SStephan Aßmus
1120c169aceSStephan Aßmus	const BString& Category() const
1130c169aceSStephan Aßmus	{
1140c169aceSStephan Aßmus		return fCategory;
1150c169aceSStephan Aßmus	}
1160c169aceSStephan Aßmus
11742479955SStephan Aßmusprivate:
11842479955SStephan Aßmus	BString		fCategory;
11942479955SStephan Aßmus};
12042479955SStephan Aßmus
12142479955SStephan Aßmus
1223a6ccc85SStephan Aßmusclass ContainedInFilter : public PackageFilter {
1233a6ccc85SStephan Aßmuspublic:
1243a6ccc85SStephan Aßmus	ContainedInFilter(const PackageList& packageList)
1253a6ccc85SStephan Aßmus		:
1263a6ccc85SStephan Aßmus		fPackageList(packageList)
1273a6ccc85SStephan Aßmus	{
1283a6ccc85SStephan Aßmus	}
1291f67148fSRene Gollent
130779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
1313a6ccc85SStephan Aßmus	{
1323a6ccc85SStephan Aßmus		return fPackageList.Contains(package);
1333a6ccc85SStephan Aßmus	}
1343a6ccc85SStephan Aßmus
1353a6ccc85SStephan Aßmusprivate:
1363a6ccc85SStephan Aßmus	const PackageList&	fPackageList;
1373a6ccc85SStephan Aßmus};
1383a6ccc85SStephan Aßmus
1393a6ccc85SStephan Aßmus
1403a6ccc85SStephan Aßmusclass ContainedInEitherFilter : public PackageFilter {
1413a6ccc85SStephan Aßmuspublic:
1423a6ccc85SStephan Aßmus	ContainedInEitherFilter(const PackageList& packageListA,
1433a6ccc85SStephan Aßmus		const PackageList& packageListB)
1443a6ccc85SStephan Aßmus		:
1453a6ccc85SStephan Aßmus		fPackageListA(packageListA),
1463a6ccc85SStephan Aßmus		fPackageListB(packageListB)
1473a6ccc85SStephan Aßmus	{
1483a6ccc85SStephan Aßmus	}
1491f67148fSRene Gollent
150779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
1513a6ccc85SStephan Aßmus	{
1523a6ccc85SStephan Aßmus		return fPackageListA.Contains(package)
1533a6ccc85SStephan Aßmus			|| fPackageListB.Contains(package);
1543a6ccc85SStephan Aßmus	}
1553a6ccc85SStephan Aßmus
1563a6ccc85SStephan Aßmusprivate:
1573a6ccc85SStephan Aßmus	const PackageList&	fPackageListA;
1583a6ccc85SStephan Aßmus	const PackageList&	fPackageListB;
1593a6ccc85SStephan Aßmus};
1603a6ccc85SStephan Aßmus
1613a6ccc85SStephan Aßmus
162634c2c68SAxel Dörflerclass NotContainedInFilter : public PackageFilter {
163634c2c68SAxel Dörflerpublic:
164634c2c68SAxel Dörfler	NotContainedInFilter(const PackageList* packageList, ...)
165634c2c68SAxel Dörfler	{
166634c2c68SAxel Dörfler		va_list args;
167634c2c68SAxel Dörfler		va_start(args, packageList);
168634c2c68SAxel Dörfler		while (true) {
169634c2c68SAxel Dörfler			const PackageList* packageList = va_arg(args, const PackageList*);
170634c2c68SAxel Dörfler			if (packageList == NULL)
171634c2c68SAxel Dörfler				break;
172634c2c68SAxel Dörfler			fPackageLists.Add(packageList);
173634c2c68SAxel Dörfler		}
174634c2c68SAxel Dörfler		va_end(args);
175634c2c68SAxel Dörfler	}
176634c2c68SAxel Dörfler
177634c2c68SAxel Dörfler	virtual bool AcceptsPackage(const PackageInfoRef& package) const
178634c2c68SAxel Dörfler	{
179be298474SAndrew Lindesay		if (package.Get() == NULL)
180634c2c68SAxel Dörfler			return false;
181a10ad54cSJonathan Schleifer
18272992391SStephan Aßmus		printf("TEST %s\n", package->Name().String());
183a10ad54cSJonathan Schleifer
184634c2c68SAxel Dörfler		for (int32 i = 0; i < fPackageLists.CountItems(); i++) {
185634c2c68SAxel Dörfler			if (fPackageLists.ItemAtFast(i)->Contains(package)) {
186998f4043SJonathan Schleifer				printf("  contained in %" B_PRId32 "\n", i);
187634c2c68SAxel Dörfler				return false;
188634c2c68SAxel Dörfler			}
189634c2c68SAxel Dörfler		}
190634c2c68SAxel Dörfler		return true;
191634c2c68SAxel Dörfler	}
192634c2c68SAxel Dörfler
193634c2c68SAxel Dörflerprivate:
194634c2c68SAxel Dörfler	List<const PackageList*, true>	fPackageLists;
195634c2c68SAxel Dörfler};
196634c2c68SAxel Dörfler
197634c2c68SAxel Dörfler
198634c2c68SAxel Dörflerclass StateFilter : public PackageFilter {
199634c2c68SAxel Dörflerpublic:
200634c2c68SAxel Dörfler	StateFilter(PackageState state)
201634c2c68SAxel Dörfler		:
202634c2c68SAxel Dörfler		fState(state)
203634c2c68SAxel Dörfler	{
204634c2c68SAxel Dörfler	}
205634c2c68SAxel Dörfler
206634c2c68SAxel Dörfler	virtual bool AcceptsPackage(const PackageInfoRef& package) const
207634c2c68SAxel Dörfler	{
208634c2c68SAxel Dörfler		return package->State() == NONE;
209634c2c68SAxel Dörfler	}
210634c2c68SAxel Dörfler
211634c2c68SAxel Dörflerprivate:
212634c2c68SAxel Dörfler	PackageState	fState;
213634c2c68SAxel Dörfler};
214634c2c68SAxel Dörfler
215634c2c68SAxel Dörfler
21609d44cecSStephan Aßmusclass SearchTermsFilter : public PackageFilter {
21709d44cecSStephan Aßmuspublic:
21809d44cecSStephan Aßmus	SearchTermsFilter(const BString& searchTerms)
21909d44cecSStephan Aßmus	{
22009d44cecSStephan Aßmus		// Separate the string into terms at spaces
22109d44cecSStephan Aßmus		int32 index = 0;
22209d44cecSStephan Aßmus		while (index < searchTerms.Length()) {
22309d44cecSStephan Aßmus			int32 nextSpace = searchTerms.FindFirst(" ", index);
22409d44cecSStephan Aßmus			if (nextSpace < 0)
22509d44cecSStephan Aßmus				nextSpace = searchTerms.Length();
22609d44cecSStephan Aßmus			if (nextSpace > index) {
22709d44cecSStephan Aßmus				BString term;
22809d44cecSStephan Aßmus				searchTerms.CopyInto(term, index, nextSpace - index);
22909d44cecSStephan Aßmus				term.ToLower();
23009d44cecSStephan Aßmus				fSearchTerms.Add(term);
23109d44cecSStephan Aßmus			}
23209d44cecSStephan Aßmus			index = nextSpace + 1;
23309d44cecSStephan Aßmus		}
23409d44cecSStephan Aßmus	}
2351f67148fSRene Gollent
236779d8213SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
23709d44cecSStephan Aßmus	{
238779d8213SStephan Aßmus		if (package.Get() == NULL)
239779d8213SStephan Aßmus			return false;
24009d44cecSStephan Aßmus		// Every search term must be found in one of the package texts
24109d44cecSStephan Aßmus		for (int32 i = fSearchTerms.CountItems() - 1; i >= 0; i--) {
24209d44cecSStephan Aßmus			const BString& term = fSearchTerms.ItemAtFast(i);
24372992391SStephan Aßmus			if (!_TextContains(package->Name(), term)
24472992391SStephan Aßmus				&& !_TextContains(package->Title(), term)
245779d8213SStephan Aßmus				&& !_TextContains(package->Publisher().Name(), term)
246779d8213SStephan Aßmus				&& !_TextContains(package->ShortDescription(), term)
247779d8213SStephan Aßmus				&& !_TextContains(package->FullDescription(), term)) {
24809d44cecSStephan Aßmus				return false;
24909d44cecSStephan Aßmus			}
25009d44cecSStephan Aßmus		}
25109d44cecSStephan Aßmus		return true;
25209d44cecSStephan Aßmus	}
25309d44cecSStephan Aßmus
2540c169aceSStephan Aßmus	BString SearchTerms() const
2550c169aceSStephan Aßmus	{
2560c169aceSStephan Aßmus		BString searchTerms;
2570c169aceSStephan Aßmus		for (int32 i = 0; i < fSearchTerms.CountItems(); i++) {
2580c169aceSStephan Aßmus			const BString& term = fSearchTerms.ItemAtFast(i);
2590c169aceSStephan Aßmus			if (term.IsEmpty())
2600c169aceSStephan Aßmus				continue;
2610c169aceSStephan Aßmus			if (!searchTerms.IsEmpty())
2620c169aceSStephan Aßmus				searchTerms.Append(" ");
2630c169aceSStephan Aßmus			searchTerms.Append(term);
2640c169aceSStephan Aßmus		}
2650c169aceSStephan Aßmus		return searchTerms;
2660c169aceSStephan Aßmus	}
2670c169aceSStephan Aßmus
26809d44cecSStephan Aßmusprivate:
26909d44cecSStephan Aßmus	bool _TextContains(BString text, const BString& string) const
27009d44cecSStephan Aßmus	{
27109d44cecSStephan Aßmus		text.ToLower();
27209d44cecSStephan Aßmus		int32 index = text.FindFirst(string);
27309d44cecSStephan Aßmus		return index >= 0;
27409d44cecSStephan Aßmus	}
27509d44cecSStephan Aßmus
27609d44cecSStephan Aßmusprivate:
2770c169aceSStephan Aßmus	StringList fSearchTerms;
27809d44cecSStephan Aßmus};
27909d44cecSStephan Aßmus
28009d44cecSStephan Aßmus
28112107be2SStephan Aßmusclass IsFeaturedFilter : public PackageFilter {
28212107be2SStephan Aßmuspublic:
28312107be2SStephan Aßmus	IsFeaturedFilter()
28412107be2SStephan Aßmus	{
28512107be2SStephan Aßmus	}
28612107be2SStephan Aßmus
28712107be2SStephan Aßmus	virtual bool AcceptsPackage(const PackageInfoRef& package) const
28812107be2SStephan Aßmus	{
28912107be2SStephan Aßmus		return package.Get() != NULL && package->IsProminent();
29012107be2SStephan Aßmus	}
29112107be2SStephan Aßmus};
29212107be2SStephan Aßmus
29312107be2SStephan Aßmus
294870554acSStephan Aßmusstatic inline bool
295870554acSStephan Aßmusis_source_package(const PackageInfoRef& package)
296870554acSStephan Aßmus{
29772992391SStephan Aßmus	const BString& packageName = package->Name();
298870554acSStephan Aßmus	return packageName.EndsWith("_source");
299870554acSStephan Aßmus}
300870554acSStephan Aßmus
301870554acSStephan Aßmus
302870554acSStephan Aßmusstatic inline bool
303870554acSStephan Aßmusis_develop_package(const PackageInfoRef& package)
304870554acSStephan Aßmus{
30572992391SStephan Aßmus	const BString& packageName = package->Name();
30674e41611SJulian Harnath	return packageName.EndsWith("_devel")
30774e41611SJulian Harnath		|| packageName.EndsWith("_debuginfo");
308870554acSStephan Aßmus}
309870554acSStephan Aßmus
310870554acSStephan Aßmus
3114abd2b71SStephan Aßmus// #pragma mark - Model
3124abd2b71SStephan Aßmus
3134abd2b71SStephan Aßmus
3146d367ba4SStephan AßmusModel::Model()
3156d367ba4SStephan Aßmus	:
316f75ec10dSStephan Aßmus	fDepots(),
317f75ec10dSStephan Aßmus
318f75ec10dSStephan Aßmus	fCategoryAudio(new PackageCategory(
319f75ec10dSStephan Aßmus		BitmapRef(),
320f75ec10dSStephan Aßmus		B_TRANSLATE("Audio"), "audio"), true),
3214a02fc4fSStephan Aßmus	fCategoryBusiness(new PackageCategory(
322f75ec10dSStephan Aßmus		BitmapRef(),
3234a02fc4fSStephan Aßmus		B_TRANSLATE("Business"), "business"), true),
3244a02fc4fSStephan Aßmus	fCategoryDevelopment(new PackageCategory(
3254a02fc4fSStephan Aßmus		BitmapRef(),
3264a02fc4fSStephan Aßmus		B_TRANSLATE("Development"), "development"), true),
3274a02fc4fSStephan Aßmus	fCategoryEducation(new PackageCategory(
3284a02fc4fSStephan Aßmus		BitmapRef(),
3294a02fc4fSStephan Aßmus		B_TRANSLATE("Education"), "education"), true),
3304a02fc4fSStephan Aßmus	fCategoryGames(new PackageCategory(
3314a02fc4fSStephan Aßmus		BitmapRef(),
3324a02fc4fSStephan Aßmus		B_TRANSLATE("Games"), "games"), true),
333f75ec10dSStephan Aßmus	fCategoryGraphics(new PackageCategory(
334f75ec10dSStephan Aßmus		BitmapRef(),
335f75ec10dSStephan Aßmus		B_TRANSLATE("Graphics"), "graphics"), true),
3364a02fc4fSStephan Aßmus	fCategoryInternetAndNetwork(new PackageCategory(
3374a02fc4fSStephan Aßmus		BitmapRef(),
3384a02fc4fSStephan Aßmus		B_TRANSLATE("Internet & Network"), "internetandnetwork"), true),
339f75ec10dSStephan Aßmus	fCategoryProductivity(new PackageCategory(
340f75ec10dSStephan Aßmus		BitmapRef(),
341f75ec10dSStephan Aßmus		B_TRANSLATE("Productivity"), "productivity"), true),
3424a02fc4fSStephan Aßmus	fCategoryScienceAndMathematics(new PackageCategory(
343f75ec10dSStephan Aßmus		BitmapRef(),
3444a02fc4fSStephan Aßmus		B_TRANSLATE("Science & Mathematics"), "scienceandmathematics"), true),
3454a02fc4fSStephan Aßmus	fCategorySystemAndUtilities(new PackageCategory(
346f75ec10dSStephan Aßmus		BitmapRef(),
3474a02fc4fSStephan Aßmus		B_TRANSLATE("System & Utilities"), "systemandutilities"), true),
3484a02fc4fSStephan Aßmus	fCategoryVideo(new PackageCategory(
3494abd2b71SStephan Aßmus		BitmapRef(),
3504a02fc4fSStephan Aßmus		B_TRANSLATE("Video"), "video"), true),
3514abd2b71SStephan Aßmus
3524abd2b71SStephan Aßmus	fCategoryFilter(PackageFilterRef(new AnyFilter(), true)),
35309d44cecSStephan Aßmus	fDepotFilter(""),
354870554acSStephan Aßmus	fSearchTermsFilter(PackageFilterRef(new AnyFilter(), true)),
35512107be2SStephan Aßmus	fIsFeaturedFilter(),
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.
380f85e0300SAndrew Lindesay}
381d07ca069SStephan Aßmus
3824ff3d17aSStephan Aßmus
383f85e0300SAndrew LindesayModel::~Model()
384f85e0300SAndrew Lindesay{
3856d367ba4SStephan Aßmus}
3866d367ba4SStephan Aßmus
3876d367ba4SStephan Aßmus
388f85e0300SAndrew LindesayLanguageModel&
389f85e0300SAndrew LindesayModel::Language()
3905bd5f161SStephan Aßmus{
391f85e0300SAndrew Lindesay	return fLanguageModel;
3925bd5f161SStephan Aßmus}
3935bd5f161SStephan Aßmus
3945bd5f161SStephan Aßmus
3958f03a0f9SStephan Aßmusbool
3968f03a0f9SStephan AßmusModel::AddListener(const ModelListenerRef& listener)
3978f03a0f9SStephan Aßmus{
3988f03a0f9SStephan Aßmus	return fListeners.Add(listener);
3998f03a0f9SStephan Aßmus}
4008f03a0f9SStephan Aßmus
4018f03a0f9SStephan Aßmus
402706edd80SStephan AßmusPackageList
4036d367ba4SStephan AßmusModel::CreatePackageList() const
4046d367ba4SStephan Aßmus{
405ab172803SStephan Aßmus	// Iterate all packages from all depots.
406ab172803SStephan Aßmus	// If configured, restrict depot, filter by search terms, status, name ...
407706edd80SStephan Aßmus	PackageList resultList;
4086d367ba4SStephan Aßmus
4096d367ba4SStephan Aßmus	for (int32 i = 0; i < fDepots.CountItems(); i++) {
41009d44cecSStephan Aßmus		const DepotInfo& depot = fDepots.ItemAtFast(i);
4111f67148fSRene Gollent
41209d44cecSStephan Aßmus		if (fDepotFilter.Length() > 0 && fDepotFilter != depot.Name())
41309d44cecSStephan Aßmus			continue;
4141f67148fSRene Gollent
41509d44cecSStephan Aßmus		const PackageList& packages = depot.Packages();
4166d367ba4SStephan Aßmus
417706edd80SStephan Aßmus		for (int32 j = 0; j < packages.CountItems(); j++) {
418779d8213SStephan Aßmus			const PackageInfoRef& package = packages.ItemAtFast(j);
41939f49f70SJulian Harnath			if (MatchesFilter(package))
4204abd2b71SStephan Aßmus				resultList.Add(package);
4216d367ba4SStephan Aßmus		}
4226d367ba4SStephan Aßmus	}
4236d367ba4SStephan Aßmus
4246d367ba4SStephan Aßmus	return resultList;
4256d367ba4SStephan Aßmus}
4266d367ba4SStephan Aßmus
4276d367ba4SStephan Aßmus
42839f49f70SJulian Harnathbool
42939f49f70SJulian HarnathModel::MatchesFilter(const PackageInfoRef& package) const
43039f49f70SJulian Harnath{
43139f49f70SJulian Harnath	return fCategoryFilter->AcceptsPackage(package)
43239f49f70SJulian Harnath			&& fSearchTermsFilter->AcceptsPackage(package)
43339f49f70SJulian Harnath			&& fIsFeaturedFilter->AcceptsPackage(package)
43439f49f70SJulian Harnath			&& (fShowAvailablePackages || package->State() != NONE)
43539f49f70SJulian Harnath			&& (fShowInstalledPackages || package->State() != ACTIVATED)
43639f49f70SJulian Harnath			&& (fShowSourcePackages || !is_source_package(package))
43739f49f70SJulian Harnath			&& (fShowDevelopPackages || !is_develop_package(package));
43839f49f70SJulian Harnath}
43939f49f70SJulian Harnath
44039f49f70SJulian Harnath
4416d367ba4SStephan Aßmusbool
4426d367ba4SStephan AßmusModel::AddDepot(const DepotInfo& depot)
4436d367ba4SStephan Aßmus{
4446d367ba4SStephan Aßmus	return fDepots.Add(depot);
4456d367ba4SStephan Aßmus}
4466d367ba4SStephan Aßmus
44742479955SStephan Aßmus
4487b2c59b9SStephan Aßmusbool
4497b2c59b9SStephan AßmusModel::HasDepot(const BString& name) const
450ebb1a01cSAndrew Lindesay{
451ebb1a01cSAndrew Lindesay	return NULL != DepotForName(name);
452ebb1a01cSAndrew Lindesay}
453ebb1a01cSAndrew Lindesay
454ebb1a01cSAndrew Lindesay
455ebb1a01cSAndrew Lindesayconst DepotInfo*
456ebb1a01cSAndrew LindesayModel::DepotForName(const BString& name) const
4577b2c59b9SStephan Aßmus{
4587b2c59b9SStephan Aßmus	for (int32 i = fDepots.CountItems() - 1; i >= 0; i--) {
4597b2c59b9SStephan Aßmus		if (fDepots.ItemAtFast(i).Name() == name)
460ebb1a01cSAndrew Lindesay			return &fDepots.ItemAtFast(i);
4617b2c59b9SStephan Aßmus	}
462ebb1a01cSAndrew Lindesay	return NULL;
4637b2c59b9SStephan Aßmus}
4647b2c59b9SStephan Aßmus
4657b2c59b9SStephan Aßmus
4667b2c59b9SStephan Aßmusbool
4677b2c59b9SStephan AßmusModel::SyncDepot(const DepotInfo& depot)
4687b2c59b9SStephan Aßmus{
4697b2c59b9SStephan Aßmus	for (int32 i = fDepots.CountItems() - 1; i >= 0; i--) {
4707b2c59b9SStephan Aßmus		const DepotInfo& existingDepot = fDepots.ItemAtFast(i);
4717b2c59b9SStephan Aßmus		if (existingDepot.Name() == depot.Name()) {
4727b2c59b9SStephan Aßmus			DepotInfo mergedDepot(existingDepot);
4737b2c59b9SStephan Aßmus			mergedDepot.SyncPackages(depot.Packages());
4747b2c59b9SStephan Aßmus			fDepots.Replace(i, mergedDepot);
4757b2c59b9SStephan Aßmus			return true;
4767b2c59b9SStephan Aßmus		}
4777b2c59b9SStephan Aßmus	}
4787b2c59b9SStephan Aßmus	return false;
4797b2c59b9SStephan Aßmus}
4807b2c59b9SStephan Aßmus
4817b2c59b9SStephan Aßmus
4822c287f5bSRene Gollentvoid
4832c287f5bSRene GollentModel::Clear()
4842c287f5bSRene Gollent{
4852c287f5bSRene Gollent	fDepots.Clear();
4862c287f5bSRene Gollent}
4872c287f5bSRene Gollent
4882c287f5bSRene Gollent
489706edd80SStephan Aßmusvoid
490779d8213SStephan AßmusModel::SetPackageState(const PackageInfoRef& package, PackageState state)
491706edd80SStephan Aßmus{
492706edd80SStephan Aßmus	switch (state) {
493706edd80SStephan Aßmus		default:
494706edd80SStephan Aßmus		case NONE:
495706edd80SStephan Aßmus			fInstalledPackages.Remove(package);
496706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
497706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
498706edd80SStephan Aßmus			break;
499706edd80SStephan Aßmus		case INSTALLED:
500706edd80SStephan Aßmus			if (!fInstalledPackages.Contains(package))
501706edd80SStephan Aßmus				fInstalledPackages.Add(package);
502706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
503706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
504706edd80SStephan Aßmus			break;
505706edd80SStephan Aßmus		case ACTIVATED:
506706edd80SStephan Aßmus			if (!fInstalledPackages.Contains(package))
507706edd80SStephan Aßmus				fInstalledPackages.Add(package);
508706edd80SStephan Aßmus			if (!fActivatedPackages.Contains(package))
509706edd80SStephan Aßmus				fActivatedPackages.Add(package);
510706edd80SStephan Aßmus			fUninstalledPackages.Remove(package);
511706edd80SStephan Aßmus			break;
512706edd80SStephan Aßmus		case UNINSTALLED:
513706edd80SStephan Aßmus			fInstalledPackages.Remove(package);
514706edd80SStephan Aßmus			fActivatedPackages.Remove(package);
515ab172803SStephan Aßmus			if (!fUninstalledPackages.Contains(package))
516ab172803SStephan Aßmus				fUninstalledPackages.Add(package);
517706edd80SStephan Aßmus			break;
518706edd80SStephan Aßmus	}
519b4c8d2ffSRene Gollent
520b4c8d2ffSRene Gollent	package->SetState(state);
521706edd80SStephan Aßmus}
522706edd80SStephan Aßmus
523706edd80SStephan Aßmus
52409d44cecSStephan Aßmus// #pragma mark - filters
52509d44cecSStephan Aßmus
52609d44cecSStephan Aßmus
52742479955SStephan Aßmusvoid
52842479955SStephan AßmusModel::SetCategory(const BString& category)
52942479955SStephan Aßmus{
5303a6ccc85SStephan Aßmus	PackageFilter* filter;
5311f67148fSRene Gollent
53242479955SStephan Aßmus	if (category.Length() == 0)
5333a6ccc85SStephan Aßmus		filter = new AnyFilter();
53442479955SStephan Aßmus	else
5353a6ccc85SStephan Aßmus		filter = new CategoryFilter(category);
5363a6ccc85SStephan Aßmus
5373a6ccc85SStephan Aßmus	fCategoryFilter.SetTo(filter, true);
53842479955SStephan Aßmus}
53942479955SStephan Aßmus
54009d44cecSStephan Aßmus
5410c169aceSStephan AßmusBString
5420c169aceSStephan AßmusModel::Category() const
5430c169aceSStephan Aßmus{
5440c169aceSStephan Aßmus	CategoryFilter* filter
5450c169aceSStephan Aßmus		= dynamic_cast<CategoryFilter*>(fCategoryFilter.Get());
5460c169aceSStephan Aßmus	if (filter == NULL)
5470c169aceSStephan Aßmus		return "";
5480c169aceSStephan Aßmus	return filter->Category();
5490c169aceSStephan Aßmus}
5500c169aceSStephan Aßmus
5510c169aceSStephan Aßmus
55209d44cecSStephan Aßmusvoid
55309d44cecSStephan AßmusModel::SetDepot(const BString& depot)
55409d44cecSStephan Aßmus{
55509d44cecSStephan Aßmus	fDepotFilter = depot;
55609d44cecSStephan Aßmus}
55709d44cecSStephan Aßmus
55809d44cecSStephan Aßmus
5590c169aceSStephan AßmusBString
5600c169aceSStephan AßmusModel::Depot() const
5610c169aceSStephan Aßmus{
5620c169aceSStephan Aßmus	return fDepotFilter;
5630c169aceSStephan Aßmus}
5640c169aceSStephan Aßmus
5650c169aceSStephan Aßmus
56609d44cecSStephan Aßmusvoid
56709d44cecSStephan AßmusModel::SetSearchTerms(const BString& searchTerms)
56809d44cecSStephan Aßmus{
56909d44cecSStephan Aßmus	PackageFilter* filter;
5701f67148fSRene Gollent
57109d44cecSStephan Aßmus	if (searchTerms.Length() == 0)
57209d44cecSStephan Aßmus		filter = new AnyFilter();
57309d44cecSStephan Aßmus	else
57409d44cecSStephan Aßmus		filter = new SearchTermsFilter(searchTerms);
57509d44cecSStephan Aßmus
57609d44cecSStephan Aßmus	fSearchTermsFilter.SetTo(filter, true);
57712107be2SStephan Aßmus	_UpdateIsFeaturedFilter();
57809d44cecSStephan Aßmus}
57909d44cecSStephan Aßmus
5807eec822dSStephan Aßmus
5810c169aceSStephan AßmusBString
5820c169aceSStephan AßmusModel::SearchTerms() const
5830c169aceSStephan Aßmus{
5840c169aceSStephan Aßmus	SearchTermsFilter* filter
5850c169aceSStephan Aßmus		= dynamic_cast<SearchTermsFilter*>(fSearchTermsFilter.Get());
5860c169aceSStephan Aßmus	if (filter == NULL)
5870c169aceSStephan Aßmus		return "";
5880c169aceSStephan Aßmus	return filter->SearchTerms();
5890c169aceSStephan Aßmus}
5900c169aceSStephan Aßmus
5910c169aceSStephan Aßmus
5924fade61fSStephan Aßmusvoid
5934fade61fSStephan AßmusModel::SetShowFeaturedPackages(bool show)
5944fade61fSStephan Aßmus{
5954fade61fSStephan Aßmus	fShowFeaturedPackages = show;
59612107be2SStephan Aßmus	_UpdateIsFeaturedFilter();
5974fade61fSStephan Aßmus}
5984fade61fSStephan Aßmus
5994fade61fSStephan Aßmus
600ab172803SStephan Aßmusvoid
601ab172803SStephan AßmusModel::SetShowAvailablePackages(bool show)
602ab172803SStephan Aßmus{
603ab172803SStephan Aßmus	fShowAvailablePackages = show;
604ab172803SStephan Aßmus}
605ab172803SStephan Aßmus
606ab172803SStephan Aßmus
607ab172803SStephan Aßmusvoid
608ab172803SStephan AßmusModel::SetShowInstalledPackages(bool show)
609ab172803SStephan Aßmus{
610ab172803SStephan Aßmus	fShowInstalledPackages = show;
611ab172803SStephan Aßmus}
612ab172803SStephan Aßmus
613ab172803SStephan Aßmus
614870554acSStephan Aßmusvoid
615870554acSStephan AßmusModel::SetShowSourcePackages(bool show)
616870554acSStephan Aßmus{
617870554acSStephan Aßmus	fShowSourcePackages = show;
618870554acSStephan Aßmus}
619870554acSStephan Aßmus
620870554acSStephan Aßmus
621870554acSStephan Aßmusvoid
622870554acSStephan AßmusModel::SetShowDevelopPackages(bool show)
623870554acSStephan Aßmus{
624870554acSStephan Aßmus	fShowDevelopPackages = show;
625870554acSStephan Aßmus}
626870554acSStephan Aßmus
627870554acSStephan Aßmus
62819c15fecSAndrew Lindesay// #pragma mark - information retrieval
6297eec822dSStephan Aßmus
6307eec822dSStephan Aßmus
631a9edb9bfSAndrew Lindesay/*! Initially only superficial data is loaded from the server into the data
632a9edb9bfSAndrew Lindesay    model of the packages.  When the package is viewed, additional data needs
633a9edb9bfSAndrew Lindesay    to be populated including ratings.  This method takes care of that.
634a9edb9bfSAndrew Lindesay*/
635a9edb9bfSAndrew Lindesay
6367eec822dSStephan Aßmusvoid
637a60a23f8SStephan AßmusModel::PopulatePackage(const PackageInfoRef& package, uint32 flags)
6387eec822dSStephan Aßmus{
6397eec822dSStephan Aßmus	// TODO: There should probably also be a way to "unpopulate" the
6407eec822dSStephan Aßmus	// package information. Maybe a cache of populated packages, so that
6417eec822dSStephan Aßmus	// packages loose their extra information after a certain amount of
6427eec822dSStephan Aßmus	// time when they have not been accessed/displayed in the UI. Otherwise
6437eec822dSStephan Aßmus	// HaikuDepot will consume more and more resources in the packages.
6447eec822dSStephan Aßmus	// Especially screen-shots will be a problem eventually.
64548045f5aSStephan Aßmus	{
64648045f5aSStephan Aßmus		BAutolock locker(&fLock);
64789e4ba17SStephan Aßmus		bool alreadyPopulated = fPopulatedPackages.Contains(package);
64889e4ba17SStephan Aßmus		if ((flags & POPULATE_FORCE) == 0 && alreadyPopulated)
64948045f5aSStephan Aßmus			return;
65089e4ba17SStephan Aßmus		if (!alreadyPopulated)
65189e4ba17SStephan Aßmus			fPopulatedPackages.Add(package);
65248045f5aSStephan Aßmus	}
6537eec822dSStephan Aßmus
65480a272eeSAndrew Lindesay	if ((flags & POPULATE_CHANGELOG) != 0) {
65580a272eeSAndrew Lindesay		_PopulatePackageChangelog(package);
65680a272eeSAndrew Lindesay	}
65780a272eeSAndrew Lindesay
6587ff94fd0SStephan Aßmus	if ((flags & POPULATE_USER_RATINGS) != 0) {
6597ff94fd0SStephan Aßmus		// Retrieve info from web-app
6607ff94fd0SStephan Aßmus		BMessage info;
66148045f5aSStephan Aßmus
66248045f5aSStephan Aßmus		BString packageName;
66387084745SMichael Lotz		BString architecture;
66448045f5aSStephan Aßmus		{
66548045f5aSStephan Aßmus			BAutolock locker(&fLock);
66672992391SStephan Aßmus			packageName = package->Name();
66748045f5aSStephan Aßmus			architecture = package->Architecture();
66848045f5aSStephan Aßmus		}
66987084745SMichael Lotz
67028075ee4SStephan Aßmus		status_t status = fWebAppInterface.RetrieveUserRatings(packageName,
67148045f5aSStephan Aßmus			architecture, 0, 50, info);
6727ff94fd0SStephan Aßmus		if (status == B_OK) {
6737ff94fd0SStephan Aßmus			// Parse message
6747ff94fd0SStephan Aßmus			BMessage result;
6757ff94fd0SStephan Aßmus			BMessage items;
6767ff94fd0SStephan Aßmus			if (info.FindMessage("result", &result) == B_OK
6777ff94fd0SStephan Aßmus				&& result.FindMessage("items", &items) == B_OK) {
67848045f5aSStephan Aßmus
67948045f5aSStephan Aßmus				BAutolock locker(&fLock);
6807eb09bfaSStephan Aßmus				package->ClearUserRatings();
68148045f5aSStephan Aßmus
682a9edb9bfSAndrew Lindesay				int32 index = 0;
6837ff94fd0SStephan Aßmus				while (true) {
6847ff94fd0SStephan Aßmus					BString name;
6857ff94fd0SStephan Aßmus					name << index++;
68687084745SMichael Lotz
6877ff94fd0SStephan Aßmus					BMessage item;
6887ff94fd0SStephan Aßmus					if (items.FindMessage(name, &item) != B_OK)
6897ff94fd0SStephan Aßmus						break;
6907ff94fd0SStephan Aßmus
691a9edb9bfSAndrew Lindesay					BString code;
692a9edb9bfSAndrew Lindesay					if (item.FindString("code", &code) != B_OK) {
693a9edb9bfSAndrew Lindesay						printf("corrupt user rating at index %" B_PRIi32 "\n",
694a9edb9bfSAndrew Lindesay							index);
695a9edb9bfSAndrew Lindesay						continue;
696a9edb9bfSAndrew Lindesay					}
697a9edb9bfSAndrew Lindesay
6987ff94fd0SStephan Aßmus					BString user;
6997ff94fd0SStephan Aßmus					BMessage userInfo;
7007ff94fd0SStephan Aßmus					if (item.FindMessage("user", &userInfo) != B_OK
7017ff94fd0SStephan Aßmus						|| userInfo.FindString("nickname", &user) != B_OK) {
702a9edb9bfSAndrew Lindesay						printf("ignored user rating [%s] without a user "
703a9edb9bfSAndrew Lindesay							"nickname\n", code.String());
7047ff94fd0SStephan Aßmus						continue;
7057ff94fd0SStephan Aßmus					}
7067ff94fd0SStephan Aßmus
70736a9b557SStephan Aßmus					// Extract basic info, all items are optional
7087ff94fd0SStephan Aßmus					BString languageCode;
7097ff94fd0SStephan Aßmus					BString comment;
7107ff94fd0SStephan Aßmus					double rating;
71136a9b557SStephan Aßmus					item.FindString("naturalLanguageCode", &languageCode);
71236a9b557SStephan Aßmus					item.FindString("comment", &comment);
71336a9b557SStephan Aßmus					if (item.FindDouble("rating", &rating) != B_OK)
71436a9b557SStephan Aßmus						rating = -1;
71536a9b557SStephan Aßmus					if (comment.Length() == 0 && rating == -1) {
716a9edb9bfSAndrew Lindesay						printf("rating [%s] has no comment or rating so will be"
717a9edb9bfSAndrew Lindesay							"ignored\n", code.String());
7187ff94fd0SStephan Aßmus						continue;
7197ff94fd0SStephan Aßmus					}
72036a9b557SStephan Aßmus
7217ff94fd0SStephan Aßmus					// For which version of the package was the rating?
7227ff94fd0SStephan Aßmus					BString major = "?";
7237ff94fd0SStephan Aßmus					BString minor = "?";
7247ff94fd0SStephan Aßmus					BString micro = "";
725a9edb9bfSAndrew Lindesay					double revision = -1;
7267ff94fd0SStephan Aßmus					BMessage version;
7277ff94fd0SStephan Aßmus					if (item.FindMessage("pkgVersion", &version) == B_OK) {
7287ff94fd0SStephan Aßmus						version.FindString("major", &major);
7297ff94fd0SStephan Aßmus						version.FindString("minor", &minor);
7307ff94fd0SStephan Aßmus						version.FindString("micro", &micro);
731a9edb9bfSAndrew Lindesay						version.FindDouble("revision", &revision);
7327ff94fd0SStephan Aßmus					}
7337ff94fd0SStephan Aßmus					BString versionString = major;
7347ff94fd0SStephan Aßmus					versionString << ".";
7357ff94fd0SStephan Aßmus					versionString << minor;
7367ff94fd0SStephan Aßmus					if (micro.Length() > 0) {
7377ff94fd0SStephan Aßmus						versionString << ".";
7387ff94fd0SStephan Aßmus						versionString << micro;
7397ff94fd0SStephan Aßmus					}
740a9edb9bfSAndrew Lindesay					if (revision > 0) {
741a9edb9bfSAndrew Lindesay						versionString << "-";
742a9edb9bfSAndrew Lindesay						versionString << (int) revision;
743a9edb9bfSAndrew Lindesay					}
744a9edb9bfSAndrew Lindesay
745a9edb9bfSAndrew Lindesay					BDateTime createTimestamp;
746a9edb9bfSAndrew Lindesay					double createTimestampMillisF;
747a9edb9bfSAndrew Lindesay					if (item.FindDouble("createTimestamp",
748a9edb9bfSAndrew Lindesay						&createTimestampMillisF) == B_OK) {
749a9edb9bfSAndrew Lindesay						double createTimestampSecsF =
750a9edb9bfSAndrew Lindesay							createTimestampMillisF / 1000.0;
751a9edb9bfSAndrew Lindesay						time_t createTimestampSecs =
752a9edb9bfSAndrew Lindesay							(time_t) createTimestampSecsF;
753a9edb9bfSAndrew Lindesay						createTimestamp.SetTime_t(createTimestampSecs);
754a9edb9bfSAndrew Lindesay					}
755a9edb9bfSAndrew Lindesay
7567ff94fd0SStephan Aßmus					// Add the rating to the PackageInfo
757a9edb9bfSAndrew Lindesay					UserRating userRating = UserRating(UserInfo(user), rating,
758a9edb9bfSAndrew Lindesay						comment, languageCode, versionString, 0, 0,
759a9edb9bfSAndrew Lindesay						createTimestamp);
760a9edb9bfSAndrew Lindesay					package->AddUserRating(userRating);
761a9edb9bfSAndrew Lindesay
762a9edb9bfSAndrew Lindesay					if (Logger::IsDebugEnabled()) {
763a9edb9bfSAndrew Lindesay						printf("rating [%s] retrieved from server\n",
764a9edb9bfSAndrew Lindesay							code.String());
765a9edb9bfSAndrew Lindesay					}
766a9edb9bfSAndrew Lindesay				}
767a9edb9bfSAndrew Lindesay
768a9edb9bfSAndrew Lindesay				if (Logger::IsDebugEnabled()) {
769a9edb9bfSAndrew Lindesay					printf("did retrieve %" B_PRIi32 " user ratings for [%s]\n",
770a9edb9bfSAndrew Lindesay						index - 1, packageName.String());
7717ff94fd0SStephan Aßmus				}
77280a272eeSAndrew Lindesay			} else {
77380a272eeSAndrew Lindesay				_MaybeLogJsonRpcError(info, "retrieve user ratings");
7747ff94fd0SStephan Aßmus			}
775a9edb9bfSAndrew Lindesay		} else {
776a9edb9bfSAndrew Lindesay			printf("unable to retrieve user ratings\n");
777a60a23f8SStephan Aßmus		}
7787eec822dSStephan Aßmus	}
77987038da7SStephan Aßmus
78087038da7SStephan Aßmus	if ((flags & POPULATE_SCREEN_SHOTS) != 0) {
78187038da7SStephan Aßmus		ScreenshotInfoList screenshotInfos;
78287038da7SStephan Aßmus		{
78387038da7SStephan Aßmus			BAutolock locker(&fLock);
78487038da7SStephan Aßmus			screenshotInfos = package->ScreenshotInfos();
7857eb09bfaSStephan Aßmus			package->ClearScreenshots();
78687038da7SStephan Aßmus		}
78787038da7SStephan Aßmus		for (int i = 0; i < screenshotInfos.CountItems(); i++) {
78887038da7SStephan Aßmus			const ScreenshotInfo& info = screenshotInfos.ItemAtFast(i);
789e0881f21SStephan Aßmus			_PopulatePackageScreenshot(package, info, 320, false);
79087038da7SStephan Aßmus		}
79187038da7SStephan Aßmus	}
7927eec822dSStephan Aßmus}
7935bd5f161SStephan Aßmus
7945bd5f161SStephan Aßmus
79580a272eeSAndrew Lindesayvoid
79680a272eeSAndrew LindesayModel::_PopulatePackageChangelog(const PackageInfoRef& package)
79780a272eeSAndrew Lindesay{
79880a272eeSAndrew Lindesay	BMessage info;
79980a272eeSAndrew Lindesay	BString packageName;
80080a272eeSAndrew Lindesay
80180a272eeSAndrew Lindesay	{
80280a272eeSAndrew Lindesay		BAutolock locker(&fLock);
80380a272eeSAndrew Lindesay		packageName = package->Name();
80480a272eeSAndrew Lindesay	}
80580a272eeSAndrew Lindesay
80680a272eeSAndrew Lindesay	status_t status = fWebAppInterface.GetChangelog(packageName, info);
80780a272eeSAndrew Lindesay
80880a272eeSAndrew Lindesay	if (status == B_OK) {
80980a272eeSAndrew Lindesay		// Parse message
81080a272eeSAndrew Lindesay		BMessage result;
81180a272eeSAndrew Lindesay		BString content;
81280a272eeSAndrew Lindesay		if (info.FindMessage("result", &result) == B_OK) {
81380a272eeSAndrew Lindesay			if (result.FindString("content", &content) == B_OK
81480a272eeSAndrew Lindesay				&& 0 != content.Length()) {
81580a272eeSAndrew Lindesay				BAutolock locker(&fLock);
81680a272eeSAndrew Lindesay				package->SetChangelog(content);
81780a272eeSAndrew Lindesay				if (Logger::IsDebugEnabled()) {
81880a272eeSAndrew Lindesay					fprintf(stdout, "changelog populated for [%s]\n",
81980a272eeSAndrew Lindesay						packageName.String());
82080a272eeSAndrew Lindesay				}
82180a272eeSAndrew Lindesay			} else {
82280a272eeSAndrew Lindesay				if (Logger::IsDebugEnabled()) {
82380a272eeSAndrew Lindesay					fprintf(stdout, "no changelog present for [%s]\n",
82480a272eeSAndrew Lindesay						packageName.String());
82580a272eeSAndrew Lindesay				}
82680a272eeSAndrew Lindesay			}
82780a272eeSAndrew Lindesay		} else {
82880a272eeSAndrew Lindesay			_MaybeLogJsonRpcError(info, "populate package changelog");
82980a272eeSAndrew Lindesay		}
83080a272eeSAndrew Lindesay	} else {
83180a272eeSAndrew Lindesay		fprintf(stdout, "unable to obtain the changelog for the package"
832a9edb9bfSAndrew Lindesay			" [%s]\n", packageName.String());
83380a272eeSAndrew Lindesay	}
83480a272eeSAndrew Lindesay}
83580a272eeSAndrew Lindesay
83680a272eeSAndrew Lindesay
83728075ee4SStephan Aßmusvoid
8388f03a0f9SStephan AßmusModel::SetUsername(BString username)
83989ec84a4SStephan Aßmus{
8408f03a0f9SStephan Aßmus	BString password;
8418f03a0f9SStephan Aßmus	if (username.Length() > 0) {
8428f03a0f9SStephan Aßmus		BPasswordKey key;
8438f03a0f9SStephan Aßmus		BKeyStore keyStore;
8448f03a0f9SStephan Aßmus		if (keyStore.GetKey(kHaikuDepotKeyring, B_KEY_TYPE_PASSWORD, username,
8458f03a0f9SStephan Aßmus				key) == B_OK) {
8468f03a0f9SStephan Aßmus			password = key.Password();
8478f03a0f9SStephan Aßmus		} else {
8488f03a0f9SStephan Aßmus			username = "";
8498f03a0f9SStephan Aßmus		}
85089ec84a4SStephan Aßmus	}
8518f03a0f9SStephan Aßmus	SetAuthorization(username, password, false);
8528f03a0f9SStephan Aßmus}
8538f03a0f9SStephan Aßmus
8548f03a0f9SStephan Aßmus
8558f03a0f9SStephan Aßmusconst BString&
8568f03a0f9SStephan AßmusModel::Username() const
8578f03a0f9SStephan Aßmus{
8588f03a0f9SStephan Aßmus	return fWebAppInterface.Username();
85989ec84a4SStephan Aßmus}
86089ec84a4SStephan Aßmus
86189ec84a4SStephan Aßmus
86289ec84a4SStephan Aßmusvoid
86389ec84a4SStephan AßmusModel::SetAuthorization(const BString& username, const BString& password,
86489ec84a4SStephan Aßmus	bool storePassword)
86528075ee4SStephan Aßmus{
8668f03a0f9SStephan Aßmus	if (storePassword && username.Length() > 0 && password.Length() > 0) {
86789ec84a4SStephan Aßmus		BPasswordKey key(password, B_KEY_PURPOSE_WEB, username);
86889ec84a4SStephan Aßmus		BKeyStore keyStore;
86989ec84a4SStephan Aßmus		keyStore.AddKeyring(kHaikuDepotKeyring);
87089ec84a4SStephan Aßmus		keyStore.AddKey(kHaikuDepotKeyring, key);
87189ec84a4SStephan Aßmus	}
87289ec84a4SStephan Aßmus
87328075ee4SStephan Aßmus	BAutolock locker(&fLock);
87428075ee4SStephan Aßmus	fWebAppInterface.SetAuthorization(username, password);
8758f03a0f9SStephan Aßmus
8768f03a0f9SStephan Aßmus	_NotifyAuthorizationChanged();
87728075ee4SStephan Aßmus}
87828075ee4SStephan Aßmus
87928075ee4SStephan Aßmus
8803d528c4aSAndrew Lindesaystatus_t
881f85e0300SAndrew LindesayModel::_LocalDataPath(const BString leaf, BPath& path) const
882ebb1a01cSAndrew Lindesay{
883f85e0300SAndrew Lindesay	BString leafAssembled(leaf);
884f85e0300SAndrew Lindesay	leafAssembled.ReplaceAll("%languageCode%",
885f85e0300SAndrew Lindesay		LanguageModel().PreferredLanguage().Code());
886f85e0300SAndrew Lindesay
8873d528c4aSAndrew Lindesay	BPath repoDataPath;
888ebb1a01cSAndrew Lindesay
8893d528c4aSAndrew Lindesay	if (find_directory(B_USER_CACHE_DIRECTORY, &repoDataPath) == B_OK
8903d528c4aSAndrew Lindesay		&& repoDataPath.Append("HaikuDepot") == B_OK
8913d528c4aSAndrew Lindesay		&& create_directory(repoDataPath.Path(), 0777) == B_OK
892f85e0300SAndrew Lindesay		&& repoDataPath.Append(leafAssembled) == B_OK) {
8933d528c4aSAndrew Lindesay		path.SetTo(repoDataPath.Path());
8943d528c4aSAndrew Lindesay		return B_OK;
8953d528c4aSAndrew Lindesay	}
896ebb1a01cSAndrew Lindesay
8973d528c4aSAndrew Lindesay	path.Unset();
898f85e0300SAndrew Lindesay	fprintf(stdout, "unable to find the user cache file for [%s] data",
899f85e0300SAndrew Lindesay		leaf.String());
9003d528c4aSAndrew Lindesay	return B_ERROR;
9013d528c4aSAndrew Lindesay}
902ebb1a01cSAndrew Lindesay
903ebb1a01cSAndrew Lindesay
904f85e0300SAndrew Lindesay/*! When bulk repository data comes down from the server, it will
905f85e0300SAndrew Lindesay    arrive as a json.gz payload.  This is stored locally as a cache
906f85e0300SAndrew Lindesay    and this method will provide the on-disk storage location for
907f85e0300SAndrew Lindesay    this file.
908f85e0300SAndrew Lindesay*/
909f85e0300SAndrew Lindesay
910f85e0300SAndrew Lindesaystatus_t
911f85e0300SAndrew LindesayModel::DumpExportRepositoryDataPath(BPath& path) const
912f85e0300SAndrew Lindesay{
913f85e0300SAndrew Lindesay	return _LocalDataPath("repository-all_%languageCode%.json.gz", path);
914f85e0300SAndrew Lindesay}
915f85e0300SAndrew Lindesay
916f85e0300SAndrew Lindesay
917f85e0300SAndrew Lindesay/*! When the system downloads reference data (eg; categories) from the server
918f85e0300SAndrew Lindesay    then the downloaded data is stored and cached at the path defined by this
919f85e0300SAndrew Lindesay    method.
920f85e0300SAndrew Lindesay*/
921f85e0300SAndrew Lindesay
922f85e0300SAndrew Lindesaystatus_t
923f85e0300SAndrew LindesayModel::DumpExportReferenceDataPath(BPath& path) const
924f85e0300SAndrew Lindesay{
925f85e0300SAndrew Lindesay	return _LocalDataPath("reference-all_%languageCode%.json.gz", path);
926f85e0300SAndrew Lindesay}
927f85e0300SAndrew Lindesay
928f85e0300SAndrew Lindesay
929c23b641cSAndrew Lindesaystatus_t
9303094fef3SAndrew LindesayModel::IconStoragePath(BPath& path) const
9313094fef3SAndrew Lindesay{
9323094fef3SAndrew Lindesay	BPath iconStoragePath;
9333094fef3SAndrew Lindesay
9343094fef3SAndrew Lindesay	if (find_directory(B_USER_CACHE_DIRECTORY, &iconStoragePath) == B_OK
9353094fef3SAndrew Lindesay		&& iconStoragePath.Append("HaikuDepot") == B_OK
9363094fef3SAndrew Lindesay		&& iconStoragePath.Append("__allicons") == B_OK
9373094fef3SAndrew Lindesay		&& create_directory(iconStoragePath.Path(), 0777) == B_OK) {
9383094fef3SAndrew Lindesay		path.SetTo(iconStoragePath.Path());
9393094fef3SAndrew Lindesay		return B_OK;
9403094fef3SAndrew Lindesay	}
9413094fef3SAndrew Lindesay
9423094fef3SAndrew Lindesay	path.Unset();
9433094fef3SAndrew Lindesay	fprintf(stdout, "unable to find the user cache directory for icons");
9443094fef3SAndrew Lindesay	return B_ERROR;
9453094fef3SAndrew Lindesay}
9463094fef3SAndrew Lindesay
9473094fef3SAndrew Lindesay
9483094fef3SAndrew Lindesaystatus_t
9493094fef3SAndrew LindesayModel::DumpExportPkgDataPath(BPath& path,
950c23b641cSAndrew Lindesay	const BString& repositorySourceCode) const
951c23b641cSAndrew Lindesay{
952c23b641cSAndrew Lindesay	BPath repoDataPath;
953c23b641cSAndrew Lindesay	BString leafName;
954c23b641cSAndrew Lindesay
955c23b641cSAndrew Lindesay	leafName.SetToFormat("pkg-all-%s-%s.json.gz", repositorySourceCode.String(),
956f85e0300SAndrew Lindesay		LanguageModel().PreferredLanguage().Code());
957c23b641cSAndrew Lindesay
958c23b641cSAndrew Lindesay	if (find_directory(B_USER_CACHE_DIRECTORY, &repoDataPath) == B_OK
959c23b641cSAndrew Lindesay		&& repoDataPath.Append("HaikuDepot") == B_OK
960c23b641cSAndrew Lindesay		&& create_directory(repoDataPath.Path(), 0777) == B_OK
961c23b641cSAndrew Lindesay		&& repoDataPath.Append(leafName.String()) == B_OK) {
962c23b641cSAndrew Lindesay		path.SetTo(repoDataPath.Path());
963c23b641cSAndrew Lindesay		return B_OK;
964c23b641cSAndrew Lindesay	}
965c23b641cSAndrew Lindesay
966c23b641cSAndrew Lindesay	path.Unset();
967c23b641cSAndrew Lindesay	fprintf(stdout, "unable to find the user cache file for pkgs' data");
968c23b641cSAndrew Lindesay	return B_ERROR;
969c23b641cSAndrew Lindesay}
970c23b641cSAndrew Lindesay
971c23b641cSAndrew Lindesay
97212107be2SStephan Aßmusvoid
97312107be2SStephan AßmusModel::_UpdateIsFeaturedFilter()
97412107be2SStephan Aßmus{
97512107be2SStephan Aßmus	if (fShowFeaturedPackages && SearchTerms().IsEmpty())
97612107be2SStephan Aßmus		fIsFeaturedFilter = PackageFilterRef(new IsFeaturedFilter(), true);
97787084745SMichael Lotz	else
97812107be2SStephan Aßmus		fIsFeaturedFilter = PackageFilterRef(new AnyFilter(), true);
97912107be2SStephan Aßmus}
98012107be2SStephan Aßmus
98112107be2SStephan Aßmus
98287038da7SStephan Aßmusvoid
98387038da7SStephan AßmusModel::_PopulatePackageScreenshot(const PackageInfoRef& package,
98487038da7SStephan Aßmus	const ScreenshotInfo& info, int32 scaledWidth, bool fromCacheOnly)
98587038da7SStephan Aßmus{
98687038da7SStephan Aßmus	// See if there is a cached screenshot
98787038da7SStephan Aßmus	BFile screenshotFile;
98887038da7SStephan Aßmus	BPath screenshotCachePath;
98987038da7SStephan Aßmus	bool fileExists = false;
99087038da7SStephan Aßmus	BString screenshotName(info.Code());
99187038da7SStephan Aßmus	screenshotName << "@" << scaledWidth;
99287038da7SStephan Aßmus	screenshotName << ".png";
99387038da7SStephan Aßmus	time_t modifiedTime;
99487038da7SStephan Aßmus	if (find_directory(B_USER_CACHE_DIRECTORY, &screenshotCachePath) == B_OK
99587038da7SStephan Aßmus		&& screenshotCachePath.Append("HaikuDepot/Screenshots") == B_OK
99687038da7SStephan Aßmus		&& create_directory(screenshotCachePath.Path(), 0777) == B_OK
99787038da7SStephan Aßmus		&& screenshotCachePath.Append(screenshotName) == B_OK) {
99887038da7SStephan Aßmus		// Try opening the file in read-only mode, which will fail if its
99987038da7SStephan Aßmus		// not a file or does not exist.
100087038da7SStephan Aßmus		fileExists = screenshotFile.SetTo(screenshotCachePath.Path(),
100187038da7SStephan Aßmus			B_READ_ONLY) == B_OK;
100287038da7SStephan Aßmus		if (fileExists)
100387038da7SStephan Aßmus			screenshotFile.GetModificationTime(&modifiedTime);
100487038da7SStephan Aßmus	}
100587038da7SStephan Aßmus
100687038da7SStephan Aßmus	if (fileExists) {
100787038da7SStephan Aßmus		time_t now;
100887038da7SStephan Aßmus		time(&now);
100987038da7SStephan Aßmus		if (fromCacheOnly || now - modifiedTime < 60 * 60) {
101087038da7SStephan Aßmus			// Cache file is recent enough, just use it and return.
101187038da7SStephan Aßmus			BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(screenshotFile),
101287038da7SStephan Aßmus				true);
101387038da7SStephan Aßmus			BAutolock locker(&fLock);
101487038da7SStephan Aßmus			package->AddScreenshot(bitmapRef);
101587038da7SStephan Aßmus			return;
101687038da7SStephan Aßmus		}
101787038da7SStephan Aßmus	}
101887038da7SStephan Aßmus
101987038da7SStephan Aßmus	if (fromCacheOnly)
102087038da7SStephan Aßmus		return;
102187038da7SStephan Aßmus
102287038da7SStephan Aßmus	// Retrieve screenshot from web-app
102387038da7SStephan Aßmus	BMallocIO buffer;
102487038da7SStephan Aßmus
102587038da7SStephan Aßmus	int32 scaledHeight = scaledWidth * info.Height() / info.Width();
102687038da7SStephan Aßmus
102728075ee4SStephan Aßmus	status_t status = fWebAppInterface.RetrieveScreenshot(info.Code(),
102887038da7SStephan Aßmus		scaledWidth, scaledHeight, &buffer);
102987038da7SStephan Aßmus	if (status == B_OK) {
103087038da7SStephan Aßmus		BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(buffer), true);
103187038da7SStephan Aßmus		BAutolock locker(&fLock);
103287038da7SStephan Aßmus		package->AddScreenshot(bitmapRef);
103387038da7SStephan Aßmus		locker.Unlock();
103487038da7SStephan Aßmus		if (screenshotFile.SetTo(screenshotCachePath.Path(),
103587038da7SStephan Aßmus				B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE) == B_OK) {
103687038da7SStephan Aßmus			screenshotFile.Write(buffer.Buffer(), buffer.BufferLength());
103787038da7SStephan Aßmus		}
1038e0881f21SStephan Aßmus	} else {
1039e0881f21SStephan Aßmus		fprintf(stderr, "Failed to retrieve screenshot for code '%s' "
1040e0881f21SStephan Aßmus			"at %" B_PRIi32 "x%" B_PRIi32 ".\n", info.Code().String(),
1041e0881f21SStephan Aßmus			scaledWidth, scaledHeight);
104287038da7SStephan Aßmus	}
104387038da7SStephan Aßmus}
104487038da7SStephan Aßmus
104587038da7SStephan Aßmus
10468f03a0f9SStephan Aßmus// #pragma mark - listener notification methods
10478f03a0f9SStephan Aßmus
10488f03a0f9SStephan Aßmus
10498f03a0f9SStephan Aßmusvoid
10508f03a0f9SStephan AßmusModel::_NotifyAuthorizationChanged()
10518f03a0f9SStephan Aßmus{
10528f03a0f9SStephan Aßmus	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
10538f03a0f9SStephan Aßmus		const ModelListenerRef& listener = fListeners.ItemAtFast(i);
10548f03a0f9SStephan Aßmus		if (listener.Get() != NULL)
10558f03a0f9SStephan Aßmus			listener->AuthorizationChanged();
10568f03a0f9SStephan Aßmus	}
10578f03a0f9SStephan Aßmus}
10583094fef3SAndrew Lindesay
1059