/* 
        
        The scanning part is derived from wireless-tools ba Jean Tourrilhes <jt@hpl.hp.com>
   
        (c) 2006 by Thomas Michel <tom.michel@arcor.de>

        Kwlan is free software; you can redistribute it and/or modify
        it under the terms of the GNU Library General Public License as
        published by the Free Software Foundation; either version 2 of
        the License, or (at your option) any later version.

        Kwlan is distributed in the hope that it will be useful,
        but WITHOUT ANY WARRANTY; without even the implied warranty of
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        GNU Library General Public License for more details.

        You should have received a copy of the GNU Library General Public License
        along with this library; see the file COPYING.LIB.  If not, write to
        the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
        Boston, MA 02110-1301, USA.
        
*/

#include "wlanlib.h"
#include "globals.h"

#include <qwidget.h>
#include <kdebug.h>
#include <kmessagebox.h>
#include <qstringlist.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <net/ethernet.h>
#include <iwlib.h>
#include <errno.h>
#include <klocale.h>
#include <kstandarddirs.h>
#include <kglobal.h>
#include <qfile.h>
#include <kdesu/client.h>
#include <kpassdlg.h>

int str_match(const char *a, const char *b)
{
    return strncmp(a, b, strlen(b)) == 0;
}

void kwlan_msg_cb(char *msg, size_t)
{
    kdDebug() << "missed message: " << msg << endl;
}


QStringList getInterfaces()
{
    char		buff[1024];
    FILE *	fh;
    struct ifconf ifc;
    struct ifreq *ifr;
    int		i;
    int skfd;
    QStringList interfaceList;

    /* Check if /proc/net/wireless is available */
    fh = fopen(PROC_NET_WIRELESS, "r");

    if (fh != NULL) {
       /* Success : use data from /proc/net/wireless */

       /* Eat 2 lines of header */
       fgets(buff, sizeof(buff), fh);
       fgets(buff, sizeof(buff), fh);

       /* Read each device line */
       while(fgets(buff, sizeof(buff), fh)){
          char	name[IFNAMSIZ + 1];
          char *s = buff;
          char *colon;

          /* Extract interface name */
          // remove leading spaces
          while (isspace(*s)) s++;
          colon = strchr(s,':');
          if((colon == NULL) || (((colon - s) + 1) > sizeof(name)))
              name[0] = '\0';
          else {
             /* Copy */
             memcpy(name, s, (colon - s));
             name[colon - s] = '\0';
          }

          if(name[0]=='\0')
            /* Failed to parse, complain and continue */
              KMessageBox::sorry(0,"Kwlan", "Cannot parse " PROC_NET_WIRELESS);
	  else
	    /* Got it, print info about this interface */
	    interfaceList.append(name);
    }

       fclose(fh);
    }
    else {
        /* Get list of configured devices using "traditional" way */
        ifc.ifc_len = sizeof(buff);
        ifc.ifc_buf = buff;
 
        if((skfd = iw_sockets_open()) < 0) {
            KMessageBox::sorry(0,"Kwlan", "Cannot parse %s" PROC_NET_WIRELESS);
            return interfaceList;
        }

        if(ioctl(skfd, SIOCGIFCONF, &ifc) < 0) {
            KMessageBox::sorry(0,"Kwlan",i18n("Could not detect WLAN interfaces!"));
            return interfaceList;
        }
        ifr = ifc.ifc_req;

        /* Print them */
        for(i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; ifr++)
            interfaceList.append(ifr->ifr_name);
    }
    
 
    return interfaceList;
}

QStringList getWiredInterfaces()
{
    char		buff[1024];
    FILE *	fh;
    QStringList interfaceList;

    /* Check if /proc/net/wireless is available */
    fh = fopen(PROC_NET_WIRED, "r");

    if (fh != NULL) {
        /* Success : use data from /proc/net/wireless */

        /* Eat 2 lines of header */
        fgets(buff, sizeof(buff), fh);
        fgets(buff, sizeof(buff), fh);

        /* Read each device line */
        while(fgets(buff, sizeof(buff), fh)){
            char	name[IFNAMSIZ + 1];
            char *s = buff;
            char *colon;

            /* Extract interface name */
          // remove leading spaces
            while (isspace(*s)) s++;
            colon = strchr(s,':');
            if((colon == NULL) || (((colon - s) + 1) > sizeof(name)))
                name[0] = '\0';
            else {
                /* Copy */
                memcpy(name, s, (colon - s));
                name[colon - s] = '\0';
            }

            if(name[0]=='\0')
                /* Failed to parse, complain and continue */
                KMessageBox::sorry(0,"Kwlan", "Cannot parse " PROC_NET_WIRED);
            else
                /* Got it, print info about this interface */
                interfaceList.append(name);
        }

        fclose(fh);
    }
 
    return interfaceList;
}


