diff --git a/README.md b/README.md index 66c7cd43..039044b3 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # `$ raspap-webgui` [![Release 1.1](https://img.shields.io/badge/Release-1.1-green.svg)](https://github.com/billz/raspap-webgui/releases) A simple, responsive web interface to control wifi, hostapd and related services on the Raspberry Pi. -This project was inspired by a [**blog post**](http://sirlagz.net/2013/02/06/script-web-configuration-page-for-raspberry-pi/) by SirLagz about using a web page rather than ssh to configure wifi and hostapd settings on the Raspberry Pi. I mostly just prettified the UI by wrapping it in [**SB Admin 2**](https://github.com/IronSummitMedia/startbootstrap-sb-admin-2), a Bootstrap based admin theme. +This project was inspired by a [**blog post**](http://sirlagz.net/2013/02/06/script-web-configuration-page-for-raspberry-pi/) by SirLagz about using a web page rather than ssh to configure wifi and hostapd settings on the Raspberry Pi. I mostly just prettified the UI by wrapping it in [**SB Admin 2**](https://github.com/BlackrockDigital/startbootstrap-sb-admin-2), a Bootstrap based admin theme. We'd be curious to hear about how you use this with your own Pi-powered access points. Ping us on Twitter ([**@billzimmerman**](https://twitter.com/billzimmerman) and [**@SirLagz**](https://twitter.com/SirLagz)). Until then, here are some screenshots: @@ -52,20 +52,24 @@ Add the following to the end of `/etc/sudoers`: ```sh www-data ALL=(ALL) NOPASSWD:/sbin/ifdown wlan0,/sbin/ifup wlan0,/bin/cat /etc/wpa_supplicant/wpa_supplicant.conf,/bin/cp /tmp/wifidata /etc/wpa_supplicant/wpa_supplicant.conf,/sbin/wpa_cli scan_results, /sbin/wpa_cli scan,/bin/cp /tmp/hostapddata /etc/hostapd/hostapd.conf, /etc/init.d/hostapd start,/etc/init.d/hostapd stop,/etc/init.d/dnsmasq start, /etc/init.d/dnsmasq stop,/bin/cp /tmp/dhcpddata /etc/dnsmasq.conf, /sbin/shutdown -h now, /sbin/reboot +www-data ALL=(ALL) NOPASSWD:/sbin/wpa_cli reconfigure ``` -Once those modifications are done, git clone the files to `/var/www`. +Once those modifications are done, git clone the files to `/var/www/html`. +**Note,** for older versions of Raspbian (before Jessie, May 2016) use +`/var/www` instead. ```sh -sudo git clone https://github.com/billz/raspap-webgui /var/www +sudo rm -rf /var/www/html +sudo git clone https://github.com/billz/raspap-webgui /var/www/html ``` Set the files ownership to `www-data` user. ```sh -sudo chown -R www-data:www-data /var/www +sudo chown -R www-data:www-data /var/www/html ``` Move the RaspAP configuration file to the correct location ```sh sudo mkdir /etc/raspap -sudo mv /var/www/raspap.php /etc/raspap/ +sudo mv /var/www/html/raspap.php /etc/raspap/ sudo chown -R www-data:www-data /etc/raspap ``` Reboot and it should be up and running! diff --git a/dist/js/functions.js b/dist/js/functions.js index 5adf581a..e9164308 100644 --- a/dist/js/functions.js +++ b/dist/js/functions.js @@ -1,74 +1,9 @@ -function WiFiDown() { - var down = confirm("Take down wlan0 ?"); - if(down) { - } else { - alert("Action cancelled"); - } -} - -function UpdateNetworks() { - var existing = document.getElementById("networkbox").getElementsByTagName('div').length; - document.getElementById("Networks").value = existing; -} - -function AddNetwork() { -// existing = document.getElementById("networkbox").getElementsByTagName('div').length; -// existing++; - Networks++ - var Networks = document.getElementById('Networks').value; - document.getElementById('networkbox').innerHTML += '

Network '+Networks+'

\ -
\ -
\ -
'; - Networks++; - document.getElementById('Networks').value=Networks; - -} - -function AddScanned(network) { - - existing = document.getElementById("networkbox").getElementsByTagName('div').length; - var Networks = document.getElementById('Networks').value; - //if(existing != 0) { - Networks++; - //} - - document.getElementById('Networks').value=Networks; - document.getElementById('networkbox').innerHTML += '

Network '+Networks+'

