1/*
2 * Copyright 2006-2010, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Axel Dörfler, axeld@pinc-software.de
7 */
8
9
10#include "Settings.h"
11
12#include <Directory.h>
13#include <FindDirectory.h>
14#include <fs_interface.h>
15#include <Path.h>
16#include <PathMonitor.h>
17#include <String.h>
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22
23
24struct settings_template {
25	uint32		type;
26	const char*	name;
27	const settings_template* sub_template;
28	bool		parent_value;
29};
30
31// Interface templates
32
33const static settings_template kInterfaceAddressTemplate[] = {
34	{B_STRING_TYPE, "family", NULL, true},
35	{B_STRING_TYPE, "address", NULL},
36	{B_STRING_TYPE, "mask", NULL},
37	{B_STRING_TYPE, "peer", NULL},
38	{B_STRING_TYPE, "broadcast", NULL},
39	{B_STRING_TYPE, "gateway", NULL},
40	{B_BOOL_TYPE, "auto_config", NULL},
41	{0, NULL, NULL}
42};
43
44const static settings_template kInterfaceTemplate[] = {
45	{B_STRING_TYPE, "device", NULL, true},
46	{B_BOOL_TYPE, "disabled", NULL},
47	{B_MESSAGE_TYPE, "address", kInterfaceAddressTemplate},
48	{B_STRING_TYPE, "network", NULL},
49	{B_INT32_TYPE, "flags", NULL},
50	{B_INT32_TYPE, "metric", NULL},
51	{B_INT32_TYPE, "mtu", NULL},
52	{0, NULL, NULL}
53};
54
55const static settings_template kInterfacesTemplate[] = {
56	{B_MESSAGE_TYPE, "interface", kInterfaceTemplate},
57	{0, NULL, NULL}
58};
59
60// Network templates
61
62const static settings_template kNetworkTemplate[] = {
63	{B_STRING_TYPE, "name", NULL, true},
64	{B_STRING_TYPE, "mac", NULL},
65	{B_STRING_TYPE, "password", NULL},
66	{B_STRING_TYPE, "authentication", NULL},
67	{B_STRING_TYPE, "cipher", NULL},
68	{B_STRING_TYPE, "group_cipher", NULL},
69	{B_STRING_TYPE, "key", NULL},
70	{0, NULL, NULL}
71};
72
73const static settings_template kNetworksTemplate[] = {
74	{B_MESSAGE_TYPE, "network", kNetworkTemplate},
75	{0, NULL, NULL}
76};
77
78// Service templates
79
80const static settings_template kServiceAddressTemplate[] = {
81	{B_STRING_TYPE, "family", NULL, true},
82	{B_STRING_TYPE, "type", NULL},
83	{B_STRING_TYPE, "protocol", NULL},
84	{B_STRING_TYPE, "address", NULL},
85	{B_INT32_TYPE, "port", NULL},
86	{0, NULL, NULL}
87};
88
89const static settings_template kServiceTemplate[] = {
90	{B_STRING_TYPE, "name", NULL, true},
91	{B_MESSAGE_TYPE, "address", kServiceAddressTemplate},
92	{B_STRING_TYPE, "user", NULL},
93	{B_STRING_TYPE, "group", NULL},
94	{B_STRING_TYPE, "launch", NULL},
95	{B_STRING_TYPE, "family", NULL},
96	{B_STRING_TYPE, "type", NULL},
97	{B_STRING_TYPE, "protocol", NULL},
98	{B_INT32_TYPE, "port", NULL},
99	{B_BOOL_TYPE, "stand_alone", NULL},
100	{0, NULL, NULL}
101};
102
103const static settings_template kServicesTemplate[] = {
104	{B_MESSAGE_TYPE, "service", kServiceTemplate},
105	{0, NULL, NULL}
106};
107
108
109Settings::Settings()
110{
111	_Load();
112}
113
114
115Settings::~Settings()
116{
117}
118
119
120status_t
121Settings::_GetPath(const char* name, BPath& path)
122{
123	if (find_directory(B_COMMON_SETTINGS_DIRECTORY, &path, true) != B_OK)
124		return B_ERROR;
125
126	path.Append("network");
127	create_directory(path.Path(), 0755);
128
129	if (name != NULL)
130		path.Append(name);
131	return B_OK;
132}
133
134
135const settings_template*
136Settings::_FindSettingsTemplate(const settings_template* settingsTemplate,
137	const char* name)
138{
139	while (settingsTemplate->name != NULL) {
140		if (!strcmp(name, settingsTemplate->name))
141			return settingsTemplate;
142
143		settingsTemplate++;
144	}
145
146	return NULL;
147}
148
149
150const settings_template*
151Settings::_FindParentValueTemplate(const settings_template* settingsTemplate)
152{
153	settingsTemplate = settingsTemplate->sub_template;
154	if (settingsTemplate == NULL)
155		return NULL;
156
157	while (settingsTemplate->name != NULL) {
158		if (settingsTemplate->parent_value)
159			return settingsTemplate;
160
161		settingsTemplate++;
162	}
163
164	return NULL;
165}
166
167
168status_t
169Settings::_AddParameter(const driver_parameter& parameter, const char* name,
170	uint32 type, BMessage& message)
171{
172	for (int32 i = 0; i < parameter.value_count; i++) {
173		switch (type) {
174			case B_STRING_TYPE:
175				message.AddString(name, parameter.values[i]);
176				break;
177			case B_INT32_TYPE:
178				message.AddInt32(name, atoi(parameter.values[i]));
179				break;
180			case B_BOOL_TYPE:
181				if (!strcasecmp(parameter.values[i], "true")
182					|| !strcasecmp(parameter.values[i], "on")
183					|| !strcasecmp(parameter.values[i], "enabled")
184					|| !strcasecmp(parameter.values[i], "1"))
185					message.AddBool(name, true);
186				else
187					message.AddBool(name, false);
188				break;
189		}
190	}
191	if (type == B_BOOL_TYPE && parameter.value_count == 0) {
192		// boolean parameters are always true
193		message.AddBool(name, true);
194	}
195
196	return B_OK;
197}
198
199
200status_t
201Settings::_ConvertFromDriverParameter(const driver_parameter& parameter,
202	const settings_template* settingsTemplate, BMessage& message)
203{
204	settingsTemplate = _FindSettingsTemplate(settingsTemplate, parameter.name);
205	if (settingsTemplate == NULL) {
206		fprintf(stderr, "unknown parameter %s\n", parameter.name);
207		return B_BAD_VALUE;
208	}
209
210	_AddParameter(parameter, parameter.name, settingsTemplate->type, message);
211
212	if (settingsTemplate->type == B_MESSAGE_TYPE
213		&& parameter.parameter_count > 0) {
214		status_t status = B_OK;
215		BMessage subMessage;
216		for (int32 j = 0; j < parameter.parameter_count; j++) {
217			status = _ConvertFromDriverParameter(parameter.parameters[j],
218				settingsTemplate->sub_template, subMessage);
219			if (status != B_OK)
220				break;
221
222			const settings_template* parentValueTemplate
223				= _FindParentValueTemplate(settingsTemplate);
224			if (parentValueTemplate != NULL) {
225				_AddParameter(parameter, parentValueTemplate->name,
226					parentValueTemplate->type, subMessage);
227			}
228		}
229		if (status == B_OK)
230			message.AddMessage(parameter.name, &subMessage);
231	}
232
233	return B_OK;
234}
235
236
237status_t
238Settings::_ConvertFromDriverSettings(const driver_settings& settings,
239	const settings_template* settingsTemplate, BMessage& message)
240{
241	message.MakeEmpty();
242
243	for (int32 i = 0; i < settings.parameter_count; i++) {
244		status_t status = _ConvertFromDriverParameter(settings.parameters[i],
245			settingsTemplate, message);
246		if (status == B_BAD_VALUE) {
247			// ignore unknown entries
248			continue;
249		}
250		if (status != B_OK)
251			return status;
252	}
253
254	return B_OK;
255}
256
257
258status_t
259Settings::_ConvertFromDriverSettings(const char* name,
260	const settings_template* settingsTemplate, BMessage& message)
261{
262	BPath path;
263	status_t status = _GetPath(name, path);
264	if (status != B_OK)
265		return status;
266
267	void* handle = load_driver_settings(path.Path());
268	if (handle == NULL)
269		return B_ENTRY_NOT_FOUND;
270
271	const driver_settings* settings = get_driver_settings(handle);
272	if (settings != NULL) {
273		status = _ConvertFromDriverSettings(*settings, settingsTemplate,
274			message);
275	}
276
277	unload_driver_settings(handle);
278	return status;
279}
280
281
282status_t
283Settings::_AppendSettings(const settings_template* settingsTemplate,
284	BString& settings, const BMessage& message, const char* name,
285	type_code type, int32 count, const char* settingName)
286{
287	const settings_template* valueTemplate
288		= _FindSettingsTemplate(settingsTemplate, name);
289	if (valueTemplate == NULL) {
290		fprintf(stderr, "unknown field %s\n", name);
291		return B_BAD_VALUE;
292	}
293
294	if (valueTemplate->type != type) {
295		fprintf(stderr, "field type mismatch %s\n", name);
296		return B_BAD_VALUE;
297	}
298
299	if (settingName == NULL)
300		settingName = name;
301
302	if (type != B_MESSAGE_TYPE) {
303		settings.Append("\n");
304		settings.Append(settingName);
305		settings.Append("\t");
306	}
307
308	for (int32 valueIndex = 0; valueIndex < count; valueIndex++) {
309		if (valueIndex > 0 && type != B_MESSAGE_TYPE)
310			settings.Append(" ");
311
312		switch (type) {
313			case B_BOOL_TYPE:
314			{
315				bool value;
316				status_t result = message.FindBool(name, valueIndex, &value);
317				if (result != B_OK)
318					return result;
319
320				settings.Append(value ? "true" : "false");
321				break;
322			}
323
324			case B_STRING_TYPE:
325			{
326				const char* value = NULL;
327				status_t result = message.FindString(name, valueIndex, &value);
328				if (result != B_OK)
329					return result;
330
331				settings.Append(value);
332				break;
333			}
334
335			case B_INT32_TYPE:
336			{
337				int32 value;
338				status_t result = message.FindInt32(name, valueIndex, &value);
339				if (result != B_OK)
340					return result;
341
342				char buffer[100];
343				snprintf(buffer, sizeof(buffer), "%"B_PRId32, value);
344				settings.Append(buffer, sizeof(buffer));
345				break;
346			}
347
348			case B_MESSAGE_TYPE:
349			{
350				BMessage subMessage;
351				status_t result = message.FindMessage(name, valueIndex,
352					&subMessage);
353				if (result != B_OK)
354					return result;
355
356				const settings_template* parentValueTemplate
357					= _FindParentValueTemplate(valueTemplate);
358				if (parentValueTemplate != NULL) {
359					_AppendSettings(valueTemplate->sub_template, settings,
360						subMessage, parentValueTemplate->name,
361						parentValueTemplate->type, 1, name);
362					subMessage.RemoveName(parentValueTemplate->name);
363				}
364
365				BString subSettings;
366				_ConvertToDriverSettings(valueTemplate->sub_template,
367					subSettings, subMessage);
368				subSettings.ReplaceAll("\n", "\n\t");
369				subSettings.RemoveFirst("\n");
370
371				settings.Append(" {\n");
372				settings.Append(subSettings);
373				settings.Append("\n}");
374			}
375		}
376	}
377
378	return B_OK;
379}
380
381
382status_t
383Settings::_ConvertToDriverSettings(const settings_template* settingsTemplate,
384	BString& settings, const BMessage& message)
385{
386	int32 index = 0;
387	char *name = NULL;
388	type_code type;
389	int32 count = 0;
390
391	while (message.GetInfo(B_ANY_TYPE, index++, &name, &type, &count) == B_OK) {
392		status_t result = _AppendSettings(settingsTemplate, settings, message,
393			name, type, count);
394		if (result != B_OK)
395			return result;
396	}
397
398	return B_OK;
399}
400
401
402status_t
403Settings::_ConvertToDriverSettings(const char* name,
404	const settings_template* settingsTemplate, const BMessage& message)
405{
406	BPath path;
407	status_t status = _GetPath(name, path);
408	if (status != B_OK)
409		return status;
410
411	BString settings;
412	status = _ConvertToDriverSettings(settingsTemplate, settings, message);
413	if (status == B_OK) {
414		settings.RemoveFirst("\n");
415		// TODO: actually write the settings.String() out into the file
416		printf("settings:\n%s\n", settings.String());
417	}
418
419	return status;
420}
421
422
423status_t
424Settings::_Load(const char* name, uint32* _type)
425{
426	status_t status = B_ENTRY_NOT_FOUND;
427
428	if (name == NULL || strcmp(name, "interfaces") == 0) {
429		status = _ConvertFromDriverSettings("interfaces", kInterfacesTemplate,
430			fInterfaces);
431		if (status == B_OK && _type != NULL)
432			*_type = kMsgInterfaceSettingsUpdated;
433	}
434	if (name == NULL || strcmp(name, "wireless_networks") == 0) {
435		status = _ConvertFromDriverSettings("wireless_networks",
436			kNetworksTemplate, fNetworks);
437		if (status == B_OK && _type != NULL)
438			*_type = kMsgInterfaceSettingsUpdated;
439	}
440	if (name == NULL || strcmp(name, "services") == 0) {
441		status = _ConvertFromDriverSettings("services", kServicesTemplate,
442			fServices);
443		if (status == B_OK && _type != NULL)
444			*_type = kMsgServiceSettingsUpdated;
445	}
446
447	return status;
448}
449
450
451status_t
452Settings::_Save(const char* name)
453{
454	status_t status = B_ENTRY_NOT_FOUND;
455
456	if (name == NULL || strcmp(name, "interfaces") == 0) {
457		status = _ConvertToDriverSettings("interfaces", kInterfacesTemplate,
458			fInterfaces);
459	}
460	if (name == NULL || strcmp(name, "wireless_networks") == 0) {
461		status = _ConvertToDriverSettings("wireless_networks",
462			kNetworksTemplate, fNetworks);
463	}
464	if (name == NULL || strcmp(name, "services") == 0) {
465		status = _ConvertToDriverSettings("services", kServicesTemplate,
466			fServices);
467	}
468
469	return status;
470}
471
472
473status_t
474Settings::_StartWatching(const char* name, const BMessenger& target)
475{
476	BPath path;
477	status_t status = _GetPath(name, path);
478	if (status != B_OK)
479		return status;
480
481	return BPrivate::BPathMonitor::StartWatching(path.Path(), B_WATCH_STAT,
482		target);
483}
484
485
486status_t
487Settings::StartMonitoring(const BMessenger& target)
488{
489	if (_IsWatching(target))
490		return B_OK;
491	if (_IsWatching())
492		StopMonitoring(fListener);
493
494	fListener = target;
495
496	status_t status = _StartWatching("interfaces", target);
497	if (status == B_OK)
498		status = _StartWatching("wireless_networks", target);
499	if (status == B_OK)
500		status = _StartWatching("services", target);
501
502	return status;
503}
504
505
506status_t
507Settings::StopMonitoring(const BMessenger& target)
508{
509	// TODO: this needs to be changed in case the server will watch
510	//	anything else but settings
511	return BPrivate::BPathMonitor::StopWatching(target);
512}
513
514
515status_t
516Settings::Update(BMessage* message)
517{
518	const char* pathName;
519	int32 opcode;
520	if (message->FindInt32("opcode", &opcode) != B_OK
521		|| message->FindString("path", &pathName) != B_OK)
522		return B_BAD_VALUE;
523
524	BPath settingsFolderPath;
525	_GetPath(NULL, 	settingsFolderPath);
526	if (strncmp(pathName, settingsFolderPath.Path(),
527		strlen(settingsFolderPath.Path()))) {
528		return B_NAME_NOT_FOUND;
529	}
530
531	if (message->FindBool("removed")) {
532		// for now, we only consider existing settings files
533		// (ie. deleting "services" won't stop any)
534		return B_OK;
535	}
536
537	int32 fields;
538	if (opcode == B_STAT_CHANGED
539		&& message->FindInt32("fields", &fields) == B_OK
540		&& (fields & (B_STAT_MODIFICATION_TIME | B_STAT_SIZE)) == 0) {
541		// only update when the modified time or size has changed
542		return B_OK;
543	}
544
545	BPath path(pathName);
546	uint32 type;
547	if (_Load(path.Leaf(), &type) == B_OK) {
548		BMessage update(type);
549		fListener.SendMessage(&update);
550	}
551
552	return B_OK;
553}
554
555
556status_t
557Settings::GetNextInterface(uint32& cookie, BMessage& interface)
558{
559	status_t status = fInterfaces.FindMessage("interface", cookie, &interface);
560	if (status != B_OK)
561		return status;
562
563	cookie++;
564	return B_OK;
565}
566
567
568int32
569Settings::CountNetworks() const
570{
571	int32 count = 0;
572	if (fNetworks.GetInfo("network", NULL, &count) != B_OK)
573		return 0;
574
575	return count;
576}
577
578
579status_t
580Settings::GetNextNetwork(uint32& cookie, BMessage& network) const
581{
582	status_t status = fNetworks.FindMessage("network", cookie, &network);
583	if (status != B_OK)
584		return status;
585
586	cookie++;
587	return B_OK;
588}
589
590
591status_t
592Settings::AddNetwork(const BMessage& network)
593{
594	const char* name = NULL;
595	network.FindString("name", &name);
596	RemoveNetwork(name);
597
598	status_t result = fNetworks.AddMessage("network", &network);
599	if (result != B_OK)
600		return result;
601
602	return _Save("wireless_networks");
603}
604
605
606status_t
607Settings::RemoveNetwork(const char* name)
608{
609	int32 index = 0;
610	BMessage network;
611	while (fNetworks.FindMessage("network", index, &network) == B_OK) {
612		const char* networkName = NULL;
613		if (network.FindString("name", &networkName) == B_OK
614			&& strcmp(networkName, name) == 0) {
615			fNetworks.RemoveData("network", index);
616			return _Save("wireless_networks");
617		}
618
619		index++;
620	}
621
622	return B_ENTRY_NOT_FOUND;
623}
624
625
626status_t
627Settings::GetNextService(uint32& cookie, BMessage& service)
628{
629	status_t status = fServices.FindMessage("service", cookie, &service);
630	if (status != B_OK)
631		return status;
632
633	cookie++;
634	return B_OK;
635}
636
637
638const BMessage&
639Settings::Services() const
640{
641	return fServices;
642}
643
644