/*
* connectwindow.cpp
*
* Created on: 28.01.2013
* Author: sr
*/
#include <QNetworkInterface>
#include "../../shared/settings.h"
#include "../../shared/network.h"
#include "../../shared/util.h"
#include "../net/serverconnection.h"
#include "../clientapp/clientapp.h"
#include "connectwindow.h"
#include "ui_connect.h"
#define UDPBUFSIZ 9000
#define SALT_LEN 18
/**
* Initialize Connection Window.
* @param parent
*/
ConnectWindow::ConnectWindow(QWidget *parent) : QWidget(parent)
{
_ui = new Ui::ConnectWindow;
_timerHide = 0;
_state = Idle;
_hashSslErrorCount = 0;
_pendingConnection = nullptr;
// Initialize the GUI
_ui->setupUi(this);
// Set window properties
setWindowFlags(Qt::WindowStaysOnTopHint | Qt::Tool);
// Set page 0 as default
_ui->stackedWidget->setCurrentIndex(0);
// Set actions of buttons
connect(_ui->btn_connection, SIGNAL(clicked()), this, SLOT(onBtnConnection()));
connect(_ui->btn_hide, SIGNAL(clicked()), this, SLOT(onBtnHide()));
connect(_ui->comboBox_rooms, SIGNAL(currentIndexChanged(int)), this, SLOT(onRoomSelection(int)));
// React on discovery signal
connect(&_serverDiscovery, SIGNAL(serverDetected(QString, quint16, QByteArray, QByteArray, bool)),
this, SLOT(onServerDetected(QString, quint16, QByteArray, QByteArray, bool)));
/* finally the most requested feature: connect on press of the enter key */
connect(_ui->lineEditName, SIGNAL(returnPressed()), _ui->btn_connection, SIGNAL(clicked()));
/* by default don't show the manual connection box */
_ui->box_manual->setVisible(false);
this->updateUserInterface();
}
/**
* @brief ConnectWindow::~ConnectWindow
*/
ConnectWindow::~ConnectWindow() {}
/**
* Handle changes in state and update window.
* Also changing TextLabel which shows the user what the program is doing.
*/
void ConnectWindow::updateUserInterface()
{
_ui->lineEditName->setEnabled(_state == Idle);
if (_state == Connected) {
_ui->btn_connection->setEnabled(true);
_ui->btn_connection->setText(tr("&Disconnect"));
_ui->lblStatus->setText(tr("Connected to %1").arg(_currentSession.isEmpty() ? _currentIp : _currentSession));
_ui->lineEditName->setEnabled(false);
_ui->stackedWidget->setCurrentIndex(1);
return;
}
if (_state != Idle)
_ui->btn_connection->setText(tr("&Stop"));
else
_ui->btn_connection->setText(tr("&Connect"));
switch (_state) {
case Idle:
_ui->lblStatus->setText(tr("Ready to connect."));
break;
case Scanning:
_ui->lblStatus->setText(tr("Scanning for session %1.").arg(_currentSession.isEmpty() ? _currentIp : _currentSession));
break;
case Connecting:
_ui->lblStatus->setText(tr("Found session, connecting..."));
break;
case AwaitingChallenge:
_ui->lblStatus->setText(tr("Waiting for server challenge..."));
break;
case AwaitingChallengeResponse:
_ui->lblStatus->setText(tr("Replied to challenge, sent own..."));
break;
case LoggingIn:
_ui->lblStatus->setText(tr("Logging in..."));
break;
case Connected:
break;
case InvalidCert:
_ui->lblStatus->setText(tr("Invalid certificate."));
break;
case InvalidSslHash:
_ui->lblStatus->setText(tr("Invalid TLS hash: %1.").arg(_hashSslErrorCount));
break;
default:
_ui->lblStatus->setText(tr("Unknown state :-("));
break;
}
}
/*
* Overrides
*/
/**
* Called when a Qt timer fires; used for server discovery and
* auto-hiding the connect dialog.
*/
void ConnectWindow::timerEvent(QTimerEvent* event)
{
if (event->timerId() == _timerHide) {
killTimer(_timerHide);
_timerHide = 0;
this->hide();
_ui->stackedWidget->setCurrentIndex(0);
} else
// Unknown/Old timer id, kill it ??? PALM -> FACE
killTimer(event->timerId());
}
/**
* Handle incoming closeEvent and hide window.
* @param e
*/
void ConnectWindow::closeEvent(QCloseEvent *e)
{
e->ignore();
this->hide();
}
void ConnectWindow::doShow()
{
/* reset to automatic connect window */
_ui->stackedWidget->setCurrentIndex(0);
_ui->comboBox_rooms->setCurrentIndex(0);
show();
showNormal();
activateWindow();
raise();
}
/**
* Gives the keyboard input focus to the input line.
* @param event
*/
void ConnectWindow::showEvent(QShowEvent* /* event */ )
{
activateWindow();
raise();
_ui->lineEditName->setFocus();
}
/**
* Public function connect to session.
* Check if currently connected to server,
* if not --> connect to given sessionName.
* @param sessionName
*/
void ConnectWindow::connectToSession(const QByteArray sessionName, QString mgrIP)
{
if (_state != Idle)
return;
_currentSession = sessionName;
_currentIp = mgrIP;
_state = Scanning;
this->updateUserInterface();
_tryReconnect = true;
_serverDiscovery.start(sessionName, mgrIP);
}
/*
* Slots
*/
void ConnectWindow::DoConnect()
{
qDebug() << "DoConnect()";
// Connect (scan for session)
// qDebug() << _ui->lineEditName->text().toUtf8();
int index = _ui->comboBox_rooms->currentIndex();
QString selectedMgrIP = _ui->comboBox_rooms->itemData(index).toString();
if (selectedMgrIP == "manual_connection") {
qDebug() << "connect to sessionName by manual connection";
QByteArray sessionName = _ui->lineEditName->text().toUtf8();
connectToSession(sessionName, "");
} else {
qDebug() << "connect to mgrIP (through room selection) " << selectedMgrIP;
connectToSession("", selectedMgrIP);
}
}
void ConnectWindow::DoDisconnect()
{
qDebug() << "DoDisconnect()";
_tryReconnect = false;
// Stop or disconnect
emit disconnect();
_state = Idle;
}
/**
* Handle click on Connect/Disconnect button.
* If already connected --> Stop/disconnect.
* Else scanning for given sessionId.
*/
void ConnectWindow::onBtnConnection()
{
if (_timerHide) {
killTimer(_timerHide);
_timerHide = 0;
_ui->stackedWidget->setCurrentIndex(0);
}
if (_serverDiscovery.isActive())
_serverDiscovery.stop();
if (_state != Idle) {
DoDisconnect();
} else {
DoConnect();
}
this->updateUserInterface();
}
/** set the available rooms.
* If the list of rooms is empty, switches automatically to the "manual
* connection" page */
void ConnectWindow::setAvailableRooms(QList<Room> m)
{
_ui->comboBox_rooms->clear();
foreach (Room r, m) {
_ui->comboBox_rooms->addItem(r.name, r.mgr);
}
/* also add a pseudo-room "manual choice" */
_ui->comboBox_rooms->addItem(tr("Session Name..."), "manual_connection");
}
/**
* Handle click on Cancel/Hide Button.
* Just hide the window.
*/
void ConnectWindow::onBtnHide()
{
this->hide();
}
/** check if "manual_connection" is selected, then switch to manual
* connection page */
void ConnectWindow::onRoomSelection(int index)
{
QString sessionName = _ui->comboBox_rooms->itemData(index).toString();
if (sessionName == "manual_connection") {
qDebug() << "switch to manual connection";
_ui->box_manual->setVisible(true);
//this->setSize(QSize(300,200));
this->resize(300, 200);
} else {
_ui->box_manual->setVisible(false);
this->resize(300, 140);
}
}
/**
* @brief ConnectWindow::onServerDetected
* @param host
* @param port
* @param sessionName
* @param certHash
*/
void ConnectWindow::onServerDetected(const QString& host, const quint16 port, const QByteArray& sessionName, const QByteArray& certHash, bool autoConnect)
{
_pendingConnection = new ServerConnection(host, port, sessionName, certHash, autoConnect);
connect(_pendingConnection, SIGNAL(stateChange(ConnectWindow::ConnectionState)), this, SLOT(onConnectionStateChange(ConnectWindow::ConnectionState)));
connect(_pendingConnection, SIGNAL(destroyed(QObject*)), this, SLOT(onConnectionClosed(QObject*)));
connect(_pendingConnection, SIGNAL(disconnected(ServerConnection*)), this, SLOT(onConnectionDisconnected(ServerConnection*)));
}
/**
* Handle connection state changes and update member variables describing state.
* @param state
*/
void ConnectWindow::onConnectionStateChange(ConnectWindow::ConnectionState state)
{
bool reset = (_state == Scanning);
if (state == InvalidSslHash)
++_hashSslErrorCount;
_state = state;
this->updateUserInterface();
if (reset) {
_state = Scanning;
}
if (state == Connected) {
QObject::disconnect(_pendingConnection, SIGNAL(stateChange(ConnectWindow::ConnectionState)), this, SLOT(onConnectionStateChange(ConnectWindow::ConnectionState)));
QObject::disconnect(_pendingConnection, SIGNAL(destroyed(QObject*)), this, SLOT(onConnectionClosed(QObject*)));
emit connected(_pendingConnection);
_pendingConnection = nullptr;
_timerHide = startTimer(2000);
}
}
/**
* If connection is closed set _pendingConnection = nullptr.
* @param connection
*/
void ConnectWindow::onConnectionClosed(QObject* /* connection */ )
{
_pendingConnection = nullptr;
}
/**
* @brief ConnectWindow::onConnectionDisconnected
*/
void ConnectWindow::onConnectionDisconnected(ServerConnection*)
{
_state = Idle;
this->updateUserInterface();
if (_tryReconnect) {
this->updateUserInterface();
connectToSession(_currentSession, _currentIp);
}
}