1/*
2 * Copyright 2018-2023, Andrew Lindesay <apl@lindesay.co.nz>.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
5#include "ProcessCoordinatorFactory.h"
6
7#include <Autolock.h>
8#include <AutoLocker.h>
9
10#include <package/Context.h>
11#include <package/PackageRoster.h>
12
13#include "AbstractServerProcess.h"
14#include "CacheScreenshotProcess.h"
15#include "DeskbarLink.h"
16#include "HaikuDepotConstants.h"
17#include "IncrementViewCounterProcess.h"
18#include "InstallPackageProcess.h"
19#include "LocalPkgDataLoadProcess.h"
20#include "LocalRepositoryUpdateProcess.h"
21#include "Logger.h"
22#include "Model.h"
23#include "OpenPackageProcess.h"
24#include "PackageInfoListener.h"
25#include "PopulatePkgSizesProcess.h"
26#include "ProcessCoordinator.h"
27#include "ServerHelper.h"
28#include "ServerIconExportUpdateProcess.h"
29#include "ServerPkgDataUpdateProcess.h"
30#include "ServerReferenceDataUpdateProcess.h"
31#include "ServerRepositoryDataUpdateProcess.h"
32#include "ServerSettings.h"
33#include "StorageUtils.h"
34#include "ThreadedProcessNode.h"
35#include "UninstallPackageProcess.h"
36#include "UserDetailVerifierProcess.h"
37
38
39using namespace BPackageKit;
40
41
42/*static*/ ProcessCoordinator*
43ProcessCoordinatorFactory::CreateIncrementViewCounter(
44	Model* model, const PackageInfoRef package)
45{
46	ProcessCoordinator* processCoordinator = new ProcessCoordinator(
47		"IncrementViewCounter");
48	AbstractProcessNode* node = new ThreadedProcessNode(
49		new IncrementViewCounterProcess(model, package));
50	processCoordinator->AddNode(node);
51	return processCoordinator;
52}
53
54
55/*static*/ ProcessCoordinator*
56ProcessCoordinatorFactory::CreateUserDetailVerifierCoordinator(
57	UserDetailVerifierListener* userDetailVerifierListener,
58	Model* model)
59{
60	ProcessCoordinator* processCoordinator = new ProcessCoordinator(
61		"UserDetailVerifier");
62	AbstractProcessNode* userDetailVerifier = new ThreadedProcessNode(
63		new UserDetailVerifierProcess(model, userDetailVerifierListener));
64	processCoordinator->AddNode(userDetailVerifier);
65	return processCoordinator;
66}
67
68
69/* static */ ProcessCoordinator*
70ProcessCoordinatorFactory::CreateBulkLoadCoordinator(
71	PackageInfoListenerRef packageInfoListener,
72	Model* model, bool forceLocalUpdate)
73{
74	bool areWorkingFilesAvailable = StorageUtils::AreWorkingFilesAvailable();
75	uint32 serverProcessOptions = _CalculateServerProcessOptions();
76	BAutolock locker(model->Lock());
77	ProcessCoordinator* processCoordinator = new ProcessCoordinator(
78		"BulkLoad", new BMessage(MSG_BULK_LOAD_DONE));
79
80	AbstractProcessNode *localRepositoryUpdate =
81		new ThreadedProcessNode(new LocalRepositoryUpdateProcess(model,
82			forceLocalUpdate));
83	processCoordinator->AddNode(localRepositoryUpdate);
84
85	AbstractProcessNode *localPkgDataLoad =
86		new ThreadedProcessNode(new LocalPkgDataLoadProcess(
87			packageInfoListener, model, forceLocalUpdate));
88	localPkgDataLoad->AddPredecessor(localRepositoryUpdate);
89	processCoordinator->AddNode(localPkgDataLoad);
90
91	if (areWorkingFilesAvailable) {
92		AbstractProcessNode *serverIconExportUpdate =
93			new ThreadedProcessNode(new ServerIconExportUpdateProcess(model,
94				serverProcessOptions));
95		serverIconExportUpdate->AddPredecessor(localPkgDataLoad);
96		processCoordinator->AddNode(serverIconExportUpdate);
97
98		AbstractProcessNode *serverRepositoryDataUpdate =
99			new ThreadedProcessNode(new ServerRepositoryDataUpdateProcess(model,
100				serverProcessOptions));
101		serverRepositoryDataUpdate->AddPredecessor(localPkgDataLoad);
102		processCoordinator->AddNode(serverRepositoryDataUpdate);
103
104		AbstractProcessNode *serverReferenceDataUpdate =
105			new ThreadedProcessNode(new ServerReferenceDataUpdateProcess(model,
106				serverProcessOptions));
107		processCoordinator->AddNode(serverReferenceDataUpdate);
108
109		// This one has to run after the server data is taken up because it
110		// will fill in the gaps based on local data that was not able to be
111		// sourced from the server. It has all of the
112		// `ServerPkgDataUpdateProcess` nodes configured as its predecessors.
113
114		AbstractProcessNode* populatePkgSizes =
115			new ThreadedProcessNode(new PopulatePkgSizesProcess(model));
116
117		// create a process for each of the repositories that are configured on
118		// the local system.  Later, only those that have a web-app repository
119		// server code will be actually processed, but this means that the
120		// creation of the 'processes' does not need to be dynamic as the
121		// process coordinator runs.
122
123		BPackageRoster roster;
124		BStringList repoNames;
125		status_t repoNamesResult = roster.GetRepositoryNames(repoNames);
126
127		if (repoNamesResult == B_OK) {
128			AutoLocker<BLocker> locker(model->Lock());
129
130			for (int32 i = 0; i < repoNames.CountStrings(); i++) {
131				AbstractProcessNode* processNode = new ThreadedProcessNode(
132					new ServerPkgDataUpdateProcess(
133						repoNames.StringAt(i), model, serverProcessOptions));
134				processNode->AddPredecessor(serverRepositoryDataUpdate);
135				processNode->AddPredecessor(serverReferenceDataUpdate);
136				processCoordinator->AddNode(processNode);
137
138				populatePkgSizes->AddPredecessor(processNode);
139			}
140		} else
141			HDERROR("a problem has arisen getting the repository names.");
142
143		processCoordinator->AddNode(populatePkgSizes);
144	}
145
146	return processCoordinator;
147}
148
149
150/*static*/ ProcessCoordinator*
151ProcessCoordinatorFactory::CreatePackageActionCoordinator(
152	Model* model, BMessage* message)
153{
154	switch (message->what) {
155		case MSG_PKG_INSTALL:
156			return _CreateInstallPackageActionCoordinator(model, message);
157		case MSG_PKG_UNINSTALL:
158			return _CreateUninstallPackageActionCoordinator(model, message);
159		case MSG_PKG_OPEN:
160			return _CreateOpenPackageActionCoordinator(model, message);
161		default:
162			HDFATAL("unexpected package action message what");
163	}
164}
165
166
167/*static*/ ProcessCoordinator*
168ProcessCoordinatorFactory::CacheScreenshotCoordinator(Model* model,
169	ScreenshotCoordinate& screenshotCoordinate)
170{
171	ProcessCoordinator* processCoordinator = new ProcessCoordinator("CacheScreenshot");
172	AbstractProcessNode* cacheScreenshotNode = new ThreadedProcessNode(
173		new CacheScreenshotProcess(model, screenshotCoordinate));
174	processCoordinator->AddNode(cacheScreenshotNode);
175	return processCoordinator;
176}
177
178
179/*static*/ PackageInfoRef
180ProcessCoordinatorFactory::_ExtractPackageFromMessage(
181	Model* model, BMessage* message)
182{
183	BString pkgName;
184	if (message->FindString(KEY_PACKAGE_NAME, &pkgName) != B_OK)
185		HDFATAL("malformed message missing key [%s]", KEY_PACKAGE_NAME);
186	return model->PackageForName(pkgName);
187}
188
189
190/*static*/ ProcessCoordinator*
191ProcessCoordinatorFactory::_CreateInstallPackageActionCoordinator(
192	Model* model, BMessage* message)
193{
194	ProcessCoordinator* processCoordinator = new ProcessCoordinator(
195		"InstallPackage", new BMessage(MSG_PACKAGE_ACTION_DONE));
196	PackageInfoRef package = _ExtractPackageFromMessage(model, message);
197	if (package.IsSet()) {
198		AbstractProcessNode *processNode =
199			new ThreadedProcessNode(
200				new InstallPackageProcess(package, model), 10);
201		processCoordinator->AddNode(processNode);
202	} else {
203		HDERROR("unable to find the package");
204	}
205	return processCoordinator;
206}
207
208
209/*static*/ ProcessCoordinator*
210ProcessCoordinatorFactory::_CreateUninstallPackageActionCoordinator(
211	Model* model, BMessage* message)
212{
213	ProcessCoordinator* processCoordinator = new ProcessCoordinator(
214		"UninstallPackage", new BMessage(MSG_PACKAGE_ACTION_DONE));
215	PackageInfoRef package = _ExtractPackageFromMessage(model, message);
216	if (package.IsSet()) {
217		AbstractProcessNode *processNode =
218			new ThreadedProcessNode(
219				new UninstallPackageProcess(package, model), 10);
220		processCoordinator->AddNode(processNode);
221	} else {
222		HDERROR("unable to find the package");
223	}
224	return processCoordinator;
225}
226
227
228/*static*/ ProcessCoordinator*
229ProcessCoordinatorFactory::_CreateOpenPackageActionCoordinator(
230	Model* model, BMessage* message)
231{
232	ProcessCoordinator* processCoordinator = new ProcessCoordinator(
233		"OpenPackage", new BMessage(MSG_PACKAGE_ACTION_DONE));
234	PackageInfoRef package = _ExtractPackageFromMessage(model, message);
235	if (package.IsSet()) {
236		BMessage deskbarLinkMessage;
237		if (message->FindMessage(KEY_DESKBAR_LINK, &deskbarLinkMessage) != B_OK)
238			HDFATAL("malformed message missing key [%s]", KEY_DESKBAR_LINK);
239		DeskbarLink deskbarLink(&deskbarLinkMessage);
240		AbstractProcessNode *processNode =
241			new ThreadedProcessNode(new OpenPackageProcess(
242				package, model, deskbarLink));
243		processCoordinator->AddNode(processNode);
244	} else {
245		HDERROR("unable to find the package");
246	}
247	return processCoordinator;
248}
249
250
251/* static */ uint32
252ProcessCoordinatorFactory::_CalculateServerProcessOptions()
253{
254	uint32 processOptions = 0;
255
256	if (ServerSettings::IsClientTooOld()) {
257		HDINFO("bulk load proceeding without network communications "
258			"because the client is too old");
259		processOptions |= SERVER_PROCESS_NO_NETWORKING;
260	}
261
262	if (!ServerHelper::IsNetworkAvailable())
263		processOptions |= SERVER_PROCESS_NO_NETWORKING;
264
265	if (ServerSettings::PreferCache())
266		processOptions |= SERVER_PROCESS_PREFER_CACHE;
267
268	if (ServerSettings::DropCache())
269		processOptions |= SERVER_PROCESS_DROP_CACHE;
270
271	return processOptions;
272}
273