You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							390 lines
						
					
					
						
							12 KiB
						
					
					
				
			
		
		
	
	
							390 lines
						
					
					
						
							12 KiB
						
					
					
				| #include <QDebug>
 | |
| #include <QHBoxLayout>
 | |
| #include <QLabel>
 | |
| #include <QPixmap>
 | |
| #include <QPushButton>
 | |
| #include <QLineEdit>
 | |
| #include <QRandomGenerator>
 | |
| #include <QtConcurrent>
 | |
| 
 | |
| #include "networking.hpp"
 | |
| 
 | |
| void clearLayout(QLayout* layout) {
 | |
|   while (QLayoutItem* item = layout->takeAt(0)) {
 | |
|     if (QWidget* widget = item->widget()) {
 | |
|       widget->deleteLater();
 | |
|     }
 | |
|     if (QLayout* childLayout = item->layout()) {
 | |
|       clearLayout(childLayout);
 | |
|     }
 | |
|     delete item;
 | |
|   }
 | |
| }
 | |
| 
 | |
| QWidget* layoutToWidget(QLayout* l, QWidget* parent){
 | |
|   QWidget* q = new QWidget(parent);
 | |
|   q->setLayout(l);
 | |
|   return q;
 | |
| }
 | |
| 
 | |
| // https://stackoverflow.com/questions/478898/how-do-i-execute-a-command-and-get-the-output-of-the-command-within-c-using-po
 | |
| std::string exec(const char* cmd) {
 | |
|   std::array<char, 128> buffer;
 | |
|   std::string result;
 | |
|   std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
 | |
|   if (!pipe) {
 | |
|     throw std::runtime_error("popen() failed!");
 | |
|   }
 | |
|   while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
 | |
|     result += buffer.data();
 | |
|   }
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| // Networking functions
 | |
| 
 | |
| Networking::Networking(QWidget* parent, bool show_advanced) : QWidget(parent){
 | |
|   try {
 | |
|     wifi = new WifiManager(this);
 | |
|   } catch (std::exception &e) {
 | |
|     QLabel* warning = new QLabel("Network manager is inactive!");
 | |
|     warning->setStyleSheet(R"(font-size: 65px;)");
 | |
| 
 | |
|     QVBoxLayout* warning_layout = new QVBoxLayout;
 | |
|     warning_layout->addWidget(warning, 0, Qt::AlignCenter);
 | |
|     setLayout(warning_layout);
 | |
|     return;
 | |
|   }
 | |
|   connect(wifi, SIGNAL(wrongPassword(QString)), this, SLOT(wrongPassword(QString)));
 | |
| 
 | |
|   s = new QStackedLayout;
 | |
| 
 | |
|   QVBoxLayout* vlayout = new QVBoxLayout;
 | |
| 
 | |
|   if (show_advanced) {
 | |
|     QPushButton* advancedSettings = new QPushButton("Advanced");
 | |
|     advancedSettings->setStyleSheet(R"(margin-right: 30px)");
 | |
|     advancedSettings->setFixedSize(300, 100);
 | |
|     connect(advancedSettings, &QPushButton::released, [=](){s->setCurrentIndex(1);});
 | |
|     vlayout->addSpacing(10);
 | |
|     vlayout->addWidget(advancedSettings, 0, Qt::AlignRight);
 | |
|     vlayout->addSpacing(10);
 | |
|   }
 | |
| 
 | |
|   wifiWidget = new WifiUI(0, 5, wifi);
 | |
|   connect(wifiWidget, SIGNAL(connectToNetwork(Network)), this, SLOT(connectToNetwork(Network)));
 | |
|   vlayout->addWidget(wifiWidget, 1);
 | |
| 
 | |
|   s->addWidget(layoutToWidget(vlayout, this));
 | |
| 
 | |
|   an = new AdvancedNetworking(this, wifi);
 | |
|   connect(an, &AdvancedNetworking::backPress, [=](){s->setCurrentIndex(0);});
 | |
|   s->addWidget(an);
 | |
| 
 | |
|   // Update network status
 | |
|   QTimer* timer = new QTimer(this);
 | |
|   QObject::connect(timer, SIGNAL(timeout()), this, SLOT(refresh()));
 | |
|   timer->start(5000);
 | |
| 
 | |
|   setStyleSheet(R"(
 | |
|     QPushButton {
 | |
|       font-size: 50px;
 | |
|       margin: 0px;
 | |
|       padding: 15px;
 | |
|       border-width: 0;
 | |
|       border-radius: 7px;
 | |
|       color: #dddddd;
 | |
|       background-color: #444444;
 | |
|     }
 | |
|     QPushButton:disabled {
 | |
|       color: #777777;
 | |
|       background-color: #222222;
 | |
|     }
 | |
|   )");
 | |
|   setLayout(s);
 | |
| }
 | |
