xref: /haiku/src/apps/haikudepot/server/ServerIconExportUpdateProcess.cpp (revision e705c841d784f0035a0ef3e9e96f6e017df16681)
1 /*
2  * Copyright 2017-2018, Andrew Lindesay <apl@lindesay.co.nz>.
3  * All rights reserved. Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "ServerIconExportUpdateProcess.h"
8 
9 #include <stdio.h>
10 #include <sys/stat.h>
11 #include <time.h>
12 
13 #include <AutoLocker.h>
14 #include <Catalog.h>
15 #include <FileIO.h>
16 #include <support/StopWatch.h>
17 #include <support/ZlibCompressionAlgorithm.h>
18 
19 #include "HaikuDepotConstants.h"
20 #include "Logger.h"
21 #include "ServerHelper.h"
22 #include "ServerSettings.h"
23 #include "StorageUtils.h"
24 #include "TarArchiveService.h"
25 
26 
27 #undef B_TRANSLATION_CONTEXT
28 #define B_TRANSLATION_CONTEXT "ServerIconExportUpdateProcess"
29 
30 
31 /*! This constructor will locate the cached data in a standardized location */
32 
33 ServerIconExportUpdateProcess::ServerIconExportUpdateProcess(
34 	Model* model,
35 	uint32 serverProcessOptions)
36 	:
37 	AbstractServerProcess(serverProcessOptions),
38 	fModel(model),
39 	fCountIconsSet(0)
40 {
41 	if (fModel->IconStoragePath(fLocalIconStoragePath) != B_OK) {
42 		printf("[%s] unable to obtain the path for storing icons\n", Name());
43 		fLocalIconStoragePath.Unset();
44 		fLocalIconStore = NULL;
45 	} else {
46 		fLocalIconStore = new LocalIconStore(fLocalIconStoragePath);
47 	}
48 }
49 
50 
51 ServerIconExportUpdateProcess::~ServerIconExportUpdateProcess()
52 {
53 	delete fLocalIconStore;
54 }
55 
56 
57 const char*
58 ServerIconExportUpdateProcess::Name() const
59 {
60 	return "ServerIconExportUpdateProcess";
61 }
62 
63 
64 const char*
65 ServerIconExportUpdateProcess::Description() const
66 {
67 	return B_TRANSLATE("Synchronizing icons");
68 }
69 
70 
71 status_t
72 ServerIconExportUpdateProcess::RunInternal()
73 {
74 	status_t result = B_OK;
75 
76 	if (NULL == fLocalIconStore || fLocalIconStoragePath.Path() == NULL)
77 		result = B_ERROR;
78 
79 	if (IsSuccess(result) && HasOption(SERVER_PROCESS_DROP_CACHE)) {
80 		result = StorageUtils::RemoveDirectoryContents(fLocalIconStoragePath);
81 	}
82 
83 	if (result == B_OK) {
84 		bool hasData;
85 
86 		result = HasLocalData(&hasData);
87 
88 		if (result == B_OK && ShouldAttemptNetworkDownload(hasData))
89 			result = _DownloadAndUnpack();
90 
91 		if (IsSuccess(result)) {
92 			status_t hasDataResult = HasLocalData(&hasData);
93 
94 			if (hasDataResult == B_OK && !hasData)
95 				result = HD_ERR_NO_DATA;
96 		}
97 	}
98 
99 	if (IsSuccess(result) && !WasStopped())
100 		result = Populate();
101 
102 	return result;
103 }
104 
105 
106 status_t
107 ServerIconExportUpdateProcess::Populate()
108 {
109 	BStopWatch watch("ServerIconExportUpdateProcess::Populate", true);
110 	DepotList depots = fModel->Depots();
111 	status_t result = B_OK;
112 
113 	{
114 		AutoLocker<BLocker> locker(fModel->Lock());
115 		depots = fModel->Depots();
116 	}
117 
118 	if (Logger::IsDebugEnabled()) {
119 		printf("[%s] will populate icons for %" B_PRId32 " depots\n", Name(),
120 			depots.CountItems());
121 	}
122 
123 	for (int32 i = 0;
124 		(i < depots.CountItems()) && !WasStopped() && (result == B_OK);
125 		i++) {
126 		AutoLocker<BLocker> locker(fModel->Lock());
127 		DepotInfo depotInfo = depots.ItemAtFast(i);
128 		result = PopulateForDepot(depotInfo);
129 	}
130 
131 	if (Logger::IsInfoEnabled()) {
132 		double secs = watch.ElapsedTime() / 1000000.0;
133 		printf("[%s] did populate %" B_PRId32 " packages' icons (%6.3g secs)\n",
134 			Name(), fCountIconsSet, secs);
135 	}
136 
137 	return result;
138 }
139 
140 
141 /*! This method assumes that the model lock has been acquired */
142 
143 status_t
144 ServerIconExportUpdateProcess::PopulateForDepot(const DepotInfo& depot)
145 {
146 	printf("[%s] will populate icons for depot [%s]\n",
147 		Name(), depot.Name().String());
148 	status_t result = B_OK;
149 	PackageList packages = depot.Packages();
150 	for(int32 j = 0;
151 		(j < packages.CountItems()) && !WasStopped() && (result == B_OK);
152 		j++) {
153 		const PackageInfoRef& packageInfoRef = packages.ItemAtFast(j);
154 		result = PopulateForPkg(packageInfoRef);
155 
156 		if (result == B_FILE_NOT_FOUND)
157 			result = B_OK;
158 	}
159 
160 	return result;
161 }
162 
163 
164 /*! This method assumes that the model lock has been acquired */
165 
166 status_t
167 ServerIconExportUpdateProcess::PopulateForPkg(const PackageInfoRef& package)
168 {
169 	BPath bestIconPath;
170 
171 	if ( fLocalIconStore->TryFindIconPath(
172 		package->Name(), bestIconPath) == B_OK) {
173 
174 		BFile bestIconFile(bestIconPath.Path(), O_RDONLY);
175 		BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(bestIconFile), true);
176 		package->SetIcon(bitmapRef);
177 
178 		if (Logger::IsDebugEnabled()) {
179 			printf("[%s] have set the package icon for [%s] from [%s]\n",
180 				Name(), package->Name().String(), bestIconPath.Path());
181 		}
182 
183 		fCountIconsSet++;
184 
185 		return B_OK;
186 	}
187 
188 	if (Logger::IsDebugEnabled()) {
189 		printf("[%s] did not set the package icon for [%s]; no data\n",
190 			Name(), package->Name().String());
191 	}
192 
193 	return B_FILE_NOT_FOUND;
194 }
195 
196 
197 status_t
198 ServerIconExportUpdateProcess::_DownloadAndUnpack()
199 {
200 	BPath tarGzFilePath(tmpnam(NULL));
201 	status_t result = B_OK;
202 
203 	printf("[%s] will start fetching icons\n", Name());
204 
205 	result = _Download(tarGzFilePath);
206 
207 	switch (result) {
208 		case HD_ERR_NOT_MODIFIED:
209 			printf("[%s] icons not modified - will use existing\n", Name());
210 			return result;
211 			break;
212 		case B_OK:
213 			return _Unpack(tarGzFilePath);
214 		default:
215 			return (_HandleDownloadFailure() != B_OK) ? result : B_OK;
216 	}
217 }
218 
219 
220 /*! if the download failed, but there are existing icons in place to use
221     then use those icons.  To detect the existing files, look for the
222     icons' meta-info file.
223 */
224 
225 status_t
226 ServerIconExportUpdateProcess::_HandleDownloadFailure()
227 {
228 	bool hasData;
229 	status_t result = HasLocalData(&hasData);
230 
231 	if (result == B_OK) {
232 		if (hasData) {
233 			printf("[%s] failed to update data, but have old data anyway "
234 				"so will carry on with that\n", Name());
235 		} else {
236 			printf("[%s] failed to obtain data\n", Name());
237 			result = HD_ERR_NO_DATA;
238 		}
239 	} else {
240 		printf("[%s] unable to detect if there is local data\n", Name());
241 	}
242 
243 	return result;
244 }
245 
246 
247 /*! The tar-ball data of icons has arrived and so old data needs to be purged
248     to make way for the new data and the new data needs to be unpacked.
249 */
250 
251 status_t
252 ServerIconExportUpdateProcess::_Unpack(BPath& tarGzFilePath)
253 {
254 	status_t result;
255 	printf("[%s] delete any existing stored data\n", Name());
256 	StorageUtils::RemoveDirectoryContents(fLocalIconStoragePath);
257 
258 	BFile *tarGzFile = new BFile(tarGzFilePath.Path(), O_RDONLY);
259 	BDataIO* tarIn;
260 
261 	BZlibDecompressionParameters* zlibDecompressionParameters
262 		= new BZlibDecompressionParameters();
263 
264 	result = BZlibCompressionAlgorithm()
265 		.CreateDecompressingInputStream(tarGzFile,
266 			zlibDecompressionParameters, tarIn);
267 
268 	if (result == B_OK) {
269 		BStopWatch watch(
270 			"ServerIconExportUpdateProcess::DownloadAndUnpack_Unpack",
271 			true);
272 
273 		result = TarArchiveService::Unpack(*tarIn,
274 			fLocalIconStoragePath, NULL);
275 
276 		if (result == B_OK) {
277 			double secs = watch.ElapsedTime() / 1000000.0;
278 			printf("[%s] did unpack icon tgz in (%6.3g secs)\n", Name(),
279 				secs);
280 
281 			if (0 != remove(tarGzFilePath.Path())) {
282 				printf("unable to delete the temporary tgz path; %s\n",
283 					tarGzFilePath.Path());
284 			}
285 		}
286 	}
287 
288 	delete tarGzFile;
289 	printf("[%s] did complete unpacking icons\n", Name());
290 	return result;
291 }
292 
293 
294 status_t
295 ServerIconExportUpdateProcess::HasLocalData(bool* result) const
296 {
297 	BPath path;
298 	status_t status = GetStandardMetaDataPath(path);
299 
300 	if (status != B_OK)
301 		return status;
302 
303 	off_t size;
304 
305 	status = StorageUtils::ExistsObject(path, result, NULL, &size);
306 
307 	if (status == B_OK && size == 0)
308 		*result = false;
309 
310 	return status;
311 }
312 
313 
314 status_t
315 ServerIconExportUpdateProcess::GetStandardMetaDataPath(BPath& path) const
316 {
317 	status_t result = fModel->IconStoragePath(path);
318 
319 	if (result != B_OK)
320 		return result;
321 
322 	path.Append("hicn/info.json");
323 	return B_OK;
324 }
325 
326 
327 void
328 ServerIconExportUpdateProcess::GetStandardMetaDataJsonPath(
329 	BString& jsonPath) const
330 {
331 		// the "$" here indicates that the data is at the top level.
332 	jsonPath.SetTo("$");
333 }
334 
335 
336 status_t
337 ServerIconExportUpdateProcess::_Download(BPath& tarGzFilePath)
338 {
339 	return DownloadToLocalFileAtomically(tarGzFilePath,
340 		ServerSettings::CreateFullUrl("/__pkgicon/all.tar.gz"));
341 }
342 
343 
344 
345