Current File : //opt/RZphp72/includes/Net/Wifi.php
<?php
/**
 * Configuration settings of a wifi network interface.
 *
 * PHP Versions 4 and 5
 *
 * @category Networking
 * @package  Net_Wifi
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html  LGPL License 2.1
 * @version  CVS: $Id$
 * @link     http://pear.php.net/package/Net_Wifi
 */

require_once 'Net/Wifi/Cell.php';
require_once 'Net/Wifi/Config.php';
//required for System::which() functionality
require_once 'System.php';

/**
* A class for scanning wireless networks and identifying
* local wireless network interfaces.
*
* @category Networking
* @package  Net_Wifi
* @author   Christian Weiske <cweiske@php.net>
* @license  http://www.gnu.org/copyleft/lesser.html  LGPL License 2.1
* @link     http://pear.php.net/package/Net_Wifi
*/
class Net_Wifi
{
    var $REG_ACCESS_POINT         = '/Access Point: ([0-9:A-F]{17})/';
    var $REG_BIT_RATE             = '/Bit Rate[:=]([0-9.]+) [mk]b\\/s/i';
    var $REG_ESSID                = '/ESSID:"([^"]+)"/';
    var $REG_INVALID_MISC         = '/Invalid misc[:=](-?[0-9]+)/';
    var $REG_MISSED_BEACON        = '/Missed beacon[:=](-?[0-9]+)/';
    var $REG_NICKNAME             = '/Nickname:"([^"]+)"/';
    var $REG_NOISE_LEVEL          = '/Noise level[:=](-?[0-9]+) dBm/';
    var $REG_POWER                = '/Power[:=]([0-9]+) dBm/';
    var $REG_PROTOCOL_1           = '/IEEE ([0-9.]+[a-z])/';
    var $REG_PROTOCOL_2           = '/([0-9.]+[a-z])\s+linked\s+ESSID/';
    var $REG_RATES                = '|([0-9.]+) Mb/s|';
    var $REG_GROUP_CIPHER         = '|Group Cipher : (.*)|';
    var $REG_PAIRWISE_CIPHERS     = '|Pairwise Ciphers \([0-9]+\) : (.*)|';
    var $REG_AUTH_SUITES          = '|Authentication Suites \([0-9]+\) : (.*)|';
    var $REG_RX_INVALID_CRYPT     = '/Rx invalid crypt[:=](-?[0-9]+)/';
    var $REG_RX_INVALID_FRAG      = '/Rx invalid frag[:=](-?[0-9]+)/';
    var $REG_RX_INVALID_NWID      = '/Rx invalid nwid[:=](-?[0-9]+)/';
    var $REG_SIGNAL_LEVEL         = '/Signal level[:=](-?[0-9]+) dBm/';
    var $REG_TX_EXCESSIVE_RETRIES = '/Tx excessive retries[:=](-?[0-9]+)/';
    var $REG_WPA_IE_STRING        = 'WPA Version 1';
    var $REG_WPA2_IE_STRING       = 'IEEE 802.11i/WPA2 Version 1';

    /**
     * Various locations of programs
     *
     * @var array
     */
    var $arFileLocation = array(
        'iwconfig'           => '/usr/sbin/iwconfig',
        'iwlist'             => '/usr/sbin/iwlist',
        '/proc/net/wireless' => '/proc/net/wireless'
    );

    /**
     * How to handle unknown lines in iwconfig output
     * - 'echo':  Echo to stderr
     * - $object: Log to object with debug priority.
     *            assume 'Log' instance
     * - null:    Ignore unknowns
     *
     * @var string
     */
    var $unknowns = null;



    /**
    * Constructor which tries to guess the paths of the tools
    */
    function __construct()
    {
        //try to find the paths
        $iwconfig = System::which('iwconfig');
        if ($iwconfig !== false) {
            $this->setPathIwconfig($iwconfig);
        } else if (file_exists('/sbin/iwconfig')) {
            $this->setPathIwconfig('/sbin/iwconfig');
        }

        $iwlist = System::which('iwlist');
        if ($iwlist !== false) {
            $this->setPathIwlist($iwlist);
        } else if (file_exists('/sbin/iwlist')) {
            $this->setPathIwlist('/sbin/iwlist');
        }
    }

