xref: /haiku/src/apps/haikudepot/server/ServerIconExportUpdateProcess.cpp (revision 60a6f1d5d7a8715cd3897dd0b626f2e4a64984a8)
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 "ServerIconExportUpdateProcess.h"
8 
9 #include <sys/stat.h>
10 #include <time.h>
11 
12 #include <AutoLocker.h>
13 #include <Catalog.h>
14 #include <FileIO.h>
15 #include <support/StopWatch.h>
16 #include <support/ZlibCompressionAlgorithm.h>
17 
18 #include "HaikuDepotConstants.h"
19 #include "Logger.h"
20 #include "ServerHelper.h"
21 #include "ServerSettings.h"
22 #include "StorageUtils.h"
23 #include "TarArchiveService.h"
24 
25 
26 #undef B_TRANSLATION_CONTEXT
27 #define B_TRANSLATION_CONTEXT "ServerIconExportUpdateProcess"
28 
29 
30 /*! This constructor will locate the cached data in a standardized location */
31 
32 ServerIconExportUpdateProcess::ServerIconExportUpdateProcess(
33 	Model* model,
34 	uint32 serverProcessOptions)
35 	:
36 	AbstractServerProcess(serverProcessOptions),
37 	fModel(model),
38 	fCountIconsSet(0)
39 {
40 	AutoLocker<BLocker> locker(fModel->Lock());
41 	if (fModel->IconStoragePath(fLocalIconStoragePath) != B_OK) {
42 		HDINFO("[%s] unable to obtain the path for storing icons", 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 	HDDEBUG("[%s] will populate icons for %" B_PRId32 " depots", Name(),
119 		depots.CountItems())
120 
121 	for (int32 i = 0;
122 		(i < depots.CountItems()) && !WasStopped() && (result == B_OK);
123 		i++) {
124 		AutoLocker<BLocker> locker(fModel->Lock());
125 		DepotInfo depotInfo = depots.ItemAtFast(i);
126 		result = PopulateForDepot(depotInfo);
127 	}
128 
129 	if (Logger::IsInfoEnabled()) {
130 		double secs = watch.ElapsedTime() / 1000000.0;
131 		HDINFO("[%s] did populate %" B_PRId32 " packages' icons (%6.3g secs)",
132 			Name(), fCountIconsSet, secs)
133 	}
134 
135 	return result;
136 }
137 
138 
139 /*! This method assumes that the model lock has been acquired */
140 
141 status_t
142 ServerIconExportUpdateProcess::PopulateForDepot(const DepotInfo& depot)
143 {
144 	HDINFO("[%s] will populate icons for depot [%s]",
145 		Name(), depot.Name().String())
146 	status_t result = B_OK;
147 	const PackageList& packages = depot.Packages();
148 	for(int32 j = 0;
149 		(j < packages.CountItems()) && !WasStopped() && (result == B_OK);
150 		j++) {
151 		const PackageInfoRef& packageInfoRef = packages.ItemAtFast(j);
152 		result = PopulateForPkg(packageInfoRef);
153 
154 		if (result == B_FILE_NOT_FOUND)
155 			result = B_OK;
156 	}
157 
158 	return result;
159 }
160 
161 
162 /*! This method assumes that the model lock has been acquired */
163 
164 status_t
165 ServerIconExportUpdateProcess::PopulateForPkg(const PackageInfoRef& package)
166 {
167 	BPath bestIconPath;
168 
169 	if ( fLocalIconStore->TryFindIconPath(
170 		package->Name(), bestIconPath) == B_OK) {
171 
172 		BFile bestIconFile(bestIconPath.Path(), O_RDONLY);
173 		BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(bestIconFile), true);
174 		package->SetIcon(bitmapRef);
175 
176 		HDDEBUG("[%s] have set the package icon for [%s] from [%s]",
177 			Name(), package->Name().String(), bestIconPath.Path())
178 
179 		fCountIconsSet++;
180 
181 		return B_OK;
182 	}
183 
184 	HDDEBUG("[%s] did not set the package icon for [%s]; no data",
185 		Name(), package->Name().String())
186 
187 	return B_FILE_NOT_FOUND;
188 }
189 
190 
191 status_t
192 ServerIconExportUpdateProcess::_DownloadAndUnpack()
193 {
194 	BPath tarGzFilePath(tmpnam(NULL));
195 	status_t result = B_OK;
196 
197 	HDINFO("[%s] will start fetching icons", Name())
198 
199 	result = _Download(tarGzFilePath);
200 
201 	switch (result) {
202 		case HD_ERR_NOT_MODIFIED:
203 			HDINFO("[%s] icons not modified - will use existing", Name())
204 			return result;
205 			break;
206 		case B_OK:
207 			return _Unpack(tarGzFilePath);
208 		default:
209 			return (_HandleDownloadFailure() != B_OK) ? result : B_OK;
210 	}
211 }
212 
213 
214 /*! if the download failed, but there are existing icons in place to use
215     then use those icons.  To detect the existing files, look for the
216     icons' meta-info file.
217 */
218 
219 status_t
220 ServerIconExportUpdateProcess::_HandleDownloadFailure()
221 {
222 	bool hasData;
223 	status_t result = HasLocalData(&hasData);
224 
225 	if (result == B_OK) {
226 		if (hasData) {
227 			HDINFO("[%s] failed to update data, but have old data anyway "
228 				"so will carry on with that", Name())
229 		} else {
230 			HDINFO("[%s] failed to obtain data", Name())
231 			result = HD_ERR_NO_DATA;
232 		}
233 	} else {
234 		HDERROR("[%s] unable to detect if there is local data\n", Name())
235 	}
236 
237 	return result;
238 }
239 
240 
241 /*! The tar-ball data of icons has arrived and so old data needs to be purged
242     to make way for the new data and the new data needs to be unpacked.
243 */
244 
245 status_t
246 ServerIconExportUpdateProcess::_Unpack(BPath& tarGzFilePath)
247 {
248 	status_t result;
249 	HDINFO("[%s] delete any existing stored data", Name())
250 	StorageUtils::RemoveDirectoryContents(fLocalIconStoragePath);
251 
252 	BFile *tarGzFile = new BFile(tarGzFilePath.Path(), O_RDONLY);
253 	BDataIO* tarIn;
254 
255 	BZlibDecompressionParameters* zlibDecompressionParameters
256 		= new BZlibDecompressionParameters();
257 
258 	result = BZlibCompressionAlgorithm()
259 		.CreateDecompressingInputStream(tarGzFile,
260 			zlibDecompressionParameters, tarIn);
261 
262 	if (result == B_OK) {
263 		BStopWatch watch(
264 			"ServerIconExportUpdateProcess::DownloadAndUnpack_Unpack",
265 			true);
266 
267 		result = TarArchiveService::Unpack(*tarIn,
268 			fLocalIconStoragePath, NULL);
269 
270 		if (result == B_OK) {
271 			double secs = watch.ElapsedTime() / 1000000.0;
272 			HDINFO("[%s] did unpack icon tgz in (%6.3g secs)", Name(), secs)
273 
274 			if (0 != remove(tarGzFilePath.Path())) {
275 				HDERROR("[%s] unable to delete the temporary tgz path; %s",
276 					Name(), tarGzFilePath.Path())
277 			}
278 		}
279 	}
280 
281 	delete tarGzFile;
282 	HDINFO("[%s] did complete unpacking icons", Name())
283 	return result;
284 }
285 
286 
287 status_t
288 ServerIconExportUpdateProcess::HasLocalData(bool* result) const
289 {
290 	BPath path;
291 	status_t status = GetStandardMetaDataPath(path);
292 
293 	if (status != B_OK)
294 		return status;
295 
296 	off_t size;
297 
298 	status = StorageUtils::ExistsObject(path, result, NULL, &size);
299 
300 	if (status == B_OK && size == 0)
301 		*result = false;
302 
303 	return status;
304 }
305 
306 
307 status_t
308 ServerIconExportUpdateProcess::GetStandardMetaDataPath(BPath& path) const
309 {
310 	status_t result = fModel->IconStoragePath(path);
311 
312 	if (result != B_OK)
313 		return result;
314 
315 	path.Append("hicn/info.json");
316 	return B_OK;
317 }
318 
319 
320 void
321 ServerIconExportUpdateProcess::GetStandardMetaDataJsonPath(
322 	BString& jsonPath) const
323 {
324 		// the "$" here indicates that the data is at the top level.
325 	jsonPath.SetTo("$");
326 }
327 
328 
329 status_t
330 ServerIconExportUpdateProcess::_Download(BPath& tarGzFilePath)
331 {
332 	return DownloadToLocalFileAtomically(tarGzFilePath,
333 		ServerSettings::CreateFullUrl("/__pkgicon/all.tar.gz"));
334 }
335 
336 
337 
338