\ -
\ -
\ -
'; - document.getElementById('ssid'+Networks).value = network; - if(existing == 0) { - Networks++ - document.getElementById('Networks').value = Networks; - } -} - -function CheckSSID(ssid) { - if(ssid.value.length>31) { - ssid.style.background='#FFD0D0'; - document.getElementById('Save').disabled = true; - } else { - ssid.style.background='#D0FFD0' - document.getElementById('Save').disabled = false; - } -} - -function CheckPSK(psk) { - if(psk.value.length < 8) { - psk.style.background='#FFD0D0'; - document.getElementById('Save').disabled = true; - } else { - psk.style.background='#D0FFD0'; - document.getElementById('Save').disabled = false; - } -} - -function DeleteNetwork(network) { - element = document.getElementById('Networkbox'+network); - element.parentNode.removeChild(element); - var Networks = document.getElementById('Networks').value; - Networks-- - document.getElementById('Networks').value = Networks; +function CheckPSK(psk, id) { + if(psk.value.length < 8 || psk.value.length > 63) { + psk.style.background='#FFD0D0'; + document.getElementById(id).disabled = true; + } else { + psk.style.background='#D0FFD0'; + document.getElementById(id).disabled = false; + } } diff --git a/includes/admin.php b/includes/admin.php index a13f9bbd..ba927224 100755 --- a/includes/admin.php +++ b/includes/admin.php @@ -1,77 +1,74 @@ '.$message; - if ($dismissable) $status .= ''; - $status .= '
'; - - return $status; -} +include_once( 'includes/status_messages.php' ); function DisplayAuthConfig($username, $password){ - $status = ''; + $status = new StatusMessages(); if (isset($_POST['UpdateAdminPassword'])) { - if (password_verify($_POST['oldpass'], $password)) { - $new_username=trim($_POST['username']); - if ($_POST['newpass'] != $_POST['newpassagain']) { - $status = Status('New passwords do not match', 'danger'); - } else if ($new_username == '') { - $status = Status('Username must not be empty', 'danger'); - } else { - if ($auth_file = fopen(RASPI_ADMIN_DETAILS, 'w')) { - fwrite($auth_file, $new_username.PHP_EOL); - fwrite($auth_file, password_hash($_POST['newpass'], PASSWORD_BCRYPT).PHP_EOL); - fclose($auth_file); - $username = $new_username; - $status = Status('Admin password updated'); - } else { - $status = Status('Failed to update admin password', 'danger'); - } - } - } else { - $status = Status('Old password does not match', 'danger'); - } + if (CSRFValidate()) { + if (password_verify($_POST['oldpass'], $password)) { + $new_username=trim($_POST['username']); + if ($_POST['newpass'] != $_POST['newpassagain']) { + $status->addMessage('New passwords do not match', 'danger'); + } else if ($new_username == '') { + $status->addMessage('Username must not be empty', 'danger'); + } else { + if ($auth_file = fopen(RASPI_ADMIN_DETAILS, 'w')) { + fwrite($auth_file, $new_username.PHP_EOL); + fwrite($auth_file, password_hash($_POST['newpass'], PASSWORD_BCRYPT).PHP_EOL); + fclose($auth_file); + $username = $new_username; + $status->addMessage('Admin password updated'); + } else { + $status->addMessage('Failed to update admin password', 'danger'); + } + } + } else { + $status->addMessage('Old password does not match', 'danger'); + } + } else { + error_log('CSRF violation'); + } } ?> -
-
-
-
Configure Auth
-
-

-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
- -
-
-
-
-
+
+
+
+
Configure Auth
+
+

showMessages(); ?>

