1/*
2 * Copyright 2011, 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 <Path.h>
18#include <String.h>
19#include <StringList.h>
20
21#include <package/PackageInfo.h>
22#include <package/PackageInfoContentHandler.h>
23#include <package/PackageInfoSet.h>
24#include <package/RepositoryCache.h>
25#include <package/RepositoryConfig.h>
26
27#include <package/hpkg/PackageReader.h>
28
29
30namespace BPackageKit {
31
32
33using namespace BHPKG;
34
35
36BPackageRoster::BPackageRoster()
37{
38}
39
40
41BPackageRoster::~BPackageRoster()
42{
43}
44
45
46status_t
47BPackageRoster::GetCommonRepositoryConfigPath(BPath* path, bool create) const
48{
49	return _GetRepositoryPath(path, create, B_COMMON_SETTINGS_DIRECTORY);
50}
51
52
53status_t
54BPackageRoster::GetUserRepositoryConfigPath(BPath* path, bool create) const
55{
56	return _GetRepositoryPath(path, create, B_USER_SETTINGS_DIRECTORY);
57}
58
59
60status_t
61BPackageRoster::GetCommonRepositoryCachePath(BPath* path, bool create) const
62{
63	return _GetRepositoryPath(path, create, B_COMMON_CACHE_DIRECTORY);
64}
65
66
67status_t
68BPackageRoster::GetUserRepositoryCachePath(BPath* path, bool create) const
69{
70	return _GetRepositoryPath(path, create, B_USER_CACHE_DIRECTORY);
71}
72
73
74status_t
75BPackageRoster::VisitCommonRepositoryConfigs(BRepositoryConfigVisitor& visitor)
76{
77	BPath commonRepositoryConfigPath;
78	status_t result
79		= GetCommonRepositoryConfigPath(&commonRepositoryConfigPath);
80	if (result != B_OK)
81		return result;
82
83	return _VisitRepositoryConfigs(commonRepositoryConfigPath, visitor);
84}
85
86
87status_t
88BPackageRoster::VisitUserRepositoryConfigs(BRepositoryConfigVisitor& visitor)
89{
90	BPath userRepositoryConfigPath;
91	status_t result = GetUserRepositoryConfigPath(&userRepositoryConfigPath);
92	if (result != B_OK)
93		return result;
94
95	return _VisitRepositoryConfigs(userRepositoryConfigPath, visitor);
96}
97
98
99status_t
100BPackageRoster::GetRepositoryNames(BStringList& names)
101{
102	struct RepositoryNameCollector : public BRepositoryConfigVisitor {
103		RepositoryNameCollector(BStringList& _names)
104			: names(_names)
105		{
106		}
107		status_t operator()(const BEntry& entry)
108		{
109			char name[B_FILE_NAME_LENGTH];
110			status_t result = entry.GetName(name);
111			if (result != B_OK)
112				return result;
113			int32 count = names.CountStrings();
114			for (int i = 0; i < count; ++i) {
115				if (names.StringAt(i).Compare(name) == 0)
116					return B_OK;
117			}
118			names.Add(name);
119			return B_OK;
120		}
121		BStringList& names;
122	};
123	RepositoryNameCollector repositoryNameCollector(names);
124	status_t result = VisitUserRepositoryConfigs(repositoryNameCollector);
125	if (result != B_OK)
126		return result;
127
128	return VisitCommonRepositoryConfigs(repositoryNameCollector);
129}
130
131
132status_t
133BPackageRoster::GetRepositoryCache(const BString& name,
134	BRepositoryCache* repositoryCache)
135{
136	if (repositoryCache == NULL)
137		return B_BAD_VALUE;
138
139	// user path has higher precedence than common path
140	BPath path;
141	status_t result = GetUserRepositoryCachePath(&path);
142	if (result != B_OK)
143		return result;
144	path.Append(name.String());
145
146	BEntry repoCacheEntry(path.Path());
147	if (repoCacheEntry.Exists())
148		return repositoryCache->SetTo(repoCacheEntry);
149
150	if ((result = GetCommonRepositoryCachePath(&path, true)) != B_OK)
151		return result;
152	path.Append(name.String());
153
154	repoCacheEntry.SetTo(path.Path());
155	return repositoryCache->SetTo(repoCacheEntry);
156}
157
158
159status_t
160BPackageRoster::GetRepositoryConfig(const BString& name,
161	BRepositoryConfig* repositoryConfig)
162{
163	if (repositoryConfig == NULL)
164		return B_BAD_VALUE;
165
166	// user path has higher precedence than common path
167	BPath path;
168	status_t result = GetUserRepositoryConfigPath(&path);
169	if (result != B_OK)
170		return result;
171	path.Append(name.String());
172
173	BEntry repoConfigEntry(path.Path());
174	if (repoConfigEntry.Exists())
175		return repositoryConfig->SetTo(repoConfigEntry);
176
177	if ((result = GetCommonRepositoryConfigPath(&path, true)) != B_OK)
178		return result;
179	path.Append(name.String());
180
181	repoConfigEntry.SetTo(path.Path());
182	return repositoryConfig->SetTo(repoConfigEntry);
183}
184
185
186status_t
187BPackageRoster::GetActivePackages(BPackageInstallationLocation location,
188	BPackageInfoSet& packageInfos)
189{
190// This method makes sense only on an installed Haiku, but not for the build
191// tools.
192#if defined(__HAIKU__) && !defined(HAIKU_HOST_PLATFORM_HAIKU)
193	// check the given location
194	directory_which packagesDirectory;
195	switch (location) {
196		case B_PACKAGE_INSTALLATION_LOCATION_SYSTEM:
197			packagesDirectory = B_SYSTEM_PACKAGES_DIRECTORY;
198			break;
199		case B_PACKAGE_INSTALLATION_LOCATION_COMMON:
200			packagesDirectory = B_COMMON_PACKAGES_DIRECTORY;
201			break;
202		case B_PACKAGE_INSTALLATION_LOCATION_HOME:
203			packagesDirectory = B_USER_PACKAGES_DIRECTORY;
204			break;
205		default:
206			return B_BAD_VALUE;
207	}
208
209	// find the package links directory
210	BPath packageLinksPath;
211	status_t error = find_directory(B_PACKAGE_LINKS_DIRECTORY,
212		&packageLinksPath);
213	if (error != B_OK)
214		return error;
215
216	// find and open the packages directory
217	BPath packagesDirPath;
218	error = find_directory(packagesDirectory, &packagesDirPath);
219	if (error != B_OK)
220		return error;
221
222	BDirectory directory;
223	error = directory.SetTo(packagesDirPath.Path());
224	if (error != B_OK)
225		return error;
226
227	// TODO: Implement that correctly be reading the activation files/directory!
228
229	// iterate through the packages
230	char buffer[sizeof(dirent) + B_FILE_NAME_LENGTH];
231	dirent* entry = (dirent*)&buffer;
232	while (directory.GetNextDirents(entry, sizeof(buffer), 1) == 1) {
233		if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
234			continue;
235
236		// get the full package file path
237		BPath packagePath;
238		error = packagePath.SetTo(packagesDirPath.Path(), entry->d_name);
239		if (error != B_OK)
240			continue;
241
242		// read the package info from the file
243		BPackageReader packageReader(NULL);
244		error = packageReader.Init(packagePath.Path());
245		if (error != B_OK)
246			continue;
247
248		BPackageInfo info;
249		BPackageInfoContentHandler handler(info);
250		error = packageReader.ParseContent(&handler);
251		if (error != B_OK || info.InitCheck() != B_OK)
252			continue;
253
254		// check whether the package is really active by verifying that a
255		// package link exists for it
256		BString packageLinkName(info.Name());
257		packageLinkName << '-' << info.Version().ToString();
258		BPath packageLinkPath;
259		struct stat st;
260		if (packageLinkPath.SetTo(packageLinksPath.Path(), packageLinkName)
261				!= B_OK
262			|| lstat(packageLinkPath.Path(), &st) != 0) {
263			continue;
264		}
265
266		// add the info
267		error = packageInfos.AddInfo(info);
268		if (error != B_OK)
269			return error;
270	}
271
272	return B_OK;
273#else
274	return B_NOT_SUPPORTED;
275#endif
276}
277
278
279status_t
280BPackageRoster::_GetRepositoryPath(BPath* path, bool create,
281	directory_which whichDir) const
282{
283	if (path == NULL)
284		return B_BAD_VALUE;
285
286	status_t result = find_directory(whichDir, path);
287	if (result != B_OK)
288		return result;
289	if ((result = path->Append("package-repositories")) != B_OK)
290		return result;
291
292	if (create) {
293		BEntry entry(path->Path(), true);
294		if (!entry.Exists()) {
295			if (mkdir(path->Path(), 0755) != 0)
296				return errno;
297		}
298	}
299
300	return B_OK;
301}
302
303
304status_t
305BPackageRoster::_VisitRepositoryConfigs(const BPath& path,
306	BRepositoryConfigVisitor& visitor)
307{
308	BDirectory directory(path.Path());
309	status_t result = directory.InitCheck();
310	if (result == B_ENTRY_NOT_FOUND)
311		return B_OK;
312	if (result != B_OK)
313		return result;
314
315	BEntry entry;
316	while (directory.GetNextEntry(&entry, true) == B_OK) {
317		if ((result = visitor(entry)) != B_OK)
318			return result;
319	}
320
321	return B_OK;
322}
323
324
325}	// namespace BPackageKit
326