1/*
2 * Copyright 2011-2014, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Oliver Tappe <zooey@hirschkaefer.de>
7 */
8
9
10#include <package/PackageRoster.h>
11
12#include <errno.h>
13#include <sys/stat.h>
14
15#include <Directory.h>
16#include <Entry.h>
17#include <Messenger.h>
18#include <Path.h>
19#include <String.h>
20#include <StringList.h>
21
22#include <package/InstallationLocationInfo.h>
23#include <package/PackageInfo.h>
24#include <package/PackageInfoContentHandler.h>
25#include <package/PackageInfoSet.h>
26#include <package/RepositoryCache.h>
27#include <package/RepositoryConfig.h>
28
29#include <package/hpkg/PackageReader.h>
30
31
32#if defined(__HAIKU__) && !defined(HAIKU_HOST_PLATFORM_HAIKU)
33#	include <package/DaemonClient.h>
34#	include <RegistrarDefs.h>
35#	include <RosterPrivate.h>
36#endif
37
38
39namespace BPackageKit {
40
41
42using namespace BHPKG;
43
44
45BPackageRoster::BPackageRoster()
46{
47}
48
49
50BPackageRoster::~BPackageRoster()
51{
52}
53
54
55bool
56BPackageRoster::IsRebootNeeded()
57{
58	BInstallationLocationInfo info;
59
60	// We get information on the system package installation location.
61	// If we fail, we just have to assume a reboot is not needed.
62	if (GetInstallationLocationInfo(B_PACKAGE_INSTALLATION_LOCATION_SYSTEM,
63		info) != B_OK)
64		return false;
65
66	// CurrentlyActivePackageInfos() will return 0 if no packages need to be
67	// activated with a reboot. Otherwise, the method will return the total
68	// number of packages in the system package directory.
69	if (info.CurrentlyActivePackageInfos().CountInfos() != 0)
70		return true;
71
72	return false;
73}
74
75
76status_t
77BPackageRoster::GetCommonRepositoryConfigPath(BPath* path, bool create) const
78{
79	return _GetRepositoryPath(path, create, B_SYSTEM_SETTINGS_DIRECTORY);
80}
81
82
83status_t
84BPackageRoster::GetUserRepositoryConfigPath(BPath* path, bool create) const
85{
86	return _GetRepositoryPath(path, create, B_USER_SETTINGS_DIRECTORY);
87}
88
89
90status_t
91BPackageRoster::GetCommonRepositoryCachePath(BPath* path, bool create) const
92{
93	return _GetRepositoryPath(path, create, B_SYSTEM_CACHE_DIRECTORY);
94}
95
96
97status_t
98BPackageRoster::GetUserRepositoryCachePath(BPath* path, bool create) const
99{
100	return _GetRepositoryPath(path, create, B_USER_CACHE_DIRECTORY);
101}
102
103
104status_t
105BPackageRoster::VisitCommonRepositoryConfigs(BRepositoryConfigVisitor& visitor)
106{
107	BPath commonRepositoryConfigPath;
108	status_t result
109		= GetCommonRepositoryConfigPath(&commonRepositoryConfigPath);
110	if (result != B_OK)
111		return result;
112
113	return _VisitRepositoryConfigs(commonRepositoryConfigPath, visitor);
114}
115
116
117status_t
118BPackageRoster::VisitUserRepositoryConfigs(BRepositoryConfigVisitor& visitor)
119{
120	BPath userRepositoryConfigPath;
121	status_t result = GetUserRepositoryConfigPath(&userRepositoryConfigPath);
122	if (result != B_OK)
123		return result;
124
125	return _VisitRepositoryConfigs(userRepositoryConfigPath, visitor);
126}
127
128
129status_t
130BPackageRoster::GetRepositoryNames(BStringList& names)
131{
132	struct RepositoryNameCollector : public BRepositoryConfigVisitor {
133		RepositoryNameCollector(BStringList& _names)
134			: names(_names)
135		{
136		}
137		status_t operator()(const BEntry& entry)
138		{
139			char name[B_FILE_NAME_LENGTH];
140			status_t result = entry.GetName(name);
141			if (result != B_OK)
142				return result;
143			int32 count = names.CountStrings();
144			for (int i = 0; i < count; ++i) {
145				if (names.StringAt(i).Compare(name) == 0)
146					return B_OK;
147			}
148			names.Add(name);
149			return B_OK;
150		}
151		BStringList& names;
152	};
153	RepositoryNameCollector repositoryNameCollector(names);
154	status_t result = VisitUserRepositoryConfigs(repositoryNameCollector);
155	if (result != B_OK)
156		return result;
157
158	return VisitCommonRepositoryConfigs(repositoryNameCollector);
159}
160
161
162status_t
163BPackageRoster::GetRepositoryCache(const BString& name,
164	BRepositoryCache* repositoryCache)
165{
166	if (repositoryCache == NULL)
167		return B_BAD_VALUE;
168
169	// user path has higher precedence than common path
170	BPath path;
171	status_t result = GetUserRepositoryCachePath(&path);
172	if (result != B_OK)
173		return result;
174	path.Append(name.String());
175
176	BEntry repoCacheEntry(path.Path());
177	if (repoCacheEntry.Exists())
178		return repositoryCache->SetTo(repoCacheEntry);
179
180	if ((result = GetCommonRepositoryCachePath(&path, true)) != B_OK)
181		return result;
182	path.Append(name.String());
183
184	result = repoCacheEntry.SetTo(path.Path());
185	if (result != B_OK)
186		return result;
187	return repositoryCache->SetTo(repoCacheEntry);
188}
189
190
191status_t
192BPackageRoster::GetRepositoryConfig(const BString& name,
193	BRepositoryConfig* repositoryConfig)
194{
195	if (repositoryConfig == NULL)
196		return B_BAD_VALUE;
197
198	// user path has higher precedence than common path
199	BPath path;
200	status_t result = GetUserRepositoryConfigPath(&path);
201	if (result != B_OK)
202		return result;
203	path.Append(name.String());
204
205	BEntry repoConfigEntry(path.Path());
206	if (repoConfigEntry.Exists())
207		return repositoryConfig->SetTo(repoConfigEntry);
208
209	if ((result = GetCommonRepositoryConfigPath(&path, true)) != B_OK)
210		return result;
211	path.Append(name.String());
212
213	result = repoConfigEntry.SetTo(path.Path());
214	if (result != B_OK)
215		return result;
216	return repositoryConfig->SetTo(repoConfigEntry);
217}
218
219
220status_t
221BPackageRoster::GetInstallationLocationInfo(
222	BPackageInstallationLocation location, BInstallationLocationInfo& _info)
223{
224// This method makes sense only on an installed Haiku, but not for the build
225// tools.
226#if defined(__HAIKU__) && !defined(HAIKU_HOST_PLATFORM_HAIKU)
227	return BPackageKit::BPrivate::BDaemonClient().GetInstallationLocationInfo(
228		location, _info);
229#else
230	return B_NOT_SUPPORTED;
231#endif
232}
233
234
235status_t
236BPackageRoster::GetActivePackages(BPackageInstallationLocation location,
237	BPackageInfoSet& packageInfos)
238{
239// This method makes sense only on an installed Haiku, but not for the build
240// tools.
241#if defined(__HAIKU__) && !defined(HAIKU_HOST_PLATFORM_HAIKU)
242	BInstallationLocationInfo info;
243	status_t error = GetInstallationLocationInfo(location, info);
244	if (error != B_OK)
245		return error;
246
247	packageInfos = info.LatestActivePackageInfos();
248	return B_OK;
249#else
250	return B_NOT_SUPPORTED;
251#endif
252}
253
254
255status_t
256BPackageRoster::IsPackageActive(BPackageInstallationLocation location,
257	const BPackageInfo info, bool* active)
258{
259// This method makes sense only on an installed Haiku, but not for the build
260// tools.
261#if defined(__HAIKU__) && !defined(HAIKU_HOST_PLATFORM_HAIKU)
262	BPackageInfoSet packageInfos;
263	status_t error = GetActivePackages(location, packageInfos);
264	if (error != B_OK)
265		return error;
266
267	BRepositoryCache::Iterator it = packageInfos.GetIterator();
268	while (const BPackageInfo* packageInfo = it.Next()) {
269		if (info.Name() == packageInfo->Name() &&
270			info.Version().Compare(packageInfo->Version()) == 0) {
271			*active = true;
272			break;
273		}
274	}
275
276	return B_OK;
277#else
278	return B_NOT_SUPPORTED;
279#endif
280}
281
282
283status_t
284BPackageRoster::StartWatching(const BMessenger& target, uint32 eventMask)
285{
286// This method makes sense only on an installed Haiku, but not for the build
287// tools.
288#if defined(__HAIKU__) && !defined(HAIKU_HOST_PLATFORM_HAIKU)
289	// compose the registrar request
290	BMessage request(::BPrivate::B_REG_PACKAGE_START_WATCHING);
291	status_t error;
292	if ((error = request.AddMessenger("target", target)) != B_OK
293		|| (error = request.AddUInt32("events", eventMask)) != B_OK) {
294		return error;
295	}
296
297	// send it
298	BMessage reply;
299	error = BRoster::Private().SendTo(&request, &reply, false);
300	if (error != B_OK)
301		return error;
302
303	// get result
304	if (reply.what != ::BPrivate::B_REG_SUCCESS) {
305		int32 result;
306		if (reply.FindInt32("error", &result) != B_OK)
307			result = B_ERROR;
308		return (status_t)error;
309	}
310
311	return B_OK;
312#else
313	return B_NOT_SUPPORTED;
314#endif
315}
316
317
318status_t
319BPackageRoster::StopWatching(const BMessenger& target)
320{
321// This method makes sense only on an installed Haiku, but not for the build
322// tools.
323#if defined(__HAIKU__) && !defined(HAIKU_HOST_PLATFORM_HAIKU)
324	// compose the registrar request
325	BMessage request(::BPrivate::B_REG_PACKAGE_STOP_WATCHING);
326	status_t error = request.AddMessenger("target", target);
327	if (error  != B_OK)
328		return error;
329
330	// send it
331	BMessage reply;
332	error = BRoster::Private().SendTo(&request, &reply, false);
333	if (error != B_OK)
334		return error;
335
336	// get result
337	if (reply.what != ::BPrivate::B_REG_SUCCESS) {
338		int32 result;
339		if (reply.FindInt32("error", &result) != B_OK)
340			result = B_ERROR;
341		return (status_t)error;
342	}
343
344	return B_OK;
345#else
346	return B_NOT_SUPPORTED;
347#endif
348}
349
350
351status_t
352BPackageRoster::_GetRepositoryPath(BPath* path, bool create,
353	directory_which whichDir) const
354{
355	if (path == NULL)
356		return B_BAD_VALUE;
357
358	status_t result = find_directory(whichDir, path);
359	if (result != B_OK)
360		return result;
361	if ((result = path->Append("package-repositories")) != B_OK)
362		return result;
363
364	if (create) {
365		BEntry entry(path->Path(), true);
366		if (!entry.Exists()) {
367			if (mkdir(path->Path(), 0755) != 0)
368				return errno;
369		}
370	}
371
372	return B_OK;
373}
374
375
376status_t
377BPackageRoster::_VisitRepositoryConfigs(const BPath& path,
378	BRepositoryConfigVisitor& visitor)
379{
380	BDirectory directory(path.Path());
381	status_t result = directory.InitCheck();
382	if (result == B_ENTRY_NOT_FOUND)
383		return B_OK;
384	if (result != B_OK)
385		return result;
386
387	BEntry entry;
388	while (directory.GetNextEntry(&entry, true) == B_OK) {
389		if ((result = visitor(entry)) != B_OK)
390			return result;
391	}
392
393	return B_OK;
394}
395
396
397}	// namespace BPackageKit
398