diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 19:19:25 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 19:19:25 +0000 |
commit | e0311ffdf88fc311006351582b959a69d8aad328 (patch) | |
tree | dccf6212f21ef7e3824b54837be7b1d14139d2e1 /src/wlassistant.cpp | |
download | wlassistant-e0311ffdf88fc311006351582b959a69d8aad328.tar.gz wlassistant-e0311ffdf88fc311006351582b959a69d8aad328.zip |
Added KDE3 version of wlassistant
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/wlassistant@1097621 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/wlassistant.cpp')
-rw-r--r-- | src/wlassistant.cpp | 1264 |
1 files changed, 1264 insertions, 0 deletions
diff --git a/src/wlassistant.cpp b/src/wlassistant.cpp new file mode 100644 index 0000000..bf58914 --- /dev/null +++ b/src/wlassistant.cpp @@ -0,0 +1,1264 @@ +/*************************************************************************** + * Copyright (C) 2005 by Pawel Nawrocki * + * [email protected] * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#include "wlassistant.h" +#include "netlistviewitem.h" +#include "waconfig.h" +#include "watools.h" +#include "ui_netparamswizard.h" +#include "ui_netparamsedit.h" + +#include <iostream> +#include <linux/version.h> //provides LINUX_VERSION* macros + +#include <qregexp.h> +#include <qlabel.h> +#include <qprocess.h> +#include <qcursor.h> +#include <qeventloop.h> +#include <qtimer.h> +#include <qcheckbox.h> +#include <qspinbox.h> +#include <qwidgetstack.h> +#include <qtooltip.h> + +#include <kpushbutton.h> +#include <kcombobox.h> +#include <klistview.h> +#include <kapplication.h> +#include <kiconloader.h> +#include <kmessagebox.h> +#include <kpopupmenu.h> +#include <klocale.h> +#include <kstandarddirs.h> + +WirelessAssistant::WirelessAssistant(QWidget* parent, const char* name, bool modal, WFlags fl) + : mainWindow(parent,name, modal,fl) +{ + buttonScan->setIconSet( SmallIconSet("reload") ); + buttonConnect->setIconSet( SmallIconSet("connect_creating") ); + buttonOptions->setIconSet( SmallIconSet("configure") ); + buttonClose->setIconSet( SmallIconSet("fileclose") ); + + netList->setAllColumnsShowFocus(1); + netList->setItemMargin(8); + frameDevice->hide(); + + /// Network List Widget + connect( buttonScan, SIGNAL(clicked()), + this, SLOT(netScan()) ); + + connect( buttonConnect, SIGNAL(clicked()), + this, SLOT(itemAction()) ); + + connect( buttonClose, SIGNAL(clicked()), + this, SLOT(close()) ); + + connect( devCombo, SIGNAL(activated( const QString & )), + this, SLOT(setDev( const QString & )) ); + + connect( netList, SIGNAL(rightButtonPressed( QListViewItem*, const QPoint&, int )), + SLOT(showItemContextMenu( QListViewItem*, const QPoint&, int )) ); + + /// Settings Widget + connect( buttonOptions, SIGNAL(toggled(bool)), + this, SLOT(togglePage(bool)) ); + + connect( buttonEnableAllMessages, SIGNAL(clicked()), + this, SLOT(enableAllMessages()) ); + + /// Global KDE Options + connect( KApplication::kApplication(), SIGNAL(settingsChanged(int)), + this, SLOT(updateConfiguration(int)) ); + + setMouseBehaviour(); + + QTimer::singleShot(10, this, SLOT(init()) ); //WAIT FOR THE UI TO BE READY BEFORE FURTHER SETUP (msec) +} + +WirelessAssistant::~WirelessAssistant() +{} + +/*$SPECIALIZATION$*/ + + +void WirelessAssistant::init() +{ + statusLabel->setText(i18n("Initializing...")); + statusLabel->repaint(); + + //////////////////////////////////////// + ///// CHECK FOR SYSFS (KERNEL 2.6) ///// + if ( !QFile::exists("/sys") ) { + std::cout << "Sysfs not present. Exiting." << std::endl; + KMessageBox::error( 0, i18n("Kernel 2.6 or later not present.\nWireless Assistant will now quit.") ); + close(); + return; + } + + ///////////////////////////////////////////////////// + ///// LOAD CONFIG FILE INCL. ALL NET PARAMETERS ///// + WAConfig::self()->setCurrentGroup("Global Options"); + WAConfig::self()->addItemBool("Auto Quit", autoQuit); + WAConfig::self()->addItemBool("Auto Reconnect", autoReconnect); + WAConfig::self()->addItemBool("Auto Connect", autoConnect); + WAConfig::self()->addItemInt("Delay Before Scanning", DelayBeforeScanning); + WAConfig::self()->addItemBool("Group APs", groupAPs); + WAConfig::self()->addItemInt("DHCP Client Timeout", DhcpTimeout); + WAConfig::self()->addItemString("Interface", NetParams.iface); + + WAConfig::self()->setCurrentGroup("Paths"); + // Commented out cos no longer needed. Paths are detected when necessary. + /*WAConfig::self()->addItemString("DHCP Info (dhcpcd)", dhcpcdInfoPath); + WAConfig::self()->addItemString("DHCP PID File (dhcpcd)", dhcpcdPidPath); + WAConfig::self()->addItemString("DHCP Info (dhclient)", dhclientInfoPath); + WAConfig::self()->addItemString("DHCP PID File (dhclient)", dhclientPidPath);*/ + + WAConfig::self()->setCurrentGroup("Network Parameters"); + WAConfig::self()->addItemStringList("NetParamsList", NetParamsList ); + WAConfig::self()->readConfig(); + checkAutoQuit->setChecked(autoQuit); + checkAutoReconnect->setChecked(autoReconnect); + checkAutoConnect->setChecked(autoConnect); + checkGroupAPs->setChecked(groupAPs); + if (!DelayBeforeScanning) + DelayBeforeScanning = spinDelayBeforeScanning->value(); + else + spinDelayBeforeScanning->setValue(DelayBeforeScanning); + if (!DhcpTimeout) + DhcpTimeout = spinDhcpTimeout->value(); + else + spinDhcpTimeout->setValue(DhcpTimeout); + + std::cout << "Loaded application options." << std::endl; + + /////////////////////////////////// + ///// DETECT WIRELESS DEVICES ///// + QStringList devList = interfaceList(); + if ( devList.count()==0 ) { + std::cout << "No wireless interfaces found. Exiting." << std::endl; + KMessageBox::error(0, i18n("No usable wireless devices found.\nWireless Assistant will now quit.")); + close(); + return; + } + std::cout << "Wireless interface(s): " << devList.join(", ") << std::endl; + devCombo->insertStringList(devList); + + if (devCombo->count() > 1) { //check if last used (saved) interface is available (only if more that 1 interface present). + for (int i=0; i<devCombo->count(); i++) { + if ( devCombo->text(i)==NetParams.iface ) { //select matching interface. + devCombo->setCurrentItem( i ); + break; + } + } + frameDevice->show(); //only if more than 1 wireless device. + } + NetParams.iface = devCombo->currentText(); // set interface name + WATools::setInterface( NetParams.iface ); // set fallback interface for WATools + + ////////////////////////////////// + ///// CHECK FILE PERMISSIONS ///// + if (!QFileInfo("/etc/resolv.conf").isWritable()) { + std::cout << "warning: /etc/resolv.conf not writable" << std::endl; + KMessageBox::information(0, i18n("<qt><p>You might have insufficient permissions for Wireless Assistant to function properly.</p><p>Did you run it using '<tt>sudo</tt>'?</p></qt>") ); + } + std::cout << "Permissions checked." << std::endl; + + ////////////////////////////////// + ///// INITIALIZE COMMANDS + Commands.init(); + + /////////////////////////////////////// + ///// INITIALIZE GLOBAL VARIABLES ///// + wpaAvailable = ( !( Commands.wpa_supplicant.isEmpty() || Commands.wpa_cli.isEmpty() ) ); + connectedItem = 0; + timerGui = new QTimer(); + timerConnectionCheck = new QTimer(); + connect( timerGui, SIGNAL(timeout()), SLOT(updateConnectedItem()) ); + connect( timerConnectionCheck, SIGNAL(timeout()), SLOT(checkConnectionStatus()) ); + + //////////////////////// + ///// DETECT & SET PATHS ///// + if (!Commands.allFound) { //all ok or ONLY dhcpcd not found (i.e. dhclient present). + std::cout << "Missing executables (" << Commands.notFound.join("', '") << "). Exiting." << std::endl; + KMessageBox::error(0, i18n("Executable(s) '%1' could not be found.\nWireless Assistant will now quit.").arg(Commands.notFound.join("', '")) ); + close(); + return; + } + + KStandardDirs standardDirs; + wpaConfigFile = standardDirs.saveLocation("config").append("wlassistantwpa"); + + /////////////////////////////////////// + ///// SCAN FOR AVAILABLE NETWORKS ///// + if ( autoConnect ) + QTimer::singleShot( 0, this, SLOT(netAutoConnect()) ); + else + QTimer::singleShot( 0, this, SLOT(netScan()) ); +} + +void WirelessAssistant::checkConnectionStatus() +{ + QListViewItem* lvi; + if (groupAPs) lvi = getItemByEssid( WATools::essid(NetParams.iface)); + else lvi = getItemByAp( WATools::ap(NetParams.iface )); + bool needsKey; + lvi ? needsKey = static_cast<NetListViewItem*>(lvi)->enc() : needsKey = 0; + if ( WATools::isConnected(NetParams.iface) && WATools::hasKey(NetParams.iface)==needsKey ) { //connection OK + if (!connectedItem) { + std::cout << "Now connected to '" << WATools::essid(NetParams.iface) << "'" << std::endl; + if (groupAPs && NetParams.ap=="any") { + setConnectedItem( WATools::essid( NetParams.iface ) ); + setNetParamsFromConfig( WATools::essid( NetParams.iface ) ); + } else { + setConnectedItem( WATools::ap( NetParams.iface ) ); + setNetParamsFromConfig( WATools::ap( NetParams.iface ) ); + } + setNetParamsFromList( connectedItem ); + } + } else if (connectedItem) { //connection LOST + setConnectedItem(0); + timerConnectionCheck->stop(); + if ( autoReconnect || KMessageBox::questionYesNo(0, i18n("Connection to '%1' has been lost!\nWould you like to reconnect?").arg(NetParams.essid), i18n("Connection Lost") , KStdGuiItem::yes(), KStdGuiItem::no() ) == KMessageBox::Yes ) { + netDisconnect( true ); + netConnect(); + } + timerConnectionCheck->start( WA_CONNECTION_CHECK_INTERVAL ); + } +} + +void WirelessAssistant::removeNetParams() +{ + NetListViewItem *nvi = static_cast<NetListViewItem*>(netList->selectedItem()); + QString ap = nvi->ap(); QString essid = nvi->essid(); + for (QStringList::Iterator nps = NetParamsList.begin(); nps != NetParamsList.end(); nps++) { + if ( (*nps).section(",",2,2)==ap && (*nps).section(",",1,1)==essid) { + if ( KMessageBox::warningContinueCancel(0, i18n("<qt><p>Settings for network '<b>%1</b>' are about to be deleted.</p><p>Would you like to continue?</p></qt>").arg(essid)) == KMessageBox::Continue ) { + if (nvi->hidden()) // hiddenEssid = 1 + nvi->setEssid("<hidden>"); + NetParamsList.remove(nps); + WAConfig::self()->writeConfig(); + statusLabel->setText( i18n("Settings deleted.") ); + } + break; + } + } +} + + +void WirelessAssistant::setDNS( const WANetParams & np ) +{ + QFile f("/etc/resolv.conf"); + if (f.open( IO_WriteOnly | IO_Truncate )) { + QTextStream s( &f ); + if (!np.domain.isEmpty()) { + s << QString("domain " + np.domain + "\n"); + std::cout << "resolv.conf: domain " << np.domain << std::endl; + } + if (!np.dns1.isEmpty()) { + s << QString("nameserver " + np.dns1 + "\n"); + std::cout << "resolv.conf: nameserver " << np.dns1 << std::endl; + } + if (!np.dns2.isEmpty()) { + s << QString("nameserver " + np.dns2 + "\n"); + std::cout << "resolv.conf: nameserver " << np.dns2 << std::endl; + } + f.close(); + } else { + std::cout << "dns setup error: " << f.name() << " is not writeable." << std::endl; + KMessageBox::error(0, i18n("<qt><p>File '<i>%1</i>' could not be opened for writing.</p><p>Nameserver(s) and/or domain are not set.</p></qt>").arg(f.name()) ); + } +} + +void WirelessAssistant::netScan() +{ + timerConnectionCheck->stop(); //stop while scanning. + netScan( NetParams ); + if (netList->childCount() > 0) { + QTimer::singleShot( 0, this, SLOT(checkConnectionStatus()) ); + timerConnectionCheck->start(WA_CONNECTION_CHECK_INTERVAL); + } +} + +void WirelessAssistant::netScan( const WANetParams & np ) +{ + if (!radioEnabled()) { + statusLabel->setText("Radio off. Scanning aborted."); + std::cout << "Radio is off!" << std::endl; + setUi(1); + return; + } + + setUi(0); + + bool wasConnected = false; + if (connectedItem) { + wasConnected = true; + setConnectedItem( 0 ); + } + + if ( !WATools::isUp(np.iface) ) { + statusLabel->setText(i18n("Bringing interface %1 up...").arg(np.iface)); + //runCommand( Commands.cmd("ifup",np) ); + WATools::setUp(true, np.iface); + if (DelayBeforeScanning>0) { + statusLabel->setText(i18n("Waiting before scanning...")); + statusLabel->repaint(); + KApplication::eventLoop()->processEvents( QEventLoop::ExcludeUserInput ); + usleep(DelayBeforeScanning * 1000000); // delay * 1000ms + } + } + + statusLabel->setText(i18n("Scanning...")); + statusLabel->repaint(); + + netList->clear(); + + QString result; + statusLabel->setText(i18n("Scanning...")); + result = runCommand( Commands.cmd("scan",np) ); + + parseScan( result ); + + if (netList->childCount() > 0) { + std::cout << "Networks found: " << QString::number( netList->childCount() ) << std::endl; + if (wasConnected) + groupAPs ? setConnectedItem( WATools::essid() ) : setConnectedItem( WATools::ap() ); //mark item as connected. + statusLabel->setText( i18n("Done.") ); + } else { + //Workaround for cards overusing cache - bringing if down seems to solve it. + //runCommand( Commands.cmd("ifdown", NetParams) ); //Commented out b/c it seems to cause more problems than it solves. (like no scan results) + std::cout << "No networks found!" << std::endl; + statusLabel->setText( i18n("No networks found.") ); + if ( result.find("Resource temporarily unavailable")>-1 ) { + std::cout << "Radio switch seems to be off." << std::endl; + KMessageBox::information(0, i18n("Radio of your wireless card seems to be turned off using an external switch on your computer.\nYou need turn it on to be able to use wireless networks.") ); + } + } + setNetListColumns(); +} + +void WirelessAssistant::parseScan( const QString & output ) +{ + QString essid; + QStringList essidList; + QString channel; + QString mode; + int qualInt; + bool enc; //default to false + bool hidden; //default to false + QString ap; + + // security parameters + bool wpa; + QStringList wpaSettings; + //QString wpaVersion, wpaGroupCipher, wpaPairwiseCipher, wpaAuthenticationSuite; + + + bool ok_channel = true; //does iwlist return channel? + QString section; + + netList->setUpdatesEnabled( false ); //do not redraw while adding items to avoid flicker. + + for (int i=1; (!output.section("Cell ",i,i).isEmpty()); i++ ) { + section = output.section("Cell ",i,i); + + // GET ESSID VALUE + essid = getVal(section, "ESSID\\W+\"(.+)\""); + + // GET CHANNEL NUMBER + channel = getVal(section, "Channel\\D+(\\d+)" ); + if (channel.isEmpty()) { + channel = getVal(section, "Frequency\\D+(\\d.+)Hz"); + ok_channel = false; + } + + // GET MODE VALUE + mode = getVal(section, "Mode:(\\w)"); //get 1st letter of mode. + if (mode.upper()!="M") //this covers both Managed and Master. Other are unsupported. + continue; + + // GET AP + ap = getVal(section, "Address\\W+(\\S+)"); + + if (essid.isEmpty()) { + if (!ap.isEmpty()) //older wireless-tools report "", not "<hidden>" + essid = "<hidden>"; + else + continue; //some cards report one '' essid even when no network's present. Workaround. + } + + if (essid=="<hidden>") { + hidden = true; + essid = matchEssidForAp( ap ); + } else + hidden=false; + + // GET QUALITY + int wsignal; + //check if driver reports quality directly + qualInt = getVal(section, "Quality\\D+(\\d+)").toInt(); + + if (qualInt == 0) { //noise not reported? estimate. + wsignal = getVal(section, "Signal level\\D+(\\d+)" ).toInt(); + qualInt = 100-wsignal; + } + qualInt = (100*qualInt)/50; //adjust and normalize quality (0-100). 50 is the best (6 stars) noise/signal difference + if (qualInt > 100) qualInt = 100; + + // GET ENCRYPTION + if (getVal(section, "Encryption key\\W+(\\w+)" ).upper()=="OFF") + enc = false; + else { + enc = true; + + wpaSettings.clear(); + if ( section.contains("WPA2 Version") ) wpaSettings << "WPA2"; //prefer WPA2 over WPA + else if ( section.contains("WPA Version") ) wpaSettings << "WPA"; + + wpa = ( !wpaSettings.isEmpty() ); + if (wpa) { + wpaSettings << getVal(section, "Group Cipher : (\\w+)") \ + << getVal(section, "Pairwise Ciphers \\(\\d+\\) : ([\\w ]+)[\n\r]") \ + << getVal(section, "Authentication Suites \\(\\d+\\) : ([\\w ]+)[\n\r]"); + } + } + + // CHECK IF SAME ESSID ALREADY FOUND, if necessary + if (groupAPs) { + if ( !hidden && essidList.contains(essid) ) { + NetListViewItem* sameEssid = static_cast<NetListViewItem*>(getItemByEssid(essid)); + sameEssid->setAp("any"); + if (sameEssid->quality() < qualInt) { + sameEssid->setQuality(qualInt); + sameEssid->setChannel(channel); + } + continue; + } + essidList << essid; + } + + NetListViewItem* nvi = new NetListViewItem( netList, essid, channel, qualInt, enc, ap, hidden ); + if (wpa) nvi->setWpaSettings( wpaSettings ); + } + + if (!ok_channel) + netList->setColumnText( 1, i18n("Freq (Hz)") ); + + /// @fixme HACK: Test item for the network list. + /// new NetListViewItem( netList, "Test Net", "9", 76, 1, "00:00:00:00:00:11", 0 ); + + + netList->setUpdatesEnabled( true ); + setUi(1); +} + +bool WirelessAssistant::radioEnabled() +{ + bool r; + if ( WATools::txpower()==-1 ) { + if (KMessageBox::questionYesNo(0, i18n("Radio of your wireless card is off.\nWould you like to turn it on?") )== KMessageBox::Yes) { + runCommand( Commands.cmd("radio_on", NetParams) ); + r = true; + } else { + r = false; + } + } else + r = true; + + return r; +} + +void WirelessAssistant::setNetParamsFromList( QListViewItem* lvi ) +{ + NetListViewItem *nvi = static_cast<NetListViewItem*>(lvi); + NetParams.essid = nvi->essid(); + NetParams.hiddenEssid = nvi->hidden(); + //NetParams.mode = nvi->mode(); + NetParams.channel = nvi->channel(); + NetParams.ap = nvi->ap(); + NetParams.wpaSettings = nvi->wpaSettings(); + NetParams.wep = ( nvi->enc() && NetParams.wpaSettings.isEmpty() ); + NetParams.wpa = ( nvi->enc() && !NetParams.wpaSettings.isEmpty() ); +} + +bool WirelessAssistant::setNetParamsFromConfig( const QString & s ) +{ + for (QStringList::Iterator nps = NetParamsList.begin(); nps != NetParamsList.end(); nps++) { + if ( (*nps).section(",",2,2)==s || ( (*nps).section(",",1,1)==s && (*nps).section(",",2,2)=="any") ) { + NetParams.loadNetParamsString( *nps ); + if (!s.contains(":")) NetParams.ap = "any"; //if searched by essid + return 1; + } + } + return 0; +} + +void WirelessAssistant::itemAction() +{ + QListViewItem* lvi = netList->selectedItem(); + if (!lvi) + return; + + NetListViewItem* nvi = static_cast<NetListViewItem*>(lvi); + /////////////////// + ///// ACTIONS ///// + if (nvi->isConnected()) { + std::cout << "ACTION: DISCONNECT." << std::endl; + netDisconnect(); + return; + } else { + std::cout << "ACTION: CONNECT." << std::endl; + netConnect(); + return; + } +} + +void WirelessAssistant::netAutoConnect() +{ + netScan(); + if ( WATools::isConnected(NetParams.iface) ) return; + + int bestItem = -1; + int bestQuality = 0; + for ( int i = 0; i < netList->childCount(); i++ ) { + NetListViewItem* nvi = static_cast<NetListViewItem*>( netList->itemAtIndex(i) ); + QString search = nvi->ap(); + if (search == "any") search = nvi->essid(); + if ( setNetParamsFromConfig(search) ) { + if ( nvi->quality() > bestQuality ) { + bestQuality = nvi->quality(); + bestItem = i; + } + } + } + + if ( bestItem != -1 ) { + NetListViewItem* nvi = static_cast<NetListViewItem*>( netList->itemAtIndex( bestItem ) ); + setNetParamsFromList( nvi ); + QString search = nvi->ap(); + if (search == "any") search = nvi->essid(); + setNetParamsFromConfig( search ); + timerConnectionCheck->stop(); + netConnect( NetParams ); + timerConnectionCheck->start(WA_CONNECTION_CHECK_INTERVAL); + } else { + statusLabel->setText( i18n("Auto connection failed.") ); + std::cout << "Auto connection failed: no available configured networks found." << std::endl; + } +} + +void WirelessAssistant::netConnect() +{ + timerConnectionCheck->stop(); + setNetParamsFromList( netList->selectedItem() ); + //can't connect if WPA needed, and wpa_supplicant and wpa_cli not available + if ( NetParams.wpa && !wpaAvailable ) { + KMessageBox::error(0, i18n("<qt><p><b>Can not connect to network '%1'.<b></p><p>The network you are trying to connect to requires WPA authentication. The necessary executables <i>wpa_supplicant</i> and <i>wpa_cli</i> could not be found. Install <i>wpa_supplicant</i> and restart Wireless Assistant to connect.</p></qt>").arg(NetParams.essid) ); + timerConnectionCheck->start(WA_CONNECTION_CHECK_INTERVAL); //resume connection checking + return; + } + QString search = NetParams.ap; + if (search == "any") search = NetParams.essid; + if ( (NetParams.essid=="<hidden>") || (!setNetParamsFromConfig( search )) ) { + ui_NetParamsWizard *netwiz = new ui_NetParamsWizard; + if (!NetParams.hiddenEssid) + netwiz->setCaption( i18n("%1 - First Connection Wizard").arg(NetParams.essid) ); + netwiz->setEssidEnabled( NetParams.hiddenEssid ); + netwiz->setWepEnabled( NetParams.wep ); + netwiz->setWpaEnabled( NetParams.wpa, NetParams.wpaSettings ); + netwiz->exec(); + if (netwiz->result()==QDialog::Rejected) { + delete netwiz; + timerConnectionCheck->start(WA_CONNECTION_CHECK_INTERVAL); //resume connection checking + return; + } else { + NetParams = netwiz->readNetParams( NetParams ); + NetParams.wasHiddenEssid = NetParams.hiddenEssid; //first time values. + NetParams.wasWep = NetParams.wep; + NetParamsList << NetParams.netParamsString(); + if (NetParams.hiddenEssid) + static_cast<NetListViewItem*>(netList->selectedItem())->setEssid( NetParams.essid ); + WAConfig::self()->writeConfig(); + delete netwiz; + } + } + + if (NetParams.review()) + editNetParams(); + updateNetParams(); + netConnect( NetParams ); + timerConnectionCheck->start(WA_CONNECTION_CHECK_INTERVAL); +} + +void WirelessAssistant::updateNetParams() +{ + for (QStringList::Iterator nps = NetParamsList.begin(); nps != NetParamsList.end(); nps++) { + if ( (*nps).section(",",2,2)==NetParams.ap ) { + QString newNps = NetParams.netParamsString(); + if ( newNps!=(*nps) ) { + (*nps) = newNps; + WAConfig::self()->writeConfig(); + std::cout << "Network settings updated." << std::endl; + statusLabel->setText( i18n("Network settings updated.") ); + break; + } + } + } +} + +QString WirelessAssistant::matchEssidForAp( const QString & ap ) +{ + for (QStringList::Iterator nps = NetParamsList.begin(); nps != NetParamsList.end(); nps++) { + if ( (*nps).section(",",2,2)==ap ) { + return (*nps).section(",",1,1); //essid + } + } + return "<hidden>"; +} + +void WirelessAssistant::netConnect( const WANetParams & np ) +{ + setUi(0); + + if (connectedItem) + netDisconnect( true ); + else if ( dhcpClientRunning() ) + runCommand( Commands.cmd("kill_dhcp", np) ); //kill any stale DHCP client running + + if ( !np.preConnectionCommand.isEmpty() ) { + std::cout << "Running pre-connection command: " << np.preConnectionCommand << std::endl; + statusLabel->setText( i18n("Running pre-connection command...") ); + runCommand( QStringList::split( " ", np.preConnectionCommand ), np.preConnectionTimeout, np.preConnectionDetached ); + } else + std::cout << "No pre-connection command specified." << std::endl; + + + statusLabel->setText( i18n("Connecting to '%1'...").arg(np.essid) ); + statusLabel->repaint(); + if (!WATools::isUp(np.iface) ) WATools::setUp( true, np.iface ); + //runCommand( Commands.cmd("ifup", np) ); + if ( runCommand( Commands.cmd("iwconfig_set", np) ).find("8B04") > -1 ) { // error 8B04 - Request 'Set Frequency' not supported. + WANetParams np2 = np; + np2.channel = "0"; + runCommand( Commands.cmd("iwconfig_set", np2) ); + } + + runCommand( Commands.cmd("iwconfig_ap", np) ); + + /////////////////////// + ///// RUN WPA CLIENT IF NEEDED + if (np.wpa) { + if ( generateWpaConfigFile( np.essid, np.wpaSettings, np.wpaKey ) ) { + if ( !setWpaClientEnabled( true, np.iface ) ) { + setUi(1); + std::cout << "CONNECTION FAILED." << std::endl; + statusLabel->setText( i18n("Connection failed.") ); + runCommand( Commands.cmd("disconnect", np ) ); + return; + } + } + } + + //////////////////////// + ///// CONFIGURE IP ADDRESS etc. + if (np.dhcp) { //DHCP config + QString dhcp_out = runCommand( Commands.cmd("ifconfig_dhcp", np), DhcpTimeout ); + if ( dhcp_out.contains("::ERR::") && !dhcp_out.contains("bound to ") ) { // 'bound to' is a check for dhclient, which gives some output to stderr even when succeeded + if ( dhcpClientRunning() ) + runCommand( Commands.cmd("kill_dhcp", np) ); //kill any stale DHCP client running (seems it's dhclient only) + setUi(1); + std::cout << "CONNECTION FAILED." << std::endl; + statusLabel->setText( i18n("Connection failed.") ); + runCommand( Commands.cmd("disconnect", np ) ); + return; + } + } else { //manual config + runCommand( Commands.cmd("ifconfig_manual", np) ); + setDNS( np ); + runCommand( Commands.cmd("route_add", np) ); + } + + if ( !np.postConnectionCommand.isEmpty() ) { + std::cout << "Running post-connection command: " << np.postConnectionCommand << std::endl; + statusLabel->setText( i18n("Running post-connection command...") ); + runCommand( QStringList::split( " ", np.postConnectionCommand ), np.postConnectionTimeout, np.postConnectionDetached ); + } else + std::cout << "No post-connection command specified." << std::endl; + + ////////////////////// + ///// CHECK CONNECTION + statusLabel->setText(i18n("Testing connection...")); + usleep(200*1000); //sleep 200ms to make sure all parameters are set. + if ( WATools::isConnected(np.iface)) { + if (autoQuit) + this->close(); + groupAPs ? setConnectedItem( np.essid ) : setConnectedItem( np.ap ); + statusLabel->setText( i18n("Successfully connected to '%1'.").arg(np.essid) ); + setUi(1); + } else { + std::cout << "CONNECTION FAILED." << std::endl; + statusLabel->setText(i18n("Connection failed.")); + runCommand( Commands.cmd("disconnect", np ) ); + setConnectedItem( 0 ); + setUi(1); + if (KMessageBox::questionYesNo(0, i18n("Connection failed.\nWould you like to review settings for this network?"), i18n("Review Settings?") , KStdGuiItem::yes(), KStdGuiItem::no(), "ReviewSettings" ) == KMessageBox::Yes) + editNetParams(); + } +} + +void WirelessAssistant::updateConnectedItem() +{ + connectedItem->setQuality( WATools::quality() ); +} + +void WirelessAssistant::setConnectedItem( const QString & netid ) +{ + timerConnectionCheck->stop(); //stop timer while changing currentItem + if (connectedItem) { + timerGui->stop(); + connectedItem->setConnected( false ); + connectedItem = 0; + } + if (!netid.isEmpty()) { + QListViewItem* lvi; + if (netid.contains(":")) lvi = getItemByAp( netid ); //netid is an AP address + else lvi = getItemByEssid( netid ); + if (lvi) { + NetListViewItem* nvi = static_cast<NetListViewItem*>(lvi); + nvi->setConnected( true ); + connectedItem = nvi; + netList->sort(); // sort to make sure new connectedItem is 1st. + } + } + + if (connectedItem) { + timerGui->start(2500); //update quality indicator every 2.5seconds + } + updateConnectButton( netList->selectedItem() ); + timerConnectionCheck->start(WA_CONNECTION_CHECK_INTERVAL); +} + +void WirelessAssistant::netDisconnect( const bool & quiet ) +{ + if ( (quiet) || (KMessageBox::warningContinueCancel(0, i18n("<qt><p>You are about to disconnect from '<b>%1</b>'.</p><p>Would you like to continue?<p></qt>").arg(connectedItem->essid()) )== KMessageBox::Continue ) ) { + timerConnectionCheck->stop(); //stop while disconnecting. + + if ( !NetParams.preDisconnectionCommand.isEmpty() ) { + std::cout << "Running pre-disconnection command: " << NetParams.preDisconnectionCommand << std::endl; + statusLabel->setText( i18n("Running pre-disconnection command...") ); + runCommand( QStringList::split( " ", NetParams.preDisconnectionCommand ), NetParams.preDisconnectionTimeout, NetParams.preDisconnectionDetached ); + } else + std::cout << "No pre-disconnection command specified." << std::endl; + + + statusLabel->setText( i18n("Disconnecting...") ); + statusLabel->repaint(); + setConnectedItem( 0 ); + if ( NetParams.dhcp ) { + if ( dhcpClientRunning() ) { + runCommand( Commands.cmd( "kill_dhcp", NetParams ) ); + statusLabel->setText( i18n("Waiting for DHCP client to shut down...") ); + statusLabel->repaint(); + QTimer* tmr = new QTimer(); + tmr->start(1500, true); //wait 1.5sec for dhcp client to really shutdown, single shot. + while ( tmr->isActive() ) { + KApplication::eventLoop()->processEvents( QEventLoop::AllEvents ); + usleep(75*1000); //75msec on Linux + } + delete tmr; + } + } else { + runCommand( Commands.cmd( "route_del", NetParams ) ); + } + runCommand( Commands.cmd( "disconnect", NetParams ) ); + WATools::setUp( false, NetParams.iface ); + + if ( NetParams.wpa ) + setWpaClientEnabled( false ); + + std::cout << "DISCONNECTED." << std::endl; + + if ( !NetParams.postDisconnectionCommand.isEmpty() ) { + std::cout << "Running post-disconnection command: " << NetParams.postDisconnectionCommand << std::endl; + statusLabel->setText( i18n("Running post-disconnection command...") ); + runCommand( QStringList::split( " ", NetParams.postDisconnectionCommand ), NetParams.postDisconnectionTimeout, NetParams.postDisconnectionDetached ); + } else + std::cout << "No post-disconnection command specified." << std::endl; + + + statusLabel->setText( i18n("Done.") ); + timerConnectionCheck->start(WA_CONNECTION_CHECK_INTERVAL); + } else { + statusLabel->setText( i18n("Cancelled.") ); + } +} + +QListViewItem* WirelessAssistant::getItemByAp( const QString & ap ) +{ + QListViewItem* lvi = netList->firstChild(); + while (lvi) { + if ( static_cast<NetListViewItem*>(lvi)-> + ap() == ap ) { + break; + } + lvi = lvi->nextSibling(); + } + return lvi; +} + +QListViewItem* WirelessAssistant::getItemByEssid( const QString & essid ) +{ + QListViewItem* lvi = netList->firstChild(); + while (lvi) { + if ( static_cast<NetListViewItem*>(lvi)-> + essid() == essid ) { + break; + } + lvi = lvi->nextSibling(); + } + return lvi; +} + + +void WirelessAssistant::updateConfiguration(int category) +{ + if (category == KApplication::SETTINGS_MOUSE) { + setMouseBehaviour(); + return; + } + if (category == -1) { + autoQuit = checkAutoQuit->isChecked(); + autoReconnect = checkAutoReconnect->isChecked(); + autoConnect = checkAutoConnect->isChecked(); + groupAPs = checkGroupAPs->isChecked(); + DelayBeforeScanning = spinDelayBeforeScanning->value(); + DhcpTimeout = spinDhcpTimeout->value(); + } +} + +void WirelessAssistant::togglePage(bool options) +{ + buttonScan->setDisabled(options); + buttonConnect->setDisabled(options); + if (options) { + if (WAConfig::self()->config()->groupList().contains("Notification Messages")>0) + buttonEnableAllMessages->setEnabled(true); + else + buttonEnableAllMessages->setEnabled(false); + widgetStack->raiseWidget(optionsPage); + } else { + widgetStack->raiseWidget(netPage); + updateConfiguration(-1); + } +} + +void WirelessAssistant::enableAllMessages() +{ + KMessageBox::enableAllMessages(); + buttonEnableAllMessages->setEnabled( false ); +} + +void WirelessAssistant::setMouseBehaviour() +{ + if ( KGlobalSettings::singleClick() ) { + disconnect( netList, SIGNAL(selectionChanged(QListViewItem*)), + this, SLOT(updateConnectButton(QListViewItem*)) ); + disconnect( netList, SIGNAL(doubleClicked(QListViewItem*, const QPoint &, int)), + this, SLOT(itemAction()) ); + connect( netList, SIGNAL(clicked(QListViewItem*, const QPoint &, int)), + this, SLOT(itemAction()) ); + buttonConnect->hide(); + } else { + disconnect( netList, SIGNAL(clicked(QListViewItem*, const QPoint &, int)), + this, SLOT(itemAction()) ); + + connect( netList, SIGNAL(selectionChanged(QListViewItem*)), + this, SLOT(updateConnectButton(QListViewItem*)) ); + connect( netList, SIGNAL(doubleClicked(QListViewItem*, const QPoint &, int)), + this, SLOT(itemAction()) ); + buttonConnect->show(); + } +} + +void WirelessAssistant::updateConnectButton(QListViewItem* lvi) +{ + QToolTip::remove + (buttonConnect); + if ( lvi == connectedItem ) { + buttonConnect->setText( i18n("&Disconnect") ); + QToolTip::add + ( buttonConnect, i18n("Disconnect from the selected network") ); + + } else { + buttonConnect->setText( i18n("&Connect") ); + QToolTip::add + ( buttonConnect, i18n("Connect to the selected network") ); + + } +} + +void WirelessAssistant::setDev( const QString & ifname) +{ + NetParams.iface = ifname; + WATools::setInterface( ifname ); + std::cout << "Selected interface: " << ifname << std::endl; + netScan(); +} + +QString WirelessAssistant::runCommand( const QStringList & cmd, int timeout, bool detached ) +{ + if (cmd.isEmpty()) + return QString::null; + + // a very basic and easy-to-workaround attepmt to restrict using dangerous commands via custom commands setting. This *REALLY* needs a working solution. + if ( cmd[0] == "rm" || cmd[0] == "mv" || cmd[0] == "cp" || cmd[0] == "ln" ) return QString::null; + + QProcess* p = new QProcess( this ); + p->setArguments( cmd ); + + p->start(); + if (detached) { + p = 0; + return QString::null; + } + + QTimer* timerProc = new QTimer(); //timeout timer + if ( timeout>0 && !detached ) { + connect( timerProc, SIGNAL(timeout()), p, SLOT(kill()) ); + timerProc->start(timeout*1000); //convert sec to msec + } + + connect(buttonClose, SIGNAL(clicked()), + p, SLOT(kill()) ); + int i = 0; + + while ( p->isRunning() ) { // PROCESS USER EVENTS + KApplication::eventLoop()->processEvents( QEventLoop::AllEvents ); + usleep(75*1000); //75msec on Linux (75000msec on Windows...) + if (i==27) { // ca 2sec have passed and the process is still running. Replace the 'Close' button with 'Stop'. + disconnect(buttonClose, SIGNAL(clicked()), + this, SLOT(close()) ); + buttonClose->setIconSet( SmallIconSet("stop") ); + buttonClose->setText( i18n("&Stop") ); + QToolTip::remove + (buttonClose); + QToolTip::add + ( buttonClose, i18n("Terminate current process\n(%1)").arg( p->arguments().join(" ") ) ); + } + i++; + } + + disconnect(buttonClose, SIGNAL(clicked()), + p, SLOT(kill()) ); + if (i>27) {//set 'stop' back to 'close' if needed + connect(buttonClose, SIGNAL(clicked()), + this, SLOT(close()) ); + buttonClose->setIconSet( SmallIconSet("fileclose") ); + buttonClose->setText( i18n("&Quit") ); + QToolTip::remove + (buttonClose); + QToolTip::add + ( buttonClose, i18n("Quit the application") ); + } + + if (timerProc->isActive()) + timerProc->stop(); + delete timerProc; + QString e = QString( p->readStderr() ); + QString o = QString( p->readStdout() ); + if (!p->normalExit()) { + o.append("::ERR::killed"); + //std::cout << "Process terminated (timed out)." << std::endl; //too much output when checking for internet when it's not available. + } + delete p; + + if (!e.isEmpty()) { + std::cout << "==>stderr: " << e;// << std::endl; + o.append("::ERR::"); + o.append(e); + } + + return o; +} + +void WirelessAssistant::setUi(int uiState) +{ + + if (uiState==0) { + devCombo->setEnabled( false ); + buttonScan->setEnabled( false ); + buttonConnect->setEnabled( false ); + buttonOptions->setEnabled( false ); + KApplication::setOverrideCursor( QCursor(Qt::BusyCursor) ); + } else { + if (devCombo->count() > 0) { + devCombo->setEnabled( true ); + buttonScan->setEnabled( true ); + } + if (netList->childCount() > 0) + buttonConnect->setEnabled( true ); + buttonOptions->setEnabled( true ); + KApplication::restoreOverrideCursor(); + } +} + +void WirelessAssistant::showItemContextMenu( QListViewItem* i, const QPoint& p, int c ) +{ + if (!i) + return; + + NetListViewItem *nvi = static_cast<NetListViewItem*>(i); + + QString search = nvi->ap(); + if (search == "any") search = nvi->essid(); + bool isConfigured = setNetParamsFromConfig(search); + + KPopupMenu *icm = new KPopupMenu(); + icm->insertTitle(nvi->essid()); + if (isConfigured) { + if (nvi->isConnected()) { + icm->insertItem( SmallIcon("connect_no"), i18n("Disconnect..."), this, SLOT(netDisconnect()) ); + //icm->insertItem( SmallIcon("reload"), i18n("Reconnect"), this, SLOT(netConnect()) ); + } else + icm->insertItem( SmallIcon("connect_creating"), i18n("Connect"), this, SLOT(netConnect()) ); + icm->insertSeparator(); + icm->insertItem(i18n("Forget Settings..."), this, SLOT(removeNetParams()) ); + icm->insertItem(i18n("Edit Settings..."), this, SLOT(editNetParams()) ); + } else { + if (nvi->isConnected()) { + icm->insertItem( SmallIcon("connect_no"), i18n("Disconnect..."), this, SLOT(netDisconnect()) ); + //icm->insertItem( SmallIcon("reload"), i18n("Configure and Reconnect..."), this, SLOT(netConnect()) ); + } else + icm->insertItem( SmallIcon("connect_creating"), i18n("Configure and Connect..."), this, SLOT(netConnect()) ); + } + icm->exec( QCursor::pos() ); +} + +void WirelessAssistant::editNetParams() +{ + setNetParamsFromList( netList->selectedItem() ); //prepare NetParams + if (NetParams.ap!="any") setNetParamsFromConfig( NetParams.ap ); //prepare NetParams + else setNetParamsFromConfig( NetParams.essid ); + + ui_NetParamsEdit *netedit = new ui_NetParamsEdit(); + netedit->setValues( NetParams ); + netedit->setCaption( i18n("%1 Settings").arg(NetParams.essid) ); + netedit->exec(); + if (netedit->result() == QDialog::Rejected) { + delete netedit; + return; + } else { //accepted + NetParams = netedit->readNetParams( NetParams ); + updateNetParams(); + } +} + +void WirelessAssistant::setNetListColumns() +{ + int realWidth = netList->viewportSize( netList->contentsWidth(), netList->contentsHeight() ).width(); //calculate actual width taking scrollbars into account + int essidWidth = realWidth - netList->columnWidth(1) - netList->columnWidth(2) - netList->columnWidth(3); + + netList->setColumnWidth(0, essidWidth); + netList->triggerUpdate(); +} + +bool WirelessAssistant::dhcpClientRunning() +{ + QStringList pidPaths; + QString pidFile; + pidPaths << "/etc/" << "/etc/dhcpc/" << "/var/run/"; + if ( Commands.dhcpClient=="dhcpcd" ) + pidFile = QString("dhcpcd-%1.pid").arg(NetParams.iface); + else + pidFile = QString("dhclient.pid"); + + for ( QStringList::Iterator it = pidPaths.begin(); it != pidPaths.end(); ++it ) { + if ( QFile( QString(*it).append(pidFile) ).exists() ) { + std::cout << "Running DHCP client found." << std::endl; + return true; + } + } + std::cout << "No DHCP client running." << std::endl; + return false; +} + +QStringList WirelessAssistant::interfaceList() +{ + QDir d("/sys/class/net"); + QStringList ifList = d.entryList( QDir::Dirs ); + ifList.remove("."); ifList.remove(".."); ifList.remove("lo"); + std::cout << "All interfaces: " << ifList.join(", ") << std::endl; + for (QStringList::Iterator nps = ifList.begin(); nps != ifList.end(); nps++) { + const char* i = *nps; + bool w = WATools::isWireless( i ); + if ( !WATools::isWireless( (const char*)*nps ) ) { + nps = ifList.remove( nps ); nps--; + } + } + return ifList; +} + +QString WirelessAssistant::getVal(const QString & str, const QString & rxs) +{ + QRegExp rx(rxs); + rx.search(str); + return rx.cap(1).stripWhiteSpace(); +} + +bool WirelessAssistant::generateWpaConfigFile( const QString& essid, const QStringList& wpaSettings, const QString& wpaKey ) +{ + // 0 WPA version (1 or 2), 1 group, 2 pairwise, 3 suite + if ( wpaSettings.isEmpty() ) return QString(); + QString c = "ctrl_interface=/var/run/wpa_supplicant\nnetwork={\nscan_ssid=0\nssid=\""; //fast_reauth=1\n + c.append(essid).append("\"\n"); + + // WPA version + c.append("proto=").append(wpaSettings[0]).append("\n"); + + //WPA authentication suite + c.append("key_mgmt="); + if ( wpaSettings[3].contains("PSK") ) c.append("WPA-PSK\n"); + else return QString(); // not supported + + //WPA pairwise cipher + c.append("pairwise="); + c.append( wpaSettings[2] ).append("\n"); + + //WPA group cipher + c.append("group="); + c.append( wpaSettings[1] ).append("\n"); + + //WPA key + QString k = QString(); + if (wpaKey.left(2)=="s:") { // PASSPHRASE + k.append("\""); + k.append( wpaKey.right( wpaKey.length() - 2 ) ); + k.append("\"\n"); + } else + k.append( wpaKey ).append("\n"); // HEX KEY + + c.append("psk=").append(k); + + c.append("}\n"); + //std::cout << "WPA Config:\n" << c << std::endl; + +// # WPA protected network, supply your own ESSID and WPAPSK here: +// network={ +// scan_ssid=0 +// ssid="your_essid_here" +// proto=WPA +// key_mgmt=WPA-PSK +// pairwise=CCMP TKIP +// group=CCMP TKIP WEP104 WEP40 +// psk=your_psk_here +// } + + QFile file( wpaConfigFile ); + if (file.exists()) file.remove(); + if ( file.open( IO_WriteOnly ) ) { + QTextStream stream( &file ); + stream << c; + file.close(); + //std::cout << "Wrote WPA config: " << wpaConfigFile << std::endl; + return 1; + } else + return 0; +} + +bool WirelessAssistant::setWpaClientEnabled( bool e, const QString& iface, QString driver ) +{ + if (!e) { + if ( runCommand( QStringList(Commands.wpa_cli) << QString("-i%1").arg(NetParams.iface) << "terminate" ).contains("OK") ) { + QFile( wpaConfigFile ).remove(); + return 1; + } else + return 0; // wpa client was not running. + } + + if ( !runCommand( QStringList(Commands.wpa_cli) << QString("-i%1").arg(NetParams.iface) << "status" ).contains("Failed to connect") ) { + std::cout << "WPA client already running. Reconfiguring..." << std::endl; + runCommand( QStringList(Commands.wpa_cli) << "reconfigure" ); + } else { + if ( driver.isEmpty() ) { //detect needed driver + QString k = WATools::kernelModule( iface ); + if ( k.contains("hermes") ) driver = "hermes"; + else if ( k.contains("atmel") ) driver = "atmel"; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 13) + else if ( k.contains("ipw") ) driver = "ipw"; //wext should be used for kernels newer than 2.6.12 +#endif + //Commented out, because ndiswrapper newer than 1.13 works with wext driver. + //else if ( k.contains("ndiswrapper") ) driver = "ndiswrapper"; + //Commented out, because madwifi-ng works with wext driver. + //else if ( k.contains("ath") ) driver = "madwifi"; + else driver = "wext"; + std::cout << "Using wpa_supplicant driver: " << driver << std::endl; + } + + QProcess* wp = new QProcess( this ); + wp->clearArguments(); + wp->addArgument( Commands.wpa_supplicant ); + wp->addArgument( "-W" ); //wait for control interface + wp->addArgument( QString("-D%1").arg(driver) ); + wp->addArgument( QString("-i%1").arg(iface) ); + wp->addArgument( QString("-c%1").arg(wpaConfigFile) ); + //std::cout << "Starting WPA client: " << wp->arguments().join(" ") << std::endl; + if ( !wp->start() ) { + std::cout << "Failed to start WPA client." << std::endl; + return 0; + } + wp = 0; + std::cout << "WPA client started. Waiting for status..." << std::endl; + } + + usleep(200*1000); //200msec for wpa_supplicant to initiate + + QString o; + int i = 0; + while ( !(o =runCommand( QStringList(Commands.wpa_cli) << QString("-i%1").arg(NetParams.iface) << "status" )).contains("Failed to connect") ) { + for (int c = 0; c < 15; c++) { + usleep(75*1000); //75msec + KApplication::eventLoop()->processEvents( QEventLoop::AllEvents ); + i++; + } + if (i>400) { //more than 30sec have passed + runCommand( QStringList(Commands.wpa_cli) << QString("-i%1").arg(NetParams.iface) << "terminate" ); + return 0; + } + if ( o.contains("wpa_state=COMPLETED") ) { + std::cout << "WPA Authorisation successful." << std::endl; + return 1; + } + } + return 0; +} + + +bool WirelessAssistant::close() +{ + updateConfiguration(-1); //read values from setingsPage; + WAConfig::self()->writeConfig(); + std::cout << "Application options saved." << std::endl; + WATools::cleanup(); + std::cout << "Kernel socket closed." << std::endl; + return QWidget::close(); +} + + +#include "wlassistant.moc" |