    /**
     * PHP 4 constructor for backwards compatibility.
     *
     * @return void
     */
    function Net_Wifi()
    {
        self::__construct();
    }


    /**
    * Returns an object with the current state of the interface
    * (connected/not connected, AP,...).
    *
    * @param string $strInterface The interface to check
    *
    * @return Net_Wifi_Config The state information
    * @access public
    */
    function getCurrentConfig($strInterface)
    {
        //get the plain config
        $arLines = array();
        exec(
            $this->arFileLocation['iwconfig'] . ' '
            . escapeshellarg($strInterface),
            $arLines
        );
        $strAll = implode("\n", $arLines);

        return $this->parseCurrentConfig($strAll);
    }//function getCurrentConfig(..)



    /**
    * Parses the iwconfig output to collect the current config information.
    *
    * @param string $strAll The iwconfig output to parse
    *
    * @return Net_Wifi_Config  The current config object
    * @access protected
    */
    function parseCurrentConfig($strAll)
    {
        $objConfig = new Net_Wifi_Config();

        $arMatches = array();
        if (preg_match($this->REG_ESSID, $strAll, $arMatches)) {
            $objConfig->ssid = $arMatches[1];
        }
        if (preg_match($this->REG_ACCESS_POINT, $strAll, $arMatches)) {
            $objConfig->ap = $arMatches[1];
        }
        if (preg_match($this->REG_NICKNAME, $strAll, $arMatches)) {
            $objConfig->nick = $arMatches[1];
        }
        if (strpos($strAll, 'Mode:Managed')) {
            $objConfig->mode = 'managed';
        } else if (strpos($strAll, 'Mode:Ad-Hoc')) {
            $objConfig->mode = 'ad-hoc';
        }
        if (preg_match($this->REG_BIT_RATE, $strAll, $arMatches)) {
            $objConfig->rate = $arMatches[1];
        }
        if (preg_match($this->REG_POWER, $strAll, $arMatches)) {
            $objConfig->power = $arMatches[1];
        }
        if (preg_match($this->REG_SIGNAL_LEVEL, $strAll, $arMatches)) {
            $objConfig->rssi = $arMatches[1];
        }
        if (preg_match($this->REG_NOISE_LEVEL, $strAll, $arMatches)) {
            $objConfig->noise = $arMatches[1];
        }
        if (preg_match($this->REG_PROTOCOL_1, $strAll, $arMatches)) {
            $objConfig->protocol = $arMatches[1];
        } elseif (preg_match($this->REG_PROTOCOL_2, $strAll, $arMatches)) {
            $objConfig->protocol = $arMatches[1];
        }

        if (preg_match($this->REG_RX_INVALID_NWID, $strAll, $arMatches)) {
            $objConfig->packages_rx_invalid_nwid = $arMatches[1];
        }
        if (preg_match($this->REG_RX_INVALID_CRYPT, $strAll, $arMatches)) {
            $objConfig->packages_rx_invalid_crypt = $arMatches[1];
        }
        if (preg_match($this->REG_RX_INVALID_FRAG, $strAll, $arMatches)) {
            $objConfig->packages_rx_invalid_frag = $arMatches[1];
        }
        if (preg_match($this->REG_TX_EXCESSIVE_RETRIES, $strAll, $arMatches)) {
            $objConfig->packages_tx_excessive_retries = $arMatches[1];
        }
        if (preg_match($this->REG_INVALID_MISC, $strAll, $arMatches)) {
            $objConfig->packages_invalid_misc = $arMatches[1];
        }
        if (preg_match($this->REG_MISSED_BEACON, $strAll, $arMatches)) {
            $objConfig->packages_missed_beacon = $arMatches[1];
        }

        //available in ipw2200 1.0.3 only
        if (strpos($strAll, 'radio off')) {
            $objConfig->activated = false;
        }

        if (strpos($strAll, 'unassociated') === false
            && $objConfig->ap != null && $objConfig->ap != '00:00:00:00:00:00'
        ) {
            $objConfig->associated = true;
        }

        return $objConfig;
    }//function parseCurrentConfig(..)