+
+ +
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+ +
+
+
+
+
false, 'configured' => true, 'connected' => false); + } elseif ($network !== null) { + if (preg_match('/^\s*}\s*$/', $line)) { + $networks[$ssid] = $network; + $network = null; + $ssid = null; + } elseif ($lineArr = preg_split('/\s*=\s*/', trim($line))) { + switch(strtolower($lineArr[0])) { + case 'ssid': + $ssid = trim($lineArr[1], '"'); + break; + case 'psk': + if (array_key_exists('passphrase', $network)) { + break; + } + case '#psk': + $network['protocol'] = 'WPA'; + case 'wep_key0': // Untested + $network['passphrase'] = trim($lineArr[1], '"'); + break; + case 'key_mgmt': + if (! array_key_exists('passphrase', $network) && $lineArr[1] === 'NONE') { + $network['protocol'] = 'Open'; + } + break; + } + } + } + } + + if ( isset($_POST['client_settings']) && CSRFValidate() ) { + $tmp_networks = $networks; + if ($wpa_file = fopen('/tmp/wifidata', 'w')) { + fwrite($wpa_file, 'ctrl_interface=DIR=' . RASPI_WPA_CTRL_INTERFACE . ' GROUP=netdev' . PHP_EOL); + fwrite($wpa_file, 'update_config=1' . PHP_EOL); + + foreach(array_keys($_POST) as $post) { + if (preg_match('/delete(\d+)/', $post, $post_match)) { + unset($tmp_networks[$_POST['ssid' . $post_match[1]]]); + } elseif (preg_match('/update(\d+)/', $post, $post_match)) { + // NB, at the moment, the value of protocol from the form may + // contain HTML line breaks + $tmp_networks[$_POST['ssid' . $post_match[1]]] = array( + 'protocol' => ( $_POST['protocol' . $post_match[1]] === 'Open' ? 'Open' : 'WPA' ), + 'passphrase' => $_POST['passphrase' . $post_match[1]], + 'configured' => true + ); + } + } + + $ok = true; + foreach($tmp_networks as $ssid => $network) { + if ($network['protocol'] === 'Open') { + fwrite($wpa_file, "network={".PHP_EOL); + fwrite($wpa_file, "\tssid=\"".$ssid."\"".PHP_EOL); + fwrite($wpa_file, "\tkey_mgmt=NONE".PHP_EOL); + fwrite($wpa_file, "}".PHP_EOL); + } else { + if (strlen($network['passphrase']) >=8 && strlen($network['passphrase']) <= 63) { + exec( 'wpa_passphrase '.escapeshellarg($ssid). ' ' . escapeshellarg($network['passphrase']),$wpa_passphrase ); + foreach($wpa_passphrase as $line) { + fwrite($wpa_file, $line.PHP_EOL); + } + } else { + $status->addMessage('WPA passphrase must be between 8 and 63 characters', 'danger'); + $ok = false; + + } + } + + } + + if ($ok) { + system( 'sudo cp /tmp/wifidata ' . RASPI_WPA_SUPPLICANT_CONFIG, $returnval ); + if( $returnval == 0 ) { + exec('sudo wpa_cli reconfigure', $reconfigure_out, $reconfigure_return ); + if ($reconfigure_return == 0) { + $status->addMessage('Wifi settings updated successfully', 'success'); + $networks = $tmp_networks; + } else { + $status->addMessage('Wifi settings updated but cannot restart (cannon execute "wpa_cli reconfigure")', 'danger'); + } + } else { + $status->addMessage('Wifi settings failed to be updated', 'danger'); + } + } + } else { + $status->addMessage('Failed to updated wifi settings', 'danger'); + } + } + + exec( 'sudo wpa_cli scan' ); + sleep(3); + exec( 'sudo wpa_cli scan_results',$scan_return ); + for( $shift = 0; $shift < 2; $shift++ ) { + array_shift($scan_return); + } + // display output + foreach( $scan_return as $network ) { + $arrNetwork = preg_split("/[\t]+/",$network); + if (array_key_exists($arrNetwork[4], $networks)) { + $networks[$arrNetwork[4]]['visible'] = true; + $networks[$arrNetwork[4]]['channel'] = ConvertToChannel($arrNetwork[1]); + // TODO What if the security has changed? + } else { + $networks[$arrNetwork[4]] = array( + 'configured' => false, + 'protocol' => ConvertToSecurity($arrNetwork[3]), + 'channel' => ConvertToChannel($arrNetwork[1]), + 'passphrase' => '', + 'visible' => true, + 'connected' => false + ); + } + } + + exec( 'iwconfig wlan0', $iwconfig_return ); + foreach ($iwconfig_return as $line) { + if (preg_match( '/ESSID:\"(.+)\"/i',$line,$iwconfig_ssid )) { + $networks[$iwconfig_ssid[1]]['connected'] = true; + } + } +?> + +
+
+
+
Configure client
+ +
+

showMessages(); ?>

+

Client settings

+ +
+ + + + + + + + + + + + + $network) { ?> + + + + + + + + + + + + + + + + +
SSIDChannelSecurityPassphrase
+ + + + + + + + + + X--- + + +
+ + /> + + /> + + /> +
+
+
+
+ +
+
+
+ diff --git a/includes/dashboard.php b/includes/dashboard.php new file mode 100755 index 00000000..69ed7912 --- /dev/null +++ b/includes/dashboard.php @@ -0,0 +1,140 @@ +addMessage('Interface is up', 'success'); + $wlan0up = true; + } else { + $status->addMessage('Interface is down', 'warning'); + } + + if( isset($_POST['ifdown_wlan0']) ) { + exec( 'ifconfig wlan0 | grep -i running | wc -l',$test ); + if($test[0] == 1) { + exec( 'sudo ifdown wlan0',$return ); + } else { + echo 'Interface already down'; + } + } elseif( isset($_POST['ifup_wlan0']) ) { + exec( 'ifconfig wlan0 | grep -i running | wc -l',$test ); + if($test[0] == 0) { + exec( 'sudo ifup wlan0',$return ); + } else { + echo 'Interface already up'; + } + } + ?> +
+
+
+
Dashboard
+
+

showMessages(); ?>

+
+
+
+
+

Interface Information

+
Interface Name
wlan0
+
IP Address

+
Subnet Mask

+
Mac Address


+ +

Interface Statistics

+
Received Packets

+
Received Bytes


+
Transferred Packets

+
Transferred Bytes

+
+
+
+ +
+
+
+

Wireless Information

+
Connected To

+
AP Mac Address

+
Bitrate

+
Signal Level

+
Transmit Power

+
Frequency


+
Link Quality
+
+
% +
+
+
+
+
+
+ +
+
+
+ '; + } else { + echo ''; + } + ?> + +
+
+
+ +
+ +
+
+
+ diff --git a/includes/dhcp.php b/includes/dhcp.php new file mode 100755 index 00000000..d173547a --- /dev/null +++ b/includes/dhcp.php @@ -0,0 +1,220 @@ + /tmp/dhcpddata',$temp ); + system( 'sudo cp /tmp/dhcpddata '. RASPI_DNSMASQ_CONFIG, $return ); + + if( $return == 0 ) { + $status->addMessage('Dnsmasq configuration updated successfully', 'success'); + } else { + $status->addMessage('Dnsmasq configuration failed to be updated', 'danger'); + } + } else { + error_log('CSRF violation'); + } + } + + exec( 'pidof dnsmasq | wc -l',$dnsmasq ); + $dnsmasq_state = ($dnsmasq[0] > 0); + + if( isset( $_POST['startdhcpd'] ) ) { + if (CSRFValidate()) { + if ($dnsmasq_state) { + $status->addMessage('dnsmasq already running', 'info'); + } else { + exec('sudo /etc/init.d/dnsmasq start', $dnsmasq, $return); + if ($return == 0) { + $status->addMessage('Successfully started dnsmasq', 'success'); + $dnsmasq_state = true; + } else { + $status->addMessage('Failed to start dnsmasq', 'danger'); + } + } + } else { + error_log('CSRF violation'); + } + } elseif( isset($_POST['stopdhcpd'] ) ) { + if (CSRFValidate()) { + if ($dnsmasq_state) { + exec('sudo /etc/init.d/dnsmasq stop', $dnsmasq, $return); + if ($return == 0) { + $status->addMessage('Successfully stopped dnsmasq', 'success'); + $dnsmasq_state = false; + } else { + $status->addMessage('Failed to stop dnsmasq', 'danger'); + } + } else { + $status->addMessage('dnsmasq already stopped', 'info'); + } + } else { + error_log('CSRF violation'); + } + } else { + if( $dnsmasq_state ) { + $status->addMessage('Dnsmasq is running', 'success'); + } else { + $status->addMessage('Dnsmasq is not running', 'warning'); + } + } + + exec( 'cat '. RASPI_DNSMASQ_CONFIG, $return ); + $conf = ParseConfig($return); + $arrRange = explode( ",", $conf['dhcp-range'] ); + $RangeStart = $arrRange[0]; + $RangeEnd = $arrRange[1]; + $RangeMask = $arrRange[2]; + preg_match( '/([0-9]*)([a-z])/i', $arrRange[3], $arrRangeLeaseTime ); + + switch( $arrRangeLeaseTime[2] ) { + case "h": + $hselected = " selected"; + break; + case "m": + $mselected = " selected"; + break; + case "d": + $dselected = " selected"; + break; + } + + ?> +
+
+
+
Configure DHCP +
+ +
+

showMessages(); ?>

+ + + +
+
+

DHCP server settings

+
+ +
+
+ + +
+
+
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ + + '; + } else { + echo''; + } + ?> +
+
+ +
+

Client list

+
+
+
+ Active DHCP leases +
+ +
+
+ + + + + + + + + + + + + ' . $lease_item . ''; + } + echo ''; + }; + ?> + + +
Expire timeMAC AddressIP AddressHost nameClient ID
+
+
+
+
+
+
+
+ +
+
+
+ + diff --git a/includes/functions.php b/includes/functions.php index 182795e4..f391e461 100755 --- a/includes/functions.php +++ b/includes/functions.php @@ -1,5 +1,59 @@ + +"; + foreach ( $options as $opt => $label) { + $select = ''; + $key = isAssoc($options) ? $opt : $label; + if( $key == $selected ) { + $select = " selected"; + } + echo "