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