    /**
    * Checks if a network interface is connected to an access point.
    *
    * @param string $strInterface The network interface to check
    *
    * @return boolean If the interface is connected
    * @access public
    */
    function isConnected($strInterface)
    {
        $objConfig = $this->getCurrentConfig($strInterface);

        return $objConfig->associated;
    }//function isConnected(..)



    /**
    * Returns an array with the names/device files of
    *  all supported wireless lan devices.
    *
    * @access public
    * @return array   Array with wireless interfaces as values
    */
    function getSupportedInterfaces()
    {
        $arWirelessInterfaces = array();
        if (is_executable($this->arFileLocation['iwconfig'])) {
            // use iwconfig
            $arLines = array();
            exec($this->arFileLocation['iwconfig'] . ' 2>&1', $arLines);
            foreach ($arLines as $strLine) {
                if (strlen($strLine)
                    && trim($strLine[0]) != ''
                    && strpos($strLine, 'no wireless extensions') === false
                ) {
                    //there is something
                    $arWirelessInterfaces[]
                        = substr($strLine, 0, strpos($strLine, ' '));
                }
            }//foreach line

        } else if (file_exists($this->arFileLocation['/proc/net/wireless'])) {
            // use /proc/net/wireless
            $arLines = file($this->arFileLocation['/proc/net/wireless']);
            //begin with 3rd line
            if (count($arLines) > 2) {
                for ($nA = 2; $nA < count($arLines); $nA++) {
                    $nPos         = strpos($arLines[$nA], ':', 0);
                    $strInterface = trim(substr($arLines[$nA], 0, $nPos));
                    //assign interface
                    $arWirelessInterfaces[] = $strInterface;
                }
            }//we've got more than 2 lines
        }

        return $arWirelessInterfaces;
    }//function getSupportedInterfaces()



    /**
    * Scans for access points / ad hoc cells and returns them.
    *
    * @param string $strInterface The interface to use
    *
    * @return array Array with cell information objects (Net_Wifi_Cell)
    * @access public
    */
    function scan($strInterface)
    {
        $arLines = array();
        exec(
            $this->arFileLocation['iwlist'] . ' '
            . escapeshellarg($strInterface) . ' scanning'
            . ' 2>&1',
            $arLines
        );

        return $this->parseScan($arLines);
    }//function scan(..)



