summaryrefslogblamecommitdiff
path: root/ui/qt5/netsimplewifipage.cc
blob: f595d116dacecc51696dbe42b4078add5074da6c (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12











                                                                           
                               
                         
 
                   
                  

                      






                                                                                              
                                                                     
                                                        
                        






                                                             



                                                                          
                      

                                                     
                     
                             
 













                                                        
 










                                                           
                                          
                      
                          
                                    



                                                         





                                                            

                                                                                                      
     


                                                        
                                                                                    











                                                                     
                                  







                                                                                    
                     






                                                                            




                                                                               

                                                                              











                                                                 
                                       



                             
 
 

                                                  

 



                                                    

                                               
 

                      
                                                                                
                          




































































                                                                             











                                                                                
/*
 * netsimplewifipage.cc - Implementation of the UI.Network.Wireless page
 * horizon-qt5, the Qt 5 user interface for
 * Project Horizon
 *
 * Copyright (c) 2019 Adélie Linux and contributors.  All rights reserved.
 * This code is licensed under the AGPL 3.0 license, as noted in the
 * LICENSE-code file in the root directory of this repository.
 *
 * SPDX-License-Identifier: AGPL-3.0-only
 */

#include "netsimplewifipage.hh"
#include "netdhcppage.hh"

#include <assert.h>
#include <sstream>
#include <QVBoxLayout>

#ifdef HAS_INSTALL_ENV
int scanResults(wpactrl_t *control, char const *s, size_t len, void *page, tain_t *) {
    NetworkSimpleWirelessPage *our_page = reinterpret_cast<NetworkSimpleWirelessPage *>(page);
    return our_page->processScan(control, s, len);
}
#endif  /* HAS_INSTALL_ENV */

NetworkSimpleWirelessPage::NetworkSimpleWirelessPage(QWidget *parent)
    : HorizonWizardPage(parent), control(WPACTRL_ZERO) {
    QVBoxLayout *layout;

    loadWatermark("network");
    setTitle(tr("Select Your Network"));

    statusLabel = new QLabel(tr("Scanning for networks..."));

    rescanButton = new QPushButton(tr("&Rescan Networks"));
    connect(rescanButton, &QPushButton::clicked, [=](void) { doScan(); });

    ssidListView = new QListWidget;

#ifdef HAS_INSTALL_ENV
    exchange_item.filter = "CTRL-EVENT-SCAN-RESULTS";
    exchange_item.cb = &scanResults;
    notify = nullptr;
#endif  /* HAS_INSTALL_ENV */

    passphrase = new QLineEdit(this);
    passphrase->setEchoMode(QLineEdit::Password);
    passphrase->setPlaceholderText(tr("Passphrase"));
    passphrase->hide();

    layout = new QVBoxLayout;
    layout->addWidget(statusLabel, 0, Qt::AlignCenter);
    layout->addSpacing(10);
    layout->addWidget(ssidListView, 0, Qt::AlignCenter);
    layout->addWidget(rescanButton);
    layout->addSpacing(10);
    layout->addWidget(passphrase);
    setLayout(layout);
}

NetworkSimpleWirelessPage::~NetworkSimpleWirelessPage() {
#ifdef HAS_INSTALL_ENV
    wpactrl_end(&control);
#endif  /* HAS_INSTALL_ENV */
}

void NetworkSimpleWirelessPage::scanDone(QString message) {
    rescanButton->setEnabled(true);
    statusLabel->setText(message);
}

void NetworkSimpleWirelessPage::doScan() {
#ifdef HAS_INSTALL_ENV
    ssidListView->clear();
    rescanButton->setEnabled(false);
    statusLabel->setText(tr("Scanning for networks..."));

    tain_t deadline;
    wparesponse_t response;
    std::string suppsock = "/var/run/wpa_supplicant/" +
            horizonWizard()->chosen_auto_iface;

    tain_now_g();
    if(!wpactrl_start_g(&control, suppsock.c_str(), 2000)) {
        rescanButton->setEnabled(false);
        statusLabel->setText(tr("Couldn't communicate with wireless subsystem (Code %1)").arg(errno));
        return;
    }

    response = wpactrl_command_g(&control, "SCAN");
    if(response != WPA_OK && response != WPA_FAILBUSY) {
        scanDone(tr("Couldn't scan for wireless networks (Code %1)").arg(response));
        return;
    }

    tain_from_millisecs(&deadline, 15000);
    tain_add_g(&deadline, &deadline);
    wpactrl_xchg_init(&exchange, &exchange_item, 1, &deadline, this);
    if(!wpactrl_xchg_start(&control, &exchange)) {
        scanDone(tr("Failed to scan for wireless networks."));
        return;
    }

    if(notify != nullptr) {
        notify->setEnabled(false);
        notify->deleteLater();
        notify = nullptr;
    }

    notify = new QSocketNotifier(wpactrl_fd(&control), QSocketNotifier::Read, this);
    connect(notify, &QSocketNotifier::activated, [=](int) {
        QString status;

        tain_now_g();
        if(wpactrl_xchg_timeout_g(&control, &exchange)) {
            status = tr("Network scan timed out.");
        } else {
            if(wpactrl_update(&control) < 0) {
                status = tr("Issue communicating with wireless subsystem.");
            } else {
                int code = wpactrl_xchg_event_g(&control, &exchange);
                if(code == -2) {
                    /* if the callback is what failed, we already set status */
                    status = statusLabel->text();
                } else if(code < 0) {
                    /* non-callback failure */
                    status = tr("Issue processing scanned networks (Code %1)")
                            .arg(code);
                } else if(code == 0) {
                    /* Not finished yet, so don't do anything. */
                    return;
                } else {
                    status = tr("Scan successful.");
                }
            }
        }
        notify->setEnabled(false);
        notify->deleteLater();
        notify = nullptr;
        statusLabel->setText(status);
        rescanButton->setEnabled(true);
        return;
    });
    notify->setEnabled(true);
#endif  /* HAS_INSTALL_ENV */
}

void NetworkSimpleWirelessPage::initializePage() {
    doScan();
}

bool NetworkSimpleWirelessPage::isComplete() const {
    return (ssidListView->currentRow() != -1);
}

int NetworkSimpleWirelessPage::nextId() const {
    return HorizonWizard::Page_Network_DHCP;
}

#ifdef HAS_INSTALL_ENV
int NetworkSimpleWirelessPage::processScan(wpactrl_t *c, const char *, size_t) {
    assert(c == &control);

    size_t bufsize = 32768;
    char *buf = static_cast<char *>(malloc(bufsize));
    ssize_t res_size;

    errno = 0;
    res_size = wpactrl_query_g(&control, "SCAN_RESULTS", buf, bufsize);
    if(res_size == -1) {
        if(errno == EMSGSIZE) {
            statusLabel->setText(tr("Scan failed: Out of memory"));
            free(buf);
            return 0;
        } else {
            statusLabel->setText(tr("Scan failed (Code %1)").arg(errno));
            free(buf);
            return 0;
        }
    }

    std::string raw_nets(buf, static_cast<std::string::size_type>(res_size));
    std::istringstream net_streams(raw_nets);
    /* discard the first line - it's a header */
    net_streams.getline(buf, static_cast<std::streamsize>(bufsize));
    /* process networks */
    while(net_streams.getline(buf, static_cast<std::streamsize>(bufsize))) {
        std::string net_line(buf);
        std::string::size_type cur = 0, next = net_line.find_first_of('\t');
        assert(next != std::string::npos);
        std::string bssid(net_line.substr(cur, next));
        cur = next + 1;
        next = net_line.find_first_of('\t', cur);
        assert(next != std::string::npos);
        std::string freq(net_line.substr(cur, next));
        cur = next + 1;
        next = net_line.find_first_of('\t', cur);
        assert(next != std::string::npos);
        std::string signal(net_line.substr(cur, next));
        cur = next + 1;
        next = net_line.find_first_of('\t', cur);
        assert(next != std::string::npos);
        std::string flags(net_line.substr(cur, next));
        cur = next + 1;
        next = net_line.find_first_of('\t', cur);
        assert(next == std::string::npos);
        std::string ssid(net_line.substr(cur, next));

        QIcon icon;
        int strength = std::stoi(signal);
        if(strength < -90) {
            icon = QIcon::fromTheme("network-wireless-signal-none");
        } else if(strength < -80) {
            icon = QIcon::fromTheme("network-wireless-signal-weak");
        } else if(strength < -67) {
            icon = QIcon::fromTheme("network-wireless-signal-ok");
        } else if(strength < -50) {
            icon = QIcon::fromTheme("network-wireless-signal-good");
        } else {
            icon = QIcon::fromTheme("network-wireless-signal-excellent");
        }

        QListWidgetItem *network = new QListWidgetItem(ssidListView);
        network->setText(QString::fromStdString(ssid));
        network->setIcon(icon);
        network->setToolTip(tr("Frequency: %1 MHz\nBSSID: %2\nRSSI: %3")
                            .arg(freq.c_str()).arg(bssid.c_str())
                            .arg(signal.c_str()));
    }

    return 1;
}
#endif  /* HAS_INSTALL_ENV */

bool NetworkSimpleWirelessPage::validatePage() {
    /* What a hack!
     *
     * Independent Pages means the DHCP page is never cleaned, even when Back
     * is chosen.  So, we have to do it from here. */
    horizonWizard()->removePage(HorizonWizard::Page_Network_DHCP);
    horizonWizard()->setPage(HorizonWizard::Page_Network_DHCP, new NetDHCPPage);
    return true;
}