1/*
2 * Copyright 2014-2015 Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Adrien Destugues, pulkomandy@pulkomandy.tk
7 */
8
9
10#include "DNSSettingsView.h"
11
12#include <arpa/inet.h>
13#include <netinet/in.h>
14#include <resolv.h>
15#include <stdio.h>
16#include <string.h>
17#include <sys/socket.h>
18
19#include <Box.h>
20#include <Button.h>
21#include <Catalog.h>
22#include <ControlLook.h>
23#include <File.h>
24#include <FindDirectory.h>
25#include <LayoutBuilder.h>
26#include <ListView.h>
27#include <Path.h>
28#include <SeparatorView.h>
29#include <ScrollView.h>
30#include <StringView.h>
31
32#include "IPAddressControl.h"
33
34
35static const int32 kMsgAddServer = 'adds';
36static const int32 kMsgDeleteServer = 'dels';
37static const int32 kMsgSelectServer = 'sels';
38static const int32 kMsgMoveUp = 'mvup';
39static const int32 kMsgMoveDown = 'mvdn';
40static const int32 kMsgApply = 'aply';
41
42
43#undef B_TRANSLATION_CONTEXT
44#define B_TRANSLATION_CONTEXT "DNSSettingsView"
45
46
47DNSSettingsView::DNSSettingsView(BNetworkSettingsItem* item)
48	:
49	BView("dns", 0),
50	fItem(item)
51{
52	BStringView* titleView = new BStringView("title",
53		B_TRANSLATE("DNS settings"));
54	titleView->SetFont(be_bold_font);
55	titleView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
56
57	fServerListView = new BListView("nameservers");
58	fServerListView->SetSelectionMessage(new BMessage(kMsgSelectServer));
59	const char* serverLabel = B_TRANSLATE("Server:");
60	fTextControl = new IPAddressControl(AF_UNSPEC, serverLabel, "server");
61	fTextControl->SetExplicitMinSize(BSize(fTextControl->StringWidth("5") * 16
62		+ fTextControl->StringWidth(serverLabel), B_SIZE_UNSET));
63
64	fAddButton = new BButton(B_TRANSLATE("Add"), new BMessage(kMsgAddServer));
65	fAddButton->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
66	fUpButton = new BButton(B_TRANSLATE("Move up"), new BMessage(kMsgMoveUp));
67	fUpButton->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
68	fUpButton->SetEnabled(false);
69	fDownButton = new BButton(B_TRANSLATE("Move down"),
70		new BMessage(kMsgMoveDown));
71	fDownButton->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
72	fDownButton->SetEnabled(false);
73	fRemoveButton = new BButton(B_TRANSLATE("Remove"),
74		new BMessage(kMsgDeleteServer));
75	fRemoveButton->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
76	fRemoveButton->SetEnabled(false);
77	fDomain = new BTextControl(B_TRANSLATE("Domain:"), "", NULL);
78	fApplyButton = new BButton(B_TRANSLATE("Apply"), new BMessage(kMsgApply));
79
80	float spacing = be_control_look->DefaultItemSpacing();
81
82	BGridView* serviceGridView = new BGridView();
83	BLayoutBuilder::Grid<>(serviceGridView)
84		.Add(fTextControl, 0, 0)
85		.Add(fAddButton, 1, 0)
86		.Add(new BScrollView("scroll", fServerListView, 0, false, true),
87			0, 1, 1, 3)
88		.Add(fUpButton, 1, 1)
89		.Add(fDownButton, 1, 2)
90		.Add(fRemoveButton, 1, 3)
91		.SetColumnWeight(0, 10.f);
92
93	BLayoutBuilder::Group<>(this, B_VERTICAL)
94		.Add(titleView)
95		.Add(serviceGridView)
96		.Add(new BSeparatorView(B_HORIZONTAL))
97		.Add(fDomain)
98		.AddStrut(spacing)
99		.AddGroup(B_HORIZONTAL)
100			.AddGlue()
101			.Add(fApplyButton);
102
103	_LoadDNSConfiguration();
104}
105
106
107DNSSettingsView::~DNSSettingsView()
108{
109}
110
111
112status_t
113DNSSettingsView::Revert()
114{
115	int i;
116	for (i = 0; i < fRevertList.CountStrings(); i++) {
117		BStringItem* item = static_cast<BStringItem*>(
118			fServerListView->ItemAt(i));
119		if (item == NULL) {
120			item = new BStringItem("");
121			fServerListView->AddItem(item);
122		}
123
124		item->SetText(fRevertList.StringAt(i));
125	}
126
127	// Now remove any extra item
128	for (; i < fServerListView->CountItems(); i++)
129		delete fServerListView->RemoveItem(i);
130
131	return B_OK;
132}
133
134
135bool
136DNSSettingsView::IsRevertable() const
137{
138	// TODO
139	return false;
140}
141
142
143void
144DNSSettingsView::AttachedToWindow()
145{
146	fAddButton->SetTarget(this);
147	fRemoveButton->SetTarget(this);
148	fUpButton->SetTarget(this);
149	fDownButton->SetTarget(this);
150
151	fServerListView->SetTarget(this);
152
153	fTextControl->SetTarget(this);
154
155	fApplyButton->SetTarget(this);
156}
157
158
159void
160DNSSettingsView::MessageReceived(BMessage* message)
161{
162	switch (message->what) {
163		case kMsgAddServer:
164		{
165			const char* address = fTextControl->Text();
166			fServerListView->AddItem(new BStringItem(address));
167			break;
168		}
169		case kMsgDeleteServer:
170			delete fServerListView->RemoveItem(
171				fServerListView->CurrentSelection());
172			break;
173
174		case kMsgMoveUp:
175		{
176			int index = fServerListView->CurrentSelection();
177			if (index > 0)
178				fServerListView->SwapItems(index, index - 1);
179			break;
180		}
181		case kMsgMoveDown:
182		{
183			int index = fServerListView->CurrentSelection();
184			if (index < fServerListView->CountItems() - 1)
185				fServerListView->SwapItems(index, index + 1);
186			break;
187		}
188		case kMsgSelectServer:
189		{
190			bool enabled = false;
191			if (fServerListView->CurrentSelection() > -1)
192				enabled = true;
193
194			fUpButton->SetEnabled(enabled);
195			fDownButton->SetEnabled(enabled);
196			fRemoveButton->SetEnabled(enabled);
197			break;
198		}
199		case kMsgApply:
200			if (_SaveDNSConfiguration() == B_OK)
201				fItem->NotifySettingsUpdated();
202			break;
203
204		default:
205			BView::MessageReceived(message);
206			break;
207	}
208}
209
210
211status_t
212DNSSettingsView::_LoadDNSConfiguration()
213{
214	if (res_init() != 0)
215		return B_ERROR;
216
217	struct __res_state state;
218	memset(&state, 0, sizeof(struct __res_state));
219
220	if (res_ninit(&state) != 0)
221		return B_ERROR;
222
223	for (int i = 0; i < state.nscount; i++) {
224		char* address = inet_ntoa(state.nsaddr_list[i].sin_addr);
225		fServerListView->AddItem(new BStringItem(address));
226		fRevertList.Add(address);
227	}
228
229	fDomain->SetText(state.dnsrch[0]);
230
231	res_nclose(&state);
232	return B_OK;
233}
234
235
236status_t
237DNSSettingsView::_SaveDNSConfiguration()
238{
239	BPath path;
240	status_t status;
241	status = find_directory(B_SYSTEM_SETTINGS_DIRECTORY, &path);
242	if (status != B_OK)
243		return status;
244
245	path.Append("network/resolv.conf");
246
247	BFile file(path.Path(), B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY);
248	if (file.InitCheck() != B_OK) {
249		fprintf(stderr, "failed to open %s for writing: %s\n", path.Path(),
250			strerror(file.InitCheck()));
251		return file.InitCheck();
252	}
253
254	BString content("# Static DNS entries\n# Generated by Network preferences\n");
255
256	for (int i = 0; i < fServerListView->CountItems(); i++) {
257		BString item = ((BStringItem*)fServerListView->ItemAt(i))->Text();
258		if (item.Length() > 0)
259			content << "nameserver\t" << item.String() << "\n";
260	}
261
262	if (strlen(fDomain->Text()) > 0)
263		content << "domain\t" << fDomain->Text() << "\n";
264
265	content << "# Dynamic DNS entries\n# may be altered by DHCP\n";
266
267	return file.Write(content.String(), content.Length());
268}
269