1/*
2 * Copyright 2013-2022, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Ingo Weinhold <ingo_weinhold@gmx.de>
7 *		Stephan A��mus <superstippi@gmx.de>
8 *		Rene Gollent <rene@gollent.com>
9 *		Julian Harnath <julian.harnath@rwth-aachen.de>
10 *		Andrew Lindesay <apl@lindesay.co.nz>
11 *
12 * Note that this file has been re-factored from `PackageManager.cpp` and
13 * authors have been carried across in 2021.
14 */
15
16
17#include "OpenPackageProcess.h"
18
19#include <Catalog.h>
20#include <FindDirectory.h>
21#include <Roster.h>
22
23#include <package/PackageDefs.h>
24#include <package/hpkg/NoErrorOutput.h>
25#include <package/hpkg/PackageContentHandler.h>
26#include <package/hpkg/PackageEntry.h>
27#include <package/hpkg/PackageEntryAttribute.h>
28#include <package/hpkg/PackageInfoAttributeValue.h>
29#include <package/hpkg/PackageReader.h>
30
31#include "Logger.h"
32#include "PackageUtils.h"
33
34#undef B_TRANSLATION_CONTEXT
35#define B_TRANSLATION_CONTEXT "OpenPackageProcess"
36
37using namespace BPackageKit;
38
39using BPackageKit::BHPKG::BNoErrorOutput;
40using BPackageKit::BHPKG::BPackageContentHandler;
41using BPackageKit::BHPKG::BPackageEntry;
42using BPackageKit::BHPKG::BPackageEntryAttribute;
43using BPackageKit::BHPKG::BPackageInfoAttributeValue;
44using BPackageKit::BHPKG::BPackageReader;
45
46// #pragma mark - DeskbarLinkFinder
47
48
49class DeskbarLinkFinder : public BPackageContentHandler {
50public:
51	DeskbarLinkFinder(std::vector<DeskbarLink>& foundLinks)
52		:
53		fDeskbarLinks(foundLinks)
54	{
55	}
56
57	virtual status_t HandleEntry(BPackageEntry* entry)
58	{
59		BString path = MakePath(entry);
60		if (path.FindFirst("data/deskbar/menu") == 0
61				&& entry->SymlinkPath() != NULL) {
62			HDINFO("found deskbar entry: %s -> %s",
63				path.String(), entry->SymlinkPath());
64			fDeskbarLinks.push_back(DeskbarLink(path, entry->SymlinkPath()));
65		}
66		return B_OK;
67	}
68
69	virtual status_t HandleEntryAttribute(BPackageEntry* entry,
70		BPackageEntryAttribute* attribute)
71	{
72		return B_OK;
73	}
74
75	virtual status_t HandleEntryDone(BPackageEntry* entry)
76	{
77		return B_OK;
78	}
79
80	virtual status_t HandlePackageAttribute(
81		const BPackageInfoAttributeValue& value)
82	{
83		return B_OK;
84	}
85
86	virtual void HandleErrorOccurred()
87	{
88	}
89
90	BString MakePath(const BPackageEntry* entry)
91	{
92		BString path;
93		while (entry != NULL) {
94			if (!path.IsEmpty())
95				path.Prepend('/', 1);
96			path.Prepend(entry->Name());
97			entry = entry->Parent();
98		}
99		return path;
100	}
101
102private:
103	std::vector<DeskbarLink>&	fDeskbarLinks;
104};
105
106
107// #pragma mark - OpenPackageProcess
108
109
110OpenPackageProcess::OpenPackageProcess(PackageInfoRef package, Model* model,
111		const DeskbarLink& link)
112	:
113	AbstractPackageProcess(package, model),
114	fDeskbarLink(link)
115{
116	fDescription = _DeriveDescription();
117}
118
119
120OpenPackageProcess::~OpenPackageProcess()
121{
122}
123
124
125const char*
126OpenPackageProcess::Name() const
127{
128	return "OpenPackageProcess";
129}
130
131
132const char*
133OpenPackageProcess::Description() const
134{
135	return fDescription;
136}
137
138
139status_t
140OpenPackageProcess::RunInternal()
141{
142	status_t status;
143	BPath path;
144	if (fDeskbarLink.Link().FindFirst('/') == 0) {
145		status = path.SetTo(fDeskbarLink.Link());
146		HDINFO("trying to launch (absolute link): %s", path.Path());
147	} else {
148		int32 location = InstallLocation();
149		if (location == B_PACKAGE_INSTALLATION_LOCATION_SYSTEM) {
150			status = find_directory(B_SYSTEM_DIRECTORY, &path);
151			if (status != B_OK)
152				return status;
153		} else if (location == B_PACKAGE_INSTALLATION_LOCATION_HOME) {
154			status = find_directory(B_USER_DIRECTORY, &path);
155			if (status != B_OK)
156				return status;
157		} else {
158			return B_ERROR;
159		}
160
161		status = path.Append(fDeskbarLink.Path());
162		if (status == B_OK)
163			status = path.GetParent(&path);
164		if (status == B_OK) {
165			status = path.Append(fDeskbarLink.Link(), true);
166			HDINFO("trying to launch: %s", path.Path());
167		}
168	}
169
170	entry_ref ref;
171	if (status == B_OK)
172		status = get_ref_for_path(path.Path(), &ref);
173
174	if (status == B_OK)
175		status = be_roster->Launch(&ref);
176
177	return status;
178}
179
180
181BString
182OpenPackageProcess::_DeriveDescription()
183{
184	BString target = fDeskbarLink.Link();
185	int32 lastPathSeparator = target.FindLast('/');
186	if (lastPathSeparator > 0 && lastPathSeparator + 1 < target.Length())
187		target.Remove(0, lastPathSeparator + 1);
188
189	BString result = B_TRANSLATE("Opening \"%DeskbarLink%\"");
190	result.ReplaceAll("%DeskbarLink%", target);
191	return result;
192}
193
194
195/*static*/ bool
196OpenPackageProcess::FindAppToLaunch(const PackageInfoRef& package,
197		std::vector<DeskbarLink>& foundLinks)
198{
199	if (!package.IsSet())
200		return false;
201
202	BPath packagePath;
203	if (PackageUtils::DeriveLocalFilePath(package, packagePath) != B_OK) {
204		HDDEBUG("unable to derive local file path for package");
205		return false;
206	}
207
208	BNoErrorOutput errorOutput;
209	BPackageReader reader(&errorOutput);
210
211	status_t status = reader.Init(packagePath.Path());
212	if (status != B_OK) {
213		HDINFO("OpenPackageAction::FindAppToLaunch(): "
214			"failed to init BPackageReader(%s): %s",
215			packagePath.Path(), strerror(status));
216		return false;
217	}
218
219	// Scan package contents for Deskbar links
220	DeskbarLinkFinder contentHandler(foundLinks);
221	status = reader.ParseContent(&contentHandler);
222	if (status != B_OK) {
223		HDINFO("OpenPackageAction::FindAppToLaunch(): "
224			"failed parse package contents (%s): %s",
225			packagePath.Path(), strerror(status));
226		return false;
227	}
228
229	return !foundLinks.empty();
230}
231