1/*
2 * Copyright 2003-2004 Waldemar Kornewald. All rights reserved.
3 * Copyright 2017 Haiku, Inc. All rights reserved.
4 * Distributed under the terms of the MIT License.
5 */
6
7//-----------------------------------------------------------------------
8// GeneralAddon saves the loaded settings.
9// GeneralView saves the current settings.
10//-----------------------------------------------------------------------
11
12#include "GeneralAddon.h"
13
14#include "InterfaceUtils.h"
15#include "MessageDriverSettingsUtils.h"
16
17#include <Box.h>
18#include <Button.h>
19#include <MenuField.h>
20#include <MenuItem.h>
21#include <LayoutBuilder.h>
22#include <PopUpMenu.h>
23#include <StringView.h>
24
25#include <PPPDefs.h>
26
27
28// message constants
29static const uint32 kMsgSelectDevice = 'SELD';
30static const uint32 kMsgSelectAuthenticator = 'SELA';
31
32// labels
33static const char *kLabelGeneral = "General";
34static const char *kLabelDevice = "Device: ";
35static const char *kLabelNoDevicesFound = "No Devices Found!";
36static const char *kLabelAuthenticator = "Login: ";
37static const char *kLabelNoAuthenticatorsFound = "No Authenticators Found!";
38static const char *kLabelName = "Username: ";
39static const char *kLabelPassword = "Password: ";
40static const char *kLabelSavePassword = "Save Password";
41static const char *kLabelNone = "None";
42
43// string constants for information saved in the settings message
44static const char *kGeneralTabAuthentication = "Authentication";
45static const char *kGeneralTabAuthenticators = "Authenticators";
46
47
48#define DEFAULT_AUTHENTICATOR		"PAP"
49	// this authenticator is selected by default when creating a new interface
50
51
52GeneralAddon::GeneralAddon(BMessage *addons)
53	: DialUpAddon(addons),
54	fHasPassword(false),
55	fAuthenticatorsCount(0),
56	fSettings(NULL),
57	fProfile(NULL),
58	fGeneralView(NULL)
59{
60}
61
62
63GeneralAddon::~GeneralAddon()
64{
65}
66
67
68bool
69GeneralAddon::NeedsAuthenticationRequest() const
70{
71	return fGeneralView->AuthenticatorName();
72}
73
74
75DialUpAddon*
76GeneralAddon::FindDevice(const BString& moduleName) const
77{
78	DialUpAddon *addon;
79	for(int32 index = 0; Addons()->FindPointer(DUN_DEVICE_ADDON_TYPE, index,
80			reinterpret_cast<void**>(&addon)) == B_OK; index++)
81		if(addon && moduleName == addon->KernelModuleName())
82			return addon;
83
84	return NULL;
85}
86
87
88bool
89GeneralAddon::LoadSettings(BMessage *settings, BMessage *profile, bool isNew)
90{
91	fIsNew = isNew;
92	fHasPassword = false;
93	fDeviceName = fUsername = fPassword = "";
94	fDeviceAddon = NULL;
95	fAuthenticatorsCount = 0;
96	fSettings = settings;
97	fProfile = profile;
98
99	if(fGeneralView)
100		fGeneralView->Reload();
101			// reset all views (empty settings)
102
103	if(!settings || !profile || isNew)
104		return true;
105
106	if(!LoadDeviceSettings())
107		return false;
108
109	if(!LoadAuthenticationSettings())
110		return false;
111
112	if(fGeneralView)
113		fGeneralView->Reload();
114			// reload new settings
115
116	return true;
117}
118
119
120bool
121GeneralAddon::LoadDeviceSettings()
122{
123	int32 index = 0;
124	BMessage device;
125	if(!FindMessageParameter(PPP_DEVICE_KEY, *fSettings, &device, &index))
126		return false;
127			// TODO: tell user that device specification is missing
128
129	if(device.FindString(MDSU_VALUES, &fDeviceName) != B_OK)
130		return false;
131			// TODO: tell user that device specification is missing
132
133	device.AddBool(MDSU_VALID, true);
134	fSettings->ReplaceMessage(MDSU_PARAMETERS, index, &device);
135
136	fDeviceAddon = FindDevice(fDeviceName);
137	if(!fDeviceAddon)
138		return false;
139
140	return fDeviceAddon->LoadSettings(fSettings, fProfile, false);
141}
142
143
144bool
145GeneralAddon::LoadAuthenticationSettings()
146{
147	// we only handle the profile (although settings could contain different data)
148	int32 itemIndex = 0;
149	BMessage authentication, item;
150
151	if(!FindMessageParameter(PPP_AUTHENTICATOR_KEY, *fProfile, &item, &itemIndex))
152		return true;
153
154	// find authenticators (though we load all authenticators, we only use one)
155	BString name;
156	for(int32 index = 0; item.FindString(MDSU_VALUES, index, &name) == B_OK; index++) {
157		BMessage authenticator;
158		if(!GetAuthenticator(name, &authenticator))
159			return false;
160				// fatal error: we do not know how to handle this authenticator
161
162		MarkAuthenticatorAsValid(name);
163		authentication.AddString(kGeneralTabAuthenticators, name);
164		++fAuthenticatorsCount;
165	}
166
167	fSettings->AddMessage(kGeneralTabAuthentication, &authentication);
168
169	bool hasUsername = false;
170		// a username must be present
171
172	// load username and password
173	BMessage parameter;
174	int32 parameterIndex = 0;
175	if(FindMessageParameter("User", item, &parameter, &parameterIndex)
176			&& parameter.FindString(MDSU_VALUES, &fUsername) == B_OK) {
177		hasUsername = true;
178		parameter.AddBool(MDSU_VALID, true);
179		item.ReplaceMessage(MDSU_PARAMETERS, parameterIndex, &parameter);
180	}
181
182	parameterIndex = 0;
183	if(FindMessageParameter("Password", item, &parameter, &parameterIndex)
184			&& parameter.FindString(MDSU_VALUES, &fPassword) == B_OK) {
185		fHasPassword = true;
186		parameter.AddBool(MDSU_VALID, true);
187		item.ReplaceMessage(MDSU_PARAMETERS, parameterIndex, &parameter);
188	}
189
190	// tell DUN whether everything is valid
191	if(hasUsername)
192		item.AddBool(MDSU_VALID, true);
193
194	fProfile->ReplaceMessage(MDSU_PARAMETERS, itemIndex, &item);
195
196	return true;
197}
198
199
200bool
201GeneralAddon::HasTemporaryProfile() const
202{
203	return fGeneralView->HasTemporaryProfile();
204}
205
206
207void
208GeneralAddon::IsModified(bool *settings, bool *profile) const
209{
210	if(!fSettings) {
211		*settings = *profile = false;
212		return;
213	}
214
215	bool deviceSettings, authenticationSettings, deviceProfile, authenticationProfile;
216
217	IsDeviceModified(&deviceSettings, &deviceProfile);
218	IsAuthenticationModified(&authenticationSettings, &authenticationProfile);
219
220	*settings = (deviceSettings || authenticationSettings);
221	*profile = (deviceProfile || authenticationProfile);
222}
223
224
225void
226GeneralAddon::IsDeviceModified(bool *settings, bool *profile) const
227{
228	fGeneralView->IsDeviceModified(settings, profile);
229}
230
231
232void
233GeneralAddon::IsAuthenticationModified(bool *settings, bool *profile) const
234{
235	// currently we only support selecting one authenticator
236	if(fAuthenticatorsCount == 0)
237		*settings = fGeneralView->AuthenticatorName();
238	else {
239		BMessage authentication;
240		if(fSettings->FindMessage(kGeneralTabAuthentication, &authentication) != B_OK) {
241			*settings = *profile = false;
242			return;
243				// error!
244		}
245
246		BString authenticator;
247		if(authentication.FindString(kGeneralTabAuthenticators,
248				&authenticator) != B_OK) {
249			*settings = *profile = false;
250			return;
251				// error!
252		}
253
254		*settings = (!fGeneralView->AuthenticatorName()
255			|| authenticator != fGeneralView->AuthenticatorName());
256	}
257
258	*profile = (*settings || fUsername != fGeneralView->Username()
259		|| (fPassword != fGeneralView->Password() && fHasPassword)
260		|| fHasPassword != fGeneralView->DoesSavePassword());
261}
262
263
264bool
265GeneralAddon::SaveSettings(BMessage *settings, BMessage *profile, bool saveTemporary)
266{
267	if(!fSettings || !settings || !fGeneralView->DeviceName())
268		return false;
269			// TODO: tell user that a device is needed (if we fail because of this)
270
271	if(!fGeneralView->DeviceAddon() || !fGeneralView->DeviceAddon()->SaveSettings(
272			settings, profile, saveTemporary))
273		return false;
274
275	if(fGeneralView->AuthenticatorName()) {
276		BMessage authenticator;
277		authenticator.AddString(MDSU_NAME, PPP_AUTHENTICATOR_KEY);
278		authenticator.AddString(MDSU_VALUES, fGeneralView->AuthenticatorName());
279		settings->AddMessage(MDSU_PARAMETERS, &authenticator);
280
281		BMessage username;
282		username.AddString(MDSU_NAME, "User");
283		username.AddString(MDSU_VALUES, fGeneralView->Username());
284		authenticator.AddMessage(MDSU_PARAMETERS, &username);
285
286		if(saveTemporary || fGeneralView->DoesSavePassword()) {
287			// save password, too
288			BMessage password;
289			password.AddString(MDSU_NAME, "Password");
290			password.AddString(MDSU_VALUES, fGeneralView->Password());
291			authenticator.AddMessage(MDSU_PARAMETERS, &password);
292		}
293
294		profile->AddMessage(MDSU_PARAMETERS, &authenticator);
295	}
296
297	return true;
298}
299
300
301bool
302GeneralAddon::GetPreferredSize(float *width, float *height) const
303{
304	BRect rect;
305	if(Addons()->FindRect(DUN_TAB_VIEW_RECT, &rect) != B_OK)
306		rect.Set(0, 0, 200, 300);
307			// set default values
308
309	if(width)
310		*width = rect.Width();
311	if(height)
312		*height = rect.Height();
313
314	return true;
315}
316
317
318BView*
319GeneralAddon::CreateView()
320{
321	if (!fGeneralView) {
322		fGeneralView = new GeneralView(this);
323		fGeneralView->Reload();
324	}
325
326	return fGeneralView;
327}
328
329
330bool
331GeneralAddon::GetAuthenticator(const BString& moduleName, BMessage *entry) const
332{
333	if(!entry)
334		return false;
335
336	BString name;
337	for(int32 index = 0; Addons()->FindMessage(DUN_AUTHENTICATOR_ADDON_TYPE, index,
338			entry) == B_OK; index++) {
339		entry->FindString("KernelModuleName", &name);
340		if (name == moduleName)
341			return true;
342	}
343
344	return false;
345}
346
347
348bool
349GeneralAddon::MarkAuthenticatorAsValid(const BString& moduleName)
350{
351	BMessage authenticator;
352	int32 index = 0;
353	BString name;
354
355	for(; FindMessageParameter(PPP_AUTHENTICATOR_KEY, *fSettings, &authenticator,
356			&index); index++) {
357		authenticator.FindString("KernelModuleName", &name);
358		if(name == moduleName) {
359			authenticator.AddBool(MDSU_VALID, true);
360			fSettings->ReplaceMessage(MDSU_PARAMETERS, index, &authenticator);
361			return true;
362		}
363	}
364
365	return false;
366}
367
368
369GeneralView::GeneralView(GeneralAddon *addon)
370	: BView(kLabelGeneral, 0),
371	fAddon(addon)
372{
373	fDeviceBox = new BBox("Device");
374	Addon()->Addons()->AddFloat(DUN_DEVICE_VIEW_WIDTH,
375		fDeviceBox->Bounds().Width() - 10); // FIXME: remove
376
377	fDeviceField = new BMenuField("Device",
378		kLabelDevice, new BPopUpMenu(kLabelNoDevicesFound));
379	fDeviceField->Menu()->SetRadioMode(true);
380	AddDevices();
381	fDeviceBox->SetLabel(fDeviceField);
382
383	fAuthenticatorField = new BMenuField("Authenticator",
384		kLabelAuthenticator, new BPopUpMenu(kLabelNoAuthenticatorsFound));
385	fAuthenticatorField->Menu()->SetRadioMode(true);
386	AddAuthenticators();
387
388	fUsername = new BTextControl("username", kLabelName, NULL, NULL);
389	fPassword = new BTextControl("password", kLabelPassword, NULL, NULL);
390	fPassword->TextView()->HideTyping(true);
391
392	fSavePassword = new BCheckBox("SavePassword", kLabelSavePassword, NULL);
393
394	BLayoutBuilder::Group<>(this, B_VERTICAL, B_USE_HALF_ITEM_SPACING)
395		.SetInsets(B_USE_HALF_ITEM_INSETS)
396		.AddGroup(B_HORIZONTAL)
397			.Add(fDeviceBox)
398			.AddGlue()
399		.End()
400		.Add(fAuthenticatorField)
401		.Add(fUsername)
402		.Add(fPassword)
403		.Add(fSavePassword)
404		.AddGlue()
405	.End();
406}
407
408
409GeneralView::~GeneralView()
410{
411}
412
413
414void
415GeneralView::Reload()
416{
417	fDeviceAddon = NULL;
418
419	BMenuItem *item = NULL;
420	for(int32 index = 0; index < fDeviceField->Menu()->CountItems(); index++) {
421		item = fDeviceField->Menu()->ItemAt(index);
422		if(item && item->Message() && item->Message()->FindPointer("Addon",
423					reinterpret_cast<void**>(&fDeviceAddon)) == B_OK
424				&& fDeviceAddon == Addon()->DeviceAddon())
425			break;
426	}
427
428	if(fDeviceAddon && fDeviceAddon == Addon()->DeviceAddon())
429		item->SetMarked(true);
430	else if(Addon()->IsNew() && fDeviceField->Menu()->CountItems() > 0) {
431		item = fDeviceField->Menu()->ItemAt(0);
432		item->SetMarked(true);
433		item->Message()->FindPointer("Addon", reinterpret_cast<void**>(&fDeviceAddon));
434		fDeviceAddon->LoadSettings(Addon()->Settings(), Addon()->Profile(), true);
435	} else {
436		fDeviceAddon = NULL;
437		item = fDeviceField->Menu()->FindMarked();
438		if(item)
439			item->SetMarked(false);
440	}
441
442	if(Addon()->CountAuthenticators() > 0) {
443		BString kernelModule, authenticator;
444		BMessage authentication;
445		if(Addon()->Settings()->FindMessage(kGeneralTabAuthentication,
446				&authentication) == B_OK)
447			authentication.FindString(kGeneralTabAuthenticators, &authenticator);
448		BMenu *menu = fAuthenticatorField->Menu();
449		for(int32 index = 0; index < menu->CountItems(); index++) {
450			item = menu->ItemAt(index);
451			if(item && item->Message()
452					&& item->Message()->FindString("KernelModuleName",
453						&kernelModule) == B_OK && kernelModule == authenticator) {
454				item->SetMarked(true);
455				break;
456			}
457		}
458	} else if(Addon()->IsNew() && fAuthenticatorDefault)
459		fAuthenticatorDefault->SetMarked(true);
460	else
461		fAuthenticatorNone->SetMarked(true);
462
463	fUsername->SetText(Addon()->Username());
464	fPassword->SetText(Addon()->Password());
465	fSavePassword->SetValue(Addon()->HasPassword());
466
467	ReloadDeviceView();
468	UpdateControls();
469}
470
471
472const char*
473GeneralView::DeviceName() const
474{
475	if(fDeviceAddon)
476		return fDeviceAddon->KernelModuleName();
477
478	return NULL;
479}
480
481
482const char*
483GeneralView::AuthenticatorName() const
484{
485	BMenuItem *marked = fAuthenticatorField->Menu()->FindMarked();
486	if(marked && marked != fAuthenticatorNone)
487		return marked->Message()->FindString("KernelModuleName");
488
489	return NULL;
490}
491
492
493void
494GeneralView::IsDeviceModified(bool *settings, bool *profile) const
495{
496	if(fDeviceAddon != Addon()->DeviceAddon())
497		*settings = *profile = true;
498	else if(fDeviceAddon)
499		fDeviceAddon->IsModified(settings, profile);
500	else
501		*settings = *profile = false;
502}
503
504
505void
506GeneralView::AttachedToWindow()
507{
508	SetViewColor(Parent()->ViewColor());
509	fDeviceField->Menu()->SetTargetForItems(this);
510	fAuthenticatorField->Menu()->SetTargetForItems(this);
511	fUsername->SetTarget(this);
512	fPassword->SetTarget(this);
513}
514
515
516void
517GeneralView::MessageReceived(BMessage *message)
518{
519	switch(message->what) {
520		case kMsgSelectDevice:
521			if(message->FindPointer("Addon", reinterpret_cast<void**>(&fDeviceAddon))
522					!= B_OK)
523				fDeviceAddon = NULL;
524			else {
525				if(fDeviceAddon != Addon()->DeviceAddon())
526					fDeviceAddon->LoadSettings(Addon()->Settings(), Addon()->Profile(),
527						Addon()->IsNew());
528
529				ReloadDeviceView();
530			}
531		break;
532
533		case kMsgSelectAuthenticator:
534			UpdateControls();
535		break;
536
537		default:
538			BView::MessageReceived(message);
539	}
540}
541
542
543void
544GeneralView::ReloadDeviceView()
545{
546	// first remove existing device view(s)
547	while (fDeviceBox->CountChildren() > 1)
548		fDeviceBox->RemoveChild(fDeviceBox->ChildAt(1));
549
550	if (!fDeviceAddon)
551		return;
552
553	BView* deviceView = fDeviceAddon->CreateView();
554	if (deviceView) {
555		BLayoutBuilder::Group<>(fDeviceBox, B_VERTICAL)
556			.Add(deviceView)
557		.End();
558	}
559}
560
561
562void
563GeneralView::UpdateControls()
564{
565	BMenu *menu = fAuthenticatorField->Menu();
566	int32 index = menu->IndexOf(menu->FindMarked());
567	if (index < 0)
568		fAuthenticatorNone->SetMarked(true);
569
570	if (index == 0) {
571		fUsername->SetEnabled(false);
572		fPassword->SetEnabled(false);
573		fSavePassword->SetEnabled(false);
574	} else {
575		fUsername->SetEnabled(true);
576		fPassword->SetEnabled(true);
577		fSavePassword->SetEnabled(true);
578	}
579}
580
581
582void
583GeneralView::AddDevices()
584{
585	AddAddonsToMenu(Addon()->Addons(), fDeviceField->Menu(), DUN_DEVICE_ADDON_TYPE,
586		kMsgSelectDevice);
587}
588
589
590void
591GeneralView::AddAuthenticators()
592{
593	fAuthenticatorDefault = NULL;
594	fAuthenticatorNone = new BMenuItem(kLabelNone,
595		new BMessage(kMsgSelectAuthenticator));
596	fAuthenticatorField->Menu()->AddItem(fAuthenticatorNone);
597	fAuthenticatorNone->SetMarked(true);
598	fAuthenticatorField->Menu()->AddSeparatorItem();
599
600	BMenuItem *item;
601	BMessage addon;
602	for(int32 index = 0;
603			Addon()->Addons()->FindMessage(DUN_AUTHENTICATOR_ADDON_TYPE, index,
604			&addon) == B_OK; index++) {
605		BMessage *message = new BMessage(kMsgSelectAuthenticator);
606		message->AddString("KernelModuleName", addon.FindString("KernelModuleName"));
607
608		BString name, technicalName, friendlyName;
609		bool hasTechnicalName
610			= (addon.FindString("TechnicalName", &technicalName) == B_OK);
611		bool hasFriendlyName
612			= (addon.FindString("FriendlyName", &friendlyName) == B_OK);
613		if(hasTechnicalName) {
614			name << technicalName;
615			if(hasFriendlyName)
616				name << " (";
617		}
618		if(hasFriendlyName) {
619			name << friendlyName;
620			if(hasTechnicalName)
621				name << ")";
622		}
623
624		int32 insertAt = FindNextMenuInsertionIndex(fAuthenticatorField->Menu(),
625			name.String(), 2);
626		item = new BMenuItem(name.String(), message);
627		if(hasTechnicalName && technicalName == DEFAULT_AUTHENTICATOR)
628			fAuthenticatorDefault = item;
629		fAuthenticatorField->Menu()->AddItem(item, insertAt);
630	}
631}
632