1/*
2 * Copyright 2008, François Revol, <revol@free.fr>. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5#include <Catalog.h>
6#include <ScrollView.h>
7#include <String.h>
8#include <Window.h>
9#ifdef __HAIKU__
10#include <Layout.h>
11#endif
12
13#include <pwd.h>
14
15#include "LoginApp.h"
16#include "LoginView.h"
17
18#undef B_TRANSLATION_CONTEXT
19#define B_TRANSLATION_CONTEXT "Login View"
20
21#define LW 120
22#define CSEP 15
23#define BH 20
24#define BW 60
25
26class PwdItem : public BStringItem {
27public:
28					PwdItem(struct passwd *pwd, uint32 level = 0,
29						bool expanded = true)
30						 : BStringItem("", level, expanded)
31						{
32							if (pwd) {
33								BString name(pwd->pw_gecos);
34								// TODO: truncate at first ;
35								fLogin = pwd->pw_name;
36								SetText(name.String());
37							}
38						};
39	virtual			~PwdItem() {};
40	const char*		Login() const { return fLogin.String(); };
41private:
42	BString			fLogin;
43};
44
45
46LoginView::LoginView(BRect frame)
47	: BView(frame, "LoginView", B_FOLLOW_ALL, B_PULSE_NEEDED)
48{
49	// TODO: when I don't need to test in BeOS anymore,
50	// rewrite to use layout engine.
51	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
52	SetLowColor(ViewColor());
53	BRect r;
54	r.Set(CSEP, CSEP, LW, Bounds().Height() - 3 * CSEP - BH);
55	fUserList = new BListView(r, "users");
56	BScrollView *sv = new BScrollView("userssv", fUserList,
57		B_FOLLOW_LEFT | B_FOLLOW_TOP, 0, false, true);
58	AddChild(sv);
59	fUserList->SetSelectionMessage(new BMessage(kUserSelected));
60	fUserList->SetInvocationMessage(new BMessage(kUserInvoked));
61
62	r.Set(LW + 30, Bounds().top + CSEP,
63		Bounds().right - CSEP, Bounds().top + CSEP + CSEP);
64	fLoginControl = new BTextControl(r, "login", B_TRANSLATE("Login:"), "",
65		new BMessage(kLoginEdited));
66	AddChild(fLoginControl);
67
68	r.OffsetBySelf(0, CSEP + CSEP);
69	fPasswordControl = new BTextControl(r, "password",
70		B_TRANSLATE("Password:"), "", new BMessage(kPasswordEdited));
71	fPasswordControl->TextView()->HideTyping(true);
72	AddChild(fPasswordControl);
73
74	r.OffsetBySelf(0, CSEP + CSEP);
75	fHidePasswordCheckBox = new BCheckBox(r, "hidepw",
76		B_TRANSLATE("Hide password"), new BMessage(kHidePassword));
77	fHidePasswordCheckBox->SetValue(1);
78	AddChild(fHidePasswordCheckBox);
79
80	// buttons
81	float buttonWidth = BW; //(Bounds().Width() - 4 * CSEP) / 3;
82	BRect buttonRect(0, Bounds().bottom - BH,
83		buttonWidth, Bounds().bottom);
84	buttonRect.OffsetBySelf(CSEP, -CSEP);
85
86	fHaltButton = new BButton(buttonRect, "halt", B_TRANSLATE("Halt"),
87		new BMessage(kHaltAction));
88	AddChild(fHaltButton);
89
90	buttonRect.OffsetBySelf(CSEP + buttonWidth, 0);
91	fRebootButton = new BButton(buttonRect, "reboot", B_TRANSLATE("Reboot"),
92		new BMessage(kRebootAction));
93	AddChild(fRebootButton);
94
95	BRect infoRect(buttonRect);
96	infoRect.OffsetBySelf(buttonWidth + CSEP, 0);
97
98	buttonRect.OffsetToSelf(Bounds().Width() - CSEP - buttonWidth,
99		Bounds().Height() - CSEP - BH);
100	fLoginButton = new BButton(buttonRect, "ok", B_TRANSLATE("OK"),
101		new BMessage(kAttemptLogin));
102	AddChild(fLoginButton);
103
104	infoRect.right = buttonRect.left - CSEP + 5;
105	BString info;
106	//info << hostname;
107	fInfoView = new BStringView(infoRect, "info", info.String());
108	AddChild(fInfoView);
109}
110
111
112LoginView::~LoginView()
113{
114}
115
116void
117LoginView::AttachedToWindow()
118{
119	fUserList->SetTarget(this);
120	fLoginControl->SetTarget(this);
121	fPasswordControl->SetTarget(this);
122	fHidePasswordCheckBox->SetTarget(this);
123	fHaltButton->SetTarget(be_app_messenger);
124	fRebootButton->SetTarget(be_app_messenger);
125	fLoginButton->SetTarget(this);
126	Window()->SetDefaultButton(fLoginButton);
127	//fLoginControl->MakeFocus();
128	fUserList->MakeFocus();
129	// populate user list
130	BMessenger(this).SendMessage(kAddNextUser);
131}
132
133
134void
135LoginView::MessageReceived(BMessage *message)
136{
137	switch (message->what) {
138		case kSetProgress:
139			break;
140		case kAddNextUser:
141			AddNextUser();
142			break;
143		case kUserSelected:
144		{
145			int32 selection = fUserList->CurrentSelection();
146			if (selection > -1) {
147				PwdItem *item = dynamic_cast<PwdItem *>(
148					fUserList->ItemAt(selection));
149				if (item)
150					fLoginControl->SetText(item->Login());
151			}
152			break;
153		}
154		case kUserInvoked:
155			fPasswordControl->MakeFocus();
156			break;
157		case kLoginEdited:
158			break;
159		case kHidePassword:
160			fPasswordControl->TextView()->HideTyping(
161				fHidePasswordCheckBox->Value());
162			break;
163		case kAttemptLogin:
164		{
165			// if no pass specified and we were selecting the user,
166			// give a chance to enter the password
167			// else we might want to enter an empty password.
168			if (strlen(fPasswordControl->Text()) < 1
169				&& (fUserList->IsFocus() || fLoginControl->IsFocus())) {
170				fPasswordControl->MakeFocus();
171				break;
172			}
173			BMessage *m = new BMessage(kAttemptLogin);
174			m->AddString("login", fLoginControl->Text());
175			m->AddString("password", fPasswordControl->Text());
176			be_app->PostMessage(m, NULL, this);
177			break;
178		}
179		case kLoginBad:
180			fPasswordControl->SetText("");
181			EnableControls(false);
182			fInfoView->SetText(B_TRANSLATE("Invalid login!"));
183			if (Window()) {
184				BPoint savedPos = Window()->Frame().LeftTop();
185				for (int i = 0; i < 10; i++) {
186					BPoint p(savedPos);
187					p.x += (i%2) ? 10 : -10;
188					Window()->MoveTo(p);
189					Window()->UpdateIfNeeded();
190					snooze(30000);
191				}
192				Window()->MoveTo(savedPos);
193			}
194			EnableControls(true);
195			break;
196		case kLoginOk:
197			// XXX: quit ?
198			if (Window()) {
199				Window()->Hide();
200			}
201			break;
202		default:
203			message->PrintToStream();
204			BView::MessageReceived(message);
205	}
206}
207
208
209void
210LoginView::Pulse()
211{
212	BString info;
213	time_t now = time(NULL);
214	struct tm *t = localtime(&now);
215	// TODO: use strftime and locale settings
216	info << asctime(t);
217	info.RemoveSet("\r\n");
218	fInfoView->SetText(info.String());
219}
220
221
222void
223LoginView::AddNextUser()
224{
225	struct passwd *pwd;
226	if (fUserList->CountItems() < 1)
227		setpwent();
228
229	pwd = getpwent();
230
231	if (pwd && pwd->pw_shell &&
232		strcmp(pwd->pw_shell, "false") &&
233		strcmp(pwd->pw_shell, "true") &&
234		strcmp(pwd->pw_shell, "/bin/false") &&
235		strcmp(pwd->pw_shell, "/bin/true")) {
236		// not disabled
237		PwdItem *item = new PwdItem(pwd);
238		fUserList->AddItem(item);
239	}
240	if (pwd)
241		BMessenger(this).SendMessage(kAddNextUser);
242	else
243		endpwent();
244}
245
246
247void
248LoginView::EnableControls(bool enable)
249{
250	int32 i;
251	for (i = 0; i < fUserList->CountItems(); i++) {
252		fUserList->ItemAt(i)->SetEnabled(enable);
253	}
254	fLoginControl->SetEnabled(enable);
255	fPasswordControl->SetEnabled(enable);
256	fHidePasswordCheckBox->SetEnabled(enable);
257	fHaltButton->SetEnabled(enable);
258	fRebootButton->SetEnabled(enable);
259	fLoginButton->SetEnabled(enable);
260}
261
262