From 5a57d542c5238c90a639fa4d594d0fc801d95727 Mon Sep 17 00:00:00 2001 From: billz Date: Sat, 19 Jul 2025 15:11:37 -0700 Subject: [PATCH] Normalize state flags, try-catch blocks for method calls, prune legacy dhcpcd routines --- includes/hostapd.php | 284 ++++++++----------------------------------- 1 file changed, 51 insertions(+), 233 deletions(-) diff --git a/includes/hostapd.php b/includes/hostapd.php index c2e0d01b..f1aa0f39 100755 --- a/includes/hostapd.php +++ b/includes/hostapd.php @@ -21,7 +21,7 @@ function DisplayHostAPDConfig() $system = new Sysinfo(); $operatingSystem = $system->operatingSystem(); - $arrConfig = array(); + // set hostapd defaults $arr80211Standard = $hostapd->get80211Standards(); $arrSecurity = $hostapd->getSecurityModes(); $arrEncType = $hostapd->getEncTypes(); @@ -30,8 +30,6 @@ function DisplayHostAPDConfig() $countryCodes = getCountryCodes($languageCode); $reg_domain = $hostapd->getRegDomain(); $interfaces = $hostapd->getInterfaces(); - - // set defaults $arrTxPower = getDefaultNetOpts('txpower','dbm'); $managedModeEnabled = false; @@ -41,18 +39,7 @@ function DisplayHostAPDConfig() $interface = $_SESSION['ap_interface']; } $txpower = $hostapd->getTxPower($interface); - - if (!RASPI_MONITOR_ENABLED) { - if (isset($_POST['SaveHostAPDSettings'])) { - saveHostAPDConfig($arrSecurity, $arrEncType, $arr80211Standard, $interfaces, $reg_domain, $status); - } - } - - $arrHostapdConf = []; - $hostapdIni = RASPI_CONFIG . '/hostapd.ini'; - if (file_exists($hostapdIni)) { - $arrHostapdConf = parse_ini_file($hostapdIni); - } + $arrHostapdConf = $hostapd->getHostapdIni(); if (!RASPI_MONITOR_ENABLED) { if (isset($_POST['StartHotspot']) || isset($_POST['RestartHotspot'])) { @@ -73,6 +60,8 @@ function DisplayHostAPDConfig() foreach ($return as $line) { $status->addMessage($line, 'info'); } + } elseif (isset($_POST['SaveHostAPDSettings'])) { + saveHostapdConfig($arrSecurity, $arrEncType, $arr80211Standard, $interfaces, $reg_domain, $status); } elseif (isset($_POST['StopHotspot'])) { $status->addMessage('Attempting to stop hotspot', 'info'); exec('sudo /bin/systemctl stop hostapd.service', $return); @@ -111,7 +100,8 @@ function DisplayHostAPDConfig() $arrConfig['disassoc_low_ack_bool'] = isset($arrConfig['disassoc_low_ack']) ? 1 : 0; $hostapdstatus = $system->hostapdStatus(); $serviceStatus = $hostapdstatus[0] == 0 ? "down" : "up"; - + + // ensure log is writeable exec('sudo /bin/chmod o+r '.RASPI_HOSTAPD_LOG); $logdata = getLogLimited(RASPI_HOSTAPD_LOG); @@ -138,7 +128,7 @@ function DisplayHostAPDConfig() } /** - * Validate user input, save configs for hostapd, dnsmasq & dhcp + * Validates user input + saves configs for hostapd, dnsmasq & dhcp * * @param array $wpa_array * @param array $enc_types @@ -148,16 +138,14 @@ function DisplayHostAPDConfig() * @param object $status * @return boolean */ -function saveHostAPDConfig($wpa_array, $enc_types, $modes, $interfaces, $reg_domain, $status) +function saveHostapdConfig($wpa_array, $enc_types, $modes, $interfaces, $reg_domain, $status) { $hostapd = new HostapdManager(); $dnsmasq = new DnsmasqManager(); $dhcpcd = new DhcpcdManager(); + $arrHostapdConf = $hostapd->getHostapdIni(); $dualAPEnable = false; - $hostapdIniPath = RASPI_CONFIG . '/hostapd.ini'; - $arrHostapdConf = file_exists($hostapdIniPath) ? parse_ini_file($hostapdIniPath) : []; - // derive mode states $states = $hostapd->deriveModeStates($_POST, $arrHostapdConf); @@ -178,227 +166,57 @@ function saveHostAPDConfig($wpa_array, $enc_types, $modes, $interfaces, $reg_dom if ($validated !== false) { try { + // normalize state flags $validated['interface'] = $apIface; - $validated['bridge'] = $states['BridgedEnable'] ? 'br0' : null; + $validated['bridge'] = !empty($states['BridgedEnable']); + $validated['apsta'] = !empty($states['WifiAPEnable']); + $validated['repeater'] = !empty($states['RepeaterEnable']); + $validated['dualmode'] = !empty($states['DualAPEnable']); $validated['txpower'] = $txpower; - // build and save configuration + + // hostapd $config = $hostapd->buildConfig($validated, $status); $hostapd->saveConfig($config, $dualAPEnable, $validated['interface']); $status->addMessage('WiFi hotspot settings saved.', 'success'); - } catch (\RuntimeException $e) { + + // dnsmasq + try { + $syscfg = $dnsmasq->getConfig($ap_iface ?? RASPI_WIFI_AP_INTERFACE); + } catch (\RuntimeException $e) { + error_log('Error: ' . $e->getMessage()); + } + + try { + $dnsmasqConfig = $dnsmasq->buildConfig( + $syscfg, + $validated['interface'], + $validated['apsta'], + $validated['bridge'] + ); + $dnsmasq->saveConfig($dnsmasqConfig, $validated['interface']); + } catch (\RuntimeException $e) { + error_log('Error: ' . $e->getMessage()); + } + + // dhcpcd + try { + $return = $dhcpcd->buildConfig( + $validated['interface'], + $validated['bridge'], + $validated['repeater'], + $validated['apsta'], + $validated['dualmode'], + $status + ); + } catch (\RuntimeException $e) { + error_log('Error: ' . $e->getMessage()); + } + } catch (\Throwable $e) { error_log('Error: ' . $e->getMessage()); + $status->addMessage('Unable to save WiFi hotspot settings', 'danger'); } - } else { - $status->addMessage('Unable to save WiFi hotspot settings', 'danger'); - return false; } - - /// TODO: build out DHCP class - /// finish processing save - /* - if (trim($country_code) != trim($reg_domain)) { - $return = $hostapd->iwRegSet($country_code, $status); - } - // Parse dnsmasq config for selected interface - try { - $syscfg = $dnsmasq->getConfig($ap_iface ?? RASPI_WIFI_AP_INTERFACE); - } catch (\RuntimeException $e) { - error_log('Error: ' . $e->getMessage()); - } - // Build and save dsnmasq config - try { - $config = $dnsmasq->buildConfig($syscfg, $ap_iface, $wifiAPEnable, $bridgedEnable); - $dnsmasq->saveConfig($config, $ap_iface); - } catch (\RuntimeException $e) { - error_log('Error: ' . $e->getMessage()); - } - - // Set dhcp values from system config, fallback to default if undefined - $jsonData = json_decode(getNetConfig($ap_iface), true); - $ip_address = empty($jsonData['StaticIP']) - ? getDefaultNetValue('dhcp', $ap_iface, 'static ip_address') : $jsonData['StaticIP']; - $domain_name_server = empty($jsonData['StaticDNS']) - ? getDefaultNetValue('dhcp', $ap_iface, 'static domain_name_server') : $jsonData['StaticDNS']; - $routers = empty($jsonData['StaticRouters']) - ? getDefaultNetValue('dhcp', $ap_iface, 'static routers') : $jsonData['StaticRouters']; - $netmask = (empty($jsonData['SubnetMask']) || $jsonData['SubnetMask'] === '0.0.0.0') - ? getDefaultNetValue('dhcp', $ap_iface, 'subnetmask') : $jsonData['SubnetMask']; - if (isset($ip_address) && !preg_match('/.*\/\d+/', $ip_address)) { - $ip_address.='/'.mask2cidr($netmask); - } - $hasDefaults = !( - empty($ip_address) || - empty($domain_name_server) || - empty($routers) || - empty($netmask) || - $netmask === '0.0.0.0' - ); - if (!$hasDefaults) { - $status->addMessage(sprintf(_('Interface %s has no default settings.'), $ap_iface), 'warning'); - $status->addMessage(('Configure settings in DHCP Server before starting AP.'), 'warning'); - } - if ($bridgedEnable == 1) { - $config = array_keys(getDefaultNetOpts('dhcp','options')); - $config[] = PHP_EOL.'# RaspAP br0 configuration'; - $config[] = 'denyinterfaces eth0 wlan0'; - $config[] = 'interface br0'; - $config[] = PHP_EOL; - } elseif ($repeaterEnable == 1) { - $config = [ '# RaspAP '.$ap_iface.' configuration' ]; - $config[] = 'interface '.$ap_iface; - $config[] = 'static ip_address='.$ip_address; - $config[] = 'static routers='.$routers; - $config[] = 'static domain_name_server='.$domain_name_server; - $client_metric = getIfaceMetric($_SESSION['wifi_client_interface']); - if (is_int($client_metric)) { - $ap_metric = (int)$client_metric + 1; - $config[] = 'metric '.$ap_metric; - } else { - $status->addMessage('Unable to obtain metric value for client interface. Repeater mode inactive.', 'warning'); - $repeaterEnable = false; - } - } elseif ($wifiAPEnable == 1) { - $config = array_keys(getDefaultNetOpts('dhcp','options')); - $config[] = PHP_EOL.'# RaspAP uap0 configuration'; - $config[] = 'interface uap0'; - $config[] = 'static ip_address='.$ip_address; - $config[] = 'nohook wpa_supplicant'; - $config[] = PHP_EOL; - } else { - $config = updateDhcpcdConfig($ap_iface, $jsonData, $ip_address, $routers, $domain_name_server); - } - $dhcp_cfg = file_get_contents(RASPI_DHCPCD_CONFIG); - - if (preg_match('/wlan[3-9]\d*|wlan[1-9]\d+/', $ap_iface)) { - $skip_dhcp = true; - } elseif ($bridgedEnable == 1 || $wifiAPEnable == 1) { - $dhcp_cfg = join(PHP_EOL, $config); - $status->addMessage(sprintf(_('DHCP configuration for %s enabled.'), $ap_iface), 'success'); - } elseif (!preg_match('/^interface\s'.$ap_iface.'$/m', $dhcp_cfg)) { - $config[] = PHP_EOL; - $config= join(PHP_EOL, $config); - $dhcp_cfg = removeDHCPIface($dhcp_cfg,'br0'); - $dhcp_cfg = removeDHCPIface($dhcp_cfg,'uap0'); - $dhcp_cfg .= $config; - } else { - $config = join(PHP_EOL, $config); - $dhcp_cfg = removeDHCPIface($dhcp_cfg,'br0'); - $dhcp_cfg = removeDHCPIface($dhcp_cfg,'uap0'); - if (!strpos($dhcp_cfg, 'metric')) { - $dhcp_cfg = preg_replace('/^#\sRaspAP\s'.$ap_iface.'\s.*?(?=(?:\s*^\s*$|\s*nogateway))/ms', $config, $dhcp_cfg, 1); - } else { - $metrics = true; - } - } - if ($repeaterEnable && $metrics) { - $status->addMessage(_('WiFi repeater mode: A metric value is already defined for DHCP.'), 'warning'); - } else if ($repeaterEnable && !$metrics) { - $status->addMessage(sprintf(_('Metric value configured for the %s interface.'), $ap_iface), 'success'); - $status->addMessage('Restart hotspot to enable WiFi repeater mode.', 'success'); - persistDHCPConfig($dhcp_cfg, $ap_iface, $status); - } elseif (!$skip_dhcp) { - persistDHCPConfig($dhcp_cfg, $ap_iface, $status); - } else { - $status->addMessage('WiFi hotspot settings saved.', 'success'); - } - */ return true; } -/** - * Persists a DHCP configuration - * - * @param string $dhcp_cfg - * @param string $ap_iface - * @param object $status - * @return $status - */ -function persistDHCPConfig($dhcp_cfg, $ap_iface, $status) -{ - file_put_contents("/tmp/dhcpddata", $dhcp_cfg); - system('sudo cp /tmp/dhcpddata '.RASPI_DHCPCD_CONFIG, $return); - if ($return == 0) { - $status->addMessage(sprintf(_('DHCP configuration for %s updated.'), $ap_iface), 'success'); - $status->addMessage('WiFi hotspot settings saved.', 'success'); - } else { - $status->addMessage('Unable to save WiFi hotspot settings.', 'danger'); - } - return $status; -} - -/** - * Returns a count of hostapd-.conf files - * - * @return int - */ -function countHostapdConfigs(): int -{ - $configs = glob('/etc/hostapd/hostapd-*.conf'); - return is_array($configs) ? count($configs) : 0; -} - -/** - * Updates the dhcpcd configuration for a given interface, preserving existing settings - * - * @param string $ap_iface - * @param array $jsonData - * @param string $ip_address - * @param string $routers - * @param string $domain_name_server - * @return array updated configuration - */ -function updateDhcpcdConfig($ap_iface, $jsonData, $ip_address, $routers, $domain_name_server) { - $dhcp_cfg = file_get_contents(RASPI_DHCPCD_CONFIG); - $existing_config = []; - $section_regex = '/^#\sRaspAP\s'.preg_quote($ap_iface, '/').'\s.*?(?=\s*^\s*$)/ms'; - - // extract existing interface configuration - if (preg_match($section_regex, $dhcp_cfg, $matches)) { - $lines = explode(PHP_EOL, $matches[0]); - foreach ($lines as $line) { - $line = trim($line); - if (preg_match('/^(interface|static|metric|nogateway|nohook)/', $line)) { - $existing_config[] = $line; - } - } - } - - // initialize with comment - $config = [ '# RaspAP '.$ap_iface.' configuration' ]; - $config[] = 'interface '.$ap_iface; - $static_settings = [ - 'static ip_address' => $ip_address, - 'static routers' => $routers, - 'static domain_name_server' => $domain_name_server - ]; - - // merge existing settings with updates - foreach ($existing_config as $line) { - $matched = false; - foreach ($static_settings as $key => $value) { - if (strpos($line, $key) === 0) { - $config[] = "$key=$value"; - $matched = true; - unset($static_settings[$key]); - break; - } - } - if (!$matched && !preg_match('/^interface/', $line)) { - $config[] = $line; - } - } - - // add any new static settings - foreach ($static_settings as $key => $value) { - $config[] = "$key=$value"; - } - - // add metric if provided - if (!empty($jsonData['Metric']) && !in_array('metric '.$jsonData['Metric'], $config)) { - $config[] = 'metric '.$jsonData['Metric']; - } - - return $config; -} -