Serenity Operating System
1/*
2 * Copyright (c) 2022, Maciej <sppmacd@pm.me>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include "NetworkSettingsWidget.h"
8
9#include <AK/DeprecatedString.h>
10#include <AK/IPv4Address.h>
11#include <AK/JsonParser.h>
12#include <Applications/NetworkSettings/NetworkSettingsGML.h>
13#include <LibCore/Command.h>
14#include <LibGUI/CheckBox.h>
15#include <LibGUI/ComboBox.h>
16#include <LibGUI/ItemListModel.h>
17#include <LibGUI/MessageBox.h>
18#include <LibGUI/Process.h>
19#include <LibGUI/SpinBox.h>
20#include <LibGUI/TextBox.h>
21
22namespace NetworkSettings {
23
24static int netmask_to_cidr(IPv4Address const& address)
25{
26 auto address_in_host_representation = AK::convert_between_host_and_network_endian(address.to_u32());
27 return 32 - count_trailing_zeroes_safe(address_in_host_representation);
28}
29
30NetworkSettingsWidget::NetworkSettingsWidget()
31{
32 load_from_gml(network_settings_gml).release_value_but_fixme_should_propagate_errors();
33
34 m_adapters_combobox = *find_descendant_of_type_named<GUI::ComboBox>("adapters_combobox");
35 m_enabled_checkbox = *find_descendant_of_type_named<GUI::CheckBox>("enabled_checkbox");
36 m_enabled_checkbox->on_checked = [&](bool value) {
37 m_current_adapter_data->enabled = value;
38 on_switch_enabled_or_dhcp();
39 set_modified(true);
40 };
41 m_dhcp_checkbox = *find_descendant_of_type_named<GUI::CheckBox>("dhcp_checkbox");
42 m_dhcp_checkbox->on_checked = [&](bool value) {
43 m_current_adapter_data->dhcp = value;
44 on_switch_enabled_or_dhcp();
45 set_modified(true);
46 };
47 m_ip_address_textbox = *find_descendant_of_type_named<GUI::TextBox>("ip_address_textbox");
48 m_ip_address_textbox->on_change = [&]() {
49 m_current_adapter_data->ip_address = m_ip_address_textbox->text();
50 set_modified(true);
51 };
52 m_cidr_spinbox = *find_descendant_of_type_named<GUI::SpinBox>("cidr_spinbox");
53 m_cidr_spinbox->on_change = [&](int value) {
54 m_current_adapter_data->cidr = value;
55 set_modified(true);
56 };
57 m_default_gateway_textbox = *find_descendant_of_type_named<GUI::TextBox>("default_gateway_textbox");
58 m_default_gateway_textbox->on_change = [&]() {
59 m_current_adapter_data->default_gateway = m_default_gateway_textbox->text();
60 set_modified(true);
61 };
62
63 auto config_file = Core::ConfigFile::open_for_system("Network").release_value_but_fixme_should_propagate_errors();
64
65 auto proc_net_adapters_file = Core::File::open("/sys/kernel/net/adapters"sv, Core::File::OpenMode::Read).release_value_but_fixme_should_propagate_errors();
66 auto data = proc_net_adapters_file->read_until_eof().release_value_but_fixme_should_propagate_errors();
67 JsonParser parser(data);
68 JsonValue proc_net_adapters_json = parser.parse().release_value_but_fixme_should_propagate_errors();
69
70 // FIXME: This should be done before creating a window.
71 if (proc_net_adapters_json.as_array().is_empty()) {
72 GUI::MessageBox::show_error(window(), "No network adapters found!"sv);
73 ::exit(1);
74 }
75
76 size_t selected_adapter_index = 0;
77 size_t index = 0;
78 proc_net_adapters_json.as_array().for_each([&](auto& value) {
79 auto& if_object = value.as_object();
80 auto adapter_name = if_object.get_deprecated_string("name"sv).value();
81 if (adapter_name == "loop")
82 return;
83
84 bool adapter_exists_in_config = config_file->has_group(adapter_name);
85
86 bool enabled = config_file->read_bool_entry(adapter_name, "Enabled", true);
87 if (enabled)
88 selected_adapter_index = index;
89
90 NetworkAdapterData adapter_data;
91 adapter_data.enabled = enabled;
92 adapter_data.dhcp = config_file->read_bool_entry(adapter_name, "DHCP", !adapter_exists_in_config);
93 adapter_data.ip_address = config_file->read_entry(adapter_name, "IPv4Address");
94 auto netmask = IPv4Address::from_string(config_file->read_entry(adapter_name, "IPv4Netmask"));
95 adapter_data.cidr = netmask.has_value() ? netmask_to_cidr(*netmask) : 32;
96 adapter_data.default_gateway = config_file->read_entry(adapter_name, "IPv4Gateway");
97 m_network_adapters.set(adapter_name, move(adapter_data));
98 m_adapter_names.append(adapter_name);
99 index++;
100 });
101
102 m_adapters_combobox->set_model(GUI::ItemListModel<DeprecatedString>::create(m_adapter_names));
103 m_adapters_combobox->on_change = [this](DeprecatedString const& text, GUI::ModelIndex const&) {
104 on_switch_adapter(text);
105 };
106 auto const& selected_adapter = selected_adapter_index;
107 dbgln("{} in {}", selected_adapter, m_adapter_names);
108 m_adapters_combobox->set_selected_index(selected_adapter);
109 on_switch_adapter(m_adapter_names[selected_adapter_index]);
110}
111
112void NetworkSettingsWidget::on_switch_adapter(DeprecatedString const& adapter)
113{
114 auto& adapter_data = m_network_adapters.get(adapter).value();
115 m_current_adapter_data = &adapter_data;
116 on_switch_enabled_or_dhcp();
117
118 m_enabled_checkbox->set_checked(adapter_data.enabled, GUI::AllowCallback::No);
119 m_dhcp_checkbox->set_checked(adapter_data.dhcp, GUI::AllowCallback::No);
120 m_ip_address_textbox->set_text(adapter_data.ip_address, GUI::AllowCallback::No);
121 m_cidr_spinbox->set_value(adapter_data.cidr, GUI::AllowCallback::No);
122 m_default_gateway_textbox->set_text(adapter_data.default_gateway, GUI::AllowCallback::No);
123
124 VERIFY(m_current_adapter_data);
125}
126
127void NetworkSettingsWidget::on_switch_enabled_or_dhcp()
128{
129 m_dhcp_checkbox->set_enabled(m_current_adapter_data->enabled);
130 m_ip_address_textbox->set_enabled(m_current_adapter_data->enabled && !m_current_adapter_data->dhcp);
131 m_cidr_spinbox->set_enabled(m_current_adapter_data->enabled && !m_current_adapter_data->dhcp);
132 m_default_gateway_textbox->set_enabled(m_current_adapter_data->enabled && !m_current_adapter_data->dhcp);
133}
134
135void NetworkSettingsWidget::apply_settings()
136{
137 auto config_file = Core::ConfigFile::open_for_system("Network", Core::ConfigFile::AllowWriting::Yes).release_value_but_fixme_should_propagate_errors();
138 for (auto const& adapter_data : m_network_adapters) {
139 auto netmask = IPv4Address::netmask_from_cidr(adapter_data.value.cidr).to_deprecated_string();
140 config_file->write_bool_entry(adapter_data.key, "Enabled", adapter_data.value.enabled);
141 config_file->write_bool_entry(adapter_data.key, "DHCP", adapter_data.value.dhcp);
142 if (adapter_data.value.enabled && !adapter_data.value.dhcp) {
143 if (!IPv4Address::from_string(adapter_data.value.ip_address).has_value()) {
144 GUI::MessageBox::show_error(window(), DeprecatedString::formatted("Invalid IPv4 address for adapter {}", adapter_data.key));
145 return;
146 }
147 if (!IPv4Address::from_string(adapter_data.value.default_gateway).has_value()) {
148 GUI::MessageBox::show_error(window(), DeprecatedString::formatted("Invalid IPv4 gateway for adapter {}", adapter_data.key));
149 return;
150 }
151 }
152 config_file->write_entry(adapter_data.key, "IPv4Address", adapter_data.value.ip_address);
153 config_file->write_entry(adapter_data.key, "IPv4Netmask", netmask);
154 config_file->write_entry(adapter_data.key, "IPv4Gateway", adapter_data.value.default_gateway);
155 }
156
157 GUI::Process::spawn_or_show_error(window(), "/bin/NetworkServer"sv);
158}
159
160void NetworkSettingsWidget::switch_adapter(DeprecatedString const& adapter)
161{
162 m_adapters_combobox->set_text(adapter, GUI::AllowCallback::No);
163 on_switch_adapter(adapter);
164}
165
166}