1/*
2 * Copyright (c) 2010, Haiku, Inc.
3 * Distributed under the terms of the MIT license.
4 *
5 * Author:
6 *		��ukasz 'Sil2100' Zemczak <sil2100@vexillium.org>
7 */
8
9
10#include "PackageInstall.h"
11
12#include "InstalledPackageInfo.h"
13#include "PackageItem.h"
14#include "PackageView.h"
15
16#include <Alert.h>
17#include <Catalog.h>
18#include <Locale.h>
19#include <stdio.h>
20
21
22#undef B_TRANSLATION_CONTEXT
23#define B_TRANSLATION_CONTEXT "PackageInstall"
24
25
26static int32
27install_function(void *data)
28{
29	// TODO: Inform if already one thread is running
30	PackageInstall *install = static_cast<PackageInstall *>(data);
31	if (data == NULL)
32		return -1;
33
34	install->Install();
35	return 0;
36}
37
38
39PackageInstall::PackageInstall(PackageView *parent)
40	: fParent(parent),
41	fThreadId(-1),
42	fCurrentScript(NULL)
43{
44}
45
46
47PackageInstall::~PackageInstall()
48{
49}
50
51
52status_t
53PackageInstall::Start()
54{
55	status_t ret = B_OK;
56
57	fIdLocker.Lock();
58	if (fThreadId > -1) {
59		ret = B_BUSY;
60	} else {
61		fThreadId = spawn_thread(install_function, "install_package", B_NORMAL_PRIORITY,
62				static_cast<void *>(this));
63		resume_thread(fThreadId);
64	}
65	fIdLocker.Unlock();
66
67	return ret;
68}
69
70
71void
72PackageInstall::Stop()
73{
74	fIdLocker.Lock();
75	if (fThreadId > -1) {
76		kill_thread(fThreadId);
77		fThreadId = -1;
78	}
79	fIdLocker.Unlock();
80
81	fCurrentScriptLocker.Lock();
82	if (fCurrentScript != NULL) {
83		thread_id id = fCurrentScript->GetThreadId();
84		if (id > -1) {
85			fCurrentScript->SetThreadId(-1);
86			kill_thread(id);
87		}
88		fCurrentScript = NULL;
89	}
90	fCurrentScriptLocker.Unlock();
91}
92
93
94void
95PackageInstall::Install()
96{
97	// A message sending wrapper around _Install()
98	uint32 msg = _Install();
99	if (fParent && fParent->Looper())
100		fParent->Looper()->PostMessage(new BMessage(msg), fParent);
101}
102
103
104uint32
105PackageInstall::_Install()
106{
107	PackageInfo *info = fParent->GetPackageInfo();
108	pkg_profile *type = static_cast<pkg_profile *>(info->GetProfile(
109		fParent->GetCurrentType()));
110	uint32 n = type->items.CountItems(), m = info->GetScriptCount();
111
112	PackageStatus *progress = fParent->GetStatusWindow();
113	progress->Reset(n + m + 5);
114
115	progress->StageStep(1, B_TRANSLATE("Preparing package"));
116
117	InstalledPackageInfo packageInfo(info->GetName(), info->GetVersion());
118
119	status_t err = packageInfo.InitCheck();
120	if (err == B_OK) {
121		// The package is already installed, inform the user
122		BAlert *reinstall = new BAlert("reinstall",
123			B_TRANSLATE("The given package seems to be already installed on "
124				"your system. Would you like to uninstall the existing one "
125				"and continue the installation?"),
126			B_TRANSLATE("Continue"),
127			B_TRANSLATE("Abort"));
128		reinstall->SetShortcut(1, B_ESCAPE);
129
130		if (reinstall->Go() == 0) {
131			// Uninstall the package
132			err = packageInfo.Uninstall();
133			if (err != B_OK) {
134				fprintf(stderr, "Error on uninstall\n");
135				return P_MSG_I_ERROR;
136			}
137
138			err = packageInfo.SetTo(info->GetName(), info->GetVersion(), true);
139			if (err != B_OK) {
140				fprintf(stderr, "Error on SetTo\n");
141				return P_MSG_I_ERROR;
142			}
143		} else {
144			// Abort the installation
145			return P_MSG_I_ABORT;
146		}
147	} else if (err == B_ENTRY_NOT_FOUND) {
148		err = packageInfo.SetTo(info->GetName(), info->GetVersion(), true);
149		if (err != B_OK) {
150			fprintf(stderr, "Error on SetTo\n");
151			return P_MSG_I_ERROR;
152		}
153	} else if (progress->Stopped()) {
154		return P_MSG_I_ABORT;
155	} else {
156		fprintf(stderr, "returning on error\n");
157		return P_MSG_I_ERROR;
158	}
159
160	progress->StageStep(1, B_TRANSLATE("Installing files and folders"));
161
162	// Install files and directories
163	PackageItem *iter;
164	ItemState state;
165	uint32 i;
166	int32 choice;
167	BString label;
168
169	packageInfo.SetName(info->GetName());
170	// TODO: Here's a small problem, since right now it's not quite sure
171	//		which description is really used as such. The one displayed on
172	//		the installer is mostly package installation description, but
173	//		most people use it for describing the application in more detail
174	//		then in the short description.
175	//		For now, we'll use the short description if possible.
176	BString description = info->GetShortDescription();
177	if (description.Length() <= 0)
178		description = info->GetDescription();
179	packageInfo.SetDescription(description.String());
180	packageInfo.SetSpaceNeeded(type->space_needed);
181
182	fItemExistsPolicy = P_EXISTS_NONE;
183
184	const char *installPath = fParent->GetCurrentPath()->Path();
185	for (i = 0; i < n; i++) {
186		state.Reset(fItemExistsPolicy); // Reset the current item state
187		iter = static_cast<PackageItem *>(type->items.ItemAt(i));
188
189		err = iter->DoInstall(installPath, &state);
190		if (err == B_FILE_EXISTS) {
191			// Writing to path failed because path already exists - ask the user
192			// what to do and retry the writing process
193			choice = fParent->ItemExists(*iter, state.destination,
194				fItemExistsPolicy);
195			if (choice != P_EXISTS_ABORT) {
196				state.policy = choice;
197				err = iter->DoInstall(installPath, &state);
198			}
199		}
200
201		if (err != B_OK) {
202			fprintf(stderr, "Error while writing path\n");
203			return P_MSG_I_ERROR;
204		}
205
206		if (progress->Stopped())
207			return P_MSG_I_ABORT;
208		label = "";
209		label << (uint32)(i + 1) << " of " << (uint32)n;
210		progress->StageStep(1, NULL, label.String());
211
212		packageInfo.AddItem(state.destination.Path());
213	}
214
215	progress->StageStep(1, B_TRANSLATE("Running post-installation scripts"),
216		 "");
217
218	PackageScript *scr;
219	status_t status;
220	// Run all scripts
221	for (i = 0; i < m; i++) {
222		scr = info->GetScript(i);
223
224		fCurrentScriptLocker.Lock();
225		fCurrentScript = scr;
226
227		if (scr->DoInstall() != B_OK) {
228			fprintf(stderr, "Error while running script\n");
229			return P_MSG_I_ERROR;
230		}
231		fCurrentScriptLocker.Unlock();
232
233		wait_for_thread(scr->GetThreadId(), &status);
234		fCurrentScriptLocker.Lock();
235		scr->SetThreadId(-1);
236		fCurrentScript = NULL;
237		fCurrentScriptLocker.Unlock();
238
239		if (progress->Stopped())
240			return P_MSG_I_ABORT;
241		label = "";
242		label << (uint32)(i + 1) << " of " << (uint32)m;
243		progress->StageStep(1, NULL, label.String());
244	}
245
246	progress->StageStep(1, B_TRANSLATE("Finishing installation"), "");
247
248	err = packageInfo.Save();
249	if (err != B_OK)
250		return P_MSG_I_ERROR;
251
252	progress->StageStep(1, B_TRANSLATE("Done"));
253
254	// Inform our parent that we finished
255	return P_MSG_I_FINISHED;
256}
257
258