1/*
2 * Copyright 2008-2011, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Axel D��rfler, axeld@pinc-software.de
7 *		Michael Pfeiffer <laplace@users.sourceforge.net>
8 */
9
10
11#include "BootManagerController.h"
12
13#include <Alert.h>
14#include <Application.h>
15#include <Catalog.h>
16#include <File.h>
17#include <FindDirectory.h>
18#include <Locale.h>
19#include <Path.h>
20#include <String.h>
21
22#include "BootDrive.h"
23#include "DefaultPartitionPage.h"
24#include "DescriptionPage.h"
25#include "DrivesPage.h"
26#include "FileSelectionPage.h"
27#include "LegacyBootMenu.h"
28#include "PartitionsPage.h"
29#include "WizardView.h"
30
31
32#undef B_TRANSLATION_CONTEXT
33#define B_TRANSLATION_CONTEXT "BootManagerController"
34
35
36BootManagerController::BootManagerController()
37	:
38	fBootDrive(NULL),
39	fBootMenu(NULL)
40{
41	// set defaults
42	fSettings.AddBool("install", true);
43	fSettings.AddInt32("defaultPartition", 0);
44	fSettings.AddInt32("timeout", -1);
45
46	BPath path;
47	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) == B_OK) {
48		path.Append("bootman/MBR");
49		fSettings.AddString("file", path.Path());
50		// create directory
51		BPath parent;
52		if (path.GetParent(&parent) == B_OK) {
53			BDirectory directory;
54			directory.CreateDirectory(parent.Path(), NULL);
55		}
56	} else {
57		fSettings.AddString("file", "");
58	}
59
60	// That's the only boot menu we support at the moment.
61	fBootMenus.AddItem(new LegacyBootMenu());
62}
63
64
65BootManagerController::~BootManagerController()
66{
67}
68
69
70void
71BootManagerController::Previous(WizardView* wizard)
72{
73	if (CurrentState() != kStateEntry)
74		WizardController::Previous(wizard);
75	else {
76		fSettings.ReplaceBool("install", false);
77		WizardController::Next(wizard);
78	}
79}
80
81
82int32
83BootManagerController::InitialState()
84{
85	return kStateEntry;
86}
87
88
89int32
90BootManagerController::NextState(int32 state)
91{
92	switch (state) {
93		case kStateEntry:
94		{
95			const char* path;
96			if (fSettings.FindString("disk", &path) != B_OK)
97				return kStateErrorEntry;
98
99			delete fBootDrive;
100
101			fBootDrive = new BootDrive(path);
102			fBootMenu = fBootDrive->InstalledMenu(fBootMenus);
103
104			if (fSettings.FindBool("install")) {
105				int32 nextState = fBootMenu != NULL
106					? kStatePartitions : kStateSaveMBR;
107
108				// TODO: call BootDrive::AddSupportedMenus() once we support
109				// more than one type of boot menu - we'll probably need a
110				// requester to choose from them then as well.
111				if (fBootMenu == NULL)
112					fBootMenu = fBootMenus.ItemAt(0);
113
114				fCollectPartitionsStatus = fBootMenu->CollectPartitions(
115					*fBootDrive, fSettings);
116
117				return nextState;
118			}
119
120			return kStateUninstall;
121		}
122
123		case kStateErrorEntry:
124			be_app->PostMessage(B_QUIT_REQUESTED);
125			break;
126
127		case kStateSaveMBR:
128			if (_SaveMBR())
129				return kStateMBRSaved;
130			break;
131
132		case kStateMBRSaved:
133			return kStatePartitions;
134
135		case kStatePartitions:
136			if (_HasSelectedPartitions())
137				return kStateDefaultPartitions;
138			break;
139
140		case kStateDefaultPartitions:
141			return kStateInstallSummary;
142
143		case kStateInstallSummary:
144			if (_WriteBootMenu())
145				return kStateInstalled;
146			break;
147
148		case kStateInstalled:
149			be_app->PostMessage(B_QUIT_REQUESTED);
150			break;
151
152		case kStateUninstall:
153			if (_RestoreMBR())
154				return kStateUninstalled;
155			break;
156
157		case kStateUninstalled:
158			be_app->PostMessage(B_QUIT_REQUESTED);
159			break;
160	}
161	// cannot leave the current state/page
162	return -1;
163}
164
165
166bool
167BootManagerController::_HasSelectedPartitions()
168{
169	BMessage message;
170	for (int32 i = 0; fSettings.FindMessage("partition", i, &message) == B_OK;
171			i++) {
172		bool show;
173		if (message.FindBool("show", &show) == B_OK && show)
174			return true;
175	}
176
177	BAlert* alert = new BAlert("info",
178		B_TRANSLATE("At least one partition must be selected!"),
179		B_TRANSLATE_COMMENT("OK", "Button"));
180	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
181	alert->Go();
182
183	return false;
184}
185
186
187bool
188BootManagerController::_WriteBootMenu()
189{
190	BAlert* alert = new BAlert("confirm", B_TRANSLATE("About to write the "
191			"boot menu to disk. Are you sure you want to continue?"),
192		B_TRANSLATE_COMMENT("Write boot menu", "Button"),
193		B_TRANSLATE_COMMENT("Back", "Button"), NULL, B_WIDTH_AS_USUAL,
194		B_WARNING_ALERT);
195
196	if (alert->Go() == 1)
197		return false;
198
199	fWriteBootMenuStatus = fBootMenu->Install(*fBootDrive, fSettings);
200	return true;
201}
202
203
204bool
205BootManagerController::_SaveMBR()
206{
207	BString path;
208	fSettings.FindString("file", &path);
209	BFile file(path.String(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
210	fSaveMBRStatus = fBootMenu->SaveMasterBootRecord(&fSettings, &file);
211	return true;
212}
213
214
215bool
216BootManagerController::_RestoreMBR()
217{
218	BString disk;
219	BString path;
220	fSettings.FindString("disk", &disk);
221	fSettings.FindString("file", &path);
222
223	BString message;
224	message << B_TRANSLATE_COMMENT("About to restore the Master Boot Record "
225		"(MBR) of %disk from %file. Do you wish to continue?",
226		"Don't translate the place holders: %disk and %file");
227	message.ReplaceFirst("%disk", disk);
228	message.ReplaceFirst("%file", path);
229
230	BAlert* alert = new BAlert("confirm", message.String(),
231		B_TRANSLATE_COMMENT("Restore MBR", "Button"),
232		B_TRANSLATE_COMMENT("Back", "Button"),
233		NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
234	if (alert->Go() == 1)
235		return false;
236
237	BFile file(path.String(), B_READ_ONLY);
238	fRestoreMBRStatus = fBootMenu->RestoreMasterBootRecord(&fSettings, &file);
239	return true;
240}
241
242
243WizardPageView*
244BootManagerController::CreatePage(int32 state, WizardView* wizard)
245{
246	WizardPageView* page = NULL;
247	BRect frame(0, 0, 300, 250);
248
249	switch (state) {
250		case kStateEntry:
251			fSettings.ReplaceBool("install", true);
252			page = new DrivesPage(wizard, fBootMenus, &fSettings, "drives");
253			break;
254		case kStateErrorEntry:
255			page = _CreateErrorEntryPage();
256			wizard->SetPreviousButtonHidden(true);
257			wizard->SetNextButtonLabel(B_TRANSLATE_COMMENT("Done", "Button"));
258			break;
259		case kStateSaveMBR:
260			page = _CreateSaveMBRPage(frame);
261			wizard->SetPreviousButtonHidden(false);
262			break;
263		case kStateMBRSaved:
264			page = _CreateMBRSavedPage();
265			break;
266		case kStatePartitions:
267			page = new PartitionsPage(&fSettings, "partitions");
268			wizard->SetPreviousButtonHidden(false);
269			break;
270		case kStateDefaultPartitions:
271			page = new DefaultPartitionPage(&fSettings, frame, "default");
272			break;
273		case kStateInstallSummary:
274			page = _CreateInstallSummaryPage();
275			break;
276		case kStateInstalled:
277			page = _CreateInstalledPage();
278			wizard->SetNextButtonLabel(B_TRANSLATE_COMMENT("Done", "Button"));
279			break;
280		case kStateUninstall:
281			page = _CreateUninstallPage(frame);
282			wizard->SetPreviousButtonHidden(false);
283			break;
284		case kStateUninstalled:
285			// TODO prevent overwriting MBR after clicking "Previous"
286			page = _CreateUninstalledPage();
287			wizard->SetNextButtonLabel(B_TRANSLATE_COMMENT("Done", "Button"));
288			break;
289	}
290
291	return page;
292}
293
294
295WizardPageView*
296BootManagerController::_CreateErrorEntryPage()
297{
298	BString description;
299
300	if (fCollectPartitionsStatus == B_ENTRY_NOT_FOUND) {
301		description << B_TRANSLATE_COMMENT("Partition table not compatible",
302				"Title") << "\n\n"
303			<< B_TRANSLATE("The partition table of the first hard disk is not "
304				"compatible with Boot Manager.\n"
305				"Boot Manager only works with IBM PC MBR partitions.");
306	} else if (fCollectPartitionsStatus == B_PARTITION_TOO_SMALL) {
307		description << B_TRANSLATE_COMMENT("First partition starts too early",
308				"Title") << "\n\n"
309			<< B_TRANSLATE("The first partition on the disk starts too early "
310				"and does not leave enough space free for a boot menu.\n"
311				"Boot Manager needs 2 KiB available space before the first "
312				"partition.");
313	} else {
314		description << B_TRANSLATE_COMMENT("Error reading partition table",
315				"Title") << "\n\n"
316			<< B_TRANSLATE("Boot Manager is unable to read the partition "
317				"table!");
318	}
319
320	return new DescriptionPage("errorEntry", description.String(), true);
321}
322
323
324WizardPageView*
325BootManagerController::_CreateSaveMBRPage(BRect frame)
326{
327	BString description;
328	BString disk;
329	fSettings.FindString("disk", &disk);
330
331	description << B_TRANSLATE_COMMENT("Backup Master Boot Record", "Title")
332		<< "\n" << B_TRANSLATE("The Master Boot Record (MBR) of the boot "
333			"device:\n"
334			"\t%s\n"
335			"will now be saved to disk. Please select a file to "
336			"save the MBR into.\n\n"
337			"If something goes wrong with the installation or if "
338			"you later wish to remove the boot menu, simply run the "
339			"bootman program and choose the 'Uninstall' option.");
340	description.ReplaceFirst("%s", disk);
341
342	return new FileSelectionPage(&fSettings, frame, "saveMBR",
343		description.String(),
344		B_SAVE_PANEL);
345}
346
347
348WizardPageView*
349BootManagerController::_CreateMBRSavedPage()
350{
351	BString description;
352	BString file;
353	fSettings.FindString("file", &file);
354
355	if (fSaveMBRStatus == B_OK) {
356		description << B_TRANSLATE_COMMENT("Old Master Boot Record saved",
357				"Title") << "\n"
358			<< B_TRANSLATE("The old Master Boot Record was successfully "
359				"saved to %s.") << "\n";
360	} else {
361		description << B_TRANSLATE_COMMENT("Old Master Boot Record backup "
362				"failure", "Title") << "\n"
363			<< B_TRANSLATE("The old Master Boot Record could not be saved "
364				"to %s. You can continue the installation but there will be no "
365				"way to uninstall the boot menu.") << "\n";
366	}
367	description.ReplaceFirst("%s", file);
368
369	return new DescriptionPage("summary", description.String(), true);
370}
371
372
373WizardPageView*
374BootManagerController::_CreateInstallSummaryPage()
375{
376	BString description;
377	BString disk;
378	fSettings.FindString("disk", &disk);
379
380	description << B_TRANSLATE_COMMENT("Summary", "Title") << "\n"
381		<< B_TRANSLATE("About to write the following boot menu to the boot "
382			"disk (%s). Please verify the information below before continuing.")
383		<< "\n\n";
384	description.ReplaceFirst("%s", disk);
385
386	BMessage message;
387	for (int32 i = 0; fSettings.FindMessage("partition", i, &message) == B_OK;
388			i++) {
389		bool show;
390		if (message.FindBool("show", &show) != B_OK || !show)
391			continue;
392
393		BString name;
394		BString path;
395		message.FindString("name", &name);
396		message.FindString("path", &path);
397
398		BString displayName;
399		if (fBootMenu->GetDisplayText(name.String(), displayName) == B_OK)
400			description << displayName << "\t(" << path << ")\n";
401		else
402			description << name << "\t(" << path << ")\n";
403	}
404
405	return new DescriptionPage("summary", description.String(), true);
406}
407
408
409WizardPageView*
410BootManagerController::_CreateInstalledPage()
411{
412	BString description;
413
414	if (fWriteBootMenuStatus == B_OK) {
415		description << B_TRANSLATE_COMMENT("Installation of boot menu "
416				"completed", "Title") << "\n"
417			<< B_TRANSLATE("The boot manager has been successfully installed "
418				"on your system.");
419	} else {
420		description << B_TRANSLATE_COMMENT("Installation of boot menu failed",
421				"Title") << "\n"
422			<< B_TRANSLATE("An error occurred writing the boot menu. "
423				"The Master Boot Record might be destroyed, "
424				"you should restore the MBR now!");
425	}
426
427	return new DescriptionPage("done", description, true);
428}
429
430
431WizardPageView*
432BootManagerController::_CreateUninstallPage(BRect frame)
433{
434	BString description;
435	description << B_TRANSLATE_COMMENT("Uninstall boot manager", "Title")
436		<< "\n\n"
437		<< B_TRANSLATE("Please locate the Master Boot Record (MBR) save file "
438			"to restore from. This is the file that was created when the "
439			"boot manager was first installed.");
440
441	return new FileSelectionPage(&fSettings, frame, "restoreMBR",
442		description.String(), B_OPEN_PANEL);
443}
444
445
446WizardPageView*
447BootManagerController::_CreateUninstalledPage()
448{
449	BString description;
450	BString disk;
451	BString file;
452	fSettings.FindString("disk", &disk);
453	fSettings.FindString("file", &file);
454
455	if (fRestoreMBRStatus == B_OK) {
456		description << B_TRANSLATE_COMMENT("Uninstallation of boot menu "
457				"completed", "Title") << "\n"
458			<< B_TRANSLATE("The Master Boot Record of the boot device "
459				"(%DISK) has been successfully restored from %FILE.");
460		description.ReplaceFirst("%DISK", disk);
461		description.ReplaceFirst("%FILE", file);
462	} else {
463		description << B_TRANSLATE_COMMENT("Uninstallation of boot menu "
464				"failed", "Title") << "\n"
465			<< B_TRANSLATE("The Master Boot Record could not be restored!");
466	}
467
468	return new DescriptionPage("summary", description.String(), true);
469}
470