1/*
2 * Copyright 2005, Waldemar Kornewald <wkornew@gmx.net>
3 * Distributed under the terms of the MIT License.
4 */
5
6#include "ConnectionView.h"
7#include "PPPDeskbarReplicant.h"
8#include <MessageDriverSettingsUtils.h>
9
10#include <Application.h>
11#include <Box.h>
12#include <Button.h>
13#include <Deskbar.h>
14#include <Entry.h>
15#include <File.h>
16#include <String.h>
17#include <StringView.h>
18#include <TextControl.h>
19#include <Window.h>
20
21#include <PPPInterface.h>
22#include <settings_tools.h>
23#include <algorithm>
24	// for max()
25
26using std::max;
27
28// GUI constants
29static const uint32 kDefaultButtonWidth = 80;
30
31// message constants
32static const uint32 kMsgCancel = 'CANC';
33static const uint32 kMsgConnect = 'CONN';
34static const uint32 kMsgUpdate = 'MUPD';
35
36// labels
37static const char *kLabelSavePassword = "Save password";
38static const char *kLabelName = "Username: ";
39static const char *kLabelPassword = "Password: ";
40static const char *kLabelConnect = "Connect";
41static const char *kLabelCancel = "Cancel";
42static const char *kLabelAuthentication = "Authentication";
43
44// connection status strings
45static const char *kTextConnecting = "Connecting...";
46static const char *kTextConnectionEstablished = "Connection established.";
47static const char *kTextNotConnected = "Not connected.";
48static const char *kTextDeviceUpFailed = "Failed to connect.";
49static const char *kTextAuthenticating = "Authenticating...";
50static const char *kTextAuthenticationFailed = "Authentication failed!";
51static const char *kTextConnectionLost = "Connection lost!";
52
53
54ConnectionView::ConnectionView(BRect rect, const BString& interfaceName)
55	: BView(rect, "ConnectionView", B_FOLLOW_NONE, 0),
56	fListener(this),
57	fInterfaceName(interfaceName),
58	fKeepLabel(false)
59{
60	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
61
62	rect = Bounds();
63	rect.InsetBy(5, 5);
64	rect.bottom = rect.top
65		+ 25 // space for topmost control
66		+ 3 * 20 // size of controls
67		+ 3 * 5; // space beween controls and bottom of box
68	BBox *authenticationBox = new BBox(rect, "Authentication");
69	authenticationBox->SetLabel(kLabelAuthentication);
70	rect = authenticationBox->Bounds();
71	rect.InsetBy(10, 20);
72	rect.bottom = rect.top + 20;
73	fUsername = new BTextControl(rect, "username", kLabelName, NULL, NULL);
74	rect.top = rect.bottom + 5;
75	rect.bottom = rect.top + 20;
76	fPassword = new BTextControl(rect, "password", kLabelPassword, NULL, NULL);
77	fPassword->TextView()->HideTyping(true);
78
79	// set dividers
80	float width = max(StringWidth(fUsername->Label()),
81		StringWidth(fPassword->Label()));
82	fUsername->SetDivider(width + 5);
83	fPassword->SetDivider(width + 5);
84
85	rect.top = rect.bottom + 5;
86	rect.bottom = rect.top + 20;
87	fSavePassword = new BCheckBox(rect, "SavePassword", kLabelSavePassword, NULL);
88
89	authenticationBox->AddChild(fUsername);
90	authenticationBox->AddChild(fPassword);
91	authenticationBox->AddChild(fSavePassword);
92	AddChild(authenticationBox);
93
94	rect = authenticationBox->Frame();
95	rect.top = rect.bottom + 10;
96	rect.bottom = rect.top + 15;
97	fAttemptView = new BStringView(rect, "AttemptView", "");
98	AddChild(fAttemptView);
99
100	// add status view
101	rect.top = rect.bottom + 5;
102	rect.bottom = rect.top + 15;
103	fStatusView = new BStringView(rect, "StatusView", "");
104	AddChild(fStatusView);
105
106	// add "Connect" and "Cancel" buttons
107	rect.top = rect.bottom + 10;
108	rect.bottom = rect.top + 25;
109	rect.right = rect.left + kDefaultButtonWidth;
110	fConnectButton = new BButton(rect, "ConnectButton", kLabelConnect,
111		new BMessage(kMsgConnect));
112
113	rect.left = rect.right + 10;
114	rect.right = rect.left + kDefaultButtonWidth;
115	fCancelButton = new BButton(rect, "CancelButton", kLabelCancel,
116		new BMessage(kMsgCancel));
117
118	AddChild(fConnectButton);
119	AddChild(fCancelButton);
120}
121
122
123void
124ConnectionView::AttachedToWindow()
125{
126	Reload();
127	fListener.WatchManager();
128	WatchInterface(fListener.Manager().InterfaceWithName(fInterfaceName.String()));
129
130	Window()->SetDefaultButton(fConnectButton);
131	fConnectButton->SetTarget(this);
132	fCancelButton->SetTarget(this);
133}
134
135
136void
137ConnectionView::MessageReceived(BMessage *message)
138{
139	switch(message->what) {
140		case PPP_REPORT_MESSAGE:
141			HandleReportMessage(message);
142		break;
143
144		case kMsgConnect:
145			Connect();
146		break;
147
148		case kMsgCancel:
149			Cancel();
150		break;
151
152		default:
153			BView::MessageReceived(message);
154	}
155}
156
157
158// update authentication UI
159void
160ConnectionView::Reload()
161{
162	// load username and password
163	BString path("ptpnet/");
164	path << fInterfaceName;
165	fSettings.MakeEmpty();
166	ReadMessageDriverSettings(path.String(), &fSettings);
167
168	fHasUsername = fHasPassword = false;
169	BString username, password;
170
171	BMessage parameter;
172	int32 parameterIndex = 0;
173	if(FindMessageParameter(PPP_USERNAME_KEY, fSettings, &parameter, &parameterIndex)
174			&& parameter.FindString(MDSU_VALUES, &username) == B_OK)
175		fHasUsername = true;
176
177	parameterIndex = 0;
178	if(FindMessageParameter(PPP_PASSWORD_KEY, fSettings, &parameter, &parameterIndex)
179			&& parameter.FindString(MDSU_VALUES, &password) == B_OK)
180		fHasPassword = true;
181
182	fUsername->SetText(username.String());
183	fPassword->SetText(password.String());
184	fSavePassword->SetValue(fHasPassword);
185
186	fUsername->SetEnabled(fHasUsername);
187	fPassword->SetEnabled(fHasUsername);
188	fSavePassword->SetEnabled(fHasUsername);
189}
190
191
192void
193ConnectionView::Connect()
194{
195	PPPInterface interface(PPPManager().CreateInterfaceWithName(
196		fInterfaceName.String()));
197	interface.SetUsername(Username());
198	interface.SetPassword(Password());
199	interface.SetAskBeforeConnecting(false);
200	interface.Up();
201
202	// save settings
203	if(fHasUsername) {
204		BMessage parameter;
205		int32 index = 0;
206		if(FindMessageParameter(PPP_USERNAME_KEY, fSettings, &parameter, &index))
207			fSettings.RemoveData(MDSU_PARAMETERS, index);
208		parameter.MakeEmpty();
209		parameter.AddString(MDSU_NAME, PPP_USERNAME_KEY);
210		parameter.AddString(MDSU_VALUES, Username());
211		fSettings.AddMessage(MDSU_PARAMETERS, &parameter);
212
213		index = 0;
214		if(FindMessageParameter(PPP_PASSWORD_KEY, fSettings, &parameter, &index))
215			fSettings.RemoveData(MDSU_PARAMETERS, index);
216		if(DoesSavePassword()) {
217			parameter.MakeEmpty();
218			parameter.AddString(MDSU_NAME, PPP_PASSWORD_KEY);
219			parameter.AddString(MDSU_VALUES, Password());
220			fSettings.AddMessage(MDSU_PARAMETERS, &parameter);
221		}
222
223		BEntry entry;
224		if(interface.GetSettingsEntry(&entry) == B_OK) {
225			BFile file(&entry, B_WRITE_ONLY);
226			WriteMessageDriverSettings(file, fSettings);
227		}
228	}
229
230	Reload();
231}
232
233
234void
235ConnectionView::Cancel()
236{
237	PPPInterface interface(fListener.Interface());
238	bool quit = false;
239	ppp_interface_info_t info;
240	if(interface.GetInterfaceInfo(&info) && info.info.phase < PPP_ESTABLISHMENT_PHASE)
241		quit = true;
242	interface.Down();
243	if(quit)
244		Window()->Quit();
245}
246
247
248// Clean up before our window quits (called by ConnectionWindow).
249void
250ConnectionView::CleanUp()
251{
252	fListener.StopWatchingInterface();
253	fListener.StopWatchingManager();
254}
255
256
257BString
258ConnectionView::AttemptString() const
259{
260	PPPInterface interface(fListener.Interface());
261	ppp_interface_info_t info;
262	if(!interface.GetInterfaceInfo(&info))
263		return BString("");
264	BString attempt;
265	attempt << "Attempt " << info.info.connectAttempt << " of " <<
266		info.info.connectRetriesLimit + 1;
267
268	return attempt;
269}
270
271
272void
273ConnectionView::HandleReportMessage(BMessage *message)
274{
275	ppp_interface_id id;
276	if(message->FindInt32("interface", reinterpret_cast<int32*>(&id)) != B_OK
277			|| (fListener.Interface() != PPP_UNDEFINED_INTERFACE_ID
278				&& id != fListener.Interface()))
279		return;
280
281	int32 type, code;
282	message->FindInt32("type", &type);
283	message->FindInt32("code", &code);
284
285	if(type == PPP_MANAGER_REPORT && code == PPP_REPORT_INTERFACE_CREATED) {
286		PPPInterface interface(id);
287		if(interface.InitCheck() != B_OK || fInterfaceName != interface.Name())
288			return;
289
290		WatchInterface(id);
291
292		if(((fHasUsername && !fHasPassword) || fAskBeforeConnecting)
293				&& Window()->IsHidden())
294			Window()->Show();
295	} else if(type == PPP_CONNECTION_REPORT)
296		UpdateStatus(code);
297	else if(type == PPP_DESTRUCTION_REPORT)
298		fListener.StopWatchingInterface();
299}
300
301
302void
303ConnectionView::UpdateStatus(int32 code)
304{
305	BString attemptString = AttemptString();
306	fAttemptView->SetText(attemptString.String());
307
308	if(code == PPP_REPORT_UP_SUCCESSFUL) {
309		fStatusView->SetText(kTextConnectionEstablished);
310		PPPDeskbarReplicant *item = new PPPDeskbarReplicant(fListener.Interface());
311		BDeskbar().AddItem(item);
312		delete item;
313		Window()->Quit();
314		return;
315	}
316
317	// maybe the status string must not be changed (codes that set fKeepLabel to false
318	// should still be handled)
319	if(fKeepLabel && code != PPP_REPORT_GOING_UP && code != PPP_REPORT_UP_SUCCESSFUL)
320		return;
321
322	if(fListener.InitCheck() != B_OK) {
323		fStatusView->SetText(kTextConnectionLost);
324		return;
325	}
326
327	// only errors should set fKeepLabel to true
328	switch(code) {
329		case PPP_REPORT_GOING_UP:
330			fKeepLabel = false;
331			fStatusView->SetText(kTextConnecting);
332		break;
333
334		case PPP_REPORT_DOWN_SUCCESSFUL:
335			fStatusView->SetText(kTextNotConnected);
336		break;
337
338		case PPP_REPORT_DEVICE_UP_FAILED:
339			fKeepLabel = true;
340			fStatusView->SetText(kTextDeviceUpFailed);
341		break;
342
343		case PPP_REPORT_AUTHENTICATION_REQUESTED:
344			fStatusView->SetText(kTextAuthenticating);
345		break;
346
347		case PPP_REPORT_AUTHENTICATION_FAILED:
348			fKeepLabel = true;
349			fStatusView->SetText(kTextAuthenticationFailed);
350		break;
351
352		case PPP_REPORT_CONNECTION_LOST:
353			fKeepLabel = true;
354			fStatusView->SetText(kTextConnectionLost);
355		break;
356	}
357}
358
359
360void
361ConnectionView::WatchInterface(ppp_interface_id ID)
362{
363	fListener.WatchInterface(ID);
364
365	// update status
366	Reload();
367	PPPInterface interface(fListener.Interface());
368	ppp_interface_info_t info;
369	if(!interface.GetInterfaceInfo(&info)) {
370		UpdateStatus(PPP_REPORT_DOWN_SUCCESSFUL);
371		fAskBeforeConnecting = false;
372	} else
373		fAskBeforeConnecting = info.info.askBeforeConnecting;
374}
375