1/*
2 * Copyright 2017-2023, Andrew Lindesay <apl@lindesay.co.nz>.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
5#include "ServerRepositoryDataUpdateProcess.h"
6
7#include <stdio.h>
8#include <sys/stat.h>
9#include <time.h>
10
11#include <AutoDeleter.h>
12#include <Autolock.h>
13#include <Catalog.h>
14#include <FileIO.h>
15#include <Url.h>
16
17#include "DumpExportRepository.h"
18#include "DumpExportRepositoryJsonListener.h"
19#include "DumpExportRepositorySource.h"
20#include "PackageInfo.h"
21#include "ServerSettings.h"
22#include "StorageUtils.h"
23#include "Logger.h"
24
25
26#undef B_TRANSLATION_CONTEXT
27#define B_TRANSLATION_CONTEXT "ServerRepositoryDataUpdateProcess"
28
29
30/*! This repository listener (not at the JSON level) is feeding in the
31    repositories as they are parsed and processing them.  Processing
32    includes finding the matching depot record and coupling the data
33    from the server with the data about the depot.
34*/
35
36class DepotMatchingRepositoryListener :
37	public DumpExportRepositoryListener {
38public:
39								DepotMatchingRepositoryListener(Model* model,
40									Stoppable* stoppable);
41	virtual						~DepotMatchingRepositoryListener();
42
43	virtual	bool				Handle(DumpExportRepository* item);
44			void				Handle(DumpExportRepository* repository,
45									DumpExportRepositorySource*
46										repositorySource);
47			void				Handle(const BString& identifier,
48									DumpExportRepository* repository,
49									DumpExportRepositorySource*
50										repositorySource);
51	virtual	void				Complete();
52
53private:
54			void				_SetupRepositoryData(
55									DepotInfoRef& depot,
56									DumpExportRepository* repository,
57									DumpExportRepositorySource*
58										repositorySource);
59
60private:
61			Model*				fModel;
62			Stoppable*			fStoppable;
63};
64
65
66DepotMatchingRepositoryListener::DepotMatchingRepositoryListener(
67	Model* model, Stoppable* stoppable)
68	:
69	fModel(model),
70	fStoppable(stoppable)
71{
72}
73
74
75DepotMatchingRepositoryListener::~DepotMatchingRepositoryListener()
76{
77}
78
79
80void
81DepotMatchingRepositoryListener::_SetupRepositoryData(DepotInfoRef& depot,
82	DumpExportRepository* repository,
83	DumpExportRepositorySource* repositorySource)
84{
85	BString* repositoryCode = repository->Code();
86	BString* repositorySourceCode = repositorySource->Code();
87
88	depot->SetWebAppRepositoryCode(*repositoryCode);
89	depot->SetWebAppRepositorySourceCode(*repositorySourceCode);
90
91	if (Logger::IsDebugEnabled()) {
92		HDDEBUG("[DepotMatchingRepositoryListener] associated depot [%s] (%s) "
93			"with server repository source [%s] (%s)",
94			depot->Name().String(),
95			depot->Identifier().String(),
96			repositorySourceCode->String(),
97			repositorySource->Identifier()->String());
98	} else {
99		HDINFO("[DepotMatchingRepositoryListener] associated depot [%s] with "
100			"server repository source [%s]",
101			depot->Name().String(),
102			repositorySourceCode->String());
103	}
104}
105
106
107void
108DepotMatchingRepositoryListener::Handle(const BString& identifier,
109	DumpExportRepository* repository,
110	DumpExportRepositorySource* repositorySource)
111{
112	if (!identifier.IsEmpty()) {
113		AutoLocker<BLocker> locker(fModel->Lock());
114		for (int32 i = 0; i < fModel->CountDepots(); i++) {
115			DepotInfoRef depot = fModel->DepotAtIndex(i);
116			if (identifier == depot->Identifier())
117				_SetupRepositoryData(depot, repository, repositorySource);
118		}
119	}
120}
121
122
123void
124DepotMatchingRepositoryListener::Handle(DumpExportRepository* repository,
125	DumpExportRepositorySource* repositorySource)
126{
127	if (!repositorySource->IdentifierIsNull())
128		Handle(*(repositorySource->Identifier()), repository, repositorySource);
129
130	// there may be additional identifiers for the remote repository and
131	// these should also be taken into consideration.
132
133	for(int32 i = 0;
134			i < repositorySource->CountExtraIdentifiers();
135			i++)
136	{
137		Handle(*(repositorySource->ExtraIdentifiersItemAt(i)), repository,
138			repositorySource);
139	}
140}
141
142
143bool
144DepotMatchingRepositoryListener::Handle(DumpExportRepository* repository)
145{
146	int32 i;
147	for (i = 0; i < repository->CountRepositorySources(); i++)
148		Handle(repository, repository->RepositorySourcesItemAt(i));
149	return !fStoppable->WasStopped();
150}
151
152
153void
154DepotMatchingRepositoryListener::Complete()
155{
156}
157
158
159ServerRepositoryDataUpdateProcess::ServerRepositoryDataUpdateProcess(
160	Model* model,
161	uint32 serverProcessOptions)
162	:
163	AbstractSingleFileServerProcess(serverProcessOptions),
164	fModel(model)
165{
166}
167
168
169ServerRepositoryDataUpdateProcess::~ServerRepositoryDataUpdateProcess()
170{
171}
172
173
174const char*
175ServerRepositoryDataUpdateProcess::Name() const
176{
177	return "ServerRepositoryDataUpdateProcess";
178}
179
180
181const char*
182ServerRepositoryDataUpdateProcess::Description() const
183{
184	return B_TRANSLATE("Synchronizing meta-data about repositories");
185}
186
187
188BString
189ServerRepositoryDataUpdateProcess::UrlPathComponent()
190{
191	BString result;
192	AutoLocker<BLocker> locker(fModel->Lock());
193	result.SetToFormat("/__repository/all-%s.json.gz",
194		fModel->Language()->PreferredLanguage()->ID());
195	return result;
196}
197
198
199status_t
200ServerRepositoryDataUpdateProcess::GetLocalPath(BPath& path) const
201{
202	AutoLocker<BLocker> locker(fModel->Lock());
203	return fModel->DumpExportRepositoryDataPath(path);
204}
205
206
207status_t
208ServerRepositoryDataUpdateProcess::ProcessLocalData()
209{
210	DepotMatchingRepositoryListener* itemListener =
211		new DepotMatchingRepositoryListener(fModel, this);
212	ObjectDeleter<DepotMatchingRepositoryListener>
213		itemListenerDeleter(itemListener);
214
215	BulkContainerDumpExportRepositoryJsonListener* listener =
216		new BulkContainerDumpExportRepositoryJsonListener(itemListener);
217	ObjectDeleter<BulkContainerDumpExportRepositoryJsonListener>
218		listenerDeleter(listener);
219
220	BPath localPath;
221	status_t result = GetLocalPath(localPath);
222
223	if (result != B_OK)
224		return result;
225
226	result = ParseJsonFromFileWithListener(listener, localPath);
227
228	if (result != B_OK)
229		return result;
230
231	return listener->ErrorStatus();
232}
233
234
235status_t
236ServerRepositoryDataUpdateProcess::GetStandardMetaDataPath(BPath& path) const
237{
238	return GetLocalPath(path);
239}
240
241
242void
243ServerRepositoryDataUpdateProcess::GetStandardMetaDataJsonPath(
244	BString& jsonPath) const
245{
246	jsonPath.SetTo("$.info");
247}
248