    /**
    * Parses the output of iwlist and returns the recognized cells.
    *
    * @param array $arLines Lines of the iwlist output as an array
    *
    * @return array Array with cell information objects
    * @access protected
    */
    function parseScan($arLines)
    {
        if (count($arLines) == 1) {
            //one line only -> no cells there
            return array();
        }

        //if bit rates are alone on lines
        $bStandaloneRates = false;

        //split into cells
        $arCells      = array();
        $nCurrentCell = -1;
        $nCount       = count($arLines);
        for ($nA = 1; $nA < $nCount; $nA++) {
            $strLine = trim($arLines[$nA]);
            if ($strLine == '') {
                continue;
            }

            if (substr($strLine, 0, 4) == 'Cell') {
                //we've got a new cell
                $nCurrentCell++;
                //get cell number
                $nCell = substr($strLine, 5, strpos($strLine, ' ', 5) - 5);
                //add new cell
                $arCells[$nCurrentCell]       = new Net_Wifi_Cell();
                $arCells[$nCurrentCell]->cell = $nCell;
                $arCells[$nCurrentCell]->ies = array();
                $arCells[$nCurrentCell]->wpa = false;
                $arCells[$nCurrentCell]->wpa2 = false;
                $arCells[$nCurrentCell]->wpa_group_cipher = array();
                $arCells[$nCurrentCell]->wpa_pairwise_cipher = array();
                $arCells[$nCurrentCell]->wpa_auth_suite = array();
                $arCells[$nCurrentCell]->wpa2_group_cipher = array();
                $arCells[$nCurrentCell]->wpa2_pairwise_cipher = array();
                $arCells[$nCurrentCell]->wpa2_auth_suite = array();

                //remove cell information from line for further interpreting
                $strLine = substr($strLine, strpos($strLine, '- ') + 2);
            }

            $nPos       = strpos($strLine, ':');
            $nPosEquals = strpos($strLine, '=');
            if ($nPosEquals !== false && ($nPos === false || $nPosEquals < $nPos)) {
                //sometimes there is a "=" instead of a ":"
                $nPos = $nPosEquals;
            }
            $nPos++;

            $strId    = strtolower(substr($strLine, 0, $nPos - 1));
            $strValue = trim(substr($strLine, $nPos));
            switch ($strId) {
            case 'ie':
                if ($strValue == $this->REG_WPA_IE_STRING) {
                    // WPA1: "WPA Version 1"
                    // (multiline with Group Cipher list,
                    //  Pairwise Ciphers list and Authentication Suites)
                    /*
                    * WPA Version 1
                    *     Group Cipher : TKIP
                    *     Pairwise Ciphers (2) : TKIP CCMP
                    *     Authentication Suites (1) : PSK
                    */
                    $arCells[$nCurrentCell]->wpa = true;
                    $bStandaloneRates = true;
                }

                if ($strValue == $this->REG_WPA2_IE_STRING) {
                    // WPA2: "IEEE 802.11i/WPA2 Version 1"
                    // (multiline with Group Cipher list,
                    //  Pairwise Ciphers list and Authentication Suites)
                    /*
                    * IEEE 802.11i/WPA2 Version 1
                    *     Group Cipher : CCMP
                    *     Pairwise Ciphers (1) : CCMP
                    *     Authentication Suites (1) : PSK
                    */
                    $arCells[$nCurrentCell]->wpa2 = true;
                    $bStandaloneRates = true;
                }
                $arCells[$nCurrentCell]->ies[] = $strValue;
                $arLines[$nA]     = $strValue;
                $nA--;//go back one so that this line is re-parsed
                break;

            case 'address':
                $arCells[$nCurrentCell]->mac = $strValue;
                break;

            case 'essid':
                if ($strValue[0] == '"') {
                    //has quotes around
                    $arCells[$nCurrentCell]->ssid = substr($strValue, 1, -1);
                } else {
                    $arCells[$nCurrentCell]->ssid = $strValue;
                }
                break;

            case 'bit rate':
                $nRate = floatval(substr($strValue, 0, strpos($strValue, 'Mb/s')));
                //assign rate.
                $arCells[$nCurrentCell]->rate    = $nRate;
                $arCells[$nCurrentCell]->rates[] = $nRate;
                break;

            case 'bit rates':
                $bStandaloneRates = true;
                $arLines[$nA]     = $strValue;
                $nA--;//go back one so that this line is re-parsed
                break;

            case 'protocol':
                if (substr($strValue, 0, 5) == 'IEEE ') {
                    $strValue = substr($strValue, 5);
                }
                $arCells[$nCurrentCell]->protocol = $strValue;
                break;

            case 'channel':
                $arCells[$nCurrentCell]->channel = intval($strValue);
                break;

            case 'encryption key':
                if ($strValue == 'on') {
                    $arCells[$nCurrentCell]->encryption = true;
                } else {
                    $arCells[$nCurrentCell]->encryption = false;
                }
                break;

            case 'mode':
                if (strtolower($strValue) == 'master') {
                    $arCells[$nCurrentCell]->mode = 'master';
                } else {
                    $arCells[$nCurrentCell]->mode = 'ad-hoc';
                }
                break;

            case 'signal level':
                $arCells[$nCurrentCell]->rssi
                    = substr($strValue, 0, strpos($strValue, ' '));
                break;

            case 'quality':
                $arData                          = explode('  ', $strValue);
                $arCells[$nCurrentCell]->quality = $arData[0];
                if (trim($arData[1]) != '') {
                    //bad hack
                    $arLines[$nA] = $arData[1];
                    $nA--;
                    if (isset($arData[2])) {
                        $arLines[$nA - 1] = $arData[1];
                        $nA--;
                    }
                }
                break;

            case 'frequency':
                $match = preg_match(
                    '/([0-9.]+ GHz) \(Channel ([0-9])\)/',
                    $strValue, $arMatches
                );
                if ($match) {
                    $arCells[$nCurrentCell]->frequency = $arMatches[1];
                    $arCells[$nCurrentCell]->channel   = $arMatches[2];
                } else {
                    $arCells[$nCurrentCell]->frequency = $strValue;
                }
                break;

            case 'extra':
                $nPos     = strpos($strValue, ':');
                $strSubId = strtolower(trim(substr($strValue, 0, $nPos)));
                $strValue = trim(substr($strValue, $nPos + 1));
                switch ($strSubId) {
                case 'rates (mb/s)':
                    //1 2 5.5 11 54
                    $arRates = explode(' ', $strValue);
                    //convert to float values
                    foreach ($arRates as $nB => $strRate) {
                        $arCells[$nCurrentCell]->rates[$nB] = floatval($strRate);
                    }
                    break;

                case 'signal':
                case 'rssi':
                    //-53 dBm
                    $arCells[$nCurrentCell]->rssi
                        = intval(substr($strValue, 0, strpos($strValue, ' ')));
                    break;

                case 'last beacon':
                    //25ms ago
                    $arCells[$nCurrentCell]->beacon
                        = intval(substr($strValue, 0, strpos($strValue, 'ms')));
                    break;

                default:
                    $this->handleUnknown(null, $strSubId);
                    break;
                }
                break;

            default:
                if ($bStandaloneRates) {
                    if (preg_match_all($this->REG_RATES, $strLine, $arMatches) > 0) {
                        foreach ($arMatches[1] as $nRate) {
                            $nRate                           = floatval($nRate);
                            $arCells[$nCurrentCell]->rate    = $nRate;
                            $arCells[$nCurrentCell]->rates[] = $nRate;
                        }
                        break;
                    }

                    $found = $this->parseWpaCipher(
                        $strLine, $arCells, $nCurrentCell,
                        $this->REG_GROUP_CIPHER, 'group_cipher'
                    );
                    if ($found) {
                        break;
                    }

                    $found = $this->parseWpaCipher(
                        $strLine, $arCells, $nCurrentCell,
                        $this->REG_PAIRWISE_CIPHERS, 'pairwise_cipher'
                    );
                    if ($found) {
                        break;
                    }

                    $found = $this->parseWpaCipher(
                        $strLine, $arCells, $nCurrentCell,
                        $this->REG_AUTH_SUITES, 'auth_suite'
                    );
                    if ($found) {
                        break;
                    }
                }
                $this->handleUnknown($strId, null);
                break;
            }
        }//foreach line


        //not all outputs are sorted (note the 6)
        //Extra: Rates (Mb/s): 1 2 5.5 9 11 6 12 18 24 36 48 54
        //additionally, some drivers have many single "Bit Rate:"
        // fields instead of one big one
        foreach ($arCells as $nCurrentCell => $arData) {
            sort($arCells[$nCurrentCell]->rates);
            $arCells[$nCurrentCell]->rates
                = array_unique($arCells[$nCurrentCell]->rates);
        }

        return $arCells;
    }//function parseScan(..)

