1/*
2 * Copyright 2008, François Revol, <revol@free.fr>. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <Alert.h>
8#include <Catalog.h>
9#include <Screen.h>
10#include <String.h>
11#include <View.h>
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <unistd.h>
17#include <pwd.h>
18
19#include "LoginApp.h"
20#include "LoginWindow.h"
21#include "DesktopWindow.h"
22
23#ifdef __HAIKU__
24#include <RosterPrivate.h>
25#include <shadow.h>
26#include "multiuser_utils.h"
27#endif
28
29#undef B_TRANSLATION_CONTEXT
30#define B_TRANSLATION_CONTEXT "Login App"
31
32const char *kLoginAppSig = "application/x-vnd.Haiku-Login";
33
34
35LoginApp::LoginApp()
36	: BApplication(kLoginAppSig),
37	  fEditShelfMode(false),
38	  fModalMode(true)
39{
40}
41
42
43LoginApp::~LoginApp()
44{
45}
46
47
48void
49LoginApp::ReadyToRun()
50{
51	BScreen screen;
52
53	if (fEditShelfMode) {
54		BAlert* alert = new BAlert(B_TRANSLATE("Info"), B_TRANSLATE("You can customize the "
55			"desktop shown behind the Login application by dropping replicants"
56			" onto it.\n"
57			"\n"
58			"When you are finished just quit the application (Alt-Q)."),
59			B_TRANSLATE("OK"));
60		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
61		alert->Go(NULL);
62	} else {
63		BRect frame(0, 0, 450, 150);
64		frame.OffsetBySelf(screen.Frame().Width()/2 - frame.Width()/2,
65			screen.Frame().Height()/2 - frame.Height()/2);
66		fLoginWindow = new LoginWindow(frame);
67		fLoginWindow->Show();
68	}
69
70	fDesktopWindow = new DesktopWindow(screen.Frame(), fEditShelfMode);
71	fDesktopWindow->Show();
72	// TODO: add a shelf with Activity Monitor replicant :)
73}
74
75
76void
77LoginApp::MessageReceived(BMessage *message)
78{
79	bool reboot = false;
80
81	switch (message->what) {
82		case kAttemptLogin:
83			message->PrintToStream();
84			TryLogin(message);
85			// TODO
86			break;
87#ifdef __HAIKU__
88		case kHaltAction:
89			reboot = false;
90			// FALLTHROUGH
91		case kRebootAction:
92		{
93			BRoster roster;
94			BRoster::Private rosterPrivate(roster);
95			status_t error = rosterPrivate.ShutDown(reboot, false, false);
96			if (error < B_OK) {
97				BString msg(B_TRANSLATE("Error: %1"));
98				msg.ReplaceFirst("%1", strerror(error));
99				BAlert* alert = new BAlert(("Error"), msg.String(),
100					B_TRANSLATE("OK"));
101				alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
102				alert->Go();
103			}
104			break;
105		}
106		case kSuspendAction:
107		{
108			BAlert* alert = new BAlert(B_TRANSLATE("Error"),
109				B_TRANSLATE("Unimplemented"), B_TRANSLATE("OK"));
110			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
111			alert->Go();
112			break;
113		}
114#endif
115	default:
116		BApplication::MessageReceived(message);
117	}
118}
119
120
121void
122LoginApp::ArgvReceived(int32 argc, char **argv)
123{
124	for (int i = 1; i < argc; i++) {
125		BString arg(argv[i]);
126		//printf("[%d]: %s\n", i, argv[i]);
127		if (arg == "--edit")
128			fEditShelfMode = true;
129		else if (arg == "--nonmodal")
130			fModalMode = false;
131		else /*if (arg == "--help")*/ {
132			printf(B_TRANSLATE("Login application for Haiku\nUsage:\n"));
133			printf("%s [--nonmodal] [--edit]\n", argv[0]);
134			printf(B_TRANSLATE("--nonmodal	Do not make the window modal\n"));
135			printf(B_TRANSLATE("--edit	Launch in shelf editting mode to "
136				"allow customizing the desktop.\n"));
137			// just return to the shell
138			exit((arg == "--help") ? 0 : 1);
139			return;
140		}
141	}
142}
143
144
145void
146LoginApp::TryLogin(BMessage *message)
147{
148	status_t err;
149	const char *login;
150	const char *password;
151	BMessage reply(kLoginBad);
152	if (message->FindString("login", &login) == B_OK) {
153		if (message->FindString("password", &password) < B_OK)
154			password = NULL;
155		err = ValidateLogin(login, password);
156		printf(B_TRANSLATE_COMMENT("ValidateLogin: %s\n",
157			"A message returned from the ValidateLogin function. "
158			"It can be \"B_OK\"."), strerror(err));
159		if (err == B_OK) {
160			reply.what = kLoginOk;
161			message->SendReply(&reply);
162
163			if (password == NULL)
164				return;
165
166			// start a session
167			//kSetProgress
168			StartUserSession(login);
169		} else {
170			reply.AddInt32("error", err);
171			message->SendReply(&reply);
172			return;
173		}
174
175	} else {
176		reply.AddInt32("error", EINVAL);
177		message->SendReply(&reply);
178		return;
179	}
180}
181
182
183status_t
184LoginApp::ValidateLogin(const char *login, const char *password)
185{
186	struct passwd *pwd;
187
188	pwd = getpwnam(login);
189	if (!pwd)
190		return ENOENT;
191	if (strcmp(pwd->pw_name, login))
192		return ENOENT;
193
194	if (password == NULL) {
195		// we only want to check is login exists.
196		return B_OK;
197	}
198
199#ifdef __HAIKU__
200	if (verify_password(pwd, getspnam(login), password))
201		return B_OK;
202#else
203	// for testing
204	if (strcmp(crypt(password, pwd->pw_passwd), pwd->pw_passwd) == 0)
205		return B_OK;
206#endif
207
208	return B_PERMISSION_DENIED;
209}
210
211
212status_t
213LoginApp::StartUserSession(const char *login)
214{
215	return B_ERROR;
216}
217
218
219int
220LoginApp::getpty(char *pty, char *tty)
221{
222	static const char major[] = "pqrs";
223	static const char minor[] = "0123456789abcdef";
224	uint32 i, j;
225	int32 fd = -1;
226
227	for (i = 0; i < sizeof(major); i++)
228	{
229		for (j = 0; j < sizeof(minor); j++)
230		{
231			sprintf(pty, "/dev/pt/%c%c", major[i], minor[j]);
232			sprintf(tty, "/dev/tt/%c%c", major[i], minor[j]);
233			fd = open(pty, O_RDWR|O_NOCTTY);
234			if (fd >= 0)
235			{
236				return fd;
237			}
238		}
239	}
240
241	return fd;
242}
243