diff --git a/.gitignore b/.gitignore index 4d43eccc..11ab3943 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ # User settings *.user macOSPackage/ -*.dmg +AmneziaVPN.dmg +AmneziaVPN.exe # Qt-es /.qmake.cache diff --git a/client/communicator.cpp b/client/communicator.cpp index 45710e29..0dd3ca3c 100644 --- a/client/communicator.cpp +++ b/client/communicator.cpp @@ -20,18 +20,17 @@ void Communicator::connectToServer() delete m_localClient; } - qDebug().noquote() << QString("Connect to local server '%1'").arg(SERVICE_NAME); - m_localClient = new LocalClient(this); connect(m_localClient, &LocalClient::connected, this, &Communicator::onConnected); connect(m_localClient, &LocalClient::lineAvailable, this, &Communicator::onLineAvailable); - m_localClient->connectToServer(SERVICE_NAME); + + m_localClient->connectToServer(Utils::serverName()); } void Communicator::onConnected() { qDebug().noquote() << QString("Connected to local server '%1'").arg(m_localClient->serverName()); - Message message(Message::State::Initialize, QStringList({"Ping"})); + Message message(Message::State::Initialize, QStringList({"Client"})); sendMessage(message); } @@ -62,7 +61,7 @@ QString Communicator::readData() bool Communicator::writeData(const QString& data) { - return m_localClient->write(data.toLocal8Bit()); + return m_localClient->write(data.toUtf8()); } void Communicator::sendMessage(const Message& message) diff --git a/client/communicator.h b/client/communicator.h index 27a7eac0..8546bd59 100644 --- a/client/communicator.h +++ b/client/communicator.h @@ -16,6 +16,7 @@ public: explicit Communicator(QObject* parent = nullptr); ~Communicator(); + bool connected() const; void sendMessage(const Message& message); signals: @@ -30,7 +31,6 @@ protected slots: protected: QString readData(); - bool connected() const; bool writeData(const QString& data); void connectToServer(); diff --git a/client/debug.cpp b/client/debug.cpp index 29a418f8..a54b0131 100644 --- a/client/debug.cpp +++ b/client/debug.cpp @@ -19,8 +19,8 @@ void debugMessageHandler(QtMsgType type, const QMessageLogContext& context, cons return; } - // Temporally disabled - if (msg.startsWith("Unknown property") || msg.startsWith("Could not create pixmap")) { + // Skip annoying messages from Qt + if (msg.startsWith("Unknown property") || msg.startsWith("Could not create pixmap") || msg.startsWith("Populating font")) { return; } @@ -31,7 +31,7 @@ void debugMessageHandler(QtMsgType type, const QMessageLogContext& context, cons bool Debug::init() { - QString path = logsDir(); + QString path = userLogsDir(); QDir appDir(path); if (!appDir.mkpath(path)) { return false; @@ -53,14 +53,14 @@ bool Debug::init() return true; } -QString Debug::logsDir() +QString Debug::userLogsDir() { - return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/logs"; + return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/log"; } bool Debug::openLogsFolder() { - QString path = logsDir(); + QString path = userLogsDir(); #ifdef Q_OS_WIN path = "file:///" + path; #endif diff --git a/client/debug.h b/client/debug.h index 7a89c8ca..84ab5341 100644 --- a/client/debug.h +++ b/client/debug.h @@ -10,12 +10,13 @@ class Debug { public: - static QString logsDir(); static bool init(); static bool openLogsFolder(); static QString appLogFileNamePath(); private: + static QString userLogsDir(); + static QFile m_file; static QTextStream m_textStream; static QString m_logFileName; diff --git a/client/localclient.cpp b/client/localclient.cpp index 223c6f1e..e960be73 100644 --- a/client/localclient.cpp +++ b/client/localclient.cpp @@ -43,8 +43,6 @@ quint64 LocalClient::write(const QByteArray& data) void LocalClient::onReadyRead() { - qDebug() << "On ready read"; - if (m_socket->canReadLine()) { char buf[1024]; qint64 lineLength = m_socket->readLine(buf, sizeof(buf)); @@ -60,5 +58,5 @@ void LocalClient::onReadyRead() void LocalClient::displayError(QLocalSocket::LocalSocketError socketError) { Q_UNUSED(socketError) - qDebug() << QString("The following error occurred: %1.").arg(m_socket->errorString()); + qDebug().noquote() << QString("The following error occurred: %1.").arg(m_socket->errorString()); } diff --git a/client/message.cpp b/client/message.cpp index bc63392b..eb23cda4 100644 --- a/client/message.cpp +++ b/client/message.cpp @@ -1,9 +1,9 @@ #include "message.h" Message::Message(State state, const QStringList& args) : + m_valid(true), m_state(state), - m_args(args), - m_valid(true) + m_args(args) { } diff --git a/client/openvpnprotocol.cpp b/client/openvpnprotocol.cpp index a1f0e40f..db9d67c9 100644 --- a/client/openvpnprotocol.cpp +++ b/client/openvpnprotocol.cpp @@ -29,16 +29,16 @@ void OpenVpnProtocol::onMessageReceived(const Message& message) } switch (message.state()) { - case Message::State::Started: - qDebug().noquote() << QString("OpenVPN process started"); - break; - case Message::State::Finished: - qDebug().noquote() << QString("OpenVPN process finished with status %1").arg(message.argAtIndex(1)); - onOpenVpnProcessFinished(message.argAtIndex(1).toInt()); - break; - default: - qDebug().noquote() << QString("Message received: '%1'").arg(message.toString()); - ; + case Message::State::Started: + qDebug() << "OpenVPN process started"; + break; + case Message::State::Finished: + qDebug().noquote() << QString("OpenVPN process finished with status %1").arg(message.argAtIndex(1)); + onOpenVpnProcessFinished(message.argAtIndex(1).toInt()); + break; + default: + qDebug().noquote() << QString("Message received: '%1'").arg(message.toString()); + ; } } @@ -56,7 +56,7 @@ void OpenVpnProtocol::stop() void OpenVpnProtocol::killOpenVpnProcess() { - // send command to kill openvpn process. + // send command to kill openvpn process (if any). } bool OpenVpnProtocol::setConfigFile(const QString& configFileNamePath) @@ -65,14 +65,14 @@ bool OpenVpnProtocol::setConfigFile(const QString& configFileNamePath) QFileInfo file(m_configFileName); if (file.fileName().isEmpty()) { - m_configFileName = Utils::systemConfigPath() + "/" + QCoreApplication::applicationName() + ".ovpn"; + m_configFileName = Utils::defaultVpnConfigFileName(); } if (m_configFileName.isEmpty()) { return false; } - qDebug() << "Set config file:" << configPath(); + qDebug().noquote() << QString("Set config file: '%1'").arg(configPath()); return false; } @@ -109,19 +109,24 @@ QString OpenVpnProtocol::openVpnExecPath() const bool OpenVpnProtocol::start() { - qDebug() << "Start OpenVPN connection" << openVpnExecPath(); + qDebug() << "Start OpenVPN connection"; m_requestFromUserToStop = false; m_openVpnStateSigTermHandlerTimer.stop(); stop(); + if (communicator() && !communicator()->connected()) { + setLastError("Communicator is not connected!"); + return false; + } + if (!QFileInfo::exists(openVpnExecPath())) { - qCritical() << "OpeVPN executable does not exist!"; + setLastError("OpeVPN executable does not exist!"); return false; } if (!QFileInfo::exists(configPath())) { - qCritical() << "OpeVPN config file does not exist!"; + setLastError("OpeVPN config file does not exist!"); return false; } @@ -140,10 +145,7 @@ bool OpenVpnProtocol::start() } setConnectionState(ConnectionState::Connecting); - - qDebug().noquote() << "Start OpenVPN process with args: " << args; m_communicator->sendMessage(Message(Message::State::StartRequest, args)); - startTimeoutTimer(); return true; diff --git a/client/ui/mainwindow.cpp b/client/ui/mainwindow.cpp index a11b8b59..bebdc0bf 100644 --- a/client/ui/mainwindow.cpp +++ b/client/ui/mainwindow.cpp @@ -11,6 +11,8 @@ #include "utils.h" #include "vpnconnection.h" +#include + MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), @@ -34,24 +36,19 @@ MainWindow::MainWindow(QWidget *parent) : qInfo().noquote() << QString("Started %1 version %2").arg(APPLICATION_NAME).arg(APP_VERSION); qInfo().noquote() << QString("%1 (%2)").arg(QSysInfo::prettyProductName()).arg(QSysInfo::currentCpuArchitecture()); + Utils::initializePath(Utils::configPath()); - QDir dir; - QString configPath = Utils::systemConfigPath(); - if (!dir.mkpath(configPath)) { - qWarning() << "Cannot initialize config path:" << configPath; - } - - m_vpnConnection = new VpnConnection; + m_vpnConnection = new VpnConnection(this); connect(m_vpnConnection, SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64))); connect(m_vpnConnection, SIGNAL(connectionStateChanged(VpnProtocol::ConnectionState)), this, SLOT(onConnectionStateChanged(VpnProtocol::ConnectionState))); onConnectionStateChanged(VpnProtocol::ConnectionState::Disconnected); + + qDebug().noquote() << QString("Default config: %1").arg(Utils::defaultVpnConfigFileName()); } MainWindow::~MainWindow() { - delete ui; - hide(); m_vpnConnection->disconnectFromVpn(); @@ -62,7 +59,11 @@ MainWindow::~MainWindow() break; } } - qDebug() << "Closed"; + + delete m_vpnConnection; + delete ui; + + qDebug() << "Application closed"; } void MainWindow::goToIndex(int index) @@ -150,7 +151,11 @@ void MainWindow::onConnectionStateChanged(VpnProtocol::ConnectionState state) void MainWindow::onPushButtonConnectToggled(bool checked) { if (checked) { - m_vpnConnection->connectToVpn(); + if (!m_vpnConnection->connectToVpn()) { + ui->pushButton_connect->setChecked(false); + QMessageBox::critical(this, APPLICATION_NAME, m_vpnConnection->lastError()); + return; + } ui->pushButton_connect->setEnabled(false); } else { m_vpnConnection->disconnectFromVpn(); diff --git a/client/utils.cpp b/client/utils.cpp index 21bc6f6b..93793436 100644 --- a/client/utils.cpp +++ b/client/utils.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -11,14 +12,49 @@ QString Utils::toString(bool value) return value ? "true" : "false"; } -QString Utils::systemLogPath() +QString Utils::serverName() { - return systemDataLocationPath() + "/log"; +#ifdef Q_OS_WIN + return SERVICE_NAME; +#else + return QString("/tmp/%1").arg(SERVICE_NAME); +#endif } -QString Utils::systemConfigPath() +QString Utils::defaultVpnConfigFileName() { - return systemDataLocationPath() + "/config"; + return configPath() + QString("/%1.ovpn").arg(APPLICATION_NAME); +} + +QString Utils::systemLogPath() +{ +#ifdef Q_OS_WIN + QStringList locationList = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); + QString primaryLocation = "ProgramData"; + foreach (const QString& location, locationList) { + if (location.contains(primaryLocation)) { + return QString("%1/%2/log").arg(location).arg(APPLICATION_NAME); + } + } + return QString(); +#else + return QString("/var/log/%1").arg(APPLICATION_NAME); +#endif +} + +bool Utils::initializePath(const QString& path) +{ + QDir dir; + if (!dir.mkpath(path)) { + qWarning().noquote() << QString("Cannot initialize path: '%1'").arg(path); + return false; + } + return true; +} + +QString Utils::configPath() +{ + return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/config"; } bool Utils::createEmptyFile(const QString& path) @@ -27,25 +63,6 @@ bool Utils::createEmptyFile(const QString& path) return f.open(QIODevice::WriteOnly | QIODevice::Truncate); } -QString Utils::systemDataLocationPath() -{ - QStringList locationList = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); - QString primaryLocation; -#ifdef Q_OS_WIN - primaryLocation = "ProgramData"; -#elif defined Q_OS_MAC - primaryLocation = "Users"; -#endif - - foreach (const QString& location, locationList) { - if (location.contains(primaryLocation)) { - return QString("%1/%2").arg(location).arg(APPLICATION_NAME); - } - } - - return QString(); -} - QString Utils::executable(const QString& baseName, bool absPath) { QString ext; diff --git a/client/utils.h b/client/utils.h index d5c05dd2..878cf01b 100644 --- a/client/utils.h +++ b/client/utils.h @@ -6,12 +6,14 @@ class Utils { public: + static QString configPath(); + static QString defaultVpnConfigFileName(); static QString executable(const QString& baseName, bool absPath); - static QString systemConfigPath(); - static QString systemDataLocationPath(); + static QString serverName(); static QString systemLogPath(); static QString toString(bool value); static bool createEmptyFile(const QString& path); + static bool initializePath(const QString& path); static bool processIsRunning(const QString& fileName); }; diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 8b3b08ce..4ab255e7 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -5,7 +5,7 @@ VpnConnection::VpnConnection(QObject* parent) : QObject(parent) { - + VpnProtocol::initializeCommunicator(parent); } VpnConnection::~VpnConnection() @@ -23,7 +23,16 @@ void VpnConnection::onConnectionStateChanged(VpnProtocol::ConnectionState state) emit connectionStateChanged(state); } -void VpnConnection::connectToVpn(Protocol protocol) +QString VpnConnection::lastError() const +{ + if (!m_vpnProtocol.data()) { + return "Unnown protocol"; + } + + return m_vpnProtocol.data()->lastError(); +} + +bool VpnConnection::connectToVpn(Protocol protocol) { qDebug() << "Connect to VPN"; @@ -34,19 +43,19 @@ void VpnConnection::connectToVpn(Protocol protocol) ; default: // TODO, add later - return; + return false; ; } connect(m_vpnProtocol.data(), SIGNAL(connectionStateChanged(VpnProtocol::ConnectionState)), this, SLOT(onConnectionStateChanged(VpnProtocol::ConnectionState))); connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64))); - m_vpnProtocol.data()->start(); + return m_vpnProtocol.data()->start(); } QString VpnConnection::bytesToText(quint64 bytes) { - return QString("%1 %2").arg((bytes * 8) / 1024).arg(tr("Mbps")); + return QString("%1 %2").arg(bytes / 1000000).arg(tr("Mbps")); } void VpnConnection::disconnectFromVpn() diff --git a/client/vpnconnection.h b/client/vpnconnection.h index 5f1423dc..70586c41 100644 --- a/client/vpnconnection.h +++ b/client/vpnconnection.h @@ -16,14 +16,14 @@ public: ~VpnConnection(); enum class Protocol{OpenVpn}; - void connectToVpn(Protocol protocol = Protocol::OpenVpn); + static QString bytesToText(quint64 bytes); + QString lastError() const; + bool connectToVpn(Protocol protocol = Protocol::OpenVpn); bool connected() const; bool disconnected() const; void disconnectFromVpn(); - static QString bytesToText(quint64 bytes); - signals: void bytesChanged(quint64 receivedBytes, quint64 sentBytes); void connectionStateChanged(VpnProtocol::ConnectionState state); diff --git a/client/vpnprotocol.cpp b/client/vpnprotocol.cpp index 2ac462f3..6e3a08f6 100644 --- a/client/vpnprotocol.cpp +++ b/client/vpnprotocol.cpp @@ -4,11 +4,14 @@ #include "communicator.h" #include "vpnprotocol.h" +Communicator* VpnProtocol::m_communicator = nullptr; + VpnProtocol::VpnProtocol(const QString& args, QObject* parent) : QObject(parent), m_connectionState(ConnectionState::Unknown), - m_communicator(new Communicator), - m_timeoutTimer(new QTimer(this)) + m_timeoutTimer(new QTimer(this)), + m_receivedBytes(0), + m_sentBytes(0) { m_timeoutTimer->setSingleShot(true); connect(m_timeoutTimer, &QTimer::timeout, this, &VpnProtocol::onTimeout); @@ -21,6 +24,29 @@ VpnProtocol::~VpnProtocol() } +void VpnProtocol::initializeCommunicator(QObject* parent) +{ + if (!m_communicator) { + m_communicator = new Communicator(parent); + } +} + +Communicator* VpnProtocol::communicator() +{ + return m_communicator; +} + +void VpnProtocol::setLastError(const QString& error) +{ + m_lastError = error; + qCritical().noquote() << m_lastError; +} + +QString VpnProtocol::lastError() const +{ + return m_lastError; +} + void VpnProtocol::onTimeout() { qDebug() << "Timeout"; @@ -46,7 +72,10 @@ VpnProtocol::ConnectionState VpnProtocol::connectionState() const void VpnProtocol::setBytesChanged(quint64 receivedBytes, quint64 sentBytes) { - emit bytesChanged(receivedBytes, sentBytes); + emit bytesChanged(receivedBytes - m_receivedBytes, sentBytes - m_sentBytes); + + m_receivedBytes = receivedBytes; + m_sentBytes = sentBytes; } void VpnProtocol::setConnectionState(VpnProtocol::ConnectionState state) @@ -56,6 +85,13 @@ void VpnProtocol::setConnectionState(VpnProtocol::ConnectionState state) } m_connectionState = state; + if (m_connectionState == ConnectionState::Disconnected) { + m_receivedBytes = 0; + m_sentBytes = 0; + } + + qDebug().noquote() << QString("Connection state: '%1'").arg(textConnectionState()); + emit connectionStateChanged(m_connectionState); } diff --git a/client/vpnprotocol.h b/client/vpnprotocol.h index 39adb8c9..b5f5955a 100644 --- a/client/vpnprotocol.h +++ b/client/vpnprotocol.h @@ -16,15 +16,22 @@ public: ~VpnProtocol(); enum class ConnectionState {Unknown, Disconnected, Preparing, Connecting, Connected, Disconnecting, TunnelReconnecting, Error}; - static QString textConnectionState(ConnectionState connectionState); - ConnectionState connectionState() const; - QString textConnectionState() const; + static Communicator* communicator(); + static QString textConnectionState(ConnectionState connectionState); + static void initializeCommunicator(QObject* parent = nullptr); + + virtual bool connected() const; virtual bool disconnected() const; virtual bool start() = 0; virtual void stop() = 0; + ConnectionState connectionState() const; + QString lastError() const; + QString textConnectionState() const; + void setLastError(const QString& error); + signals: void bytesChanged(quint64 receivedBytes, quint64 sentBytes); void connectionStateChanged(VpnProtocol::ConnectionState state); @@ -40,9 +47,15 @@ protected: virtual void setBytesChanged(quint64 receivedBytes, quint64 sentBytes); virtual void setConnectionState(VpnProtocol::ConnectionState state); - Communicator* m_communicator; + static Communicator* m_communicator; + ConnectionState m_connectionState; + +private: QTimer* m_timeoutTimer; + QString m_lastError; + quint64 m_receivedBytes; + quint64 m_sentBytes; }; #endif // VPNPROTOCOL_H diff --git a/service/server/localserver.cpp b/service/server/localserver.cpp index 33274ccc..3119501f 100644 --- a/service/server/localserver.cpp +++ b/service/server/localserver.cpp @@ -6,21 +6,21 @@ #include "localserver.h" #include "utils.h" -LocalServer::LocalServer(const QString& name, QObject *parent) : QObject(parent), - m_clientConnected(false), - m_clientConnection(nullptr) +LocalServer::LocalServer(QObject *parent) : QObject(parent), + m_clientConnection(nullptr), + m_clientConnected(false) { m_server = new QLocalServer(this); m_server->setSocketOptions(QLocalServer::WorldAccessOption); - if (!m_server->listen(name)) { + if (!m_server->listen(Utils::serverName())) { qDebug() << QString("Unable to start the server: %1.").arg(m_server->errorString()); return; } connect(m_server, &QLocalServer::newConnection, this, &LocalServer::onNewConnection); - qDebug() << "Local server started"; + qDebug().noquote() << QString("Local server started on '%1'").arg(m_server->serverName()); } LocalServer::~LocalServer() @@ -28,6 +28,8 @@ LocalServer::~LocalServer() m_clientConnected = false; m_server->disconnect(); + QFile::remove(Utils::serverName()); + qDebug() << "Local server stopped"; } @@ -46,7 +48,7 @@ void LocalServer::onNewConnection() connect(m_clientConnection, &QLocalSocket::disconnected, this, &LocalServer::onDisconnected); m_clientConnected = true; - qDebug() << "On new connection"; + qDebug() << "New connection"; for(;;) { qApp->processEvents(QEventLoop::ExcludeUserInputEvents); @@ -69,7 +71,7 @@ void LocalServer::onNewConnection() switch (icomingMessage.state()) { case Message::State::Initialize: - sendMessage(Message(Message::State::Initialize, QStringList({"Pong"}))); + sendMessage(Message(Message::State::Initialize, QStringList({"Server"}))); break; case Message::State::StartRequest: startProcess(icomingMessage.args()); @@ -161,7 +163,7 @@ void LocalServer::sendMessage(const Message& message) } const QString data = message.toString(); - bool status = m_clientConnection->write(QString(data + "\n").toLocal8Bit()); + bool status = m_clientConnection->write(QString(data + "\n").toUtf8()); qDebug().noquote() << QString("Send message '%1', status '%2'").arg(data).arg(Utils::toString(status)); } diff --git a/service/server/localserver.h b/service/server/localserver.h index 9a929bc0..d68d121e 100644 --- a/service/server/localserver.h +++ b/service/server/localserver.h @@ -17,7 +17,7 @@ class LocalServer : public QObject Q_OBJECT public: - explicit LocalServer(const QString& name, QObject* parent = nullptr); + explicit LocalServer(QObject* parent = nullptr); ~LocalServer(); bool isRunning() const; diff --git a/service/server/main.cpp b/service/server/main.cpp index 7cadeda9..65be5e52 100644 --- a/service/server/main.cpp +++ b/service/server/main.cpp @@ -1,33 +1,42 @@ -#include #include -#include "systemservice.h" -#include "log.h" #include "defines.h" #include "localserver.h" +#include "log.h" +#include "systemservice.h" +#include "utils.h" +int runApplication(int argc, char** argv) +{ + QCoreApplication app(argc,argv); + LocalServer localServer; + if (!localServer.isRunning()) { + return -1; + } + return app.exec(); +} int main(int argc, char **argv) { -#if !defined(Q_OS_WIN) - // QtService stores service settings in SystemScope, which normally require root privileges. - // To allow testing this example as non-root, we change the directory of the SystemScope settings file. - QSettings::setPath(QSettings::NativeFormat, QSettings::SystemScope, QDir::tempPath()); - qWarning("(Example uses dummy settings file: %s/QtSoftware.conf)", QDir::tempPath().toLatin1().constData()); -#endif + Utils::initializePath(Utils::systemLogPath()); Log::initialize(); if (argc == 2) { qInfo() << "Started as console application"; - QCoreApplication app(argc,argv); - LocalServer localServer(SERVICE_NAME); - if (!localServer.isRunning()) { - return -1; - } - return app.exec(); + return runApplication(argc, argv); } else { qInfo() << "Started as system service"; +#ifdef Q_OS_WIN SystemService systemService(argc, argv); return systemService.exec(); + +#else + //daemon(0,0); + return runApplication(argc, argv); +#endif + } + + // Never reached + return 0; } diff --git a/service/server/systemservice.cpp b/service/server/systemservice.cpp index 4afe71e4..32a7f86f 100644 --- a/service/server/systemservice.cpp +++ b/service/server/systemservice.cpp @@ -11,7 +11,7 @@ SystemService::SystemService(int argc, char **argv) void SystemService::start() { QCoreApplication* app = application(); - m_localServer = new LocalServer(SERVICE_NAME); + m_localServer = new LocalServer(); if (!m_localServer->isRunning()) { app->quit();