/// get Signal Quality of current link
IwLib::IwLib(const QString &ifname) {
            m_ifname = ifname;
            m_socket = iw_sockets_open();
        }
        
IwLib::~IwLib() {
    if (m_socket > 0)
         iw_sockets_close(m_socket);
    }
            
int IwLib::getQuality() {
    int has_range;
    iw_range range;
    iw_statistics stats;
    if (m_socket < 0)
    	return 0;
    if ( iw_get_range_info(m_socket, m_ifname.ascii(), &range) < 0 )
    	has_range = 0;
    else
        has_range = 1;
    iw_get_stats(m_socket, m_ifname.ascii(), &stats, &range, has_range);
    
        //calculate quality in range 0-100.
    return (100*stats.qual.qual)/range.max_qual.qual;
}

QString IwLib::getBitrate() {
    struct iwreq wrq;
    if (m_socket < 0)
         return QString::null;
    if (iw_get_ext(m_socket, m_ifname.ascii(), SIOCGIWRATE, &wrq) >= 0) {
        char buffer[25];
        iw_print_bitrate(buffer, 25, wrq.u.bitrate.value);
        return QString(buffer);
    }
    return QString::null;
 
}

QString IwLib::getESSID() {
    struct iwreq wrq;
    char buffer[IW_ESSID_MAX_SIZE];
    if (m_socket < 0)
	return QString::null;
        wrq.u.essid.pointer = (char *)&buffer;
    wrq.u.essid.length = IW_ESSID_MAX_SIZE;
    if (iw_get_ext(m_socket, m_ifname.ascii(), SIOCGIWESSID, &wrq) >= 0)
    	return QString::fromAscii((char *)wrq.u.essid.pointer, wrq.u.essid.length);
    else
        return QString::null;
}

bool IwLib::setWepAuth(int authMode) {
    struct iwreq wrq;
    int skfd;
    if (m_socket <0)
        return FALSE;
    wrq.u.data.flags = authMode;
    if (iw_set_ext(skfd, m_ifname.ascii(), SIOCSIWENCODE, &wrq) < 0) return FALSE;
    return TRUE;
}

/// searches dirs for file, returns path to file if found. If not found return string will be empty
QString getPath(QString file)
{
    QString path = "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin";
    return KGlobal::dirs()->findExe(file, path);
}

//Taken from <net/route.h> - there is no reasen to include the whole file
#define RTF_UP		0x0001
#define RTF_GATEWAY	0x0002

/// Returns true if there is default gateway on an active interface
bool hasGateway() {
  QFile file("/proc/net/route");

  if (file.open(IO_ReadOnly)) {
    QTextStream stream(&file);
    QStringList tokens;

    // Skip the first line containing headers
    tokens = QStringList::split("\t", stream.readLine());
    if (tokens[1] != "Destination" || tokens[3] != "Flags") {
    	qWarning("/proc/net/route layout has changed... Which year is it now?!");
    	return false;
    }

    // Iterate through the routing table and look for a dafeult route, i.e.
    // destination with address 0.0.0.0 on the active interface (RTF_UP) with RTF_GATEWAY flag set
    while (!stream.atEnd()) {
      tokens = QStringList::split("\t", stream.readLine());
      if (tokens[1]  == "00000000" && (tokens[3].toInt() & (RTF_UP | RTF_GATEWAY)))
      	return true;
    }
    // Not required - file will be closed automatically upon destruction of QFile
    file.close();
  }

  // The system we are running on has no /proc/net/route or we didn't find default gateway
  return false;
}