    /**
     * Parse a WPA/WPA2 cipher string and append it to the cell
     *
     * @param string  $strLine      Input line that gets parsed
     * @param array   $arCells      Array of cell data
     * @param integer $nCurrentCell Key of current cell in $arCells
     * @param string  $regex        Expression to match line against
     * @param string  $property     Cell property to set (without wpa_/wpa2_)
     *
     * @return boolean True if a cipher matched
     */
    protected function parseWpaCipher(
        $strLine, &$arCells, $nCurrentCell, $regex, $property
    ) {
        if (preg_match_all($regex, $strLine, $arMatches) == 0) {
            return false;
        }
        foreach ($arMatches[1] as $nCipher) {
            //WPA 1
            if (end($arCells[$nCurrentCell]->ies) == $this->REG_WPA_IE_STRING) {
                $arCells[$nCurrentCell]->{'wpa_' . $property}
                    = explode(' ', $nCipher);
            }
            //WPA 2
            if (end($arCells[$nCurrentCell]->ies) == $this->REG_WPA2_IE_STRING) {
                $arCells[$nCurrentCell]->{'wpa2_' . $property}
                    = explode(' ', $nCipher);
            }
        }
        return true;
    }

    /**
    * Tells the driver to use the access point with the given MAC address only.
    *
    * You can use "off" to enable automatic mode again without
    * changing the current AP, or "any" resp. "auto" to force
    * the card to re-associate with the currently best AP
    *
    * EXPERIMENTAL! WILL CHANGE IN FUTURE VERSIONS
    *
    * @param string $strInterface The interface to use
    * @param string $strMac       The mac address of the access point
    *
    * @return boolean True if setting was ok, false if not
    * @access public
    */
    function connectToAccessPoint($strInterface, $strMac)
    {
        $arLines    = array();
        $nReturnVar = 0;
        exec(
            $this->arFileLocation['iwconfig'] . ' ' . escapeshellarg($strInterface)
            . ' ap ' . escapeshellarg($strMac),
            $arLines,
            $nReturnVar
        );

        return $nReturnVar == 0;
    }//function connectToAccessPoint(..)



