xref: /haiku/src/apps/haikudepot/server/ServerRepositoryDataUpdateProcess.cpp (revision 2ad7efd4b54e3e06e44932df4d247609dc065198)
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 		printf("[DepotMatchingRepositoryListener] associated depot [%s] (%s) "
106 			"with server repository source [%s] (%s)\n",
107 			modifiedDepotInfo.Name().String(),
108 			modifiedDepotInfo.URL().String(),
109 			repositorySourceCode->String(),
110 			repositoryAndRepositorySource
111 				->repositorySource->Identifier()->String());
112 	} else {
113 		printf("[DepotMatchingRepositoryListener] associated depot [%s] with "
114 			"server repository source [%s]\n",
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 	Handle(*(pair.repositorySource->Identifier()), pair);
137 
138 	// there may be additional identifiers for the remote repository and
139 	// these should also be taken into consideration.
140 
141 	for(int32 i = 0;
142 			i < pair.repositorySource->CountExtraIdentifiers();
143 			i++)
144 	{
145 		Handle(*(pair.repositorySource->ExtraIdentifiersItemAt(i)), pair);
146 	}
147 }
148 
149 
150 bool
151 DepotMatchingRepositoryListener::Handle(DumpExportRepository* repository)
152 {
153 	int32 i;
154 
155 	for (i = 0; i < repository->CountRepositorySources(); i++) {
156 		repository_and_repository_source repositoryAndRepositorySource;
157 		repositoryAndRepositorySource.repository = repository;
158 		repositoryAndRepositorySource.repositorySource =
159 			repository->RepositorySourcesItemAt(i);
160 		Handle(repositoryAndRepositorySource);
161 	}
162 
163 	return !fStoppable->WasStopped();
164 }
165 
166 
167 void
168 DepotMatchingRepositoryListener::Complete()
169 {
170 }
171 
172 
173 ServerRepositoryDataUpdateProcess::ServerRepositoryDataUpdateProcess(
174 	Model* model,
175 	uint32 serverProcessOptions)
176 	:
177 	AbstractSingleFileServerProcess(serverProcessOptions),
178 	fModel(model)
179 {
180 }
181 
182 
183 ServerRepositoryDataUpdateProcess::~ServerRepositoryDataUpdateProcess()
184 {
185 }
186 
187 
188 const char*
189 ServerRepositoryDataUpdateProcess::Name() const
190 {
191 	return "ServerRepositoryDataUpdateProcess";
192 }
193 
194 
195 const char*
196 ServerRepositoryDataUpdateProcess::Description() const
197 {
198 	return B_TRANSLATE("Synchronizing meta-data about repositories");
199 }
200 
201 
202 BString
203 ServerRepositoryDataUpdateProcess::UrlPathComponent()
204 {
205 	BString result;
206 	AutoLocker<BLocker> locker(fModel->Lock());
207 	result.SetToFormat("/__repository/all-%s.json.gz",
208 		fModel->Language().PreferredLanguage()->Code());
209 	return result;
210 }
211 
212 
213 status_t
214 ServerRepositoryDataUpdateProcess::GetLocalPath(BPath& path) const
215 {
216 	AutoLocker<BLocker> locker(fModel->Lock());
217 	return fModel->DumpExportRepositoryDataPath(path);
218 }
219 
220 
221 status_t
222 ServerRepositoryDataUpdateProcess::ProcessLocalData()
223 {
224 	DepotMatchingRepositoryListener* itemListener =
225 		new DepotMatchingRepositoryListener(fModel, this);
226 	ObjectDeleter<DepotMatchingRepositoryListener>
227 		itemListenerDeleter(itemListener);
228 
229 	BulkContainerDumpExportRepositoryJsonListener* listener =
230 		new BulkContainerDumpExportRepositoryJsonListener(itemListener);
231 	ObjectDeleter<BulkContainerDumpExportRepositoryJsonListener>
232 		listenerDeleter(listener);
233 
234 	BPath localPath;
235 	status_t result = GetLocalPath(localPath);
236 
237 	if (result != B_OK)
238 		return result;
239 
240 	result = ParseJsonFromFileWithListener(listener, localPath);
241 
242 	if (result != B_OK)
243 		return result;
244 
245 	return listener->ErrorStatus();
246 }
247 
248 
249 status_t
250 ServerRepositoryDataUpdateProcess::GetStandardMetaDataPath(BPath& path) const
251 {
252 	return GetLocalPath(path);
253 }
254 
255 
256 void
257 ServerRepositoryDataUpdateProcess::GetStandardMetaDataJsonPath(
258 	BString& jsonPath) const
259 {
260 	jsonPath.SetTo("$.info");
261 }
262