/// Returns true is there is a nameserver entry in /etc/resolv.conf
bool hasNameserver() {
  QFile file("/etc/resolv.conf");

  if (file.open(IO_ReadOnly)) {
    QTextStream stream(&file);
    QString line;

    // We use glibc matching algorithm to find nameservers
    while (!stream.atEnd()) {
      line = stream.readLine();
      if (line.startsWith("nameserver ") || line.startsWith("nameserver\t"))
      	return true;
    }
    // Not required - file will be closed automatically upon destruction of QFile
    file.close();
  }

  return false;
}



/****************************** TYPES ******************************/

/*
 * Scan state and meta-information, used to decode events...
 */
typedef struct iwscan_state
{
    /* State */
    int			ap_num;		/* Access Point number 1->N */
    int			val_index;	/* Value in table 0->(N-1) */
} iwscan_state;



/***************************** SCANNING *****************************/
/*
 * This one behave quite differently from the others
 *
 * Note that we don't use the scanning capability of iwlib (functions
 * iw_process_scan() and iw_scan()). The main reason is that
 * iw_process_scan() return only a subset of the scan data to the caller,
 * for example custom elements and bitrates are ommited. Here, we
 * do the complete job...
 */

/*------------------------------------------------------------------*/
/*
 * Parse, and display the results of a WPA or WPA2 IE.
 *
 */
static void 
        iw_print_ie_unknown(unsigned char *	iebuf,
                            int			buflen)
{
    int	ielen = iebuf[1] + 2;
    int	i;

    if(ielen > buflen)
        ielen = buflen;

    printf("Unknown: ");
    for(i = 0; i < ielen; i++)
        printf("%02X", iebuf[i]);
    printf("\n");
}

/*-----------------------------------------------------------------*/
/*
 * Display the cipher type for the value passed in.
 *
 */
static inline 
        QString iw_print_ie_cipher(unsigned char	csuite)
{
    switch (csuite)
    {
        case 0x00:
            return QString("");
        //printf("None or same as Group ");
            break;
 
        case 0x01:
            return QString("WEP-40");
            break;
 
        case 0x02:
            return QString("TKIP");
            break;
 
        case 0x03:
            return QString("WRAP");
            break;
 
        case 0x04:
            return QString("CCMP");
            break;
 
        case 0x05:
            return QString("WEP-104");
            break;
 
        default:
            return QString("");
      //printf("Unknown ");
            break;
    }
}
 
/*------------------------------------------------------------------*/
/*
 * Parse, and display the results of a WPA or WPA2 IE.
 *
 */