    /**
     * Handle unknown configuration lines.
     *
     * @param string $strId    iwconfig output prefix (i.e. 'frequency')
     * @param string $strSubId iwconfig output value
     *
     * @return void
     *
     * @uses $unknowns
     */
    function handleUnknown($strId, $strSubId)
    {
        if ($this->unknowns === null) {
            return;
        }

        if ($strId !== null) {
            $strMsg = 'unknown iwconfig information: ' . $strId;
        } else {
            $strMsg = 'unknown iwconfig extra information: ' . $strSubId;
        }

        if ($this->unknowns == 'echo') {
            fwrite(STDERR, $strMsg . "\r\n");
        } else if (is_object($this->unknowns)) {
            $this->unknowns->debug($strMsg);
        }
    }//function handleUnknown(..)



    /*
     * and now some dumb getters and setters
     */



    /**
    * Returns the set path to /proc/wireless.
    *
    * @return string The path to "/proc/net/wireless"
    * @access public
    */
    function getPathProcWireless()
    {
        return $this->arFileLocation['/proc/net/wireless'];
    }//function getPathProcWireless()



    /**
    * Set the path to /proc/net/wireless.
    *
    * @param string $strProcWireless The new /proc/net/wireless path
    *
    * @return null
    * @access public
    */
    function setPathProcWireless($strProcWireless)
    {
        $this->arFileLocation['/proc/net/wireless'] = $strProcWireless;
    }//function setPathProcWireless(..)



    /**
    * Returns the set path to iwconfig.
    *
    * @return string The path to iwconfig
    * @access public
    */
    function getPathIwconfig()
    {
        return $this->arFileLocation['iwconfig'];
    }//function getPathIwconfig()



    /**
    * Set the path to iwconfig.
    *
    * @param string $strPathIwconfig The new ifwconfig path
    *
    * @return null
    * @access public
    */
    function setPathIwconfig($strPathIwconfig)
    {
        $this->arFileLocation['iwconfig'] = $strPathIwconfig;
    }//function setPathIwconfig(..)



    /**
    * Returns the set path to iwlist.
    *
    * @return string The path to iwlist
    *
    * @access public
    */
    function getPathIwlist()
    {
        return $this->arFileLocation['iwlist'];
    }//function getPathIwlist()



    /**
    * Returns the set path to iwlist.
    *
    * @param string $strPathIwlist The new iwlist path
    *
    * @return void
    * @access public
    */
    function setPathIwlist($strPathIwlist)
    {
        $this->arFileLocation['iwlist'] = $strPathIwlist;
    }//function setPathIwlist(..)


}//class Net_Wifi

?>