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// PPPoEAddon saves the loaded settings.
9// PPPoEView saves the current settings.
10//-----------------------------------------------------------------------
11
12#include "PPPoEAddon.h"
13
14#include "InterfaceUtils.h"
15#include "MessageDriverSettingsUtils.h"
16#include "TextRequestDialog.h"
17
18#include <Box.h>
19#include <MenuField.h>
20#include <MenuItem.h>
21#include <PopUpMenu.h>
22#include <StringView.h>
23
24#include <PPPManager.h>
25#include <PPPoE.h>
26	// from PPPoE addon
27
28
29// GUI constants
30static const uint32 kDefaultButtonWidth = 80;
31
32// message constants
33static const uint32 kMsgSelectInterface = 'SELI';
34static const uint32 kMsgSelectOther = 'SELO';
35static const uint32 kMsgFinishSelectOther = 'FISO';
36static const uint32 kMsgShowServiceWindow = 'SHSW';
37static const uint32 kMsgChangeService = 'CHGS';
38static const uint32 kMsgResetService = 'RESS';
39
40// labels
41static const char *kLabelInterfaceName = "Network Interface: ";
42static const char *kLabelOptional = "(Optional)";
43static const char *kLabelOtherInterface = "Other:";
44static const char *kLabelSelectInterface = "Select Interface...";
45static const char *kLabelServiceName = "Service: ";
46
47// requests
48static const char *kRequestInterfaceName = "Network Interface Name: ";
49
50// add-on descriptions
51static const char *kFriendlyName = "Broadband: DSL, Cable, etc.";
52static const char *kTechnicalName = "PPPoE";
53static const char *kKernelModuleName = "pppoe";
54
55
56PPPoEAddon::PPPoEAddon(BMessage *addons)
57	: DialUpAddon(addons),
58	fSettings(NULL),
59	fProfile(NULL),
60	fPPPoEView(NULL)
61{
62	fHeight = 20 // interface name control
63		+ 20 // service control
64		+ 5 + 2; // space between controls and bottom
65}
66
67
68PPPoEAddon::~PPPoEAddon()
69{
70	delete fPPPoEView;
71		// this may have been set to NULL from the view's destructor!
72}
73
74
75const char*
76PPPoEAddon::FriendlyName() const
77{
78	return kFriendlyName;
79}
80
81
82const char*
83PPPoEAddon::TechnicalName() const
84{
85	return kTechnicalName;
86}
87
88
89const char*
90PPPoEAddon::KernelModuleName() const
91{
92	return kKernelModuleName;
93}
94
95
96bool
97PPPoEAddon::LoadSettings(BMessage *settings, BMessage *profile, bool isNew)
98{
99	fIsNew = isNew;
100	fInterfaceName = fServiceName = "";
101	fSettings = settings;
102	fProfile = profile;
103
104	if(fPPPoEView)
105		fPPPoEView->Reload();
106
107	if(!settings || !profile || isNew)
108		return true;
109
110	BMessage device;
111	int32 deviceIndex = 0;
112	if(!FindMessageParameter(PPP_DEVICE_KEY, *fSettings, &device, &deviceIndex))
113		return false;
114			// error: no device
115
116	BString name;
117	if(device.FindString(MDSU_VALUES, &name) != B_OK || name != kKernelModuleName)
118		return false;
119			// error: no device
120
121	BMessage parameter;
122	int32 index = 0;
123	if(!FindMessageParameter(PPPoE_INTERFACE_KEY, device, &parameter, &index)
124			|| parameter.FindString(MDSU_VALUES, &fInterfaceName) != B_OK)
125		return false;
126			// error: no interface
127	else {
128		parameter.AddBool(MDSU_VALID, true);
129		device.ReplaceMessage(MDSU_PARAMETERS, index, &parameter);
130	}
131
132	index = 0;
133	if(!FindMessageParameter(PPPoE_SERVICE_NAME_KEY, device, &parameter, &index)
134			|| parameter.FindString(MDSU_VALUES, &fServiceName) != B_OK)
135		fServiceName = "";
136	else {
137		parameter.AddBool(MDSU_VALID, true);
138		device.ReplaceMessage(MDSU_PARAMETERS, index, &parameter);
139	}
140
141	device.AddBool(MDSU_VALID, true);
142	fSettings->ReplaceMessage(MDSU_PARAMETERS, deviceIndex, &device);
143
144	if(fPPPoEView)
145		fPPPoEView->Reload();
146
147	return true;
148}
149
150
151void
152PPPoEAddon::IsModified(bool *settings, bool *profile) const
153{
154	*profile = false;
155
156	if(!fSettings) {
157		*settings = false;
158		return;
159	}
160
161	*settings = (fInterfaceName != fPPPoEView->InterfaceName()
162		|| fServiceName != fPPPoEView->ServiceName());
163}
164
165
166bool
167PPPoEAddon::SaveSettings(BMessage *settings, BMessage *profile, bool saveTemporary)
168{
169	if(!fSettings || !settings || !fPPPoEView->InterfaceName()
170			|| strlen(fPPPoEView->InterfaceName()) == 0)
171		return false;
172			// TODO: tell user that an interface is needed (if we fail because of this)
173
174	BMessage device, interface;
175	device.AddString(MDSU_NAME, PPP_DEVICE_KEY);
176	device.AddString(MDSU_VALUES, kKernelModuleName);
177
178	interface.AddString(MDSU_NAME, PPPoE_INTERFACE_KEY);
179	interface.AddString(MDSU_VALUES, fPPPoEView->InterfaceName());
180	device.AddMessage(MDSU_PARAMETERS, &interface);
181
182	if(fPPPoEView->ServiceName() && strlen(fPPPoEView->ServiceName()) > 0) {
183		// save service name, too
184		BMessage service;
185		service.AddString(MDSU_NAME, PPPoE_SERVICE_NAME_KEY);
186		service.AddString(MDSU_VALUES, fPPPoEView->ServiceName());
187		device.AddMessage(MDSU_PARAMETERS, &service);
188	}
189
190	settings->AddMessage(MDSU_PARAMETERS, &device);
191
192	return true;
193}
194
195
196bool
197PPPoEAddon::GetPreferredSize(float *width, float *height) const
198{
199	float viewWidth;
200	if(Addons()->FindFloat(DUN_DEVICE_VIEW_WIDTH, &viewWidth) != B_OK)
201		viewWidth = 270;
202			// default value
203
204	if(width)
205		*width = viewWidth;
206	if(height)
207		*height = fHeight;
208
209	return true;
210}
211
212
213BView*
214PPPoEAddon::CreateView()
215{
216	if(!fPPPoEView) {
217		float width;
218		if(!Addons()->FindFloat(DUN_DEVICE_VIEW_WIDTH, &width))
219			width = 270;
220				// default value
221
222		BRect rect(0, 0, width, fHeight);
223		fPPPoEView = new PPPoEView(this, rect);
224		fPPPoEView->Reload();
225	}
226
227	return fPPPoEView;
228}
229
230
231PPPoEView::PPPoEView(PPPoEAddon *addon, BRect frame)
232	: BView(frame, "PPPoEView", B_FOLLOW_NONE, 0),
233	fAddon(addon)
234{
235	BRect rect = Bounds();
236	rect.InsetBy(5, 0);
237	rect.bottom = 20;
238	fInterface = new BMenuField(rect, "interface", kLabelInterfaceName,
239		new BPopUpMenu(kLabelSelectInterface));
240	fInterface->SetDivider(StringWidth(fInterface->Label()) + 5);
241	fInterface->Menu()->AddSeparatorItem();
242	fOtherInterface = new BMenuItem(kLabelOtherInterface,
243		new BMessage(kMsgSelectOther));
244	fInterface->Menu()->AddItem(fOtherInterface);
245	rect.top = rect.bottom + 5;
246	rect.bottom = rect.top + 20;
247	rect.right -= 75;
248	fServiceName = new BTextControl(rect, "service", kLabelServiceName, NULL, NULL);
249	fServiceName->SetDivider(StringWidth(fServiceName->Label()) + 5);
250	rect.left = rect.right + 5;
251	rect.right += 75;
252	rect.bottom = rect.top + 15;
253	AddChild(new BStringView(rect, "optional", kLabelOptional));
254
255	AddChild(fInterface);
256	AddChild(fServiceName);
257}
258
259
260PPPoEView::~PPPoEView()
261{
262	Addon()->UnregisterView();
263}
264
265
266void
267PPPoEView::Reload()
268{
269	ReloadInterfaces();
270	fServiceName->SetText(Addon()->ServiceName());
271}
272
273
274void
275PPPoEView::AttachedToWindow()
276{
277	SetViewColor(Parent()->ViewColor());
278	fInterface->Menu()->SetTargetForItems(this);
279	fServiceName->SetTarget(this);
280}
281
282
283void
284PPPoEView::MessageReceived(BMessage *message)
285{
286	switch(message->what) {
287		case kMsgSelectInterface: {
288			BMenuItem *item = fInterface->Menu()->FindMarked();
289			if(item)
290				fInterfaceName = item->Label();
291		} break;
292
293		case kMsgSelectOther:
294			(new TextRequestDialog("InterfaceName", NULL, kRequestInterfaceName,
295				fInterfaceName.String()))->Go(new BInvoker(
296				new BMessage(kMsgFinishSelectOther), this));
297		break;
298
299		case kMsgFinishSelectOther: {
300			int32 which;
301			message->FindInt32("which", &which);
302
303			const char *name = message->FindString("text");
304			BMenu *menu = fInterface->Menu();
305			BMenuItem *item;
306			if(which != 1 || !name || strlen(name) == 0) {
307				item = menu->FindItem(fInterfaceName.String());
308				if(item && menu->IndexOf(item) <= menu->CountItems() - 2)
309					item->SetMarked(true);
310				else
311					fOtherInterface->SetMarked(true);
312
313				return;
314			}
315
316			fInterfaceName = name;
317
318			item = menu->FindItem(fInterfaceName.String());
319			if(item && menu->IndexOf(item) <= menu->CountItems() - 2) {
320				item->SetMarked(true);
321				return;
322			}
323
324			BString label(kLabelOtherInterface);
325			label << " " << name;
326			fOtherInterface->SetLabel(label.String());
327			fOtherInterface->SetMarked(true);
328				// XXX: this is needed to tell the owning menu to update its label
329		} break;
330
331		default:
332			BView::MessageReceived(message);
333	}
334}
335
336
337void
338PPPoEView::ReloadInterfaces()
339{
340	// delete all items and request a new bunch from the pppoe kernel module
341	BMenu *menu = fInterface->Menu();
342	while(menu->CountItems() > 2)
343		delete menu->RemoveItem((int32) 0);
344	fOtherInterface->SetLabel(kLabelOtherInterface);
345
346	PPPManager manager;
347	char *interfaces = new char[8192];
348		// reserve enough space for approximately 512 entries
349	int32 count = manager.ControlModule("pppoe", PPPoE_GET_INTERFACES, interfaces,
350		8192);
351
352	BMenuItem *item;
353	char *name = interfaces;
354	int32 insertAt;
355	for(int32 index = 0; index < count; index++) {
356		item = new BMenuItem(name, new BMessage(kMsgSelectInterface));
357		insertAt = FindNextMenuInsertionIndex(menu, name);
358		if(insertAt > menu->CountItems() - 2)
359			insertAt = menu->CountItems() - 2;
360
361		item->SetTarget(this);
362		menu->AddItem(item, insertAt);
363		name += strlen(name) + 1;
364	}
365
366	// set interface or some default value if nothing was found
367	if(Addon()->InterfaceName() && strlen(Addon()->InterfaceName()) > 0)
368		fInterfaceName = Addon()->InterfaceName();
369	else if(count > 0)
370		fInterfaceName = interfaces;
371	else
372		fInterfaceName = "";
373
374	delete interfaces;
375
376	item = menu->FindItem(fInterfaceName.String());
377	if(item && menu->IndexOf(item) <= menu->CountItems() - 2)
378		item->SetMarked(true);
379	else if(Addon()->InterfaceName()) {
380		BString label(kLabelOtherInterface);
381		label << " " << fInterfaceName;
382		fOtherInterface->SetLabel(label.String());
383		fOtherInterface->SetMarked(true);
384	}
385}
386