2 * Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
3 * Copyright 2014, Axel Dörfler <axeld@pinc-software.de>.
4 * Copyright 2016-2017, Andrew Lindesay <apl@lindesay.co.nz>.
5 * All rights reserved. Distributed under the terms of the MIT License.
17 #include <Directory.h>
21 #include <LocaleRoster.h>
26 #include "StorageUtils.h"
29 #undef B_TRANSLATION_CONTEXT
30 #define B_TRANSLATION_CONTEXT "Model"
33 static const char* kHaikuDepotKeyring
= "HaikuDepot";
36 PackageFilter::~PackageFilter()
41 ModelListener::~ModelListener()
46 // #pragma mark - PackageFilters
49 class AnyFilter
: public PackageFilter
{
51 virtual bool AcceptsPackage(const PackageInfoRef
& package
) const
58 class DepotFilter
: public PackageFilter
{
60 DepotFilter(const DepotInfo
& depot
)
66 virtual bool AcceptsPackage(const PackageInfoRef
& package
) const
68 // TODO: Maybe a PackageInfo ought to know the Depot it came from?
69 // But right now the same package could theoretically be provided
70 // from different depots and the filter would work correctly.
71 // Also the PackageList could actually contain references to packages
72 // instead of the packages as objects. The equal operator is quite
74 return fDepot
.Packages().Contains(package
);
77 const BString
& Depot() const
87 class CategoryFilter
: public PackageFilter
{
89 CategoryFilter(const BString
& category
)
95 virtual bool AcceptsPackage(const PackageInfoRef
& package
) const
97 if (package
.Get() == NULL
)
100 const CategoryList
& categories
= package
->Categories();
101 for (int i
= categories
.CountItems() - 1; i
>= 0; i
--) {
102 const CategoryRef
& category
= categories
.ItemAtFast(i
);
103 if (category
.Get() == NULL
)
105 if (category
->Name() == fCategory
)
111 const BString
& Category() const
121 class ContainedInFilter
: public PackageFilter
{
123 ContainedInFilter(const PackageList
& packageList
)
125 fPackageList(packageList
)
129 virtual bool AcceptsPackage(const PackageInfoRef
& package
) const
131 return fPackageList
.Contains(package
);
135 const PackageList
& fPackageList
;
139 class ContainedInEitherFilter
: public PackageFilter
{
141 ContainedInEitherFilter(const PackageList
& packageListA
,
142 const PackageList
& packageListB
)
144 fPackageListA(packageListA
),
145 fPackageListB(packageListB
)
149 virtual bool AcceptsPackage(const PackageInfoRef
& package
) const
151 return fPackageListA
.Contains(package
)
152 || fPackageListB
.Contains(package
);
156 const PackageList
& fPackageListA
;
157 const PackageList
& fPackageListB
;
161 class NotContainedInFilter
: public PackageFilter
{
163 NotContainedInFilter(const PackageList
* packageList
, ...)
166 va_start(args
, packageList
);
168 const PackageList
* packageList
= va_arg(args
, const PackageList
*);
169 if (packageList
== NULL
)
171 fPackageLists
.Add(packageList
);
176 virtual bool AcceptsPackage(const PackageInfoRef
& package
) const
178 if (package
.Get() == NULL
)
181 printf("TEST %s\n", package
->Name().String());
183 for (int32 i
= 0; i
< fPackageLists
.CountItems(); i
++) {
184 if (fPackageLists
.ItemAtFast(i
)->Contains(package
)) {
185 printf(" contained in %" B_PRId32
"\n", i
);
193 List
<const PackageList
*, true> fPackageLists
;
197 class StateFilter
: public PackageFilter
{
199 StateFilter(PackageState state
)
205 virtual bool AcceptsPackage(const PackageInfoRef
& package
) const
207 return package
->State() == NONE
;
215 class SearchTermsFilter
: public PackageFilter
{
217 SearchTermsFilter(const BString
& searchTerms
)
219 // Separate the string into terms at spaces
221 while (index
< searchTerms
.Length()) {
222 int32 nextSpace
= searchTerms
.FindFirst(" ", index
);
224 nextSpace
= searchTerms
.Length();
225 if (nextSpace
> index
) {
227 searchTerms
.CopyInto(term
, index
, nextSpace
- index
);
229 fSearchTerms
.Add(term
);
231 index
= nextSpace
+ 1;
235 virtual bool AcceptsPackage(const PackageInfoRef
& package
) const
237 if (package
.Get() == NULL
)
239 // Every search term must be found in one of the package texts
240 for (int32 i
= fSearchTerms
.CountItems() - 1; i
>= 0; i
--) {
241 const BString
& term
= fSearchTerms
.ItemAtFast(i
);
242 if (!_TextContains(package
->Name(), term
)
243 && !_TextContains(package
->Title(), term
)
244 && !_TextContains(package
->Publisher().Name(), term
)
245 && !_TextContains(package
->ShortDescription(), term
)
246 && !_TextContains(package
->FullDescription(), term
)) {
253 BString
SearchTerms() const
256 for (int32 i
= 0; i
< fSearchTerms
.CountItems(); i
++) {
257 const BString
& term
= fSearchTerms
.ItemAtFast(i
);
260 if (!searchTerms
.IsEmpty())
261 searchTerms
.Append(" ");
262 searchTerms
.Append(term
);
268 bool _TextContains(BString text
, const BString
& string
) const
271 int32 index
= text
.FindFirst(string
);
276 StringList fSearchTerms
;
280 class IsFeaturedFilter
: public PackageFilter
{
286 virtual bool AcceptsPackage(const PackageInfoRef
& package
) const
288 return package
.Get() != NULL
&& package
->IsProminent();
294 is_source_package(const PackageInfoRef
& package
)
296 const BString
& packageName
= package
->Name();
297 return packageName
.EndsWith("_source");
302 is_develop_package(const PackageInfoRef
& package
)
304 const BString
& packageName
= package
->Name();
305 return packageName
.EndsWith("_devel")
306 || packageName
.EndsWith("_debuginfo");
310 // #pragma mark - Model
317 fCategoryAudio(new PackageCategory(
319 B_TRANSLATE("Audio"), "audio"), true),
320 fCategoryBusiness(new PackageCategory(
322 B_TRANSLATE("Business"), "business"), true),
323 fCategoryDevelopment(new PackageCategory(
325 B_TRANSLATE("Development"), "development"), true),
326 fCategoryEducation(new PackageCategory(
328 B_TRANSLATE("Education"), "education"), true),
329 fCategoryGames(new PackageCategory(
331 B_TRANSLATE("Games"), "games"), true),
332 fCategoryGraphics(new PackageCategory(
334 B_TRANSLATE("Graphics"), "graphics"), true),
335 fCategoryInternetAndNetwork(new PackageCategory(
337 B_TRANSLATE("Internet & Network"), "internetandnetwork"), true),
338 fCategoryProductivity(new PackageCategory(
340 B_TRANSLATE("Productivity"), "productivity"), true),
341 fCategoryScienceAndMathematics(new PackageCategory(
343 B_TRANSLATE("Science & Mathematics"), "scienceandmathematics"), true),
344 fCategorySystemAndUtilities(new PackageCategory(
346 B_TRANSLATE("System & Utilities"), "systemandutilities"), true),
347 fCategoryVideo(new PackageCategory(
349 B_TRANSLATE("Video"), "video"), true),
351 fCategoryFilter(PackageFilterRef(new AnyFilter(), true)),
353 fSearchTermsFilter(PackageFilterRef(new AnyFilter(), true)),
356 fShowFeaturedPackages(true),
357 fShowAvailablePackages(true),
358 fShowInstalledPackages(true),
359 fShowSourcePackages(false),
360 fShowDevelopPackages(false)
362 _UpdateIsFeaturedFilter();
364 // Don't forget to add new categories to this list:
365 fCategories
.Add(fCategoryGames
);
366 fCategories
.Add(fCategoryBusiness
);
367 fCategories
.Add(fCategoryAudio
);
368 fCategories
.Add(fCategoryVideo
);
369 fCategories
.Add(fCategoryGraphics
);
370 fCategories
.Add(fCategoryEducation
);
371 fCategories
.Add(fCategoryProductivity
);
372 fCategories
.Add(fCategorySystemAndUtilities
);
373 fCategories
.Add(fCategoryInternetAndNetwork
);
374 fCategories
.Add(fCategoryDevelopment
);
375 fCategories
.Add(fCategoryScienceAndMathematics
);
376 // TODO: The server will eventually support an API to
377 // get the defined categories and their translated names.
378 // This should then be used instead of hard-coded
379 // categories and translations in the app.
381 fPreferredLanguage
= "en";
382 BLocaleRoster
* localeRoster
= BLocaleRoster::Default();
383 if (localeRoster
!= NULL
) {
384 BMessage preferredLanguages
;
385 if (localeRoster
->GetPreferredLanguages(&preferredLanguages
) == B_OK
) {
387 if (preferredLanguages
.FindString("language", 0, &language
) == B_OK
)
388 language
.CopyInto(fPreferredLanguage
, 0, 2);
392 // TODO: Fetch this from the web-app.
393 fSupportedLanguages
.Add("en");
394 fSupportedLanguages
.Add("de");
395 fSupportedLanguages
.Add("fr");
396 fSupportedLanguages
.Add("ja");
397 fSupportedLanguages
.Add("es");
398 fSupportedLanguages
.Add("zh");
399 fSupportedLanguages
.Add("pt");
400 fSupportedLanguages
.Add("ru");
402 if (!fSupportedLanguages
.Contains(fPreferredLanguage
)) {
403 // Force the preferred language to one of the currently supported
404 // ones, until the web application supports all ISO language codes.
405 printf("User preferred language '%s' not currently supported, "
406 "defaulting to 'en'.", fPreferredLanguage
.String());
407 fPreferredLanguage
= "en";
409 fWebAppInterface
.SetPreferredLanguage(fPreferredLanguage
);
419 Model::AddListener(const ModelListenerRef
& listener
)
421 return fListeners
.Add(listener
);
426 Model::CreatePackageList() const
428 // Iterate all packages from all depots.
429 // If configured, restrict depot, filter by search terms, status, name ...
430 PackageList resultList
;
432 for (int32 i
= 0; i
< fDepots
.CountItems(); i
++) {
433 const DepotInfo
& depot
= fDepots
.ItemAtFast(i
);
435 if (fDepotFilter
.Length() > 0 && fDepotFilter
!= depot
.Name())
438 const PackageList
& packages
= depot
.Packages();
440 for (int32 j
= 0; j
< packages
.CountItems(); j
++) {
441 const PackageInfoRef
& package
= packages
.ItemAtFast(j
);
442 if (MatchesFilter(package
))
443 resultList
.Add(package
);
452 Model::MatchesFilter(const PackageInfoRef
& package
) const
454 return fCategoryFilter
->AcceptsPackage(package
)
455 && fSearchTermsFilter
->AcceptsPackage(package
)
456 && fIsFeaturedFilter
->AcceptsPackage(package
)
457 && (fShowAvailablePackages
|| package
->State() != NONE
)
458 && (fShowInstalledPackages
|| package
->State() != ACTIVATED
)
459 && (fShowSourcePackages
|| !is_source_package(package
))
460 && (fShowDevelopPackages
|| !is_develop_package(package
));
465 Model::AddDepot(const DepotInfo
& depot
)
467 return fDepots
.Add(depot
);
472 Model::HasDepot(const BString
& name
) const
474 return NULL
!= DepotForName(name
);
479 Model::DepotForName(const BString
& name
) const
481 for (int32 i
= fDepots
.CountItems() - 1; i
>= 0; i
--) {
482 if (fDepots
.ItemAtFast(i
).Name() == name
)
483 return &fDepots
.ItemAtFast(i
);
490 Model::SyncDepot(const DepotInfo
& depot
)
492 for (int32 i
= fDepots
.CountItems() - 1; i
>= 0; i
--) {
493 const DepotInfo
& existingDepot
= fDepots
.ItemAtFast(i
);
494 if (existingDepot
.Name() == depot
.Name()) {
495 DepotInfo
mergedDepot(existingDepot
);
496 mergedDepot
.SyncPackages(depot
.Packages());
497 fDepots
.Replace(i
, mergedDepot
);
513 Model::SetPackageState(const PackageInfoRef
& package
, PackageState state
)
518 fInstalledPackages
.Remove(package
);
519 fActivatedPackages
.Remove(package
);
520 fUninstalledPackages
.Remove(package
);
523 if (!fInstalledPackages
.Contains(package
))
524 fInstalledPackages
.Add(package
);
525 fActivatedPackages
.Remove(package
);
526 fUninstalledPackages
.Remove(package
);
529 if (!fInstalledPackages
.Contains(package
))
530 fInstalledPackages
.Add(package
);
531 if (!fActivatedPackages
.Contains(package
))
532 fActivatedPackages
.Add(package
);
533 fUninstalledPackages
.Remove(package
);
536 fInstalledPackages
.Remove(package
);
537 fActivatedPackages
.Remove(package
);
538 if (!fUninstalledPackages
.Contains(package
))
539 fUninstalledPackages
.Add(package
);
543 package
->SetState(state
);
547 // #pragma mark - filters
551 Model::SetCategory(const BString
& category
)
553 PackageFilter
* filter
;
555 if (category
.Length() == 0)
556 filter
= new AnyFilter();
558 filter
= new CategoryFilter(category
);
560 fCategoryFilter
.SetTo(filter
, true);
565 Model::Category() const
567 CategoryFilter
* filter
568 = dynamic_cast<CategoryFilter
*>(fCategoryFilter
.Get());
571 return filter
->Category();
576 Model::SetDepot(const BString
& depot
)
578 fDepotFilter
= depot
;
590 Model::SetSearchTerms(const BString
& searchTerms
)
592 PackageFilter
* filter
;
594 if (searchTerms
.Length() == 0)
595 filter
= new AnyFilter();
597 filter
= new SearchTermsFilter(searchTerms
);
599 fSearchTermsFilter
.SetTo(filter
, true);
600 _UpdateIsFeaturedFilter();
605 Model::SearchTerms() const
607 SearchTermsFilter
* filter
608 = dynamic_cast<SearchTermsFilter
*>(fSearchTermsFilter
.Get());
611 return filter
->SearchTerms();
616 Model::SetShowFeaturedPackages(bool show
)
618 fShowFeaturedPackages
= show
;
619 _UpdateIsFeaturedFilter();
624 Model::SetShowAvailablePackages(bool show
)
626 fShowAvailablePackages
= show
;
631 Model::SetShowInstalledPackages(bool show
)
633 fShowInstalledPackages
= show
;
638 Model::SetShowSourcePackages(bool show
)
640 fShowSourcePackages
= show
;
645 Model::SetShowDevelopPackages(bool show
)
647 fShowDevelopPackages
= show
;
651 // #pragma mark - information retrieval
655 Model::PopulatePackage(const PackageInfoRef
& package
, uint32 flags
)
657 // TODO: There should probably also be a way to "unpopulate" the
658 // package information. Maybe a cache of populated packages, so that
659 // packages loose their extra information after a certain amount of
660 // time when they have not been accessed/displayed in the UI. Otherwise
661 // HaikuDepot will consume more and more resources in the packages.
662 // Especially screen-shots will be a problem eventually.
664 BAutolock
locker(&fLock
);
665 bool alreadyPopulated
= fPopulatedPackages
.Contains(package
);
666 if ((flags
& POPULATE_FORCE
) == 0 && alreadyPopulated
)
668 if (!alreadyPopulated
)
669 fPopulatedPackages
.Add(package
);
672 if ((flags
& POPULATE_USER_RATINGS
) != 0) {
673 // Retrieve info from web-app
677 BString architecture
;
679 BAutolock
locker(&fLock
);
680 packageName
= package
->Name();
681 architecture
= package
->Architecture();
684 status_t status
= fWebAppInterface
.RetrieveUserRatings(packageName
,
685 architecture
, 0, 50, info
);
686 if (status
== B_OK
) {
690 if (info
.FindMessage("result", &result
) == B_OK
691 && result
.FindMessage("items", &items
) == B_OK
) {
693 BAutolock
locker(&fLock
);
694 package
->ClearUserRatings();
702 if (items
.FindMessage(name
, &item
) != B_OK
)
707 if (item
.FindMessage("user", &userInfo
) != B_OK
708 || userInfo
.FindString("nickname", &user
) != B_OK
) {
709 // Ignore, we need the user name
713 // Extract basic info, all items are optional
714 BString languageCode
;
717 item
.FindString("naturalLanguageCode", &languageCode
);
718 item
.FindString("comment", &comment
);
719 if (item
.FindDouble("rating", &rating
) != B_OK
)
721 if (comment
.Length() == 0 && rating
== -1) {
722 // No useful information given.
726 // For which version of the package was the rating?
731 if (item
.FindMessage("pkgVersion", &version
) == B_OK
) {
732 version
.FindString("major", &major
);
733 version
.FindString("minor", &minor
);
734 version
.FindString("micro", µ
);
736 BString versionString
= major
;
737 versionString
<< ".";
738 versionString
<< minor
;
739 if (micro
.Length() > 0) {
740 versionString
<< ".";
741 versionString
<< micro
;
743 // Add the rating to the PackageInfo
744 package
->AddUserRating(
745 UserRating(UserInfo(user
), rating
,
746 comment
, languageCode
, versionString
, 0, 0)
749 } else if (info
.FindMessage("error", &result
) == B_OK
) {
750 result
.PrintToStream();
755 if ((flags
& POPULATE_SCREEN_SHOTS
) != 0) {
756 ScreenshotInfoList screenshotInfos
;
758 BAutolock
locker(&fLock
);
759 screenshotInfos
= package
->ScreenshotInfos();
760 package
->ClearScreenshots();
762 for (int i
= 0; i
< screenshotInfos
.CountItems(); i
++) {
763 const ScreenshotInfo
& info
= screenshotInfos
.ItemAtFast(i
);
764 _PopulatePackageScreenshot(package
, info
, 320, false);
771 Model::SetUsername(BString username
)
774 if (username
.Length() > 0) {
777 if (keyStore
.GetKey(kHaikuDepotKeyring
, B_KEY_TYPE_PASSWORD
, username
,
779 password
= key
.Password();
784 SetAuthorization(username
, password
, false);
789 Model::Username() const
791 return fWebAppInterface
.Username();
796 Model::SetAuthorization(const BString
& username
, const BString
& password
,
799 if (storePassword
&& username
.Length() > 0 && password
.Length() > 0) {
800 BPasswordKey
key(password
, B_KEY_PURPOSE_WEB
, username
);
802 keyStore
.AddKeyring(kHaikuDepotKeyring
);
803 keyStore
.AddKey(kHaikuDepotKeyring
, key
);
806 BAutolock
locker(&fLock
);
807 fWebAppInterface
.SetAuthorization(username
, password
);
809 _NotifyAuthorizationChanged();
813 /*! When bulk repository data comes down from the server, it will
814 arrive as a json.gz payload. This is stored locally as a cache
815 and this method will provide the on-disk storage location for
820 Model::DumpExportRepositoryDataPath(BPath
& path
) const
824 if (find_directory(B_USER_CACHE_DIRECTORY
, &repoDataPath
) == B_OK
825 && repoDataPath
.Append("HaikuDepot") == B_OK
826 && create_directory(repoDataPath
.Path(), 0777) == B_OK
827 && repoDataPath
.Append("repository-all_en.json.gz") == B_OK
) {
828 path
.SetTo(repoDataPath
.Path());
833 fprintf(stdout
, "unable to find the user cache file for repositories'"
840 Model::IconStoragePath(BPath
& path
) const
842 BPath iconStoragePath
;
844 if (find_directory(B_USER_CACHE_DIRECTORY
, &iconStoragePath
) == B_OK
845 && iconStoragePath
.Append("HaikuDepot") == B_OK
846 && iconStoragePath
.Append("__allicons") == B_OK
847 && create_directory(iconStoragePath
.Path(), 0777) == B_OK
) {
848 path
.SetTo(iconStoragePath
.Path());
853 fprintf(stdout
, "unable to find the user cache directory for icons");
859 Model::DumpExportPkgDataPath(BPath
& path
,
860 const BString
& repositorySourceCode
) const
865 leafName
.SetToFormat("pkg-all-%s-%s.json.gz", repositorySourceCode
.String(),
866 fPreferredLanguage
.String());
868 if (find_directory(B_USER_CACHE_DIRECTORY
, &repoDataPath
) == B_OK
869 && repoDataPath
.Append("HaikuDepot") == B_OK
870 && create_directory(repoDataPath
.Path(), 0777) == B_OK
871 && repoDataPath
.Append(leafName
.String()) == B_OK
) {
872 path
.SetTo(repoDataPath
.Path());
877 fprintf(stdout
, "unable to find the user cache file for pkgs' data");
883 Model::_UpdateIsFeaturedFilter()
885 if (fShowFeaturedPackages
&& SearchTerms().IsEmpty())
886 fIsFeaturedFilter
= PackageFilterRef(new IsFeaturedFilter(), true);
888 fIsFeaturedFilter
= PackageFilterRef(new AnyFilter(), true);
893 Model::_PopulatePackageScreenshot(const PackageInfoRef
& package
,
894 const ScreenshotInfo
& info
, int32 scaledWidth
, bool fromCacheOnly
)
896 // See if there is a cached screenshot
897 BFile screenshotFile
;
898 BPath screenshotCachePath
;
899 bool fileExists
= false;
900 BString
screenshotName(info
.Code());
901 screenshotName
<< "@" << scaledWidth
;
902 screenshotName
<< ".png";
904 if (find_directory(B_USER_CACHE_DIRECTORY
, &screenshotCachePath
) == B_OK
905 && screenshotCachePath
.Append("HaikuDepot/Screenshots") == B_OK
906 && create_directory(screenshotCachePath
.Path(), 0777) == B_OK
907 && screenshotCachePath
.Append(screenshotName
) == B_OK
) {
908 // Try opening the file in read-only mode, which will fail if its
909 // not a file or does not exist.
910 fileExists
= screenshotFile
.SetTo(screenshotCachePath
.Path(),
911 B_READ_ONLY
) == B_OK
;
913 screenshotFile
.GetModificationTime(&modifiedTime
);
919 if (fromCacheOnly
|| now
- modifiedTime
< 60 * 60) {
920 // Cache file is recent enough, just use it and return.
921 BitmapRef
bitmapRef(new(std::nothrow
)SharedBitmap(screenshotFile
),
923 BAutolock
locker(&fLock
);
924 package
->AddScreenshot(bitmapRef
);
932 // Retrieve screenshot from web-app
935 int32 scaledHeight
= scaledWidth
* info
.Height() / info
.Width();
937 status_t status
= fWebAppInterface
.RetrieveScreenshot(info
.Code(),
938 scaledWidth
, scaledHeight
, &buffer
);
939 if (status
== B_OK
) {
940 BitmapRef
bitmapRef(new(std::nothrow
)SharedBitmap(buffer
), true);
941 BAutolock
locker(&fLock
);
942 package
->AddScreenshot(bitmapRef
);
944 if (screenshotFile
.SetTo(screenshotCachePath
.Path(),
945 B_WRITE_ONLY
| B_CREATE_FILE
| B_ERASE_FILE
) == B_OK
) {
946 screenshotFile
.Write(buffer
.Buffer(), buffer
.BufferLength());
949 fprintf(stderr
, "Failed to retrieve screenshot for code '%s' "
950 "at %" B_PRIi32
"x%" B_PRIi32
".\n", info
.Code().String(),
951 scaledWidth
, scaledHeight
);
956 // #pragma mark - listener notification methods
960 Model::_NotifyAuthorizationChanged()
962 for (int32 i
= fListeners
.CountItems() - 1; i
>= 0; i
--) {
963 const ModelListenerRef
& listener
= fListeners
.ItemAtFast(i
);
964 if (listener
.Get() != NULL
)
965 listener
->AuthorizationChanged();
970 // temporary - should not be required once the repo info url is used.
972 normalize_repository_base_url(BUrl
& url
)
974 if (url
.Protocol() == "https")
975 url
.SetProtocol("http");
977 BString
path(url
.Path());
979 if (path
.EndsWith("/"))
980 url
.SetPath(path
.Truncate(path
.Length() - 1));
985 Model::ForAllDepots(void (*func
)(const DepotInfo
& depot
, void* context
),
988 for (int32 i
= 0; i
< fDepots
.CountItems(); i
++) {
989 DepotInfo depotInfo
= fDepots
.ItemAtFast(i
);
990 func(depotInfo
, context
);
995 // TODO; should use the repo.info url and not the base url.
998 Model::ReplaceDepotByUrl(const BString
& url
,
999 DepotMapper
* depotMapper
, void* context
)
1001 for (int32 i
= 0; i
< fDepots
.CountItems(); i
++) {
1002 DepotInfo depotInfo
= fDepots
.ItemAtFast(i
);
1005 BUrl
depotUrlNormalized(depotInfo
.BaseURL());
1007 normalize_repository_base_url(url
);
1008 normalize_repository_base_url(depotUrlNormalized
);
1010 if (url
== depotUrlNormalized
) {
1011 BAutolock
locker(&fLock
);
1012 fDepots
.Replace(i
, depotMapper
->MapDepot(depotInfo
, context
));
1019 Model::ForAllPackages(PackageConsumer
* packageConsumer
, void* context
)
1021 for (int32 i
= 0; i
< fDepots
.CountItems(); i
++) {
1022 DepotInfo depotInfo
= fDepots
.ItemAtFast(i
);
1023 PackageList packages
= depotInfo
.Packages();
1024 for(int32 j
= 0; j
< packages
.CountItems(); j
++) {
1025 const PackageInfoRef
& packageInfoRef
= packages
.ItemAtFast(j
);
1027 if (packageInfoRef
!= NULL
) {
1028 BAutolock
locker(&fLock
);
1029 if (!packageConsumer
->ConsumePackage(packageInfoRef
, context
))
1038 Model::ForPackageByNameInDepot(const BString
& depotName
,
1039 const BString
& packageName
, PackageConsumer
* packageConsumer
, void* context
)
1041 int32 depotCount
= fDepots
.CountItems();
1043 for (int32 i
= 0; i
< depotCount
; i
++) {
1044 DepotInfo depotInfo
= fDepots
.ItemAtFast(i
);
1046 if (depotInfo
.Name() == depotName
) {
1047 int32 packageIndex
= depotInfo
.PackageIndexByName(packageName
);
1049 if (-1 != packageIndex
) {
1050 PackageList packages
= depotInfo
.Packages();
1051 const PackageInfoRef
& packageInfoRef
=
1052 packages
.ItemAtFast(packageIndex
);
1054 BAutolock
locker(&fLock
);
1055 packageConsumer
->ConsumePackage(packageInfoRef
,
1066 Model::LogDepotsWithNoWebAppRepositoryCode() const
1070 for (i
= 0; i
< fDepots
.CountItems(); i
++) {
1071 const DepotInfo
& depot
= fDepots
.ItemAt(i
);
1073 if (depot
.WebAppRepositoryCode().Length() == 0) {
1074 printf("depot [%s]", depot
.Name().String());
1076 if (depot
.BaseURL().Length() > 0)
1077 printf(" (%s)", depot
.BaseURL().String());
1079 printf(" correlates with no repository in the haiku"
1080 "depot server system\n");