static inline void 
        iw_print_ie_wpa(unsigned char *	iebuf,
                        int		buflen,
                        kwlNetwork &net)
{
    int			ielen = iebuf[1] + 2;
    int			offset = 2;	/* Skip the IE id, and the length. */
    unsigned char		wpa1_oui[3] = {0x00, 0x50, 0xf2};
    unsigned char		wpa2_oui[3] = {0x00, 0x0f, 0xac};
    unsigned char *	wpa_oui;
    int			i;
    uint16_t		ver = 0;
    uint16_t		cnt = 0;

    if(ielen > buflen)
        ielen = buflen;

    switch(iebuf[0])
    {
        case 0x30:		/* WPA2 */
            /* Check if we have enough data */
            if(ielen < 4)
            {
                iw_print_ie_unknown(iebuf, buflen);
                return;
            }

            wpa_oui = wpa2_oui;
            break;

            case 0xdd:		/* WPA or else */
                wpa_oui = wpa1_oui;
 
      /* Not all IEs that start with 0xdd are WPA. 
      * So check that the OUI is valid. */
                if((ielen < 8)
                    || ((memcmp(&iebuf[offset], wpa_oui, 3) != 0)
                    && (iebuf[offset+3] == 0x01)))
                {
                    iw_print_ie_unknown(iebuf, buflen);
                    return;
                }

                offset += 4;
                break;

        default:
            return;
    }
  
    /* Pick version number (little endian) */
    ver = iebuf[offset] | (iebuf[offset + 1] << 8);
    offset += 2;

    net.m_flags="[";
    if(iebuf[0] == 0xdd)
        net.m_flags +="WPA";
    //printf("WPA Version %d\n", ver);
    if(iebuf[0] == 0x30)
        net.m_flags +="WPA2";
  //printf("IEEE 802.11i/WPA2 Version %d\n", ver);

    /* From here, everything is technically optional. */

    /* Check if we are done */
    if(ielen < (offset + 4))
    {
        // We have a short IE.  So we should assume TKIP/TKIP. 
        // as group cipher and pairwise cipher
        
        net.m_flags += "-TKIP]";
        return;
    }
 
    /* Next we have our group cipher. */
    if(memcmp(&iebuf[offset], wpa_oui, 3) != 0)
    {
      //printf("                        Group Cipher : Proprietary\n");
    }
    else
    {
        net.m_flags +="-";
        net.m_flags +=iw_print_ie_cipher(iebuf[offset+3]);
    }
    offset += 4;

    /* Check if we are done */
    if(ielen < (offset + 2))
    {
        /* We don't have a pairwise cipher, or auth method. Assume TKIP. */
        net.m_flags +="-";
        net.m_flags += "TKIP]";
        return;
    }

    /* Otherwise, we have some number of pairwise ciphers. */
    cnt = iebuf[offset] | (iebuf[offset + 1] << 8);
    offset += 2;
  //printf("                        Pairwise Ciphers (%d) : ", cnt);

    if(ielen < (offset + 4*cnt))
    {
        net.m_flags +="]";
        return;
    }

    for(i = 0; i < cnt; i++)
    {
        if(memcmp(&iebuf[offset], wpa_oui, 3) != 0)
        {
 	  //printf("Proprietary  ");
        }
        else
        {
            net.m_flags +="-";
            net.m_flags +=iw_print_ie_cipher(iebuf[offset+3]);
        }
        offset+=4;
    }
 
    /* Check if we are done */
    if(ielen < (offset + 2))
    {
        net.m_flags +="]";
        return;
    }

    /* Now, we have authentication suites. */
    cnt = iebuf[offset] | (iebuf[offset + 1] << 8);
    offset += 2;
  //printf("                        Authentication Suites (%d) : ", cnt);

    if(ielen < (offset + 4*cnt))
    {
        net.m_flags +="]";
        return;
    }

    for(i = 0; i < cnt; i++)
    {
        if(memcmp(&iebuf[offset], wpa_oui, 3) != 0)
        {
 	  //printf("Proprietary  ");
        }
        else
        {
            switch(iebuf[offset+3])
            {
                case 0x00:
 	      //printf("Reserved  ");
                    break;

                case 0x01:
 	      //printf("802.1X  ");
                    break;

                case 0x02:
                    net.m_flags +="-";
                    net.m_flags +=("PSK  ");
                    break;

                default:
 	      //printf("Unknown  ");
                    break;
            }
        }
        offset+=4;
    }
 
    /* Check if we are done */
    if(ielen < (offset + 1))
    {
        net.m_flags +="]";
        return;
    }

  /* Otherwise, we have capabilities bytes.
    * For now, we only care about preauth which is in bit position 1 of the
    * first byte.  (But, preauth with WPA version 1 isn't supposed to be 
  * allowed.) 8-) */
    if(iebuf[offset] & 0x01)
    {
      //printf("                       Preauthentication Supported\n");
    }
    net.m_flags+="]";
}
 
/*------------------------------------------------------------------*/
/*
 * Process a generic IE and display the info in human readable form
 * for some of the most interesting ones.
 * For now, we only decode the WPA IEs.
 */
static inline void
        iw_print_gen_ie(unsigned char *	buffer,
                        int		buflen,
                        kwlNetwork &net)
{
    int offset = 0;
  
    net.m_flags ="[";

    /* Loop on each IE, each IE is minimum 2 bytes */
    while(offset <= (buflen - 2))
    {
        /* Check IE type */
        switch(buffer[offset])
        {
            case 0xdd:	/* WPA1 (and other) */
                case 0x30:	/* WPA2 */
                    iw_print_ie_wpa(buffer + offset, buflen,net);
                    break;
            default:
                iw_print_ie_unknown(buffer + offset, buflen);
        }
        /* Skip over this IE to the next one in the list. */
        offset += buffer[offset+1] + 2;
    }
}

/*------------------------------------------------------------------*/
/*
 * Print one element from the scanning results
 */