| 
 | |
| void Networking::refresh(){
 | |
|   if(!this->isVisible()){
 | |
|     return;
 | |
|   }
 | |
|   wifiWidget->refresh();
 | |
|   an->refresh();
 | |
| }
 | |
| 
 | |
| void Networking::connectToNetwork(Network n) {
 | |
|   if (n.security_type == SecurityType::OPEN) {
 | |
|     wifi->connect(n);
 | |
|   } else if (n.security_type == SecurityType::WPA) {
 | |
|     QString pass = InputDialog::getText("Enter password for \"" + n.ssid + "\"");
 | |
|     wifi->connect(n, pass);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void Networking::wrongPassword(QString ssid) {
 | |
|   return; // TODO: add this back
 | |
|   /*
 | |
|   for (Network n : wifi->seen_networks) {
 | |
|     if (n.ssid == ssid) {
 | |
|       inputField->setPromptText("Wrong password for \"" + n.ssid +"\"");
 | |
|       s->setCurrentIndex(0);
 | |
|       emit openKeyboard();
 | |
|       return;
 | |
|     }
 | |
|   }
 | |
|   */
 | |
| }
 | |
| 
 | |
| QFrame* hline(QWidget* parent = 0){
 | |
|   QFrame* line = new QFrame(parent);
 | |
|   line->setFrameShape(QFrame::StyledPanel);
 | |
|   line->setStyleSheet("margin-left: 40px; margin-right: 40px; border-width: 1px; border-bottom-style: solid; border-color: gray;");
 | |
|   line->setFixedHeight(2);
 | |
|   return line;
 | |
| }
 | |
| 
 | |
| // AdvancedNetworking functions
 | |
| 
 | |
| AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWidget(parent), wifi(wifi){
 | |
|   s = new QStackedLayout; // mainPage, SSH settings
 | |
| 
 | |
|   QVBoxLayout* vlayout = new QVBoxLayout;
 | |
| 
 | |
|   // Back button
 | |
|   QHBoxLayout* backLayout = new QHBoxLayout;
 | |
|   QPushButton* back = new QPushButton("Back");
 | |
|   back->setFixedSize(500, 100);
 | |
|   connect(back, &QPushButton::released, [=](){emit backPress();});
 | |
|   backLayout->addWidget(back, 0, Qt::AlignLeft);
 | |
|   vlayout->addWidget(layoutToWidget(backLayout, this), 0, Qt::AlignLeft);
 | |
| 
 | |
|   // Enable tethering layout
 | |
|   QHBoxLayout* tetheringToggleLayout = new QHBoxLayout;
 | |
|   tetheringToggleLayout->addWidget(new QLabel("Enable tethering"));
 | |
|   Toggle* toggle_switch = new Toggle;
 | |
|   toggle_switch->setFixedSize(150, 100);
 | |
|   tetheringToggleLayout->addWidget(toggle_switch);
 | |
|   tetheringToggleLayout->addSpacing(40);
 | |
|   if (wifi->tetheringEnabled()) {
 | |
|     toggle_switch->togglePosition();
 | |
|   }
 | |
|   QObject::connect(toggle_switch, SIGNAL(stateChanged(int)), this, SLOT(toggleTethering(int)));
 | |
|   vlayout->addWidget(layoutToWidget(tetheringToggleLayout, this), 0);
 | |
|   vlayout->addWidget(hline(), 0);
 | |
| 
 | |
|   // Change tethering password
 | |
|   QHBoxLayout *tetheringPassword = new QHBoxLayout;
 | |
|   tetheringPassword->addWidget(new QLabel("Edit tethering password"), 1);
 | |
|   editPasswordButton = new QPushButton("EDIT");
 | |
|   editPasswordButton->setFixedWidth(500);
 | |
|   connect(editPasswordButton, &QPushButton::released, [=](){
 | |
|     QString pass = InputDialog::getText("Enter new tethering password");
 | |
|     if (pass.size()) {
 | |
|       wifi->changeTetheringPassword(pass);
 | |
|     }
 | |
|   });
 | |
|   tetheringPassword->addWidget(editPasswordButton, 1, Qt::AlignRight);
 | |
|   vlayout->addWidget(layoutToWidget(tetheringPassword, this), 0);
 | |
|   vlayout->addWidget(hline(), 0);
 | |
| 
 | |
|   // IP adress
 | |
|   QHBoxLayout* IPlayout = new QHBoxLayout;
 | |
|   IPlayout->addWidget(new QLabel("IP address"), 0);
 | |
|   ipLabel = new QLabel(wifi->ipv4_address);
 | |
|   ipLabel->setStyleSheet("color: #aaaaaa");
 | |
|   IPlayout->addWidget(ipLabel, 0, Qt::AlignRight);
 | |
|   vlayout->addWidget(layoutToWidget(IPlayout, this), 0);
 | |
|   vlayout->addWidget(hline(), 0);
 | |
| 
 | |
|   // Enable SSH
 | |
|   QHBoxLayout* enableSSHLayout = new QHBoxLayout(this);
 | |
|   enableSSHLayout->addWidget(new QLabel("Enable SSH", this));
 | |
|   toggle_switch_SSH = new Toggle(this);
 | |
|   toggle_switch_SSH->setFixedSize(150, 100);
 | |
|   if (isSSHEnabled()) {
 | |
|     toggle_switch_SSH->togglePosition();
 | |
|   }
 | |
|   QObject::connect(toggle_switch_SSH, SIGNAL(stateChanged(int)), this, SLOT(toggleSSH(int)));
 | |
|   enableSSHLayout->addWidget(toggle_switch_SSH);
 | |
|   vlayout->addWidget(layoutToWidget(enableSSHLayout, this));
 | |
|   vlayout->addWidget(hline(), 0);
 | |
| 
 | |
|   // SSH keys
 | |
|   QHBoxLayout* authSSHLayout = new QHBoxLayout(this);
 | |
|   authSSHLayout->addWidget(new QLabel("SSH keys", this));
 | |
|   QPushButton* editAuthSSHButton = new QPushButton("EDIT", this);
 | |
|   editAuthSSHButton->setFixedWidth(500);
 | |
|   connect(editAuthSSHButton, &QPushButton::released, [=](){s->setCurrentWidget(ssh);});
 | |
|   authSSHLayout->addWidget(editAuthSSHButton);
 | |
|   vlayout->addWidget(layoutToWidget(authSSHLayout, this));
 | |
|   vlayout->addSpacing(50);
 | |
| 
 | |
|   // //Disconnect or delete connections
 | |
|   // QHBoxLayout* dangerZone = new QHBoxLayout(this);
 | |
|   // QPushButton* disconnect = new QPushButton("Disconnect from WiFi", this);
 | |
|   // dangerZone->addWidget(disconnect);
 | |
|   // QPushButton* deleteAll = new QPushButton("DELETE ALL NETWORKS", this);
 | |
|   // dangerZone->addWidget(deleteAll);
 | |
|   // vlayout->addWidget(layoutToWidget(dangerZone, this));
 | |
| 
 | |
|   // vlayout to widget
 | |
|   QWidget* settingsWidget = layoutToWidget(vlayout, this);
 | |
|   settingsWidget->setStyleSheet("margin-left: 40px; margin-right: 40px;");
 | |
|   s->addWidget(settingsWidget);
 | |
| 
 | |
|   ssh = new SSH;
 | |
|   connect(ssh, &SSH::closeSSHSettings, [=](){s->setCurrentIndex(0);});
 | |
|   s->addWidget(ssh);
 | |
| 
 | |
|   setLayout(s);
 | |
| }
 | |
| 
 | |
| bool AdvancedNetworking::isSSHEnabled(){
 | |
|   QString response = QString::fromStdString(exec("systemctl is-active ssh"));
 | |
|   return response.startsWith("active");
 | |
| }
 | |
| 
 | |
| void AdvancedNetworking::refresh(){
 | |
|   ipLabel->setText(wifi->ipv4_address);
 | |
|   // Don't refresh while changing SSH state
 | |
|   if(!toggle_switch_SSH->getEnabled()){
 | |
|     return;
 | |
|   }
 | |
|   if (toggle_switch_SSH->on != isSSHEnabled()) {
 | |
|     toggle_switch_SSH->togglePosition();
 | |
|   }
 | |
|   // Qt can be lazy
 | |
|   repaint();
 | |
| }
 | |
| 
 | |
| void AdvancedNetworking::toggleTethering(int enable) {
 | |
|   if (enable) {
 | |
|     wifi->enableTethering();
 | |
|   } else {
 | |
|     wifi->disableTethering();
 | |
|   }
 | |
|   editPasswordButton->setEnabled(!enable);
 | |
| }
 | |
| 
 | |
| void enableSSH(Toggle* toggle_switch_SSH){
 | |
|   system("sudo systemctl enable ssh");
 | |
|   system("sudo systemctl start ssh");
 | |
|   toggle_switch_SSH->setEnabled(true);
 | |
| }
 | |
| 
 | |
| void disableSSH(Toggle* toggle_switch_SSH){
 | |
|   system("sudo systemctl stop ssh");
 | |
|   system("sudo systemctl disable ssh");
 | |
|   toggle_switch_SSH->setEnabled(true);
 | |
| }
 | |
| 
 | |
| void AdvancedNetworking::toggleSSH(int enable) {
 | |
|   toggle_switch_SSH->setEnabled(false);
 | |
|   if (enable) {
 | |
|     QtConcurrent::run(enableSSH, toggle_switch_SSH);
 | |
|   } else {
 | |
|     QtConcurrent::run(disableSSH, toggle_switch_SSH);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| // WifiUI functions
 | |
| 
 | |
| WifiUI::WifiUI(QWidget *parent, int page_length, WifiManager* wifi) : QWidget(parent), networks_per_page(page_length), wifi(wifi) {
 | |
|   vlayout = new QVBoxLayout;
 | |
| 
 | |
|   // Scan on startup
 | |
|   QLabel *scanning = new QLabel("Scanning for networks");
 | |
|   scanning->setStyleSheet(R"(font-size: 65px;)");
 | |
|   vlayout->addWidget(scanning, 0, Qt::AlignCenter);
 | |
|   vlayout->setSpacing(25);
 | |
| 
 | |
|   setLayout(vlayout);
 | |
|   page = 0;
 | |
| }
 | |
| 
 | |
| void WifiUI::refresh() {
 | |
|   wifi->request_scan();
 | |
|   wifi->refreshNetworks();
 | |
|   clearLayout(vlayout);
 | |
| 
 | |
|   connectButtons = new QButtonGroup(this); // TODO check if this is a leak
 | |
|   QObject::connect(connectButtons, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(handleButton(QAbstractButton*)));
 | |
| 
 | |
|   int i = 0;
 | |
|   int countWidgets = 0;
 | |
|   for (Network &network : wifi->seen_networks) {
 | |
|     QHBoxLayout *hlayout = new QHBoxLayout;
 | |
|     if (page * networks_per_page <= i && i < (page + 1) * networks_per_page) {
 | |
|       // SSID
 | |
|       hlayout->addSpacing(50);
 | |
|       QString ssid = QString::fromUtf8(network.ssid);
 | |
|       if(ssid.length() > 30){
 | |
|         ssid = ssid.left(30)+"…";
 | |
|       }
 | |
|       QLabel *ssid_label = new QLabel(ssid);
 | |
|       ssid_label->setStyleSheet(R"(
 | |
|         font-size: 55px;
 | |
|       )");
 | |
|       hlayout->addWidget(ssid_label);
 | |
| 
 | |
|       // TODO: don't use images for this
 | |
|       // strength indicator
 | |
|       unsigned int strength_scale = network.strength / 17;
 | |
|       QPixmap pix("../assets/images/network_" + QString::number(strength_scale) + ".png");
 | |
|       QLabel *icon = new QLabel();
 | |
|       icon->setPixmap(pix.scaledToWidth(100, Qt::SmoothTransformation));
 | |
|       icon->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
 | |
|       hlayout->addWidget(icon, 0, Qt::AlignRight);
 | |
| 
 | |
|       // connect button
 | |
|       QPushButton* btn = new QPushButton(network.security_type == SecurityType::UNSUPPORTED ? "Unsupported" : (network.connected == ConnectedType::CONNECTED ? "Connected" : (network.connected == ConnectedType::CONNECTING ? "Connecting" : "Connect")));
 | |
|       btn->setDisabled(network.connected == ConnectedType::CONNECTED || network.connected == ConnectedType::CONNECTING || network.security_type == SecurityType::UNSUPPORTED);
 | |
|       btn->setFixedWidth(350);
 | |
|       hlayout->addWidget(btn, 0, Qt::AlignRight);
 | |
| 
 | |
|       connectButtons->addButton(btn, i);
 | |
| 
 | |
|       vlayout->addLayout(hlayout, 1);
 | |
|       // Don't add the last horizontal line
 | |
|       if (page * networks_per_page <= i+1 && i+1 < (page + 1) * networks_per_page && i+1 < wifi->seen_networks.size()) {
 | |
|         vlayout->addWidget(hline(), 0);
 | |
|       }
 | |
|       countWidgets++;
 | |
|     }
 | |
|     i++;
 | |
|   }
 | |
|   vlayout->addStretch(3);
 | |
| 
 | |
| 
 | |
|   // Setup buttons for pagination
 | |
|   QHBoxLayout *prev_next_buttons = new QHBoxLayout;
 | |
| 
 | |
|   QPushButton* prev = new QPushButton("Previous");
 | |
|   prev->setEnabled(page);
 | |
|   QObject::connect(prev, SIGNAL(released()), this, SLOT(prevPage()));
 | |
|   prev_next_buttons->addWidget(prev);
 | |
| 
 | |
|   QPushButton* next = new QPushButton("Next");
 | |
|   next->setEnabled(wifi->seen_networks.size() > (page + 1) * networks_per_page);
 | |
|   QObject::connect(next, SIGNAL(released()), this, SLOT(nextPage()));
 | |
|   prev_next_buttons->addWidget(next);
 | |
| 
 | |
|   vlayout->addLayout(prev_next_buttons, 2);
 | |
| }
 | |
| 
 | |
| void WifiUI::handleButton(QAbstractButton* button) {
 | |
|   QPushButton* btn = static_cast<QPushButton*>(button);
 | |
|   Network n = wifi->seen_networks[connectButtons->id(btn)];
 | |
|   emit connectToNetwork(n);
 | |
| }
 | |
| 
 | |
| void WifiUI::prevPage() {
 | |
|   page--;
 | |
|   refresh();
 | |
| }
 | |
| 
 | |
| void WifiUI::nextPage() {
 | |
|   page++;
 | |
|   refresh();
 | |
| }
 | |
| 
 |