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