void print_scanning_token(struct stream_descr *	stream,	/* Stream of events */
                             struct iw_event *		event,	/* Extracted token */
                             struct iwscan_state *	state,
                             struct iw_range *	iw_range,	/* Range info */
                             int		has_range,
                             kwlNetworkList &netList)
{
    char		buffer[128];	/* Temporary buffer */
    kwlNetworkList::Iterator net;
    int ret = 1;

  // SIOCGIWAP should be first cmd, else something's wrong....
    /* Now, let's decode the event */
    while (event->cmd != SIOCGIWAP)
    {
        ret = iw_extract_event_stream(stream, event,
                                      iw_range->we_version_compiled);
        if (ret <= 0 ) return;
    }
    if (event->cmd != SIOCGIWAP)
        return;
    else {
        net  = netList.begin();
        (*net).m_bssid =iw_saether_ntop(&event->u.ap_addr, buffer);
    }
  // now that we have the first network, fill in all parameters. 
  // if another network is found, a new network will be added to the list
    while (ret > 0)
    {
        switch(event->cmd)
        {
            case SIOCGIWAP:
        // add network and create new one
                if (::debugOutput)
                {
                    kdDebug() << "\n\n" << endl;
                    kdDebug() << "found accesspoint "<< (*net).m_bssid << endl;
                    kdDebug() << "ssid "<< (*net).m_ssid << endl;
                    kdDebug() << "signal "<< (*net).m_signal << endl;
                    kdDebug() << "quality "<< (*net).m_quality << endl;
                    kdDebug() << "channel "<< (*net).m_channel << endl;
                    kdDebug() << "flags " << (*net).m_flags << endl;
                }
                net  = netList.append(kwlNetwork(""));
                (*net).m_bssid =iw_saether_ntop(&event->u.ap_addr, buffer);
                break;
            case SIOCGIWNWID:
              /*
                if(event->u.nwid.disabled)
                printf("                    NWID:off/any\n");
                else
                printf("                    NWID:%X\n", event->u.nwid.value);
              */
                break;
            case SIOCGIWFREQ:
            {
                double		freq;			/* Frequency/channel */
                int		channel = -1;		/* Converted to channel */
                freq = iw_freq2float(&(event->u.freq));
                /* Convert to channel if possible */
                if(has_range)
                    channel = iw_freq_to_channel(freq, iw_range);
                /* Check if channel only */
                if(freq < KILO)
                {
                    (*net).m_channel = QString("%1").arg(freq);
                }
                else
                {
                    /* Frequency. Check if we have a channel as well */
                    if(channel >= 0)
                    {
                        char vbuf[16];
                        iw_print_freq_value(vbuf,sizeof(vbuf),freq);
                        (*net).m_channel = channel;
                        (*net).m_frequency = vbuf;
                    }
                }
            }
            break;
            case SIOCGIWMODE:
            {
                QString adhoc = iw_operation_mode[event->u.mode];
                if (adhoc == "Master")
                    (*net).m_adhoc = FALSE;
                else 
                    (*net).m_adhoc = TRUE;
            }
            break;
            case SIOCGIWNAME:
              //printf("                    Protocol:%-1.16s\n", event->u.name);
                break;
            case SIOCGIWESSID:
            {
                char essid[IW_ESSID_MAX_SIZE+1];
                memset(essid, '\0', sizeof(essid));
                if((event->u.essid.pointer) && (event->u.essid.length))
                    memcpy(essid, event->u.essid.pointer, event->u.essid.length);
                if(event->u.essid.flags)
                {
                    (*net).m_ssid = essid;
                }
                else
                    (*net).m_ssid = "<hidden>";
            }
            break;
            case SIOCGIWENCODE:
            {
                unsigned char	key[IW_ENCODING_TOKEN_MAX];
                if(event->u.data.pointer)
                    memcpy(key, event->u.data.pointer, event->u.data.length);
                else
                    event->u.data.flags |= IW_ENCODE_NOKEY;
                if (!(event->u.data.flags & IW_ENCODE_DISABLED))
                {
                    /* Other info... */
                    if(event->u.data.flags & IW_ENCODE_RESTRICTED)
                        (*net).m_secMode = "restricted";
                    if(event->u.data.flags & IW_ENCODE_OPEN)
                        (*net).m_secMode = "open";
                }
            }
            break;
            case SIOCGIWRATE:
              /*
                if(state->val_index == 0)
                printf("                    Bit Rates:");
                else
                if((state->val_index % 5) == 0)
                printf("\n                              ");
                else
                printf("; ");
                iw_print_bitrate(buffer, sizeof(buffer), event->u.bitrate.value);
                printf("%s", buffer);
              // Check for termination 
                if(stream->value == NULL)
            {
                printf("\n");
                state->val_index = 0;
        }
                else
                state->val_index++;
              */
                break;
            case IWEVQUAL:
            {
                iwqual *qual = &event->u.qual;
                iwrange *range = iw_range;
                if(has_range && ((qual->level != 0) || (qual->updated & IW_QUAL_DBM)))
                {
                    /* Deal with quality : always a relative value */
                    if(!(qual->updated & IW_QUAL_QUAL_INVALID))
                    {
                        (*net).m_quality = QString("%1").arg(qual->qual);
                    }

                    /* Check if the statistics are in dBm or relative */
                    if((qual->updated & IW_QUAL_DBM)
                        || (qual->level > range->max_qual.level))
                    {
                        /* Deal with signal level in dBm  (absolute power measurement) */
                        if(!(qual->updated & IW_QUAL_LEVEL_INVALID))
                        {
                            (*net).m_signal = QString("%1").arg(qual->level - 0x100);
                        }
                    }
                    else
                    {
                        /* Deal with signal level as relative value (0 -> max) */
                        if(!(qual->updated & IW_QUAL_LEVEL_INVALID))
                        {
                            (*net).m_signal = QString("%1").arg(qual->level);// +"/"+ range->max_qual.level;
                        }
                    }
                }
                else
                {
                    /* We can't read the range, so we don't know... */
                    (*net).m_quality = QString("%1").arg(qual->qual);
                    (*net).m_signal = QString("%1").arg(qual->level);
                }
                break;
            }
            case IWEVGENIE:
                /* Informations Elements are complex, let's do only some of them */
                iw_print_gen_ie((unsigned char *) event->u.data.pointer,  event->u.data.length, *net);
                break;
            case IWEVCUSTOM:
            {
              /*
                char custom[IW_CUSTOM_MAX+1];
                if((event->u.data.pointer) && (event->u.data.length))
                memcpy(custom, event->u.data.pointer, event->u.data.length);
                custom[event->u.data.length] = '\0';
                printf("                    Extra:%s\n", custom);
              */
            }
            break;
        }	/* switch(event->cmd) */
      //switch to next event
        ret = iw_extract_event_stream(stream, event,
                                      iw_range->we_version_compiled);
      
    }
}



/************************* COMMON UTILITIES *************************/
/*
 * This section was initially written by Michael Tokarev <mjt@tls.msk.ru>
 * but heavily modified by me ;-)
 */

/*------------------------------------------------------------------*/
/*
 * Map command line arguments to the proper procedure...
 */
typedef struct iwlist_entry {
    const char *cmd;
    iw_enum_handler fn;
    int min_count;
    int max_count;
} iwlist_cmd;



void kwireless_scan(kwlNetworkList &profiles, char *ifname)
{
    int skfd;			/* generic raw socket desc.	*/
    struct iwreq		wrq;
    unsigned char *	buffer = NULL;		/* Results */
    int			buflen = IW_SCAN_MAX_DATA; /* Min for compat WE<17 */
    struct iw_range	range;
    int			has_range;
    struct timeval	tv;				/* Select timeout */
    int			timeout = 15000000;		/* 15s */

    /* Create a channel to the NET kernel. */
    if((skfd = iw_sockets_open()) < 0)
    {
        kdDebug() << "Cannot get socket for scanning" << endl;
        return;
    }

    /* Get range stuff */
    has_range = (iw_get_range_info(skfd, ifname, &range) >= 0);

    /* Check if the interface could support scanning. */
    if((!has_range) || (range.we_version_compiled < 14))
    {
        kdDebug() << ifname << " doesn't support scanning." << endl;
        iw_sockets_close(skfd);
        return;
    }

    /* Init timeout value -> 250ms*/
    tv.tv_sec = 0;
    tv.tv_usec = 250000;

    wrq.u.data.pointer = NULL;		/* Later */
    wrq.u.data.flags = 0;
    wrq.u.data.length = 0;


    /* Initiate Scanning */
    if(iw_set_ext(skfd, ifname, SIOCSIWSCAN, &wrq) < 0)
    {
        if(errno != EPERM)
        {
            kdDebug()<< ifname << " doesn't support scanning" << endl;
            iw_sockets_close(skfd);
            return;
        }
      /* If we don't have the permission to initiate the scan, we may
        * still have permission to read left-over results.
      * But, don't wait !!! */
      tv.tv_usec = 0;
    }
    timeout -= tv.tv_usec;


    /* Forever */
    while(1)
    {
        fd_set		rfds;		/* File descriptors for select */
        int		last_fd;	/* Last fd */
        int		ret;

        /* Guess what ? We must re-generate rfds each time */
        FD_ZERO(&rfds);
        last_fd = -1;

        /* In here, add the rtnetlink fd in the list */

        /* Wait until something happens */
        ret = select(last_fd + 1, &rfds, NULL, NULL, &tv);

        /* Check if there was an error */
        if(ret < 0)
        {
            if(errno == EAGAIN || errno == EINTR)
                continue;
            kdDebug() << "Unhandled signal during scanning- exiting..." << endl;
            iw_sockets_close(skfd);
            return;
        }

        /* Check if there was a timeout */
        if(ret == 0)
        {
            unsigned char *	newbuf;

	realloc:
                /* (Re)allocate the buffer - realloc(NULL, len) == malloc(len) */
                newbuf = (unsigned char *) realloc(buffer, buflen);
        if(newbuf == NULL)
        {
            if(buffer)
                free(buffer);
            kdDebug() <<  "Allocation failed" << endl;
            return;
        }
        buffer = newbuf;

        /* Try to read the results */
        wrq.u.data.pointer = buffer;
        wrq.u.data.flags = 0;
        wrq.u.data.length = buflen;
        if(iw_get_ext(skfd, ifname, SIOCGIWSCAN, &wrq) < 0)
        {
            /* Check if buffer was too small (WE-17 only) */
            if((errno == E2BIG) && (range.we_version_compiled > 16))
            {
		  /* Some driver may return very large scan results, either
                * because there are many cells, or because they have many
                * large elements in cells (like IWEVCUSTOM). Most will
                * only need the regular sized buffer. We now use a dynamic
                * allocation of the buffer to satisfy everybody. Of course,
                * as we don't know in advance the size of the array, we try
                  * various increasing sizes. Jean II */

                /* Check if the driver gave us any hints. */
                if(wrq.u.data.length > buflen)
                    buflen = wrq.u.data.length;
                else
                    buflen *= 2;

                /* Try again */
                goto realloc;
            }

            /* Check if results not available yet */
            if(errno == EAGAIN)
            {
                /* Restart timer for only 100ms*/
                tv.tv_sec = 0;
                tv.tv_usec = 100000;
                timeout -= tv.tv_usec;
                if(timeout > 0)
                    continue;	/* Try again later */
            }

            /* Bad error */
            free(buffer);
            kdDebug() <<  "Failed to read scan data for " << ifname << endl;
            iw_sockets_close(skfd);
            return;
        }
        else
            /* We have the results, go to process them */
            break;
        }

      /* In here, check if event and event type
      * if scan event, read results. All errors bad & no reset timeout */
    }

    if(wrq.u.data.length)
    {
        struct iw_event		iwe;
        struct stream_descr	stream;
        struct iwscan_state	state;
        state.ap_num = 1; state.val_index = 0;
        int			ret;
      
        iw_init_event_stream(&stream, (char *) buffer, wrq.u.data.length);
        /* Extract an event and print it */
        ret = iw_extract_event_stream(&stream, &iwe,
                                       range.we_version_compiled);
        if(ret > 0)
                       //profiles.clear();
            print_scanning_token(&stream, &iwe, &state,
                                             &range, has_range, profiles);
    }
    iw_sockets_close(skfd);
    free(buffer);
    return;
}

