Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 40a5b2e3f3 | |||
| c683884868 | |||
| 65961d8d2e | |||
| 7dc1f1e225 | |||
| a47ab15aef | |||
| c74efdaa9b | |||
| 39224c7bf7 | |||
| 96aa3d409d | |||
| c63990f720 | |||
| ad643bf76e | |||
| c7ea4966fd | |||
| 8fd81be477 |
@@ -0,0 +1,274 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) Itay Grudev 2015 - 2020
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#include <QtCore/QElapsedTimer>
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QSharedMemory>
|
||||
|
||||
#include "singleapplication.h"
|
||||
#include "singleapplication_p.h"
|
||||
|
||||
/**
|
||||
* @brief Constructor. Checks and fires up LocalServer or closes the program
|
||||
* if another instance already exists
|
||||
* @param argc
|
||||
* @param argv
|
||||
* @param allowSecondary Whether to enable secondary instance support
|
||||
* @param options Optional flags to toggle specific behaviour
|
||||
* @param timeout Maximum time blocking functions are allowed during app load
|
||||
*/
|
||||
SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary, Options options, int timeout, const QString &userData )
|
||||
: app_t( argc, argv ), d_ptr( new SingleApplicationPrivate( this ) )
|
||||
{
|
||||
Q_D( SingleApplication );
|
||||
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||
// On Android and iOS since the library is not supported fallback to
|
||||
// standard QApplication behaviour by simply returning at this point.
|
||||
qWarning() << "SingleApplication is not supported on Android and iOS systems.";
|
||||
return;
|
||||
#endif
|
||||
|
||||
// Store the current mode of the program
|
||||
d->options = options;
|
||||
|
||||
// Add any unique user data
|
||||
if ( ! userData.isEmpty() )
|
||||
d->addAppData( userData );
|
||||
|
||||
// Generating an application ID used for identifying the shared memory
|
||||
// block and QLocalServer
|
||||
d->genBlockServerName();
|
||||
|
||||
// To mitigate QSharedMemory issues with large amount of processes
|
||||
// attempting to attach at the same time
|
||||
SingleApplicationPrivate::randomSleep();
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
// By explicitly attaching it and then deleting it we make sure that the
|
||||
// memory is deleted even after the process has crashed on Unix.
|
||||
d->memory = new QSharedMemory( d->blockServerName );
|
||||
d->memory->attach();
|
||||
delete d->memory;
|
||||
#endif
|
||||
// Guarantee thread safe behaviour with a shared memory block.
|
||||
d->memory = new QSharedMemory( d->blockServerName );
|
||||
|
||||
// Create a shared memory block
|
||||
if( d->memory->create( sizeof( InstancesInfo ) )){
|
||||
// Initialize the shared memory block
|
||||
if( ! d->memory->lock() ){
|
||||
qCritical() << "SingleApplication: Unable to lock memory block after create.";
|
||||
abortSafely();
|
||||
}
|
||||
d->initializeMemoryBlock();
|
||||
} else {
|
||||
if( d->memory->error() == QSharedMemory::AlreadyExists ){
|
||||
// Attempt to attach to the memory segment
|
||||
if( ! d->memory->attach() ){
|
||||
qCritical() << "SingleApplication: Unable to attach to shared memory block.";
|
||||
abortSafely();
|
||||
}
|
||||
if( ! d->memory->lock() ){
|
||||
qCritical() << "SingleApplication: Unable to lock memory block after attach.";
|
||||
abortSafely();
|
||||
}
|
||||
} else {
|
||||
qCritical() << "SingleApplication: Unable to create block.";
|
||||
abortSafely();
|
||||
}
|
||||
}
|
||||
|
||||
auto *inst = static_cast<InstancesInfo*>( d->memory->data() );
|
||||
QElapsedTimer time;
|
||||
time.start();
|
||||
|
||||
// Make sure the shared memory block is initialised and in consistent state
|
||||
while( true ){
|
||||
// If the shared memory block's checksum is valid continue
|
||||
if( d->blockChecksum() == inst->checksum ) break;
|
||||
|
||||
// If more than 5s have elapsed, assume the primary instance crashed and
|
||||
// assume it's position
|
||||
if( time.elapsed() > 5000 ){
|
||||
qWarning() << "SingleApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
|
||||
d->initializeMemoryBlock();
|
||||
}
|
||||
|
||||
// Otherwise wait for a random period and try again. The random sleep here
|
||||
// limits the probability of a collision between two racing apps and
|
||||
// allows the app to initialise faster
|
||||
if( ! d->memory->unlock() ){
|
||||
qDebug() << "SingleApplication: Unable to unlock memory for random wait.";
|
||||
qDebug() << d->memory->errorString();
|
||||
}
|
||||
SingleApplicationPrivate::randomSleep();
|
||||
if( ! d->memory->lock() ){
|
||||
qCritical() << "SingleApplication: Unable to lock memory after random wait.";
|
||||
abortSafely();
|
||||
}
|
||||
}
|
||||
|
||||
if( inst->primary == false ){
|
||||
d->startPrimary();
|
||||
if( ! d->memory->unlock() ){
|
||||
qDebug() << "SingleApplication: Unable to unlock memory after primary start.";
|
||||
qDebug() << d->memory->errorString();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if another instance can be started
|
||||
if( allowSecondary ){
|
||||
d->startSecondary();
|
||||
if( d->options & Mode::SecondaryNotification ){
|
||||
d->connectToPrimary( timeout, SingleApplicationPrivate::SecondaryInstance );
|
||||
}
|
||||
if( ! d->memory->unlock() ){
|
||||
qDebug() << "SingleApplication: Unable to unlock memory after secondary start.";
|
||||
qDebug() << d->memory->errorString();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if( ! d->memory->unlock() ){
|
||||
qDebug() << "SingleApplication: Unable to unlock memory at end of execution.";
|
||||
qDebug() << d->memory->errorString();
|
||||
}
|
||||
|
||||
d->connectToPrimary( timeout, SingleApplicationPrivate::NewInstance );
|
||||
|
||||
delete d;
|
||||
|
||||
::exit( EXIT_SUCCESS );
|
||||
}
|
||||
|
||||
SingleApplication::~SingleApplication()
|
||||
{
|
||||
Q_D( SingleApplication );
|
||||
delete d;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current application instance is primary.
|
||||
* @return Returns true if the instance is primary, false otherwise.
|
||||
*/
|
||||
bool SingleApplication::isPrimary() const
|
||||
{
|
||||
Q_D( const SingleApplication );
|
||||
return d->server != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current application instance is secondary.
|
||||
* @return Returns true if the instance is secondary, false otherwise.
|
||||
*/
|
||||
bool SingleApplication::isSecondary() const
|
||||
{
|
||||
Q_D( const SingleApplication );
|
||||
return d->server == nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows you to identify an instance by returning unique consecutive instance
|
||||
* ids. It is reset when the first (primary) instance of your app starts and
|
||||
* only incremented afterwards.
|
||||
* @return Returns a unique instance id.
|
||||
*/
|
||||
quint32 SingleApplication::instanceId() const
|
||||
{
|
||||
Q_D( const SingleApplication );
|
||||
return d->instanceNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the OS PID (Process Identifier) of the process running the primary
|
||||
* instance. Especially useful when SingleApplication is coupled with OS.
|
||||
* specific APIs.
|
||||
* @return Returns the primary instance PID.
|
||||
*/
|
||||
qint64 SingleApplication::primaryPid() const
|
||||
{
|
||||
Q_D( const SingleApplication );
|
||||
return d->primaryPid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the username the primary instance is running as.
|
||||
* @return Returns the username the primary instance is running as.
|
||||
*/
|
||||
QString SingleApplication::primaryUser() const
|
||||
{
|
||||
Q_D( const SingleApplication );
|
||||
return d->primaryUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the username the current instance is running as.
|
||||
* @return Returns the username the current instance is running as.
|
||||
*/
|
||||
QString SingleApplication::currentUser() const
|
||||
{
|
||||
return SingleApplicationPrivate::getUsername();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends message to the Primary Instance.
|
||||
* @param message The message to send.
|
||||
* @param timeout the maximum timeout in milliseconds for blocking functions.
|
||||
* @return true if the message was sent successfuly, false otherwise.
|
||||
*/
|
||||
bool SingleApplication::sendMessage( const QByteArray &message, int timeout )
|
||||
{
|
||||
Q_D( SingleApplication );
|
||||
|
||||
// Nobody to connect to
|
||||
if( isPrimary() ) return false;
|
||||
|
||||
// Make sure the socket is connected
|
||||
if( ! d->connectToPrimary( timeout, SingleApplicationPrivate::Reconnect ) )
|
||||
return false;
|
||||
|
||||
d->socket->write( message );
|
||||
bool dataWritten = d->socket->waitForBytesWritten( timeout );
|
||||
d->socket->flush();
|
||||
return dataWritten;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the shared memory block and exits with a failure.
|
||||
* This function halts program execution.
|
||||
*/
|
||||
void SingleApplication::abortSafely()
|
||||
{
|
||||
Q_D( SingleApplication );
|
||||
|
||||
qCritical() << "SingleApplication: " << d->memory->error() << d->memory->errorString();
|
||||
delete d;
|
||||
::exit( EXIT_FAILURE );
|
||||
}
|
||||
|
||||
QStringList SingleApplication::userData() const
|
||||
{
|
||||
Q_D( const SingleApplication );
|
||||
return d->appData();
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) Itay Grudev 2015 - 2018
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#ifndef SINGLE_APPLICATION_H
|
||||
#define SINGLE_APPLICATION_H
|
||||
|
||||
#include <QtCore/QtGlobal>
|
||||
#include <QtNetwork/QLocalSocket>
|
||||
|
||||
#ifndef QAPPLICATION_CLASS
|
||||
#define QAPPLICATION_CLASS QApplication
|
||||
#endif
|
||||
|
||||
#include QT_STRINGIFY(QAPPLICATION_CLASS)
|
||||
|
||||
class SingleApplicationPrivate;
|
||||
|
||||
/**
|
||||
* @brief The SingleApplication class handles multiple instances of the same
|
||||
* Application
|
||||
* @see QCoreApplication
|
||||
*/
|
||||
class SingleApplication : public QAPPLICATION_CLASS
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
using app_t = QAPPLICATION_CLASS;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Mode of operation of SingleApplication.
|
||||
* Whether the block should be user-wide or system-wide and whether the
|
||||
* primary instance should be notified when a secondary instance had been
|
||||
* started.
|
||||
* @note Operating system can restrict the shared memory blocks to the same
|
||||
* user, in which case the User/System modes will have no effect and the
|
||||
* block will be user wide.
|
||||
* @enum
|
||||
*/
|
||||
enum Mode {
|
||||
User = 1 << 0,
|
||||
System = 1 << 1,
|
||||
SecondaryNotification = 1 << 2,
|
||||
ExcludeAppVersion = 1 << 3,
|
||||
ExcludeAppPath = 1 << 4
|
||||
};
|
||||
Q_DECLARE_FLAGS(Options, Mode)
|
||||
|
||||
/**
|
||||
* @brief Intitializes a SingleApplication instance with argc command line
|
||||
* arguments in argv
|
||||
* @arg {int &} argc - Number of arguments in argv
|
||||
* @arg {const char *[]} argv - Supplied command line arguments
|
||||
* @arg {bool} allowSecondary - Whether to start the instance as secondary
|
||||
* if there is already a primary instance.
|
||||
* @arg {Mode} mode - Whether for the SingleApplication block to be applied
|
||||
* User wide or System wide.
|
||||
* @arg {int} timeout - Timeout to wait in milliseconds.
|
||||
* @note argc and argv may be changed as Qt removes arguments that it
|
||||
* recognizes
|
||||
* @note Mode::SecondaryNotification only works if set on both the primary
|
||||
* instance and the secondary instance.
|
||||
* @note The timeout is just a hint for the maximum time of blocking
|
||||
* operations. It does not guarantee that the SingleApplication
|
||||
* initialisation will be completed in given time, though is a good hint.
|
||||
* Usually 4*timeout would be the worst case (fail) scenario.
|
||||
* @see See the corresponding QAPPLICATION_CLASS constructor for reference
|
||||
*/
|
||||
explicit SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000, const QString &userData = {} );
|
||||
~SingleApplication() override;
|
||||
|
||||
/**
|
||||
* @brief Returns if the instance is the primary instance
|
||||
* @returns {bool}
|
||||
*/
|
||||
bool isPrimary() const;
|
||||
|
||||
/**
|
||||
* @brief Returns if the instance is a secondary instance
|
||||
* @returns {bool}
|
||||
*/
|
||||
bool isSecondary() const;
|
||||
|
||||
/**
|
||||
* @brief Returns a unique identifier for the current instance
|
||||
* @returns {qint32}
|
||||
*/
|
||||
quint32 instanceId() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the process ID (PID) of the primary instance
|
||||
* @returns {qint64}
|
||||
*/
|
||||
qint64 primaryPid() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the username of the user running the primary instance
|
||||
* @returns {QString}
|
||||
*/
|
||||
QString primaryUser() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the username of the current user
|
||||
* @returns {QString}
|
||||
*/
|
||||
QString currentUser() const;
|
||||
|
||||
/**
|
||||
* @brief Sends a message to the primary instance. Returns true on success.
|
||||
* @param {int} timeout - Timeout for connecting
|
||||
* @returns {bool}
|
||||
* @note sendMessage() will return false if invoked from the primary
|
||||
* instance.
|
||||
*/
|
||||
bool sendMessage( const QByteArray &message, int timeout = 100 );
|
||||
|
||||
/**
|
||||
* @brief Get the set user data.
|
||||
* @returns {QStringList}
|
||||
*/
|
||||
QStringList userData() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void instanceStarted();
|
||||
void receivedMessage( quint32 instanceId, QByteArray message );
|
||||
|
||||
private:
|
||||
SingleApplicationPrivate *d_ptr;
|
||||
Q_DECLARE_PRIVATE(SingleApplication)
|
||||
void abortSafely();
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(SingleApplication::Options)
|
||||
|
||||
#endif // SINGLE_APPLICATION_H
|
||||
@@ -0,0 +1,15 @@
|
||||
QT += core network
|
||||
CONFIG += c++11
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/singleapplication.h \
|
||||
$$PWD/singleapplication_p.h
|
||||
SOURCES += $$PWD/singleapplication.cpp \
|
||||
$$PWD/singleapplication_p.cpp
|
||||
|
||||
INCLUDEPATH += $$PWD
|
||||
|
||||
win32 {
|
||||
msvc:LIBS += Advapi32.lib
|
||||
gcc:LIBS += -ladvapi32
|
||||
}
|
||||
@@ -0,0 +1,486 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) Itay Grudev 2015 - 2020
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
//
|
||||
// W A R N I N G !!!
|
||||
// -----------------
|
||||
//
|
||||
// This file is not part of the SingleApplication API. It is used purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or may even be removed.
|
||||
//
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QDataStream>
|
||||
#include <QtCore/QElapsedTimer>
|
||||
#include <QtCore/QCryptographicHash>
|
||||
#include <QtNetwork/QLocalServer>
|
||||
#include <QtNetwork/QLocalSocket>
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
#include <QtCore/QRandomGenerator>
|
||||
#else
|
||||
#include <QtCore/QDateTime>
|
||||
#endif
|
||||
|
||||
#include "singleapplication.h"
|
||||
#include "singleapplication_p.h"
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <pwd.h>
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX 1
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <lmcons.h>
|
||||
#endif
|
||||
|
||||
SingleApplicationPrivate::SingleApplicationPrivate( SingleApplication *q_ptr )
|
||||
: q_ptr( q_ptr )
|
||||
{
|
||||
server = nullptr;
|
||||
socket = nullptr;
|
||||
memory = nullptr;
|
||||
instanceNumber = 0;
|
||||
}
|
||||
|
||||
SingleApplicationPrivate::~SingleApplicationPrivate()
|
||||
{
|
||||
if( socket != nullptr ){
|
||||
socket->close();
|
||||
delete socket;
|
||||
}
|
||||
|
||||
if( memory != nullptr ){
|
||||
memory->lock();
|
||||
auto *inst = static_cast<InstancesInfo*>(memory->data());
|
||||
if( server != nullptr ){
|
||||
server->close();
|
||||
delete server;
|
||||
inst->primary = false;
|
||||
inst->primaryPid = -1;
|
||||
inst->primaryUser[0] = '\0';
|
||||
inst->checksum = blockChecksum();
|
||||
}
|
||||
memory->unlock();
|
||||
|
||||
delete memory;
|
||||
}
|
||||
}
|
||||
|
||||
QString SingleApplicationPrivate::getUsername()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
wchar_t username[UNLEN + 1];
|
||||
// Specifies size of the buffer on input
|
||||
DWORD usernameLength = UNLEN + 1;
|
||||
if( GetUserNameW( username, &usernameLength ) )
|
||||
return QString::fromWCharArray( username );
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
|
||||
return QString::fromLocal8Bit( qgetenv( "USERNAME" ) );
|
||||
#else
|
||||
return qEnvironmentVariable( "USERNAME" );
|
||||
#endif
|
||||
#endif
|
||||
#ifdef Q_OS_UNIX
|
||||
QString username;
|
||||
uid_t uid = geteuid();
|
||||
struct passwd *pw = getpwuid( uid );
|
||||
if( pw )
|
||||
username = QString::fromLocal8Bit( pw->pw_name );
|
||||
if ( username.isEmpty() ){
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
|
||||
username = QString::fromLocal8Bit( qgetenv( "USER" ) );
|
||||
#else
|
||||
username = qEnvironmentVariable( "USER" );
|
||||
#endif
|
||||
}
|
||||
return username;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::genBlockServerName()
|
||||
{
|
||||
QCryptographicHash appData( QCryptographicHash::Sha256 );
|
||||
appData.addData( "SingleApplication", 17 );
|
||||
appData.addData( SingleApplication::app_t::applicationName().toUtf8() );
|
||||
appData.addData( SingleApplication::app_t::organizationName().toUtf8() );
|
||||
appData.addData( SingleApplication::app_t::organizationDomain().toUtf8() );
|
||||
|
||||
if ( ! appDataList.isEmpty() )
|
||||
appData.addData( appDataList.join( "" ).toUtf8() );
|
||||
|
||||
if( ! (options & SingleApplication::Mode::ExcludeAppVersion) ){
|
||||
appData.addData( SingleApplication::app_t::applicationVersion().toUtf8() );
|
||||
}
|
||||
|
||||
if( ! (options & SingleApplication::Mode::ExcludeAppPath) ){
|
||||
#ifdef Q_OS_WIN
|
||||
appData.addData( SingleApplication::app_t::applicationFilePath().toLower().toUtf8() );
|
||||
#else
|
||||
appData.addData( SingleApplication::app_t::applicationFilePath().toUtf8() );
|
||||
#endif
|
||||
}
|
||||
|
||||
// User level block requires a user specific data in the hash
|
||||
if( options & SingleApplication::Mode::User ){
|
||||
appData.addData( getUsername().toUtf8() );
|
||||
}
|
||||
|
||||
// Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with
|
||||
// server naming requirements.
|
||||
blockServerName = appData.result().toBase64().replace("/", "_");
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::initializeMemoryBlock() const
|
||||
{
|
||||
auto *inst = static_cast<InstancesInfo*>( memory->data() );
|
||||
inst->primary = false;
|
||||
inst->secondary = 0;
|
||||
inst->primaryPid = -1;
|
||||
inst->primaryUser[0] = '\0';
|
||||
inst->checksum = blockChecksum();
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::startPrimary()
|
||||
{
|
||||
// Reset the number of connections
|
||||
auto *inst = static_cast <InstancesInfo*>( memory->data() );
|
||||
|
||||
inst->primary = true;
|
||||
inst->primaryPid = QCoreApplication::applicationPid();
|
||||
qstrncpy( inst->primaryUser, getUsername().toUtf8().data(), sizeof(inst->primaryUser) );
|
||||
inst->checksum = blockChecksum();
|
||||
instanceNumber = 0;
|
||||
// Successful creation means that no main process exists
|
||||
// So we start a QLocalServer to listen for connections
|
||||
QLocalServer::removeServer( blockServerName );
|
||||
server = new QLocalServer();
|
||||
|
||||
// Restrict access to the socket according to the
|
||||
// SingleApplication::Mode::User flag on User level or no restrictions
|
||||
if( options & SingleApplication::Mode::User ){
|
||||
server->setSocketOptions( QLocalServer::UserAccessOption );
|
||||
} else {
|
||||
server->setSocketOptions( QLocalServer::WorldAccessOption );
|
||||
}
|
||||
|
||||
server->listen( blockServerName );
|
||||
QObject::connect(
|
||||
server,
|
||||
&QLocalServer::newConnection,
|
||||
this,
|
||||
&SingleApplicationPrivate::slotConnectionEstablished
|
||||
);
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::startSecondary()
|
||||
{
|
||||
auto *inst = static_cast <InstancesInfo*>( memory->data() );
|
||||
|
||||
inst->secondary += 1;
|
||||
inst->checksum = blockChecksum();
|
||||
instanceNumber = inst->secondary;
|
||||
}
|
||||
|
||||
bool SingleApplicationPrivate::connectToPrimary( int msecs, ConnectionType connectionType )
|
||||
{
|
||||
QElapsedTimer time;
|
||||
time.start();
|
||||
|
||||
// Connect to the Local Server of the Primary Instance if not already
|
||||
// connected.
|
||||
if( socket == nullptr ){
|
||||
socket = new QLocalSocket();
|
||||
}
|
||||
|
||||
if( socket->state() == QLocalSocket::ConnectedState ) return true;
|
||||
|
||||
if( socket->state() != QLocalSocket::ConnectedState ){
|
||||
|
||||
while( true ){
|
||||
randomSleep();
|
||||
|
||||
if( socket->state() != QLocalSocket::ConnectingState )
|
||||
socket->connectToServer( blockServerName );
|
||||
|
||||
if( socket->state() == QLocalSocket::ConnectingState ){
|
||||
socket->waitForConnected( static_cast<int>(msecs - time.elapsed()) );
|
||||
}
|
||||
|
||||
// If connected break out of the loop
|
||||
if( socket->state() == QLocalSocket::ConnectedState ) break;
|
||||
|
||||
// If elapsed time since start is longer than the method timeout return
|
||||
if( time.elapsed() >= msecs ) return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialisation message according to the SingleApplication protocol
|
||||
QByteArray initMsg;
|
||||
QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
||||
writeStream.setVersion(QDataStream::Qt_5_6);
|
||||
#endif
|
||||
|
||||
writeStream << blockServerName.toLatin1();
|
||||
writeStream << static_cast<quint8>(connectionType);
|
||||
writeStream << instanceNumber;
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
quint16 checksum = qChecksum(QByteArray(initMsg, static_cast<quint32>(initMsg.length())));
|
||||
#else
|
||||
quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
|
||||
#endif
|
||||
writeStream << checksum;
|
||||
|
||||
// The header indicates the message length that follows
|
||||
QByteArray header;
|
||||
QDataStream headerStream(&header, QIODevice::WriteOnly);
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
||||
headerStream.setVersion(QDataStream::Qt_5_6);
|
||||
#endif
|
||||
headerStream << static_cast <quint64>( initMsg.length() );
|
||||
|
||||
socket->write( header );
|
||||
socket->write( initMsg );
|
||||
bool result = socket->waitForBytesWritten( static_cast<int>(msecs - time.elapsed()) );
|
||||
socket->flush();
|
||||
return result;
|
||||
}
|
||||
|
||||
quint16 SingleApplicationPrivate::blockChecksum() const
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
quint16 checksum = qChecksum(QByteArray(static_cast<const char*>(memory->constData()), offsetof(InstancesInfo, checksum)));
|
||||
#else
|
||||
quint16 checksum = qChecksum(static_cast<const char*>(memory->constData()), offsetof(InstancesInfo, checksum));
|
||||
#endif
|
||||
return checksum;
|
||||
}
|
||||
|
||||
qint64 SingleApplicationPrivate::primaryPid() const
|
||||
{
|
||||
qint64 pid;
|
||||
|
||||
memory->lock();
|
||||
auto *inst = static_cast<InstancesInfo*>( memory->data() );
|
||||
pid = inst->primaryPid;
|
||||
memory->unlock();
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
QString SingleApplicationPrivate::primaryUser() const
|
||||
{
|
||||
QByteArray username;
|
||||
|
||||
memory->lock();
|
||||
auto *inst = static_cast<InstancesInfo*>( memory->data() );
|
||||
username = inst->primaryUser;
|
||||
memory->unlock();
|
||||
|
||||
return QString::fromUtf8( username );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Executed when a connection has been made to the LocalServer
|
||||
*/
|
||||
void SingleApplicationPrivate::slotConnectionEstablished()
|
||||
{
|
||||
QLocalSocket *nextConnSocket = server->nextPendingConnection();
|
||||
connectionMap.insert(nextConnSocket, ConnectionInfo());
|
||||
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose,
|
||||
[nextConnSocket, this](){
|
||||
auto &info = connectionMap[nextConnSocket];
|
||||
Q_EMIT this->slotClientConnectionClosed( nextConnSocket, info.instanceId );
|
||||
}
|
||||
);
|
||||
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::disconnected, nextConnSocket, &QLocalSocket::deleteLater);
|
||||
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::destroyed,
|
||||
[nextConnSocket, this](){
|
||||
connectionMap.remove(nextConnSocket);
|
||||
}
|
||||
);
|
||||
|
||||
QObject::connect(nextConnSocket, &QLocalSocket::readyRead,
|
||||
[nextConnSocket, this](){
|
||||
auto &info = connectionMap[nextConnSocket];
|
||||
switch(info.stage){
|
||||
case StageHeader:
|
||||
readInitMessageHeader(nextConnSocket);
|
||||
break;
|
||||
case StageBody:
|
||||
readInitMessageBody(nextConnSocket);
|
||||
break;
|
||||
case StageConnected:
|
||||
Q_EMIT this->slotDataAvailable( nextConnSocket, info.instanceId );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::readInitMessageHeader( QLocalSocket *sock )
|
||||
{
|
||||
if (!connectionMap.contains( sock )){
|
||||
return;
|
||||
}
|
||||
|
||||
if( sock->bytesAvailable() < ( qint64 )sizeof( quint64 ) ){
|
||||
return;
|
||||
}
|
||||
|
||||
QDataStream headerStream( sock );
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
||||
headerStream.setVersion( QDataStream::Qt_5_6 );
|
||||
#endif
|
||||
|
||||
// Read the header to know the message length
|
||||
quint64 msgLen = 0;
|
||||
headerStream >> msgLen;
|
||||
ConnectionInfo &info = connectionMap[sock];
|
||||
info.stage = StageBody;
|
||||
info.msgLen = msgLen;
|
||||
|
||||
if ( sock->bytesAvailable() >= (qint64) msgLen ){
|
||||
readInitMessageBody( sock );
|
||||
}
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
|
||||
{
|
||||
Q_Q(SingleApplication);
|
||||
|
||||
if (!connectionMap.contains( sock )){
|
||||
return;
|
||||
}
|
||||
|
||||
ConnectionInfo &info = connectionMap[sock];
|
||||
if( sock->bytesAvailable() < ( qint64 )info.msgLen ){
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the message body
|
||||
QByteArray msgBytes = sock->read(info.msgLen);
|
||||
QDataStream readStream(msgBytes);
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
||||
readStream.setVersion( QDataStream::Qt_5_6 );
|
||||
#endif
|
||||
|
||||
// server name
|
||||
QByteArray latin1Name;
|
||||
readStream >> latin1Name;
|
||||
|
||||
// connection type
|
||||
ConnectionType connectionType = InvalidConnection;
|
||||
quint8 connTypeVal = InvalidConnection;
|
||||
readStream >> connTypeVal;
|
||||
connectionType = static_cast <ConnectionType>( connTypeVal );
|
||||
|
||||
// instance id
|
||||
quint32 instanceId = 0;
|
||||
readStream >> instanceId;
|
||||
|
||||
// checksum
|
||||
quint16 msgChecksum = 0;
|
||||
readStream >> msgChecksum;
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
const quint16 actualChecksum = qChecksum(QByteArray(msgBytes, static_cast<quint32>(msgBytes.length() - sizeof(quint16))));
|
||||
#else
|
||||
const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast<quint32>(msgBytes.length() - sizeof(quint16)));
|
||||
#endif
|
||||
|
||||
bool isValid = readStream.status() == QDataStream::Ok &&
|
||||
QLatin1String(latin1Name) == blockServerName &&
|
||||
msgChecksum == actualChecksum;
|
||||
|
||||
if( !isValid ){
|
||||
sock->close();
|
||||
return;
|
||||
}
|
||||
|
||||
info.instanceId = instanceId;
|
||||
info.stage = StageConnected;
|
||||
|
||||
if( connectionType == NewInstance ||
|
||||
( connectionType == SecondaryInstance &&
|
||||
options & SingleApplication::Mode::SecondaryNotification ) )
|
||||
{
|
||||
Q_EMIT q->instanceStarted();
|
||||
}
|
||||
|
||||
if (sock->bytesAvailable() > 0){
|
||||
Q_EMIT this->slotDataAvailable( sock, instanceId );
|
||||
}
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::slotDataAvailable( QLocalSocket *dataSocket, quint32 instanceId )
|
||||
{
|
||||
Q_Q(SingleApplication);
|
||||
Q_EMIT q->receivedMessage( instanceId, dataSocket->readAll() );
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId )
|
||||
{
|
||||
if( closedSocket->bytesAvailable() > 0 )
|
||||
Q_EMIT slotDataAvailable( closedSocket, instanceId );
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::randomSleep()
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK( 5, 10, 0 )
|
||||
QThread::msleep( QRandomGenerator::global()->bounded( 8u, 18u ));
|
||||
#else
|
||||
qsrand( QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max() );
|
||||
QThread::msleep( 8 + static_cast <unsigned long>( static_cast <float>( qrand() ) / RAND_MAX * 10 ));
|
||||
#endif
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::addAppData(const QString &data)
|
||||
{
|
||||
appDataList.push_back(data);
|
||||
}
|
||||
|
||||
QStringList SingleApplicationPrivate::appData() const
|
||||
{
|
||||
return appDataList;
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) Itay Grudev 2015 - 2020
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
//
|
||||
// W A R N I N G !!!
|
||||
// -----------------
|
||||
//
|
||||
// This file is not part of the SingleApplication API. It is used purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or may even be removed.
|
||||
//
|
||||
|
||||
#ifndef SINGLEAPPLICATION_P_H
|
||||
#define SINGLEAPPLICATION_P_H
|
||||
|
||||
#include <QtCore/QSharedMemory>
|
||||
#include <QtNetwork/QLocalServer>
|
||||
#include <QtNetwork/QLocalSocket>
|
||||
#include "singleapplication.h"
|
||||
|
||||
struct InstancesInfo {
|
||||
bool primary;
|
||||
quint32 secondary;
|
||||
qint64 primaryPid;
|
||||
char primaryUser[128];
|
||||
quint16 checksum; // Must be the last field
|
||||
};
|
||||
|
||||
struct ConnectionInfo {
|
||||
qint64 msgLen = 0;
|
||||
quint32 instanceId = 0;
|
||||
quint8 stage = 0;
|
||||
};
|
||||
|
||||
class SingleApplicationPrivate : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum ConnectionType : quint8 {
|
||||
InvalidConnection = 0,
|
||||
NewInstance = 1,
|
||||
SecondaryInstance = 2,
|
||||
Reconnect = 3
|
||||
};
|
||||
enum ConnectionStage : quint8 {
|
||||
StageHeader = 0,
|
||||
StageBody = 1,
|
||||
StageConnected = 2,
|
||||
};
|
||||
Q_DECLARE_PUBLIC(SingleApplication)
|
||||
|
||||
SingleApplicationPrivate( SingleApplication *q_ptr );
|
||||
~SingleApplicationPrivate() override;
|
||||
|
||||
static QString getUsername();
|
||||
void genBlockServerName();
|
||||
void initializeMemoryBlock() const;
|
||||
void startPrimary();
|
||||
void startSecondary();
|
||||
bool connectToPrimary( int msecs, ConnectionType connectionType );
|
||||
quint16 blockChecksum() const;
|
||||
qint64 primaryPid() const;
|
||||
QString primaryUser() const;
|
||||
void readInitMessageHeader(QLocalSocket *socket);
|
||||
void readInitMessageBody(QLocalSocket *socket);
|
||||
static void randomSleep();
|
||||
void addAppData(const QString &data);
|
||||
QStringList appData() const;
|
||||
|
||||
SingleApplication *q_ptr;
|
||||
QSharedMemory *memory;
|
||||
QLocalSocket *socket;
|
||||
QLocalServer *server;
|
||||
quint32 instanceNumber;
|
||||
QString blockServerName;
|
||||
SingleApplication::Options options;
|
||||
QMap<QLocalSocket*, ConnectionInfo> connectionMap;
|
||||
QStringList appDataList;
|
||||
|
||||
public Q_SLOTS:
|
||||
void slotConnectionEstablished();
|
||||
void slotDataAvailable( QLocalSocket*, quint32 );
|
||||
void slotClientConnectionClosed( QLocalSocket*, quint32 );
|
||||
};
|
||||
|
||||
#endif // SINGLEAPPLICATION_P_H
|
||||
@@ -8,6 +8,7 @@ DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
include("3rd/QtSsh/src/ssh/ssh.pri")
|
||||
include("3rd/QtSsh/src/botan/botan.pri")
|
||||
include("3rd/SingleApplication/singleapplication.pri")
|
||||
|
||||
HEADERS += \
|
||||
../ipc/ipc.h \
|
||||
@@ -20,10 +21,10 @@ HEADERS += \
|
||||
defines.h \
|
||||
managementserver.h \
|
||||
protocols/shadowsocksvpnprotocol.h \
|
||||
runguard.h \
|
||||
settings.h \
|
||||
ui/Controls/SlidingStackedWidget.h \
|
||||
ui/mainwindow.h \
|
||||
ui/qautostart.h \
|
||||
utils.h \
|
||||
vpnconnection.h \
|
||||
protocols/vpnprotocol.h \
|
||||
@@ -37,10 +38,10 @@ SOURCES += \
|
||||
main.cpp \
|
||||
managementserver.cpp \
|
||||
protocols/shadowsocksvpnprotocol.cpp \
|
||||
runguard.cpp \
|
||||
settings.cpp \
|
||||
ui/Controls/SlidingStackedWidget.cpp \
|
||||
ui/mainwindow.cpp \
|
||||
ui/qautostart.cpp \
|
||||
utils.cpp \
|
||||
vpnconnection.cpp \
|
||||
protocols/vpnprotocol.cpp \
|
||||
|
||||
@@ -56,6 +56,7 @@ enum ErrorCode
|
||||
// Distro errors
|
||||
OpenVpnExecutableMissing,
|
||||
EasyRsaExecutableMissing,
|
||||
ShadowSocksExecutableMissing,
|
||||
AmneziaServiceConnectionFailed,
|
||||
|
||||
// VPN errors
|
||||
|
||||
@@ -237,6 +237,9 @@ QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentia
|
||||
config.replace("$PRIV_KEY", connData.privKey);
|
||||
config.replace("$TA_KEY", connData.taKey);
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
config.replace("block-outside-dns", "");
|
||||
#endif
|
||||
//qDebug().noquote() << config;
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ QString ServerController::getContainerName(DockerContainer container)
|
||||
|
||||
ErrorCode ServerController::runScript(DockerContainer container,
|
||||
const SshConnectionParameters &sshParams, QString script,
|
||||
const std::function<void(const QString &)> &cbReadStdOut,
|
||||
const std::function<void(const QString &)> &cbReadStdErr)
|
||||
const std::function<void(const QString &, QSharedPointer<SshRemoteProcess>)> &cbReadStdOut,
|
||||
const std::function<void(const QString &, QSharedPointer<SshRemoteProcess>)> &cbReadStdErr)
|
||||
{
|
||||
QLoggingCategory::setFilterRules(QStringLiteral("qtc.ssh=false"));
|
||||
|
||||
@@ -74,7 +74,7 @@ ErrorCode ServerController::runScript(DockerContainer container,
|
||||
if (s != "." && !s.isEmpty()) {
|
||||
qDebug().noquote() << "stdout" << s;
|
||||
}
|
||||
if (cbReadStdOut) cbReadStdOut(s);
|
||||
if (cbReadStdOut) cbReadStdOut(s, proc);
|
||||
});
|
||||
|
||||
QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardError, &wait, [proc, cbReadStdErr](){
|
||||
@@ -82,7 +82,7 @@ ErrorCode ServerController::runScript(DockerContainer container,
|
||||
if (s != "." && !s.isEmpty()) {
|
||||
qDebug().noquote() << "stderr" << s;
|
||||
}
|
||||
if (cbReadStdErr) cbReadStdErr(s);
|
||||
if (cbReadStdErr) cbReadStdErr(s, proc);
|
||||
});
|
||||
|
||||
proc->start();
|
||||
@@ -124,9 +124,9 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
||||
QEventLoop wait;
|
||||
int exitStatus = -1;
|
||||
|
||||
// QObject::connect(proc.data(), &SshRemoteProcess::started, &wait, [](){
|
||||
// qDebug() << "Command started";
|
||||
// });
|
||||
// QObject::connect(proc.data(), &SshRemoteProcess::started, &wait, [](){
|
||||
// qDebug() << "uploadTextFileToContainer started";
|
||||
// });
|
||||
|
||||
QObject::connect(proc.data(), &SshRemoteProcess::closed, &wait, [&](int status){
|
||||
//qDebug() << "Remote process exited with status" << status;
|
||||
@@ -143,7 +143,6 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
||||
});
|
||||
|
||||
proc->start();
|
||||
//wait.exec();
|
||||
|
||||
if (exitStatus < 0) {
|
||||
wait.exec();
|
||||
@@ -309,7 +308,8 @@ ErrorCode ServerController::removeServer(const ServerCredentials &credentials, P
|
||||
ErrorCode ServerController::setupServer(const ServerCredentials &credentials, Protocol proto)
|
||||
{
|
||||
if (proto == Protocol::OpenVpn) {
|
||||
return setupOpenVpnServer(credentials);
|
||||
return ErrorCode::NoError;
|
||||
//return setupOpenVpnServer(credentials);
|
||||
}
|
||||
else if (proto == Protocol::ShadowSocks) {
|
||||
return setupShadowSocksServer(credentials);
|
||||
@@ -318,7 +318,7 @@ ErrorCode ServerController::setupServer(const ServerCredentials &credentials, Pr
|
||||
//return ErrorCode::NotImplementedError;
|
||||
|
||||
// TODO: run concurently
|
||||
setupOpenVpnServer(credentials);
|
||||
//setupOpenVpnServer(credentials);
|
||||
setupShadowSocksServer(credentials);
|
||||
}
|
||||
|
||||
@@ -336,10 +336,14 @@ ErrorCode ServerController::setupOpenVpnServer(const ServerCredentials &credenti
|
||||
if (scriptData.isEmpty()) return ErrorCode::InternalError;
|
||||
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data) {
|
||||
auto cbReadStdOut = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
|
||||
stdOut += data + "\n";
|
||||
|
||||
if (data.contains("Automatically restart Docker daemon?")) {
|
||||
proc->write("yes\n");
|
||||
}
|
||||
};
|
||||
auto cbReadStdErr = [&](const QString &data) {
|
||||
auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
|
||||
stdOut += data + "\n";
|
||||
};
|
||||
|
||||
@@ -364,7 +368,19 @@ ErrorCode ServerController::setupShadowSocksServer(const ServerCredentials &cred
|
||||
scriptData = file.readAll();
|
||||
if (scriptData.isEmpty()) return ErrorCode::InternalError;
|
||||
|
||||
ErrorCode e = runScript(DockerContainer::ShadowSocks, sshParams(credentials), scriptData);
|
||||
QString stdOut;
|
||||
auto cbReadStdOut = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
|
||||
stdOut += data + "\n";
|
||||
|
||||
if (data.contains("Automatically restart Docker daemon?")) {
|
||||
proc->write("yes\n");
|
||||
}
|
||||
};
|
||||
auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
|
||||
stdOut += data + "\n";
|
||||
};
|
||||
|
||||
ErrorCode e = runScript(DockerContainer::ShadowSocks, sshParams(credentials), scriptData, cbReadStdOut, cbReadStdErr);
|
||||
if (e) return e;
|
||||
|
||||
// Create ss config
|
||||
@@ -385,7 +401,7 @@ ErrorCode ServerController::setupShadowSocksServer(const ServerCredentials &cred
|
||||
uploadTextFileToContainer(DockerContainer::ShadowSocks, credentials, configData, sSConfigPath);
|
||||
|
||||
// Start ss
|
||||
QString script = QString("docker exec -i %1 sh -c \"ss-server -c %2 &\"").
|
||||
QString script = QString("docker exec -d %1 sh -c \"ss-server -c %2\"").
|
||||
arg(getContainerName(DockerContainer::ShadowSocks)).arg(sSConfigPath);
|
||||
|
||||
e = runScript(DockerContainer::ShadowSocks, sshParams(credentials), script);
|
||||
|
||||
@@ -49,8 +49,8 @@ private:
|
||||
static QSsh::SshConnection *connectToHost(const QSsh::SshConnectionParameters &sshParams);
|
||||
static ErrorCode runScript(DockerContainer container,
|
||||
const QSsh::SshConnectionParameters &sshParams, QString script,
|
||||
const std::function<void(const QString &)> &cbReadStdOut = nullptr,
|
||||
const std::function<void(const QString &)> &cbReadStdErr = nullptr);
|
||||
const std::function<void(const QString &, QSharedPointer<QSsh::SshRemoteProcess>)> &cbReadStdOut = nullptr,
|
||||
const std::function<void(const QString &, QSharedPointer<QSsh::SshRemoteProcess>)> &cbReadStdErr = nullptr);
|
||||
|
||||
static ErrorCode setupOpenVpnServer(const ServerCredentials &credentials);
|
||||
static ErrorCode setupShadowSocksServer(const ServerCredentials &credentials);
|
||||
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 342 B After Width: | Height: | Size: 344 B |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 690 B |
@@ -6,10 +6,14 @@
|
||||
|
||||
#include "debug.h"
|
||||
#include "defines.h"
|
||||
#include "runguard.h"
|
||||
#include "singleapplication.h"
|
||||
|
||||
#include "ui/mainwindow.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include "Windows.h"
|
||||
#endif
|
||||
|
||||
static void loadTranslator()
|
||||
{
|
||||
QTranslator* translator = new QTranslator;
|
||||
@@ -21,17 +25,19 @@ static void loadTranslator()
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
|
||||
RunGuard::instance(APPLICATION_NAME).activate();
|
||||
|
||||
QApplication app(argc, argv);
|
||||
#ifdef Q_OS_WIN
|
||||
AllowSetForegroundWindow(ASFW_ANY);
|
||||
#endif
|
||||
|
||||
SingleApplication app(argc, argv);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
AllowSetForegroundWindow(0);
|
||||
#endif
|
||||
|
||||
loadTranslator();
|
||||
|
||||
if (!RunGuard::instance().tryToRun()) {
|
||||
qDebug() << "Tried to run second instance. Exiting...";
|
||||
QMessageBox::information(NULL, QObject::tr("Notification"), QObject::tr("AmneziaVPN is already running."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
QFontDatabase::addApplicationFont(":/fonts/Lato-Black.ttf");
|
||||
QFontDatabase::addApplicationFont(":/fonts/Lato-BlackItalic.ttf");
|
||||
QFontDatabase::addApplicationFont(":/fonts/Lato-Bold.ttf");
|
||||
@@ -65,5 +71,12 @@ int main(int argc, char *argv[])
|
||||
MainWindow mainWindow;
|
||||
mainWindow.show();
|
||||
|
||||
if (app.isPrimary()) {
|
||||
QObject::connect(&app, &SingleApplication::instanceStarted, &mainWindow, [&](){
|
||||
mainWindow.show();
|
||||
mainWindow.raise();
|
||||
});
|
||||
}
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <QRegularExpression>
|
||||
#include <QTcpSocket>
|
||||
|
||||
//#include "communicator.h"
|
||||
#include "debug.h"
|
||||
#include "openvpnprotocol.h"
|
||||
#include "utils.h"
|
||||
@@ -104,12 +103,13 @@ void OpenVpnProtocol::sendManagementCommand(const QString& command)
|
||||
QIODevice *device = dynamic_cast<QIODevice*>(m_managementServer.socket().data());
|
||||
if (device) {
|
||||
QTextStream stream(device);
|
||||
stream << command << endl;
|
||||
stream << command << Qt::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenVpnProtocol::updateRouteGateway(QString line)
|
||||
{
|
||||
// TODO: fix for macos
|
||||
line = line.split("ROUTE_GATEWAY", QString::SkipEmptyParts).at(1);
|
||||
if (!line.contains("/")) return;
|
||||
m_routeGateway = line.split("/", QString::SkipEmptyParts).first();
|
||||
@@ -188,7 +188,6 @@ ErrorCode OpenVpnProtocol::start()
|
||||
|
||||
m_openVpnProcess->start();
|
||||
|
||||
//m_communicator->sendMessage(Message(Message::State::StartRequest, args));
|
||||
//startTimeoutTimer();
|
||||
|
||||
return ErrorCode::NoError;
|
||||
@@ -229,7 +228,6 @@ void OpenVpnProtocol::onReadyReadDataFromManagementServer()
|
||||
if (line.contains("CONNECTED,SUCCESS")) {
|
||||
sendByteCount();
|
||||
stopTimeoutTimer();
|
||||
updateVpnGateway();
|
||||
setConnectionState(VpnProtocol::ConnectionState::Connected);
|
||||
continue;
|
||||
} else if (line.contains("EXITING,SIGTER")) {
|
||||
@@ -246,6 +244,10 @@ void OpenVpnProtocol::onReadyReadDataFromManagementServer()
|
||||
updateRouteGateway(line);
|
||||
}
|
||||
|
||||
if (line.contains("PUSH: Received control message")) {
|
||||
updateVpnGateway(line);
|
||||
}
|
||||
|
||||
if (line.contains("FATAL")) {
|
||||
if (line.contains("tap-windows6 adapters on this system are currently in use or disabled")) {
|
||||
emit protocolError(ErrorCode::OpenVpnAdaptersInUseError);
|
||||
@@ -272,46 +274,61 @@ void OpenVpnProtocol::onReadyReadDataFromManagementServer()
|
||||
}
|
||||
}
|
||||
|
||||
void OpenVpnProtocol::updateVpnGateway()
|
||||
void OpenVpnProtocol::updateVpnGateway(const QString &line)
|
||||
{
|
||||
QProcess ipconfig;
|
||||
ipconfig.start("ipconfig", QStringList() << "/all");
|
||||
ipconfig.waitForStarted();
|
||||
ipconfig.waitForFinished();
|
||||
// line looks like
|
||||
// PUSH: Received control message: 'PUSH_REPLY,route 10.8.0.1,topology net30,ping 10,ping-restart 120,ifconfig 10.8.0.6 10.8.0.5,peer-id 0,cipher AES-256-GCM'
|
||||
|
||||
QString d = ipconfig.readAll();
|
||||
d.replace("\r", "");
|
||||
//qDebug().noquote() << d;
|
||||
QStringList params = line.split(",");
|
||||
for (const QString &l : params) {
|
||||
if (l.contains("ifconfig")) {
|
||||
if (l.split(" ").size() == 3) {
|
||||
m_vpnAddress = l.split(" ").at(1);
|
||||
m_vpnGateway = l.split(" ").at(2);
|
||||
|
||||
QStringList adapters = d.split(":\n");
|
||||
|
||||
bool isTapV9Present = false;
|
||||
QString tapV9;
|
||||
for (int i = 0; i < adapters.size(); ++i) {
|
||||
if (adapters.at(i).contains("TAP-Windows Adapter V9")) {
|
||||
isTapV9Present = true;
|
||||
tapV9 = adapters.at(i);
|
||||
break;
|
||||
qDebug() << QString("Set vpn address %1, gw %2").arg(m_vpnAddress).arg(vpnGateway());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isTapV9Present) {
|
||||
m_vpnGateway = "";
|
||||
}
|
||||
|
||||
QStringList lines = tapV9.split("\n");
|
||||
for (int i = 0; i < lines.size(); ++i) {
|
||||
if (!lines.at(i).contains("DHCP")) continue;
|
||||
// QProcess ipconfig;
|
||||
// ipconfig.start("ipconfig", QStringList() << "/all");
|
||||
// ipconfig.waitForStarted();
|
||||
// ipconfig.waitForFinished();
|
||||
|
||||
QRegularExpression re("(: )([\\d\\.]+)($)");
|
||||
QRegularExpressionMatch match = re.match(lines.at(i));
|
||||
// QString d = ipconfig.readAll();
|
||||
// d.replace("\r", "");
|
||||
// //qDebug().noquote() << d;
|
||||
|
||||
if (match.hasMatch()) {
|
||||
qDebug().noquote() << "Current VPN Gateway IP Address: " << match.captured(0);
|
||||
m_vpnGateway = match.captured(2);
|
||||
return;
|
||||
}
|
||||
else continue;
|
||||
}
|
||||
// QStringList adapters = d.split(":\n");
|
||||
|
||||
m_vpnGateway = "";
|
||||
// bool isTapV9Present = false;
|
||||
// QString tapV9;
|
||||
// for (int i = 0; i < adapters.size(); ++i) {
|
||||
// if (adapters.at(i).contains("TAP-Windows Adapter V9")) {
|
||||
// isTapV9Present = true;
|
||||
// tapV9 = adapters.at(i);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// if (!isTapV9Present) {
|
||||
// m_vpnGateway = "";
|
||||
// }
|
||||
|
||||
// QStringList lines = tapV9.split("\n");
|
||||
// for (int i = 0; i < lines.size(); ++i) {
|
||||
// if (!lines.at(i).contains("DHCP")) continue;
|
||||
|
||||
// QRegularExpression re("(: )([\\d\\.]+)($)");
|
||||
// QRegularExpressionMatch match = re.match(lines.at(i));
|
||||
|
||||
// if (match.hasMatch()) {
|
||||
// qDebug().noquote() << "Current VPN Gateway IP Address: " << match.captured(0);
|
||||
// m_vpnGateway = match.captured(2);
|
||||
// return;
|
||||
// }
|
||||
// else continue;
|
||||
// }
|
||||
|
||||
// m_vpnGateway = "";
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ private:
|
||||
|
||||
private:
|
||||
void updateRouteGateway(QString line);
|
||||
void updateVpnGateway();
|
||||
void updateVpnGateway(const QString &line);
|
||||
|
||||
QSharedPointer<IpcProcessInterfaceReplica> m_openVpnProcess;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "shadowsocksvpnprotocol.h"
|
||||
#include "core/servercontroller.h"
|
||||
|
||||
//#include "communicator.h"
|
||||
#include "debug.h"
|
||||
#include "utils.h"
|
||||
|
||||
@@ -14,6 +13,12 @@ ShadowSocksVpnProtocol::ShadowSocksVpnProtocol(const QJsonObject &configuration,
|
||||
readShadowSocksConfiguration(configuration);
|
||||
}
|
||||
|
||||
ShadowSocksVpnProtocol::~ShadowSocksVpnProtocol()
|
||||
{
|
||||
qDebug() << "ShadowSocksVpnProtocol::stop()";
|
||||
ShadowSocksVpnProtocol::stop();
|
||||
}
|
||||
|
||||
ErrorCode ShadowSocksVpnProtocol::start()
|
||||
{
|
||||
qDebug() << "ShadowSocksVpnProtocol::start()";
|
||||
@@ -40,7 +45,7 @@ ErrorCode ShadowSocksVpnProtocol::start()
|
||||
|
||||
return OpenVpnProtocol::start();
|
||||
}
|
||||
else return ErrorCode::FailedToStartRemoteProcessError;
|
||||
else return ErrorCode::ShadowSocksExecutableMissing;
|
||||
}
|
||||
|
||||
void ShadowSocksVpnProtocol::stop()
|
||||
|
||||
@@ -8,6 +8,7 @@ class ShadowSocksVpnProtocol : public OpenVpnProtocol
|
||||
{
|
||||
public:
|
||||
ShadowSocksVpnProtocol(const QJsonObject& configuration, QObject* parent = nullptr);
|
||||
virtual ~ShadowSocksVpnProtocol() override;
|
||||
|
||||
ErrorCode start() override;
|
||||
void stop() override;
|
||||
|
||||
@@ -55,6 +55,7 @@ protected:
|
||||
|
||||
ConnectionState m_connectionState;
|
||||
QString m_routeGateway;
|
||||
QString m_vpnAddress;
|
||||
QString m_vpnGateway;
|
||||
|
||||
QJsonObject m_rawConfig;
|
||||
|
||||
@@ -40,5 +40,6 @@
|
||||
<file>server_scripts/setup_shadowsocks_server.sh</file>
|
||||
<file>server_scripts/template_shadowsocks.ovpn</file>
|
||||
<file>server_scripts/setup_firewall.sh</file>
|
||||
<file>images/reload.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
#include "runguard.h"
|
||||
#include <QCryptographicHash>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
QString generateKeyHash( const QString& key, const QString& salt )
|
||||
{
|
||||
QByteArray data;
|
||||
|
||||
data.append( key.toUtf8() );
|
||||
data.append( salt.toUtf8() );
|
||||
data = QCryptographicHash::hash( data, QCryptographicHash::Sha1 ).toHex();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RunGuard::RunGuard(const QString& key)
|
||||
: key( key )
|
||||
, memLockKey( generateKeyHash( key, "_memLockKey" ) )
|
||||
, sharedmemKey( generateKeyHash( key, "_sharedmemKey" ) )
|
||||
, sharedMem( sharedmemKey )
|
||||
, memLock( memLockKey, 1 )
|
||||
{
|
||||
qDebug() << "RunGuard::RunGuard key" << key;
|
||||
}
|
||||
|
||||
RunGuard &RunGuard::instance(const QString& key)
|
||||
{
|
||||
static RunGuard s(key);
|
||||
return s;
|
||||
}
|
||||
|
||||
void RunGuard::activate()
|
||||
{
|
||||
memLock.acquire();
|
||||
{
|
||||
QSharedMemory fix(sharedmemKey); // Fix for *nix: http://habrahabr.ru/post/173281/
|
||||
fix.attach();
|
||||
}
|
||||
memLock.release();
|
||||
}
|
||||
|
||||
RunGuard::~RunGuard()
|
||||
{
|
||||
release();
|
||||
}
|
||||
|
||||
bool RunGuard::isAnotherRunning() const
|
||||
{
|
||||
if ( sharedMem.isAttached() )
|
||||
return false;
|
||||
|
||||
memLock.acquire();
|
||||
const bool isRunning = sharedMem.attach();
|
||||
if ( isRunning )
|
||||
sharedMem.detach();
|
||||
memLock.release();
|
||||
|
||||
return isRunning;
|
||||
}
|
||||
|
||||
bool RunGuard::tryToRun()
|
||||
{
|
||||
if ( isAnotherRunning() ) // Extra check
|
||||
return false;
|
||||
|
||||
memLock.acquire();
|
||||
const bool result = sharedMem.create( sizeof( quint64 ) );
|
||||
memLock.release();
|
||||
if ( !result )
|
||||
{
|
||||
release();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RunGuard::release()
|
||||
{
|
||||
memLock.acquire();
|
||||
if ( sharedMem.isAttached() )
|
||||
sharedMem.detach();
|
||||
memLock.release();
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
#ifndef RUNGUARD_H
|
||||
#define RUNGUARD_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSharedMemory>
|
||||
#include <QSystemSemaphore>
|
||||
#include <QDebug>
|
||||
|
||||
/**
|
||||
* @brief The RunGuard class - The application single instance (via shared memory)
|
||||
*/
|
||||
class RunGuard
|
||||
{
|
||||
|
||||
public:
|
||||
static RunGuard &instance(const QString& key = QString());
|
||||
|
||||
~RunGuard();
|
||||
|
||||
void activate();
|
||||
bool isAnotherRunning() const;
|
||||
bool tryToRun();
|
||||
void release();
|
||||
|
||||
private:
|
||||
RunGuard(const QString& key);
|
||||
Q_DISABLE_COPY( RunGuard )
|
||||
|
||||
const QString key;
|
||||
const QString memLockKey;
|
||||
const QString sharedmemKey;
|
||||
|
||||
mutable QSharedMemory sharedMem;
|
||||
mutable QSystemSemaphore memLock;
|
||||
|
||||
};
|
||||
#endif // RUNGUARD_H
|
||||
@@ -1,4 +1,5 @@
|
||||
#CONTAINER_NAME=... this var will be set in ServerController
|
||||
# CONTAINER_NAME=... this var will be set in ServerController
|
||||
# Don't run commands in background like sh -c "openvpn &"
|
||||
|
||||
apt-get update
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#CONTAINER_NAME=... this var will be set in ServerController
|
||||
# CONTAINER_NAME=... this var will be set in ServerController
|
||||
# Don't run commands in background like sh -c "openvpn &"
|
||||
|
||||
apt-get update
|
||||
|
||||
|
||||
@@ -34,6 +34,9 @@ public:
|
||||
void setServerCredentials(const ServerCredentials &credentials);
|
||||
bool haveAuthData() const;
|
||||
|
||||
bool isAutoConnect() const { return m_settings.value("Conf/autoConnect", QString()).toBool(); }
|
||||
void setAutoConnect(bool enabled) { m_settings.setValue("Conf/autoConnect", enabled); }
|
||||
|
||||
bool customRouting() const { return m_settings.value("Conf/customRouting", false).toBool(); }
|
||||
void setCustomRouting(bool customRouting) { m_settings.setValue("Conf/customRouting", customRouting); }
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QDesktopServices>
|
||||
#include <QHBoxLayout>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QKeyEvent>
|
||||
@@ -11,11 +12,10 @@
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
|
||||
//#include "communicator.h"
|
||||
|
||||
#include "core/errorstrings.h"
|
||||
#include "core/openvpnconfigurator.h"
|
||||
#include "core/servercontroller.h"
|
||||
#include "ui/qautostart.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "defines.h"
|
||||
@@ -46,6 +46,7 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
ui->widget_tittlebar->hide();
|
||||
resize(width(), height() - ui->stackedWidget_main->y());
|
||||
ui->stackedWidget_main->move(0,0);
|
||||
fixWidget(this);
|
||||
#endif
|
||||
@@ -61,12 +62,6 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
setupTray();
|
||||
setupUiConnections();
|
||||
|
||||
customSitesModel = new QStringListModel();
|
||||
ui->listView_sites_custom->setModel(customSitesModel);
|
||||
|
||||
connect(ui->listView_sites_custom, &QListView::doubleClicked, [&](const QModelIndex &index){
|
||||
QDesktopServices::openUrl("https://" + index.data().toString());
|
||||
});
|
||||
connect(ui->lineEdit_sites_add_custom, &QLineEdit::returnPressed, [&](){
|
||||
ui->pushButton_sites_add_custom->click();
|
||||
});
|
||||
@@ -74,7 +69,6 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
updateSettings();
|
||||
|
||||
//ui->pushButton_general_settings_exit->hide();
|
||||
//ui->pushButton_share_connection->hide();
|
||||
|
||||
setFixedSize(width(),height());
|
||||
|
||||
@@ -90,7 +84,20 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
|
||||
onConnectionStateChanged(VpnProtocol::ConnectionState::Disconnected);
|
||||
|
||||
if (m_settings.isAutoConnect() && m_settings.haveAuthData()) {
|
||||
QTimer::singleShot(1000, this, [this](){
|
||||
ui->pushButton_connect->setEnabled(false);
|
||||
onConnect();
|
||||
});
|
||||
}
|
||||
|
||||
qDebug().noquote() << QString("Default config: %1").arg(Utils::defaultVpnConfigFileName());
|
||||
|
||||
m_ipAddressValidator.setRegExp(Utils::ipAddressRegExp());
|
||||
|
||||
ui->lineEdit_new_server_ip->setValidator(&m_ipAddressValidator);
|
||||
ui->lineEdit_network_settings_dns1->setValidator(&m_ipAddressValidator);
|
||||
ui->lineEdit_network_settings_dns2->setValidator(&m_ipAddressValidator);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
@@ -146,6 +153,8 @@ QWidget *MainWindow::getPageWidget(MainWindow::Page page)
|
||||
case(Page::NewServer): return ui->page_new_server;
|
||||
case(Page::Vpn): return ui->page_vpn;
|
||||
case(Page::GeneralSettings): return ui->page_general_settings;
|
||||
case(Page::AppSettings): return ui->page_app_settings;
|
||||
case(Page::NetworkSettings): return ui->page_network_settings;
|
||||
case(Page::ServerSettings): return ui->page_server_settings;
|
||||
case(Page::ShareConnection): return ui->page_share_connection;
|
||||
case(Page::Sites): return ui->page_sites;
|
||||
@@ -263,7 +272,6 @@ void MainWindow::onPushButtonNewServerConnectWithExistingCode(bool)
|
||||
s.replace("vpn://", "");
|
||||
QJsonObject o = QJsonDocument::fromJson(QByteArray::fromBase64(s.toUtf8())).object();
|
||||
|
||||
qDebug().noquote() << QByteArray::fromBase64(s.toUtf8());
|
||||
ServerCredentials credentials;
|
||||
credentials.hostName = o.value("h").toString();
|
||||
credentials.port = o.value("p").toInt();
|
||||
@@ -298,6 +306,7 @@ bool MainWindow::installServer(ServerCredentials credentials,
|
||||
|
||||
|
||||
ErrorCode e = ServerController::setupServer(credentials, Protocol::Any);
|
||||
qDebug() << "Setup server finished with code" << e;
|
||||
if (e) {
|
||||
page->setEnabled(true);
|
||||
button->setVisible(true);
|
||||
@@ -453,9 +462,11 @@ void MainWindow::onPushButtonConnectClicked(bool checked)
|
||||
void MainWindow::setupTray()
|
||||
{
|
||||
m_menu = new QMenu();
|
||||
//m_menu->setStyleSheet(styleSheet());
|
||||
|
||||
m_menu->addAction(QIcon(":/images/tray/application.png"), tr("Show") + " " + APPLICATION_NAME, this, SLOT(show()));
|
||||
m_menu->addAction(QIcon(":/images/tray/application.png"), tr("Show") + " " + APPLICATION_NAME, this, [this](){
|
||||
show();
|
||||
raise();
|
||||
});
|
||||
m_menu->addSeparator();
|
||||
m_trayActionConnect = m_menu->addAction(tr("Connect"), this, SLOT(onConnect()));
|
||||
m_trayActionDisconnect = m_menu->addAction(tr("Disconnect"), this, SLOT(onDisconnect()));
|
||||
@@ -526,6 +537,8 @@ void MainWindow::setupUiConnections()
|
||||
|
||||
connect(ui->pushButton_vpn_add_site, &QPushButton::clicked, this, [this](){ goToPage(Page::Sites); });
|
||||
connect(ui->pushButton_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::GeneralSettings); });
|
||||
connect(ui->pushButton_app_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::AppSettings); });
|
||||
connect(ui->pushButton_network_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::NetworkSettings); });
|
||||
connect(ui->pushButton_server_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::ServerSettings); });
|
||||
connect(ui->pushButton_share_connection, &QPushButton::clicked, this, [this](){
|
||||
goToPage(Page::ShareConnection);
|
||||
@@ -536,7 +549,7 @@ void MainWindow::setupUiConnections()
|
||||
QGuiApplication::clipboard()->setText(ui->textEdit_sharing_code->toPlainText());
|
||||
ui->pushButton_copy_sharing_code->setText(tr("Copied"));
|
||||
|
||||
QTimer::singleShot(3000, [this]() {
|
||||
QTimer::singleShot(3000, this, [this]() {
|
||||
ui->pushButton_copy_sharing_code->setText(tr("Copy"));
|
||||
});
|
||||
});
|
||||
@@ -545,17 +558,50 @@ void MainWindow::setupUiConnections()
|
||||
connect(ui->pushButton_back_from_sites, &QPushButton::clicked, this, [this](){ goToPage(Page::Vpn); });
|
||||
connect(ui->pushButton_back_from_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::Vpn); });
|
||||
connect(ui->pushButton_back_from_new_server, &QPushButton::clicked, this, [this](){ goToPage(Page::Start); });
|
||||
connect(ui->pushButton_back_from_app_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::GeneralSettings); });
|
||||
connect(ui->pushButton_back_from_network_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::GeneralSettings); });
|
||||
connect(ui->pushButton_back_from_server_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::GeneralSettings); });
|
||||
connect(ui->pushButton_back_from_share, &QPushButton::clicked, this, [this](){ goToPage(Page::GeneralSettings); });
|
||||
|
||||
connect(ui->pushButton_sites_add_custom, &QPushButton::clicked, this, [this](){ onPushButtonAddCustomSitesClicked(); });
|
||||
connect(ui->pushButton_sites_delete_custom, &QPushButton::clicked, this, [this](){ onPushButtonDeleteCustomSiteClicked(); });
|
||||
|
||||
connect(ui->radioButton_mode_selected_sites, &QRadioButton::toggled, ui->pushButton_vpn_add_site, &QPushButton::setEnabled);
|
||||
|
||||
connect(ui->radioButton_mode_selected_sites, &QRadioButton::toggled, this, [this](bool toggled) {
|
||||
m_settings.setCustomRouting(toggled);
|
||||
});
|
||||
|
||||
connect(ui->checkBox_autostart, &QCheckBox::stateChanged, this, [this](int state){
|
||||
if (state == Qt::Unchecked) {
|
||||
ui->checkBox_autoconnect->setChecked(false);
|
||||
}
|
||||
Autostart::setAutostart(state == Qt::Checked);
|
||||
});
|
||||
|
||||
connect(ui->checkBox_autoconnect, &QCheckBox::stateChanged, this, [this](int state){
|
||||
m_settings.setAutoConnect(state == Qt::Checked);
|
||||
});
|
||||
|
||||
connect(ui->pushButton_network_settings_resetdns1, &QPushButton::clicked, this, [this](){
|
||||
m_settings.setPrimaryDns(m_settings.cloudFlareNs1());
|
||||
updateSettings();
|
||||
});
|
||||
connect(ui->pushButton_network_settings_resetdns2, &QPushButton::clicked, this, [this](){
|
||||
m_settings.setPrimaryDns(m_settings.cloudFlareNs2());
|
||||
updateSettings();
|
||||
});
|
||||
|
||||
connect(ui->lineEdit_network_settings_dns1, &QLineEdit::textEdited, this, [this](const QString &newText){
|
||||
if (m_ipAddressValidator.regExp().exactMatch(newText)) {
|
||||
m_settings.setPrimaryDns(newText);
|
||||
}
|
||||
});
|
||||
connect(ui->lineEdit_network_settings_dns2, &QLineEdit::textEdited, this, [this](const QString &newText){
|
||||
if (m_ipAddressValidator.regExp().exactMatch(newText)) {
|
||||
m_settings.setSecondaryDns(newText);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::setTrayState(VpnProtocol::ConnectionState state)
|
||||
@@ -689,11 +735,8 @@ void MainWindow::onPushButtonAddCustomSitesClicked()
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::onPushButtonDeleteCustomSiteClicked()
|
||||
void MainWindow::onPushButtonDeleteCustomSiteClicked(const QString &siteToDelete)
|
||||
{
|
||||
QModelIndex index = ui->listView_sites_custom->currentIndex();
|
||||
QString siteToDelete = index.data(Qt::DisplayRole).toString();
|
||||
|
||||
if (siteToDelete.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -710,7 +753,6 @@ void MainWindow::onPushButtonDeleteCustomSiteClicked()
|
||||
qDebug() << "Deleted custom ip:" << ipToDelete;
|
||||
m_settings.setCustomIps(customIps);
|
||||
|
||||
|
||||
updateSettings();
|
||||
|
||||
if (m_vpnConnection->connectionState() == VpnProtocol::ConnectionState::Connected) {
|
||||
@@ -721,9 +763,20 @@ void MainWindow::onPushButtonDeleteCustomSiteClicked()
|
||||
|
||||
void MainWindow::updateSettings()
|
||||
{
|
||||
customSitesModel->setStringList(m_settings.customSites());
|
||||
ui->radioButton_mode_selected_sites->setChecked(m_settings.customRouting());
|
||||
ui->pushButton_vpn_add_site->setEnabled(m_settings.customRouting());
|
||||
|
||||
ui->checkBox_autostart->setChecked(Autostart::isAutostart());
|
||||
ui->checkBox_autoconnect->setChecked(m_settings.isAutoConnect());
|
||||
|
||||
ui->lineEdit_network_settings_dns1->setText(m_settings.primaryDns());
|
||||
ui->lineEdit_network_settings_dns2->setText(m_settings.secondaryDns());
|
||||
|
||||
|
||||
ui->listWidget_sites->clear();
|
||||
for(const QString &site : m_settings.customSites()) {
|
||||
makeSitesListItem(ui->listWidget_sites, site);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::updateShareCode()
|
||||
@@ -739,3 +792,32 @@ void MainWindow::updateShareCode()
|
||||
|
||||
//qDebug() << "Share code" << QJsonDocument(o).toJson();
|
||||
}
|
||||
|
||||
void MainWindow::makeSitesListItem(QListWidget *listWidget, const QString &address)
|
||||
{
|
||||
QSize size(310, 25);
|
||||
QWidget* widget = new QWidget;
|
||||
widget->resize(size);
|
||||
|
||||
QLabel *label = new QLabel(address, widget);
|
||||
label->resize(size);
|
||||
|
||||
QPushButton* btn = new QPushButton(widget);
|
||||
btn->resize(size);
|
||||
|
||||
QPushButton* btn1 = new QPushButton(widget);
|
||||
btn1->resize(30, 25);
|
||||
btn1->move(280, 0);
|
||||
btn1->setCursor(QCursor(Qt::PointingHandCursor));
|
||||
|
||||
connect(btn1, &QPushButton::clicked, this, [this, label]() {
|
||||
onPushButtonDeleteCustomSiteClicked(label->text());
|
||||
return;
|
||||
});
|
||||
|
||||
QListWidgetItem* item = new QListWidgetItem(listWidget);
|
||||
item->setSizeHint(size);
|
||||
listWidget->setItemWidget(item, widget);
|
||||
|
||||
widget->setStyleSheet(styleSheet());
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <QLabel>
|
||||
#include <QListWidget>
|
||||
#include <QMainWindow>
|
||||
#include <QProgressBar>
|
||||
#include <QPushButton>
|
||||
#include <QRegExpValidator>
|
||||
#include <QStringListModel>
|
||||
#include <QSystemTrayIcon>
|
||||
|
||||
@@ -35,7 +37,7 @@ public:
|
||||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow();
|
||||
|
||||
enum Page {Start, NewServer, Vpn, GeneralSettings, ServerSettings, ShareConnection, Sites};
|
||||
enum Page {Start, NewServer, Vpn, GeneralSettings, AppSettings, NetworkSettings, ServerSettings, ShareConnection, Sites};
|
||||
Q_ENUM(Page)
|
||||
|
||||
private slots:
|
||||
@@ -52,7 +54,7 @@ private slots:
|
||||
void onPushButtonForgetServer(bool);
|
||||
|
||||
void onPushButtonAddCustomSitesClicked();
|
||||
void onPushButtonDeleteCustomSiteClicked();
|
||||
void onPushButtonDeleteCustomSiteClicked(const QString &siteToDelete);
|
||||
|
||||
void onTrayActionConnect(); // connect from context menu
|
||||
void setTrayState(VpnProtocol::ConnectionState state);
|
||||
@@ -77,7 +79,7 @@ private:
|
||||
void updateSettings();
|
||||
|
||||
void updateShareCode();
|
||||
|
||||
void makeSitesListItem(QListWidget* listWidget, const QString &address);
|
||||
|
||||
Ui::MainWindow *ui;
|
||||
VpnConnection* m_vpnConnection;
|
||||
@@ -89,7 +91,7 @@ private:
|
||||
QSystemTrayIcon m_tray;
|
||||
QMenu* m_menu;
|
||||
|
||||
QStringListModel *customSitesModel = nullptr;
|
||||
QRegExpValidator m_ipAddressValidator;
|
||||
|
||||
bool canMove = false;
|
||||
QPoint offset;
|
||||
|
||||
@@ -60,31 +60,26 @@ QLineEdit:disabled {
|
||||
|
||||
|
||||
QCheckBox {
|
||||
color: rgb(200, 200, 200);
|
||||
color: #181922;
|
||||
|
||||
font: 63 11pt "Open Sans";
|
||||
/*font: 11pt "JMH Cthulhumbus";*/
|
||||
background: transparent;
|
||||
|
||||
|
||||
}
|
||||
|
||||
QCheckBox::indicator {
|
||||
min-height: 20px;
|
||||
min-width: 20px;
|
||||
|
||||
border-image: url(:/images/controls/checkbox_unchecked.png) 0 0 0 0 stretch stretch;
|
||||
border-image: url(:/images/controls/check_off.png) 0 0 0 0 stretch stretch;
|
||||
}
|
||||
|
||||
QCheckBox::indicator:unchecked {
|
||||
border-image: url(:/images/controls/checkbox_unchecked.png) 0 0 0 0 stretch stretch;
|
||||
}
|
||||
QCheckBox::indicator:unchecked:hover {
|
||||
border-image: url(:/images/controls/checkbox_hover.png);
|
||||
border-image: url(:/images/controls/check_off.png) 0 0 0 0 stretch stretch;
|
||||
}
|
||||
|
||||
QCheckBox::indicator:checked {
|
||||
border-image: url(:/images/controls/checkbox_checked.png);
|
||||
border-image: url(:/images/controls/check_on.png);
|
||||
|
||||
}
|
||||
|
||||
@@ -259,7 +254,7 @@ QPushButton:hover {
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>2</number>
|
||||
<number>3</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="page_start">
|
||||
<widget class="QLabel" name="label_23">
|
||||
@@ -383,6 +378,10 @@ line-height: 21px;
|
||||
background: #100A44;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
QPushButton:hover {
|
||||
background: #211966;
|
||||
}
|
||||
</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
@@ -634,7 +633,9 @@ line-height: 21px;
|
||||
background: #100A44;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</string>
|
||||
QPushButton:hover {
|
||||
background: #211966;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Connect</string>
|
||||
@@ -952,6 +953,10 @@ line-height: 21px;
|
||||
|
||||
QPushButton:!enabled {
|
||||
background: #484952;
|
||||
}
|
||||
|
||||
QPushButton:hover {
|
||||
background: #282932;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
@@ -1119,9 +1124,9 @@ color: #181922;
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>300</y>
|
||||
<y>290</y>
|
||||
<width>381</width>
|
||||
<height>51</height>
|
||||
<height>61</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
@@ -1170,22 +1175,6 @@ background: rgba(167, 167, 167, 0.1);
|
||||
color: #181922;
|
||||
}</string>
|
||||
</property>
|
||||
<widget class="QListView" name="listView_sites">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>381</width>
|
||||
<height>0</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="cursor" stdset="0">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="pushButton_back_from_sites">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
@@ -1243,10 +1232,10 @@ QPushButton:hover {
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>19</x>
|
||||
<x>20</x>
|
||||
<y>50</y>
|
||||
<width>341</width>
|
||||
<height>41</height>
|
||||
<width>340</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
@@ -1255,11 +1244,8 @@ font-style: normal;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
line-height: 25px;
|
||||
|
||||
/* or 125% */
|
||||
|
||||
/* black */
|
||||
color: #181922;</string>
|
||||
color: #100A44;
|
||||
</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>These sites will be opened using VPN</string>
|
||||
@@ -1271,22 +1257,6 @@ color: #181922;</string>
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QListView" name="listView_sites_custom">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>200</y>
|
||||
<width>341</width>
|
||||
<height>361</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="cursor" stdset="0">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="lineEdit_sites_add_custom">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
@@ -1338,37 +1308,20 @@ border: 1px solid #A7A7A7;
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background: #100A44;
|
||||
<string notr="true">QPushButton {
|
||||
background: #100A44;
|
||||
border-radius: 4px;
|
||||
font-size: 24px;
|
||||
color: white</string>
|
||||
color: white
|
||||
}
|
||||
QPushButton:hover {
|
||||
background: #211966;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>+</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="pushButton_sites_delete_custom">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>90</x>
|
||||
<y>584</y>
|
||||
<width>201</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color: #181922;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete selected site</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
@@ -1394,6 +1347,49 @@ color: #333333;</string>
|
||||
<string>Web site or hostname or IP address</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QListWidget" name="listWidget_sites">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>200</y>
|
||||
<width>340</width>
|
||||
<height>400</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QWidget {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
QPushButton:hover {
|
||||
image: url(:/images/close.png);
|
||||
image-position: right center;
|
||||
}
|
||||
|
||||
QListView {
|
||||
show-decoration-selected: 1; /* make the selection span the entire width of the view */
|
||||
}
|
||||
|
||||
QListView::item:selected:!active {
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
QListView::item:selected:active {
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
QListView::item:hover {
|
||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #FAFBFE, stop: 1 #ECEEFF);
|
||||
}</string>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_general_settings">
|
||||
<widget class="QPushButton" name="pushButton_back_from_settings">
|
||||
@@ -1434,7 +1430,7 @@ QPushButton:hover {
|
||||
<x>10</x>
|
||||
<y>40</y>
|
||||
<width>360</width>
|
||||
<height>1</height>
|
||||
<height>10</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
@@ -1448,7 +1444,7 @@ QPushButton:hover {
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>60</y>
|
||||
<y>180</y>
|
||||
<width>330</width>
|
||||
<height>30</height>
|
||||
</rect>
|
||||
@@ -1456,6 +1452,9 @@ QPushButton:hover {
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Reinstall server, clear server</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font-family: Lato;
|
||||
font-style: normal;
|
||||
@@ -1474,16 +1473,16 @@ background-repeat: no-repeat;
|
||||
background-position: left center;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Server settings</string>
|
||||
<string>Server management</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>110</y>
|
||||
<y>160</y>
|
||||
<width>360</width>
|
||||
<height>1</height>
|
||||
<height>10</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
@@ -1500,7 +1499,7 @@ background-repeat: no-repeat;
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>130</y>
|
||||
<y>240</y>
|
||||
<width>330</width>
|
||||
<height>30</height>
|
||||
</rect>
|
||||
@@ -1533,9 +1532,9 @@ background-repeat: no-repeat;
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>180</y>
|
||||
<y>220</y>
|
||||
<width>360</width>
|
||||
<height>1</height>
|
||||
<height>10</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
@@ -1551,7 +1550,7 @@ background-repeat: no-repeat;
|
||||
<x>10</x>
|
||||
<y>620</y>
|
||||
<width>360</width>
|
||||
<height>1</height>
|
||||
<height>10</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
@@ -1568,7 +1567,7 @@ background-repeat: no-repeat;
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>570</y>
|
||||
<y>580</y>
|
||||
<width>330</width>
|
||||
<height>30</height>
|
||||
</rect>
|
||||
@@ -1600,9 +1599,9 @@ background-repeat: no-repeat;
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>550</y>
|
||||
<y>560</y>
|
||||
<width>360</width>
|
||||
<height>1</height>
|
||||
<height>10</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
@@ -1612,6 +1611,463 @@ background-repeat: no-repeat;
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="pushButton_app_settings">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>60</y>
|
||||
<width>330</width>
|
||||
<height>30</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Auto start, Auto connect</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font-family: Lato;
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
line-height: 25px;
|
||||
Text-align:left;
|
||||
padding-left: 30px;
|
||||
|
||||
|
||||
/* black */
|
||||
color: #100A44;
|
||||
|
||||
background-image: url(:/images/settings.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: left center;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>App settings</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_19">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>280</y>
|
||||
<width>360</width>
|
||||
<height>10</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">image: url(:/images/line.png);</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_21">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>100</y>
|
||||
<width>360</width>
|
||||
<height>10</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">image: url(:/images/line.png);</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="pushButton_network_settings">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>120</y>
|
||||
<width>330</width>
|
||||
<height>30</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Dns, Kill Switch</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font-family: Lato;
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
line-height: 25px;
|
||||
Text-align:left;
|
||||
padding-left: 30px;
|
||||
|
||||
|
||||
/* black */
|
||||
color: #100A44;
|
||||
|
||||
background-image: url(:/images/settings.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: left center;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Network settings</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_app_settings">
|
||||
<widget class="QPushButton" name="pushButton_back_from_app_settings">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>10</y>
|
||||
<width>26</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton {
|
||||
image: url(:/images/arrow_right.png);
|
||||
image-position: left;
|
||||
text-align: left;
|
||||
/*font: 17pt "Ancient";*/
|
||||
|
||||
padding: 1px;
|
||||
image: url(:/images/arrow_left.png);
|
||||
}
|
||||
QPushButton:hover {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_22">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>110</x>
|
||||
<y>590</y>
|
||||
<width>150</width>
|
||||
<height>22</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">image: url(:/images/AmneziaVPN.png);</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QCheckBox" name="checkBox_autostart">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>100</y>
|
||||
<width>211</width>
|
||||
<height>31</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Auto start</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_27">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>50</y>
|
||||
<width>340</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font-family: Lato;
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
line-height: 25px;
|
||||
color: #100A44;
|
||||
</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Application Settings</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QCheckBox" name="checkBox_autoconnect">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>140</y>
|
||||
<width>211</width>
|
||||
<height>31</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Auto connect</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="pushButton_check_for_updates">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>220</y>
|
||||
<width>321</width>
|
||||
<height>41</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton {
|
||||
color:rgb(212, 212, 212);
|
||||
border-radius: 4px;
|
||||
|
||||
font-family: Lato;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
|
||||
background: #100A44;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
QPushButton:hover {
|
||||
background: #211966;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Check for updates</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_network_settings">
|
||||
<widget class="QPushButton" name="pushButton_back_from_network_settings">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>20</y>
|
||||
<width>26</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton {
|
||||
image: url(:/images/arrow_right.png);
|
||||
image-position: left;
|
||||
text-align: left;
|
||||
/*font: 17pt "Ancient";*/
|
||||
|
||||
padding: 1px;
|
||||
image: url(:/images/arrow_left.png);
|
||||
}
|
||||
QPushButton:hover {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_26">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>110</x>
|
||||
<y>590</y>
|
||||
<width>150</width>
|
||||
<height>22</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">image: url(:/images/AmneziaVPN.png);</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="lineEdit_network_settings_dns1">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>40</x>
|
||||
<y>120</y>
|
||||
<width>271</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background: #F4F4F4;
|
||||
|
||||
/* grey */
|
||||
border: 1px solid #A7A7A7;
|
||||
color: #333333;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_28">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>50</y>
|
||||
<width>340</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font-family: Lato;
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
line-height: 25px;
|
||||
color: #100A44;
|
||||
</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>DNS Servers</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="lineEdit_network_settings_dns2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>40</x>
|
||||
<y>200</y>
|
||||
<width>271</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background: #F4F4F4;
|
||||
|
||||
/* grey */
|
||||
border: 1px solid #A7A7A7;
|
||||
color: #333333;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="pushButton_network_settings_resetdns1">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>320</x>
|
||||
<y>130</y>
|
||||
<width>18</width>
|
||||
<height>18</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Reset to default value</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton {
|
||||
image: url(:/images/reload.png);
|
||||
padding:1px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
padding:0px;
|
||||
}
|
||||
</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="pushButton_network_settings_resetdns2">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>320</x>
|
||||
<y>210</y>
|
||||
<width>18</width>
|
||||
<height>18</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Reset to default value</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton {
|
||||
image: url(:/images/reload.png);
|
||||
padding:1px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
padding:0px;
|
||||
}
|
||||
</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_new_server_wait_info_2">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>40</x>
|
||||
<y>95</y>
|
||||
<width>291</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Primary DNS server</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_new_server_wait_info_3">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>40</x>
|
||||
<y>175</y>
|
||||
<width>291</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Secondray DNS server</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_server_settings">
|
||||
<widget class="QLabel" name="label_server_settings_wait_info">
|
||||
@@ -1633,31 +2089,6 @@ background-repeat: no-repeat;
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_13">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>40</x>
|
||||
<y>100</y>
|
||||
<width>171</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font-family: Lato;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
line-height: 150%;
|
||||
|
||||
/* or 24px */
|
||||
|
||||
/* text */
|
||||
color: #333333;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>You connected to</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="pushButton_server_settings_reinstall">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
@@ -1684,7 +2115,9 @@ line-height: 21px;
|
||||
background: #100A44;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</string>
|
||||
QPushButton:hover {
|
||||
background: #211966;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Reinstall Amnezia server</string>
|
||||
@@ -1725,17 +2158,17 @@ QPushButton:hover {
|
||||
<widget class="QLabel" name="label_16">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<x>20</x>
|
||||
<y>40</y>
|
||||
<width>361</width>
|
||||
<height>71</height>
|
||||
<width>340</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font-family: Lato;
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
font-size: 24px;
|
||||
font-size: 20px;
|
||||
line-height: 25px;
|
||||
color: #100A44;
|
||||
</string>
|
||||
@@ -1836,7 +2269,9 @@ line-height: 21px;
|
||||
background: #100A44;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</string>
|
||||
QPushButton:hover {
|
||||
background: #211966;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Clear server from Amnezia software</string>
|
||||
@@ -1868,7 +2303,9 @@ line-height: 21px;
|
||||
background: #100A44;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</string>
|
||||
QPushButton:hover {
|
||||
background: #211966;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Forget this server</string>
|
||||
@@ -1877,9 +2314,9 @@ border-radius: 4px;
|
||||
<widget class="QLabel" name="label_server_settings_server">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>40</x>
|
||||
<y>130</y>
|
||||
<width>321</width>
|
||||
<x>20</x>
|
||||
<y>80</y>
|
||||
<width>341</width>
|
||||
<height>41</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -1893,9 +2330,11 @@ color: #333333;</string>
|
||||
<property name="text">
|
||||
<string>root@yourserver.org</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
<zorder>label_server_settings_wait_info</zorder>
|
||||
<zorder>label_13</zorder>
|
||||
<zorder>pushButton_back_from_server_settings</zorder>
|
||||
<zorder>label_16</zorder>
|
||||
<zorder>progressBar_server_settings_reinstall</zorder>
|
||||
@@ -1949,8 +2388,8 @@ QPushButton:hover {
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>40</y>
|
||||
<width>341</width>
|
||||
<height>41</height>
|
||||
<width>340</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
@@ -1959,17 +2398,14 @@ font-style: normal;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
line-height: 25px;
|
||||
|
||||
/* or 125% */
|
||||
|
||||
/* black */
|
||||
color: #181922;</string>
|
||||
color: #100A44;
|
||||
</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Connection string</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
@@ -2049,7 +2485,9 @@ font-size: 16px;
|
||||
line-height: 21px;
|
||||
|
||||
}
|
||||
</string>
|
||||
QPushButton:hover {
|
||||
background: #282932;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Copy</string>
|
||||
@@ -2065,21 +2503,18 @@ line-height: 21px;
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">
|
||||
|
||||
font-family: Lato;
|
||||
<string notr="true">font-family: Lato;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
line-height: 150%;
|
||||
/* or 24px */
|
||||
|
||||
/* grey */
|
||||
color: #A7A7A7;
|
||||
color: #181922;
|
||||
</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Anyone who logs in with this code will have the same rights to use the VPN as you. To create a new code, change your login and / or password for connection in your server settings.</string>
|
||||
<string>Anyone who logs in with this code will have the same rights to use the VPN as you. This code includes your server credentials!
|
||||
Provide this code only to TRUSTED users.</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignJustify|Qt::AlignVCenter</set>
|
||||
@@ -2088,6 +2523,22 @@ color: #A7A7A7;
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_29">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>110</x>
|
||||
<y>590</y>
|
||||
<width>150</width>
|
||||
<height>22</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">image: url(:/images/AmneziaVPN.png);</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (C) 2016 Mostafa Sedaghat Joo (mostafa.sedaghat@gmail.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#include "qautostart.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QTextStream>
|
||||
#include <QFileInfo>
|
||||
#include <QSettings>
|
||||
#include <QProcess>
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
|
||||
#if defined (Q_OS_WIN)
|
||||
#define REG_KEY "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"
|
||||
|
||||
bool Autostart::isAutostart() {
|
||||
QSettings settings(REG_KEY, QSettings::NativeFormat);
|
||||
|
||||
if (settings.value(appName()).isNull()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Autostart::setAutostart(bool autostart) {
|
||||
QSettings settings(REG_KEY, QSettings::NativeFormat);
|
||||
|
||||
if (autostart) {
|
||||
settings.setValue(appName() , appPath().replace('/','\\'));
|
||||
} else {
|
||||
settings.remove(appName());
|
||||
}
|
||||
}
|
||||
|
||||
QString Autostart::appPath() {
|
||||
return QCoreApplication::applicationFilePath() + " --autostart";
|
||||
}
|
||||
|
||||
#elif defined (Q_OS_MAC)
|
||||
|
||||
bool Autostart::isAutostart() {
|
||||
QProcess process;
|
||||
process.start("osascript", {
|
||||
"-e tell application \"System Events\" to get the path of every login item"
|
||||
});
|
||||
process.waitForFinished(3000);
|
||||
const auto output = QString::fromLocal8Bit(process.readAllStandardOutput());
|
||||
return output.contains(appPath());
|
||||
}
|
||||
|
||||
void Autostart::setAutostart(bool autostart) {
|
||||
// Remove any existing login entry for this app first, in case there was one
|
||||
// from a previous installation, that may be under a different launch path.
|
||||
{
|
||||
QProcess::execute("osascript", {
|
||||
"-e tell application \"System Events\" to delete every login item whose name is \"" + appName() + "\""
|
||||
});
|
||||
}
|
||||
|
||||
// Now install the login item, if needed.
|
||||
if ( autostart )
|
||||
{
|
||||
QProcess::execute("osascript", {
|
||||
"-e tell application \"System Events\" to make login item at end with properties {path:\"" + appPath() + "\", hidden:true, name: \"" + appName() + "\"}"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
QString Autostart::appPath() {
|
||||
QDir appDir = QDir(QCoreApplication::applicationDirPath());
|
||||
appDir.cdUp();
|
||||
appDir.cdUp();
|
||||
QString absolutePath = appDir.absolutePath();
|
||||
|
||||
return absolutePath;
|
||||
}
|
||||
|
||||
#elif defined (Q_OS_LINUX)
|
||||
bool Autostart::isAutostart() {
|
||||
QFileInfo check_file(QDir::homePath() + "/.config/autostart/" + appName() +".desktop");
|
||||
|
||||
if (check_file.exists() && check_file.isFile()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Autostart::setAutostart(bool autostart) {
|
||||
QString path = QDir::homePath() + "/.config/autostart/";
|
||||
QString name = appName() +".desktop";
|
||||
QFile file(path+name);
|
||||
|
||||
file.remove();
|
||||
|
||||
if(autostart) {
|
||||
QDir dir(path);
|
||||
if(!dir.exists()) {
|
||||
dir.mkpath(path);
|
||||
}
|
||||
|
||||
if (file.open(QIODevice::ReadWrite)) {
|
||||
QTextStream stream(&file);
|
||||
stream << "[Desktop Entry]" << endl;
|
||||
stream << "Exec=" << appPath() << endl;
|
||||
stream << "Type=Application" << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString Autostart::appPath() {
|
||||
return QCoreApplication::applicationFilePath() + " --autostart";
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
bool Autostart::isAutostart() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Autostart::setAutostart(bool autostart) {
|
||||
Q_UNUSED(autostart);
|
||||
}
|
||||
|
||||
QString Autostart::appPath() {
|
||||
return QString();
|
||||
}
|
||||
#endif
|
||||
|
||||
QString Autostart::appName() {
|
||||
return QCoreApplication::applicationName();
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (C) 2016 Mostafa Sedaghat Joo (mostafa.sedaghat@gmail.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#ifndef AUTOSTART_H
|
||||
#define AUTOSTART_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
class Autostart
|
||||
{
|
||||
public:
|
||||
static bool isAutostart();
|
||||
static void setAutostart(bool autostart);
|
||||
|
||||
protected:
|
||||
static QString appPath();
|
||||
static QString appName();
|
||||
};
|
||||
|
||||
#endif // AUTOSTART_H
|
||||
@@ -1,6 +1,8 @@
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QHostAddress>
|
||||
#include <QHostInfo>
|
||||
#include <QProcess>
|
||||
#include <QStandardPaths>
|
||||
|
||||
@@ -113,45 +115,17 @@ bool Utils::processIsRunning(const QString& fileName)
|
||||
|
||||
QString Utils::getIPAddress(const QString& host)
|
||||
{
|
||||
//TODO rewrite to api calls
|
||||
qDebug().noquote() << "GetIPAddress: checking " + host;
|
||||
if(host.isEmpty()) {
|
||||
qDebug().noquote() << "GetIPAddress: host is empty.";
|
||||
return QString();
|
||||
if (ipAddressRegExp().exactMatch(host)) {
|
||||
return host;
|
||||
}
|
||||
|
||||
if(checkIPFormat(host)) {
|
||||
qDebug().noquote() << "GetIPAddress host is ip:" << host << host;
|
||||
return host; // it is a ip address.
|
||||
QList<QHostAddress> adresses = QHostInfo::fromName(host).addresses();
|
||||
if (!adresses.isEmpty()) {
|
||||
qDebug() << "Resolved address for" << host << adresses.first().toString();
|
||||
return adresses.first().toString();
|
||||
}
|
||||
QProcess ping;
|
||||
|
||||
#ifdef Q_OS_MACX
|
||||
ping.start("ping", QStringList() << "-c1" << host);
|
||||
#endif
|
||||
#ifdef Q_OS_WIN
|
||||
ping.start("ping", QStringList() << QString("/n") << "1" << QString("/w") << "1" << host);
|
||||
#endif
|
||||
ping.waitForStarted();
|
||||
|
||||
QEventLoop loop;
|
||||
loop.connect(&ping, SIGNAL(finished(int)), &loop, SLOT(quit()));
|
||||
loop.exec();
|
||||
|
||||
QString d = ping.readAll();
|
||||
if(d.size() == 0)
|
||||
return QString();
|
||||
qDebug().noquote() << d;
|
||||
|
||||
QString ip;
|
||||
#ifdef Q_OS_MACX
|
||||
ip = getStringBetween(d, "(", ")");
|
||||
#endif
|
||||
#ifdef Q_OS_WIN
|
||||
ip = getStringBetween(d, "[", "]");
|
||||
#endif
|
||||
qDebug().noquote() << "GetIPAddress:" << host << ip;
|
||||
return ip;
|
||||
qDebug() << "Unable to resolve address for " << host;
|
||||
return "";
|
||||
}
|
||||
|
||||
QString Utils::getStringBetween(const QString& s, const QString& a, const QString& b)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
#include <QRegExp>
|
||||
#include <QString>
|
||||
|
||||
class Utils {
|
||||
@@ -19,6 +20,7 @@ public:
|
||||
static QString getIPAddress(const QString& host);
|
||||
static QString getStringBetween(const QString& s, const QString& a, const QString& b);
|
||||
static bool checkIPFormat(const QString& ip);
|
||||
static QRegExp ipAddressRegExp() { return QRegExp("^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\\.(?!$)|$)){4}$"); }
|
||||
};
|
||||
|
||||
#endif // UTILS_H
|
||||
|
||||
@@ -49,7 +49,7 @@ void VpnConnection::onConnectionStateChanged(VpnProtocol::ConnectionState state)
|
||||
IpcClient::Interface()->routeAddList(m_vpnProtocol->vpnGateway(), black_custom);
|
||||
}
|
||||
}
|
||||
else if (state == VpnProtocol::ConnectionState::Error || state == VpnProtocol::ConnectionState::Disconnected) {
|
||||
else if (state == VpnProtocol::ConnectionState::Error) {
|
||||
IpcClient::Interface()->flushDns();
|
||||
|
||||
if (m_settings.customRouting()) {
|
||||
@@ -105,7 +105,7 @@ ErrorCode VpnConnection::createVpnConfiguration(const ServerCredentials &credent
|
||||
ErrorCode VpnConnection::connectToVpn(const ServerCredentials &credentials, Protocol protocol)
|
||||
{
|
||||
qDebug() << "connectToVpn, CustomRouting is" << m_settings.customRouting();
|
||||
//protocol = Protocol::ShadowSocks;
|
||||
protocol = Protocol::ShadowSocks;
|
||||
|
||||
// TODO: Try protocols one by one in case of Protocol::Any
|
||||
// TODO: Implement some behavior in case if connection not stable
|
||||
@@ -116,10 +116,11 @@ ErrorCode VpnConnection::connectToVpn(const ServerCredentials &credentials, Prot
|
||||
if (m_vpnProtocol) {
|
||||
disconnect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError);
|
||||
m_vpnProtocol->stop();
|
||||
m_vpnProtocol->deleteLater();
|
||||
m_vpnProtocol.reset();
|
||||
//m_vpnProtocol->deleteLater();
|
||||
}
|
||||
|
||||
qApp->processEvents();
|
||||
//qApp->processEvents();
|
||||
|
||||
if (protocol == Protocol::Any || protocol == Protocol::OpenVpn) {
|
||||
ErrorCode e = createVpnConfiguration(credentials, Protocol::OpenVpn);
|
||||
@@ -143,6 +144,11 @@ ErrorCode VpnConnection::connectToVpn(const ServerCredentials &credentials, Prot
|
||||
}
|
||||
|
||||
m_vpnProtocol.reset(new ShadowSocksVpnProtocol(m_vpnConfiguration));
|
||||
e = static_cast<OpenVpnProtocol *>(m_vpnProtocol.data())->checkAndSetupTapDriver();
|
||||
if (e) {
|
||||
emit connectionStateChanged(VpnProtocol::ConnectionState::Error);
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError);
|
||||
@@ -162,8 +168,11 @@ void VpnConnection::disconnectFromVpn()
|
||||
{
|
||||
qDebug() << "Disconnect from VPN";
|
||||
|
||||
// m_vpnProtocol->communicator()->sendMessage(Message(Message::State::ClearSavedRoutesRequest, QStringList()));
|
||||
// m_vpnProtocol->communicator()->sendMessage(Message(Message::State::FlushDnsRequest, QStringList()));
|
||||
IpcClient::Interface()->flushDns();
|
||||
|
||||
if (m_settings.customRouting()) {
|
||||
IpcClient::Interface()->clearSavedRoutes();
|
||||
}
|
||||
|
||||
if (!m_vpnProtocol.data()) {
|
||||
return;
|
||||
|
||||
@@ -65,6 +65,8 @@ cd %PROJECT_DIR%
|
||||
cd %WORK_DIR%
|
||||
set CL=/MP
|
||||
nmake /A /NOLOGO
|
||||
if %errorlevel% neq 0 exit /b %errorlevel%
|
||||
|
||||
nmake clean
|
||||
rem if not exist "%OUT_APP_DIR:"=%\%APP_FILENAME:"=%" break
|
||||
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* shadowsocks.h - Header files of library interfaces
|
||||
*
|
||||
* Copyright (C) 2013 - 2019, Max Lv <max.c.lv@gmail.com>
|
||||
*
|
||||
* This file is part of the shadowsocks-libev.
|
||||
* shadowsocks-libev 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* shadowsocks-libev 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 shadowsocks-libev; see the file COPYING. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _SHADOWSOCKS_H
|
||||
#define _SHADOWSOCKS_H
|
||||
|
||||
typedef struct {
|
||||
/* Required */
|
||||
char *remote_host; // hostname or ip of remote server
|
||||
char *local_addr; // local ip to bind
|
||||
char *method; // encryption method
|
||||
char *password; // password of remote server
|
||||
int remote_port; // port number of remote server
|
||||
int local_port; // port number of local server
|
||||
int timeout; // connection timeout
|
||||
|
||||
/* Optional, set NULL if not valid */
|
||||
char *acl; // file path to acl
|
||||
char *log; // file path to log
|
||||
int fast_open; // enable tcp fast open
|
||||
int mode; // enable udp relay
|
||||
int mtu; // MTU of interface
|
||||
int mptcp; // enable multipath TCP
|
||||
int verbose; // verbose mode
|
||||
} profile_t;
|
||||
|
||||
/* An example profile
|
||||
*
|
||||
* const profile_t EXAMPLE_PROFILE = {
|
||||
* .remote_host = "example.com",
|
||||
* .local_addr = "127.0.0.1",
|
||||
* .method = "bf-cfb",
|
||||
* .password = "barfoo!",
|
||||
* .remote_port = 8338,
|
||||
* .local_port = 1080,
|
||||
* .timeout = 600;
|
||||
* .acl = NULL,
|
||||
* .log = NULL,
|
||||
* .fast_open = 0,
|
||||
* .mode = 0,
|
||||
* .verbose = 0
|
||||
* };
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void (*ss_local_callback)(int socks_fd, int udp_fd, void *data);
|
||||
|
||||
/*
|
||||
* Create and start a shadowsocks local server.
|
||||
*
|
||||
* Calling this function will block the current thread forever if the server
|
||||
* starts successfully.
|
||||
*
|
||||
* Make sure start the server in a separate process to avoid any potential
|
||||
* memory and socket leak.
|
||||
*
|
||||
* If failed, -1 is returned. Errors will output to the log file.
|
||||
*/
|
||||
int start_ss_local_server(profile_t profile);
|
||||
|
||||
/*
|
||||
* Create and start a shadowsocks local server, specifying a callback.
|
||||
*
|
||||
* The callback is invoked when the local server has started successfully. It passes the SOCKS
|
||||
* server and UDP relay file descriptors, along with any supplied user data.
|
||||
*
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int start_ss_local_server_with_callback(profile_t profile, ss_local_callback callback, void *udata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
// To stop the service on posix system, just kill the daemon process
|
||||
// kill(pid, SIGKILL);
|
||||
// Otherwise, If you start the service in a thread, you may need to send a signal SIGUSER1 to the thread.
|
||||
// pthread_kill(pthread_t, SIGUSR1);
|
||||
|
||||
#endif // _SHADOWSOCKS_H
|
||||
@@ -1 +1 @@
|
||||
"C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\Tools\VsDevCmd.bat"
|
||||
"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\VsDevCmd.bat"
|
||||
|
||||
@@ -55,27 +55,27 @@ int IpcServer::createPrivilegedProcess()
|
||||
|
||||
bool IpcServer::routeAdd(const QString &ip, const QString &gw, const QString &mask)
|
||||
{
|
||||
return Router::Instance().routeAdd(ip, gw, mask);
|
||||
return Router::routeAdd(ip, gw, mask);
|
||||
}
|
||||
|
||||
int IpcServer::routeAddList(const QString &gw, const QStringList &ips)
|
||||
{
|
||||
return Router::Instance().routeAddList(gw, ips);
|
||||
return Router::routeAddList(gw, ips);
|
||||
}
|
||||
|
||||
bool IpcServer::clearSavedRoutes()
|
||||
{
|
||||
return Router::Instance().clearSavedRoutes();
|
||||
return Router::clearSavedRoutes();
|
||||
}
|
||||
|
||||
bool IpcServer::routeDelete(const QString &ip)
|
||||
{
|
||||
return Router::Instance().routeDelete(ip);
|
||||
return Router::routeDelete(ip);
|
||||
}
|
||||
|
||||
void IpcServer::flushDns()
|
||||
{
|
||||
return Router::Instance().flushDns();
|
||||
return Router::flushDns();
|
||||
}
|
||||
|
||||
bool IpcServer::checkAndInstallDriver()
|
||||
|
||||
@@ -1,327 +1,54 @@
|
||||
#include "router.h"
|
||||
|
||||
#include <QProcess>
|
||||
#ifdef Q_OS_WIN
|
||||
#include "router_win.h"
|
||||
#elif defined (Q_OS_MAC)
|
||||
#include "router_mac.h"
|
||||
#endif
|
||||
|
||||
|
||||
Router &Router::Instance()
|
||||
{
|
||||
static Router s;
|
||||
return s;
|
||||
}
|
||||
|
||||
bool Router::routeAdd(const QString &ip, const QString &gw, QString mask)
|
||||
{
|
||||
qDebug().noquote() << QString("ROUTE ADD: IP:%1 %2 GW %3")
|
||||
.arg(ip)
|
||||
.arg(mask)
|
||||
.arg(gw);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
if (mask == "") {
|
||||
mask = "255.255.255.255";
|
||||
if (ip.endsWith(".0")) mask = "255.255.255.0";
|
||||
if (ip.endsWith(".0.0")) mask = "255.255.0.0";
|
||||
if (ip.endsWith(".0.0.0")) mask = "255.0.0.0";
|
||||
}
|
||||
|
||||
|
||||
PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
|
||||
MIB_IPFORWARDROW ipfrow;
|
||||
DWORD dwSize = 0;
|
||||
BOOL bOrder = FALSE;
|
||||
DWORD dwStatus = 0;
|
||||
|
||||
|
||||
// Find out how big our buffer needs to be.
|
||||
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
|
||||
if (dwStatus == ERROR_INSUFFICIENT_BUFFER) {
|
||||
// Allocate the memory for the table
|
||||
if (!(pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc(dwSize))) {
|
||||
qDebug() << "Malloc failed. Out of memory.";
|
||||
return false;
|
||||
}
|
||||
// Now get the table.
|
||||
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
|
||||
}
|
||||
|
||||
|
||||
if (dwStatus != ERROR_SUCCESS) {
|
||||
qDebug() << "getIpForwardTable failed.";
|
||||
if (pIpForwardTable)
|
||||
free(pIpForwardTable);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set iface for route
|
||||
IPAddr dwGwAddr = inet_addr(gw.toStdString().c_str());
|
||||
if (GetBestInterface(dwGwAddr, &ipfrow.dwForwardIfIndex) != NO_ERROR) {
|
||||
qDebug() << "Router::routeAdd : GetBestInterface failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
// address
|
||||
ipfrow.dwForwardDest = inet_addr(ip.toStdString().c_str());
|
||||
|
||||
// mask
|
||||
in_addr maskAddr;
|
||||
inet_pton(AF_INET, mask.toStdString().c_str(), &maskAddr);
|
||||
ipfrow.dwForwardMask = maskAddr.S_un.S_addr;
|
||||
|
||||
// Get TAP iface metric to set it for new routes
|
||||
MIB_IPINTERFACE_ROW tap_iface;
|
||||
InitializeIpInterfaceEntry(&tap_iface);
|
||||
tap_iface.InterfaceIndex = ipfrow.dwForwardIfIndex;
|
||||
tap_iface.Family = AF_INET;
|
||||
dwStatus = GetIpInterfaceEntry(&tap_iface);
|
||||
if (dwStatus == NO_ERROR){
|
||||
ipfrow.dwForwardMetric1 = tap_iface.Metric;
|
||||
}
|
||||
else {
|
||||
qDebug() << "Router::routeAdd: failed GetIpInterfaceEntry(), Error:" << dwStatus;
|
||||
ipfrow.dwForwardMetric1 = 256;
|
||||
}
|
||||
ipfrow.dwForwardMetric2 = 0;
|
||||
ipfrow.dwForwardMetric3 = 0;
|
||||
ipfrow.dwForwardMetric4 = 0;
|
||||
ipfrow.dwForwardMetric5 = 0;
|
||||
|
||||
ipfrow.dwForwardAge = 0;
|
||||
|
||||
ipfrow.dwForwardNextHop = inet_addr(gw.toStdString().c_str());
|
||||
ipfrow.dwForwardType = 4; /* XXX - next hop != final dest */
|
||||
ipfrow.dwForwardProto = 3; /* XXX - MIB_PROTO_NETMGMT */
|
||||
|
||||
|
||||
dwStatus = CreateIpForwardEntry(&ipfrow);
|
||||
if (dwStatus == NO_ERROR){
|
||||
ipForwardRows.append(ipfrow);
|
||||
//qDebug() << "Gateway changed successfully";
|
||||
}
|
||||
else {
|
||||
qDebug() << "Router::routeAdd: failed CreateIpForwardEntry()";
|
||||
qDebug() << "Error: " << dwStatus;
|
||||
}
|
||||
|
||||
// Free resources
|
||||
if (pIpForwardTable)
|
||||
free(pIpForwardTable);
|
||||
|
||||
return (dwStatus == NO_ERROR);
|
||||
#else
|
||||
// Not implemented yet
|
||||
return false;
|
||||
return RouterWin::Instance().routeAdd(ip, gw, mask);
|
||||
#elif defined (Q_OS_MAC)
|
||||
return RouterMac::Instance().routeAdd(ip, gw, mask);
|
||||
#endif
|
||||
}
|
||||
|
||||
int Router::routeAddList(const QString &gw, const QStringList &ips)
|
||||
{
|
||||
qDebug().noquote() << QString("ROUTE ADD List: IPs size:%1, GW: %2")
|
||||
.arg(ips.size())
|
||||
.arg(gw);
|
||||
|
||||
qDebug().noquote() << QString("ROUTE ADD List: IPs:\n%1")
|
||||
.arg(ips.join("\n"));
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
|
||||
DWORD dwSize = 0;
|
||||
BOOL bOrder = FALSE;
|
||||
DWORD dwStatus = 0;
|
||||
|
||||
|
||||
// Find out how big our buffer needs to be.
|
||||
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
|
||||
if (dwStatus == ERROR_INSUFFICIENT_BUFFER) {
|
||||
// Allocate the memory for the table
|
||||
if (!(pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc(dwSize))) {
|
||||
qDebug() << "Malloc failed. Out of memory.";
|
||||
return 0;
|
||||
}
|
||||
// Now get the table.
|
||||
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
|
||||
}
|
||||
|
||||
|
||||
if (dwStatus != ERROR_SUCCESS) {
|
||||
qDebug() << "getIpForwardTable failed.";
|
||||
if (pIpForwardTable)
|
||||
free(pIpForwardTable);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int success_count = 0;
|
||||
|
||||
|
||||
|
||||
QString mask;
|
||||
|
||||
MIB_IPFORWARDROW ipfrow;
|
||||
|
||||
|
||||
ipfrow.dwForwardPolicy = 0;
|
||||
ipfrow.dwForwardAge = 0;
|
||||
|
||||
ipfrow.dwForwardNextHop = inet_addr(gw.toStdString().c_str());
|
||||
ipfrow.dwForwardType = 4; /* XXX - next hop != final dest */
|
||||
ipfrow.dwForwardProto = 3; /* XXX - MIB_PROTO_NETMGMT */
|
||||
|
||||
|
||||
// Set iface for route
|
||||
IPAddr dwGwAddr = inet_addr(gw.toStdString().c_str());
|
||||
if (GetBestInterface(dwGwAddr, &ipfrow.dwForwardIfIndex) != NO_ERROR) {
|
||||
qDebug() << "Router::routeAddList : GetBestInterface failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get TAP iface metric to set it for new routes
|
||||
MIB_IPINTERFACE_ROW tap_iface;
|
||||
InitializeIpInterfaceEntry(&tap_iface);
|
||||
tap_iface.InterfaceIndex = ipfrow.dwForwardIfIndex;
|
||||
tap_iface.Family = AF_INET;
|
||||
dwStatus = GetIpInterfaceEntry(&tap_iface);
|
||||
if (dwStatus == NO_ERROR){
|
||||
ipfrow.dwForwardMetric1 = tap_iface.Metric;
|
||||
}
|
||||
else {
|
||||
qDebug() << "Router::routeAddList: failed GetIpInterfaceEntry(), Error:" << dwStatus;
|
||||
ipfrow.dwForwardMetric1 = 256;
|
||||
}
|
||||
ipfrow.dwForwardMetric2 = 0;
|
||||
ipfrow.dwForwardMetric3 = 0;
|
||||
ipfrow.dwForwardMetric4 = 0;
|
||||
ipfrow.dwForwardMetric5 = 0;
|
||||
|
||||
for (int i = 0; i < ips.size(); ++i) {
|
||||
QString ip = ips.at(i);
|
||||
if (ip.isEmpty()) continue;
|
||||
|
||||
mask = "255.255.255.255";
|
||||
if (ip.endsWith(".0")) mask = "255.255.255.0";
|
||||
if (ip.endsWith(".0.0")) mask = "255.255.0.0";
|
||||
if (ip.endsWith(".0.0.0")) mask = "255.0.0.0";
|
||||
|
||||
// address
|
||||
ipfrow.dwForwardDest = inet_addr(ip.toStdString().c_str());
|
||||
|
||||
|
||||
// mask
|
||||
in_addr maskAddr;
|
||||
inet_pton(AF_INET, mask.toStdString().c_str(), &maskAddr);
|
||||
ipfrow.dwForwardMask = maskAddr.S_un.S_addr;
|
||||
|
||||
dwStatus = CreateIpForwardEntry(&ipfrow);
|
||||
if (dwStatus == NO_ERROR){
|
||||
ipForwardRows.append(ipfrow);
|
||||
//qDebug() << "Gateway changed successfully";
|
||||
}
|
||||
else {
|
||||
qDebug() << "Router::routeAdd: failed CreateIpForwardEntry(), Error:" << ip << dwStatus;
|
||||
}
|
||||
|
||||
if (dwStatus == NO_ERROR) success_count++;
|
||||
}
|
||||
|
||||
|
||||
// Free resources
|
||||
if (pIpForwardTable)
|
||||
free(pIpForwardTable);
|
||||
|
||||
qDebug() << "Router::routeAddList finished, success: " << success_count << "/" << ips.size();
|
||||
return success_count;
|
||||
#else
|
||||
// Not implemented yet
|
||||
return false;
|
||||
return RouterWin::Instance().routeAddList(gw, ips);
|
||||
#elif defined (Q_OS_MAC)
|
||||
return RouterMac::Instance().routeAddList(gw, ips);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Router::clearSavedRoutes()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
if (ipForwardRows.isEmpty()) return true;
|
||||
|
||||
qDebug() << "forward rows size:" << ipForwardRows.size();
|
||||
|
||||
// Declare and initialize variables
|
||||
PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
|
||||
DWORD dwSize = 0;
|
||||
BOOL bOrder = FALSE;
|
||||
DWORD dwStatus = 0;
|
||||
|
||||
// Find out how big our buffer needs to be.
|
||||
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
|
||||
if (dwStatus == ERROR_INSUFFICIENT_BUFFER) {
|
||||
// Allocate the memory for the table
|
||||
if (!(pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc(dwSize))) {
|
||||
qDebug() << "Router::clearSavedRoutes : Malloc failed. Out of memory";
|
||||
return false;
|
||||
}
|
||||
// Now get the table.
|
||||
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
|
||||
}
|
||||
|
||||
if (dwStatus != ERROR_SUCCESS) {
|
||||
qDebug() << "Router::clearSavedRoutes : getIpForwardTable failed";
|
||||
if (pIpForwardTable)
|
||||
free(pIpForwardTable);
|
||||
return false;
|
||||
}
|
||||
|
||||
int removed_count = 0;
|
||||
for (int i = 0; i < ipForwardRows.size(); ++i) {
|
||||
dwStatus = DeleteIpForwardEntry(&ipForwardRows[i]);
|
||||
|
||||
if (dwStatus != ERROR_SUCCESS) {
|
||||
qDebug() << "Router::clearSavedRoutes : Could not delete old row" << i;
|
||||
}
|
||||
else removed_count++;
|
||||
}
|
||||
|
||||
if (pIpForwardTable)
|
||||
free(pIpForwardTable);
|
||||
|
||||
qDebug() << "Router::clearSavedRoutes : removed routes:" << removed_count << "of" << ipForwardRows.size();
|
||||
ipForwardRows.clear();
|
||||
|
||||
return true;
|
||||
#else
|
||||
// Not implemented yet
|
||||
return false;
|
||||
return RouterWin::Instance().clearSavedRoutes();
|
||||
#elif defined (Q_OS_MAC)
|
||||
return RouterMac::Instance().clearSavedRoutes();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Router::routeDelete(const QString &ip)
|
||||
{
|
||||
qDebug().noquote() << QString("ROUTE DELETE, IP: %1").arg(ip);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
QProcess p;
|
||||
p.setProcessChannelMode(QProcess::MergedChannels);
|
||||
QString command = QString("route delete %1")
|
||||
.arg(ip);
|
||||
|
||||
p.start(command);
|
||||
p.waitForFinished();
|
||||
qDebug().noquote() << "OUTPUT route delete: " + p.readAll();
|
||||
|
||||
return true;
|
||||
#else
|
||||
// Not implemented yet
|
||||
return false;
|
||||
#endif
|
||||
return RouterWin::Instance().routeDelete(ip);
|
||||
#elif defined (Q_OS_MAC)
|
||||
return RouterMac::Instance().routeDelete(ip);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Router::flushDns()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
QProcess p;
|
||||
p.setProcessChannelMode(QProcess::MergedChannels);
|
||||
QString command = QString("ipconfig /flushdns");
|
||||
|
||||
p.start(command);
|
||||
p.waitForFinished();
|
||||
//qDebug().noquote() << "OUTPUT ipconfig /flushdns: " + p.readAll();
|
||||
RouterWin::Instance().flushDns();
|
||||
#elif defined (Q_OS_MAC)
|
||||
RouterMac::Instance().flushDns();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -6,29 +6,7 @@
|
||||
#include <QSettings>
|
||||
#include <QHash>
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <WinSock2.h> //includes Windows.h
|
||||
#include <WS2tcpip.h>
|
||||
|
||||
|
||||
#include <iphlpapi.h>
|
||||
#include <IcmpAPI.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
typedef uint8_t u8_t ;
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
|
||||
#endif //Q_OS_WIN
|
||||
|
||||
#include <QObject>
|
||||
|
||||
/**
|
||||
* @brief The Router class - General class for handling ip routing
|
||||
@@ -37,24 +15,12 @@ class Router : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static Router& Instance();
|
||||
|
||||
bool routeAdd(const QString &ip, const QString &gw, QString mask = QString());
|
||||
int routeAddList(const QString &gw, const QStringList &ips);
|
||||
bool clearSavedRoutes();
|
||||
bool routeDelete(const QString &ip);
|
||||
void flushDns();
|
||||
|
||||
public slots:
|
||||
|
||||
private:
|
||||
Router() {}
|
||||
Router(Router const &) = delete;
|
||||
Router& operator= (Router const&) = delete;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
QList<MIB_IPFORWARDROW> ipForwardRows;
|
||||
#endif
|
||||
static bool routeAdd(const QString &ip, const QString &gw, QString mask = QString());
|
||||
static int routeAddList(const QString &gw, const QStringList &ips);
|
||||
static bool clearSavedRoutes();
|
||||
static bool routeDelete(const QString &ip);
|
||||
static void flushDns();
|
||||
};
|
||||
|
||||
#endif // ROUTER_H
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
#include "router_mac.h"
|
||||
|
||||
#include <QProcess>
|
||||
|
||||
RouterMac &RouterMac::Instance()
|
||||
{
|
||||
static RouterMac s;
|
||||
return s;
|
||||
}
|
||||
|
||||
bool RouterMac::routeAdd(const QString &ip, const QString &gw, QString mask)
|
||||
{
|
||||
// route add -host ip gw
|
||||
QProcess p;
|
||||
p.setProcessChannelMode(QProcess::MergedChannels);
|
||||
|
||||
p.start("route", QStringList() << "add" << "-host" << ip << gw);
|
||||
p.waitForFinished();
|
||||
qDebug().noquote() << "OUTPUT routeAdd: " + p.readAll();
|
||||
bool ok = (p.exitCode() == 0);
|
||||
if (ok) {
|
||||
m_addedRoutes.append(ip);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
int RouterMac::routeAddList(const QString &gw, const QStringList &ips)
|
||||
{
|
||||
int cnt = 0;
|
||||
for (const QString &ip: ips) {
|
||||
if (routeAdd(ip, gw)) cnt++;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
bool RouterMac::clearSavedRoutes()
|
||||
{
|
||||
// No need to delete routes after iface down
|
||||
return true;
|
||||
|
||||
// int cnt = 0;
|
||||
// for (const QString &ip: m_addedRoutes) {
|
||||
// if (routeDelete(ip)) cnt++;
|
||||
// }
|
||||
// return (cnt == m_addedRoutes.count());
|
||||
}
|
||||
|
||||
bool RouterMac::routeDelete(const QString &ip)
|
||||
{
|
||||
// route delete ip gw
|
||||
QProcess p;
|
||||
p.setProcessChannelMode(QProcess::MergedChannels);
|
||||
|
||||
p.start("route", QStringList() << "delete" << ip);
|
||||
p.waitForFinished();
|
||||
qDebug().noquote() << "OUTPUT routeDelete: " + p.readAll();
|
||||
|
||||
return p.exitCode() == 0;}
|
||||
|
||||
void RouterMac::flushDns()
|
||||
{
|
||||
// sudo killall -HUP mDNSResponder
|
||||
QProcess p;
|
||||
p.setProcessChannelMode(QProcess::MergedChannels);
|
||||
|
||||
p.start("killall", QStringList() << "-HUP" << "mDNSResponder");
|
||||
p.waitForFinished();
|
||||
qDebug().noquote() << "OUTPUT killall -HUP mDNSResponder: " + p.readAll();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef ROUTERMAC_H
|
||||
#define ROUTERMAC_H
|
||||
|
||||
#include <QTimer>
|
||||
#include <QString>
|
||||
#include <QSettings>
|
||||
#include <QHash>
|
||||
#include <QDebug>
|
||||
#include <QObject>
|
||||
|
||||
|
||||
/**
|
||||
* @brief The Router class - General class for handling ip routing
|
||||
*/
|
||||
class RouterMac : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static RouterMac& Instance();
|
||||
|
||||
bool routeAdd(const QString &ip, const QString &gw, QString mask = QString());
|
||||
int routeAddList(const QString &gw, const QStringList &ips);
|
||||
bool clearSavedRoutes();
|
||||
bool routeDelete(const QString &ip);
|
||||
void flushDns();
|
||||
|
||||
public slots:
|
||||
|
||||
private:
|
||||
RouterMac() {}
|
||||
RouterMac(RouterMac const &) = delete;
|
||||
RouterMac& operator= (RouterMac const&) = delete;
|
||||
|
||||
QList<QString> m_addedRoutes;
|
||||
};
|
||||
|
||||
#endif // ROUTERMAC_H
|
||||
|
||||
@@ -0,0 +1,302 @@
|
||||
#include "router_win.h"
|
||||
|
||||
#include <QProcess>
|
||||
|
||||
RouterWin &RouterWin::Instance()
|
||||
{
|
||||
static RouterWin s;
|
||||
return s;
|
||||
}
|
||||
|
||||
bool RouterWin::routeAdd(const QString &ip, const QString &gw, QString mask)
|
||||
{
|
||||
qDebug().noquote() << QString("ROUTE ADD: IP:%1 %2 GW %3")
|
||||
.arg(ip)
|
||||
.arg(mask)
|
||||
.arg(gw);
|
||||
|
||||
if (mask == "") {
|
||||
mask = "255.255.255.255";
|
||||
if (ip.endsWith(".0")) mask = "255.255.255.0";
|
||||
if (ip.endsWith(".0.0")) mask = "255.255.0.0";
|
||||
if (ip.endsWith(".0.0.0")) mask = "255.0.0.0";
|
||||
}
|
||||
|
||||
|
||||
PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
|
||||
MIB_IPFORWARDROW ipfrow;
|
||||
DWORD dwSize = 0;
|
||||
BOOL bOrder = FALSE;
|
||||
DWORD dwStatus = 0;
|
||||
|
||||
|
||||
// Find out how big our buffer needs to be.
|
||||
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
|
||||
if (dwStatus == ERROR_INSUFFICIENT_BUFFER) {
|
||||
// Allocate the memory for the table
|
||||
if (!(pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc(dwSize))) {
|
||||
qDebug() << "Malloc failed. Out of memory.";
|
||||
return false;
|
||||
}
|
||||
// Now get the table.
|
||||
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
|
||||
}
|
||||
|
||||
|
||||
if (dwStatus != ERROR_SUCCESS) {
|
||||
qDebug() << "getIpForwardTable failed.";
|
||||
if (pIpForwardTable)
|
||||
free(pIpForwardTable);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set iface for route
|
||||
IPAddr dwGwAddr = inet_addr(gw.toStdString().c_str());
|
||||
if (GetBestInterface(dwGwAddr, &ipfrow.dwForwardIfIndex) != NO_ERROR) {
|
||||
qDebug() << "Router::routeAdd : GetBestInterface failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
// address
|
||||
ipfrow.dwForwardDest = inet_addr(ip.toStdString().c_str());
|
||||
|
||||
// mask
|
||||
in_addr maskAddr;
|
||||
inet_pton(AF_INET, mask.toStdString().c_str(), &maskAddr);
|
||||
ipfrow.dwForwardMask = maskAddr.S_un.S_addr;
|
||||
|
||||
// Get TAP iface metric to set it for new routes
|
||||
MIB_IPINTERFACE_ROW tap_iface;
|
||||
InitializeIpInterfaceEntry(&tap_iface);
|
||||
tap_iface.InterfaceIndex = ipfrow.dwForwardIfIndex;
|
||||
tap_iface.Family = AF_INET;
|
||||
dwStatus = GetIpInterfaceEntry(&tap_iface);
|
||||
if (dwStatus == NO_ERROR){
|
||||
ipfrow.dwForwardMetric1 = tap_iface.Metric;
|
||||
}
|
||||
else {
|
||||
qDebug() << "Router::routeAdd: failed GetIpInterfaceEntry(), Error:" << dwStatus;
|
||||
ipfrow.dwForwardMetric1 = 256;
|
||||
}
|
||||
ipfrow.dwForwardMetric2 = 0;
|
||||
ipfrow.dwForwardMetric3 = 0;
|
||||
ipfrow.dwForwardMetric4 = 0;
|
||||
ipfrow.dwForwardMetric5 = 0;
|
||||
|
||||
ipfrow.dwForwardAge = 0;
|
||||
|
||||
ipfrow.dwForwardNextHop = inet_addr(gw.toStdString().c_str());
|
||||
ipfrow.dwForwardType = 4; /* XXX - next hop != final dest */
|
||||
ipfrow.dwForwardProto = 3; /* XXX - MIB_PROTO_NETMGMT */
|
||||
|
||||
|
||||
dwStatus = CreateIpForwardEntry(&ipfrow);
|
||||
if (dwStatus == NO_ERROR){
|
||||
ipForwardRows.append(ipfrow);
|
||||
//qDebug() << "Gateway changed successfully";
|
||||
}
|
||||
else {
|
||||
qDebug() << "Router::routeAdd: failed CreateIpForwardEntry()";
|
||||
qDebug() << "Error: " << dwStatus;
|
||||
}
|
||||
|
||||
// Free resources
|
||||
if (pIpForwardTable)
|
||||
free(pIpForwardTable);
|
||||
|
||||
return (dwStatus == NO_ERROR);
|
||||
}
|
||||
|
||||
int RouterWin::routeAddList(const QString &gw, const QStringList &ips)
|
||||
{
|
||||
qDebug().noquote() << QString("ROUTE ADD List: IPs size:%1, GW: %2")
|
||||
.arg(ips.size())
|
||||
.arg(gw);
|
||||
|
||||
qDebug().noquote() << QString("ROUTE ADD List: IPs:\n%1")
|
||||
.arg(ips.join("\n"));
|
||||
|
||||
|
||||
|
||||
PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
|
||||
DWORD dwSize = 0;
|
||||
BOOL bOrder = FALSE;
|
||||
DWORD dwStatus = 0;
|
||||
|
||||
|
||||
// Find out how big our buffer needs to be.
|
||||
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
|
||||
if (dwStatus == ERROR_INSUFFICIENT_BUFFER) {
|
||||
// Allocate the memory for the table
|
||||
if (!(pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc(dwSize))) {
|
||||
qDebug() << "Malloc failed. Out of memory.";
|
||||
return 0;
|
||||
}
|
||||
// Now get the table.
|
||||
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
|
||||
}
|
||||
|
||||
|
||||
if (dwStatus != ERROR_SUCCESS) {
|
||||
qDebug() << "getIpForwardTable failed.";
|
||||
if (pIpForwardTable)
|
||||
free(pIpForwardTable);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int success_count = 0;
|
||||
|
||||
QString mask;
|
||||
|
||||
MIB_IPFORWARDROW ipfrow;
|
||||
|
||||
|
||||
ipfrow.dwForwardPolicy = 0;
|
||||
ipfrow.dwForwardAge = 0;
|
||||
|
||||
ipfrow.dwForwardNextHop = inet_addr(gw.toStdString().c_str());
|
||||
ipfrow.dwForwardType = 4; /* XXX - next hop != final dest */
|
||||
ipfrow.dwForwardProto = 3; /* XXX - MIB_PROTO_NETMGMT */
|
||||
|
||||
|
||||
// Set iface for route
|
||||
IPAddr dwGwAddr = inet_addr(gw.toStdString().c_str());
|
||||
if (GetBestInterface(dwGwAddr, &ipfrow.dwForwardIfIndex) != NO_ERROR) {
|
||||
qDebug() << "Router::routeAddList : GetBestInterface failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get TAP iface metric to set it for new routes
|
||||
MIB_IPINTERFACE_ROW tap_iface;
|
||||
InitializeIpInterfaceEntry(&tap_iface);
|
||||
tap_iface.InterfaceIndex = ipfrow.dwForwardIfIndex;
|
||||
tap_iface.Family = AF_INET;
|
||||
dwStatus = GetIpInterfaceEntry(&tap_iface);
|
||||
if (dwStatus == NO_ERROR){
|
||||
ipfrow.dwForwardMetric1 = tap_iface.Metric;
|
||||
}
|
||||
else {
|
||||
qDebug() << "Router::routeAddList: failed GetIpInterfaceEntry(), Error:" << dwStatus;
|
||||
ipfrow.dwForwardMetric1 = 256;
|
||||
}
|
||||
ipfrow.dwForwardMetric2 = 0;
|
||||
ipfrow.dwForwardMetric3 = 0;
|
||||
ipfrow.dwForwardMetric4 = 0;
|
||||
ipfrow.dwForwardMetric5 = 0;
|
||||
|
||||
for (int i = 0; i < ips.size(); ++i) {
|
||||
QString ip = ips.at(i);
|
||||
if (ip.isEmpty()) continue;
|
||||
|
||||
mask = "255.255.255.255";
|
||||
if (ip.endsWith(".0")) mask = "255.255.255.0";
|
||||
if (ip.endsWith(".0.0")) mask = "255.255.0.0";
|
||||
if (ip.endsWith(".0.0.0")) mask = "255.0.0.0";
|
||||
|
||||
// address
|
||||
ipfrow.dwForwardDest = inet_addr(ip.toStdString().c_str());
|
||||
|
||||
|
||||
// mask
|
||||
in_addr maskAddr;
|
||||
inet_pton(AF_INET, mask.toStdString().c_str(), &maskAddr);
|
||||
ipfrow.dwForwardMask = maskAddr.S_un.S_addr;
|
||||
|
||||
dwStatus = CreateIpForwardEntry(&ipfrow);
|
||||
if (dwStatus == NO_ERROR){
|
||||
ipForwardRows.append(ipfrow);
|
||||
//qDebug() << "Gateway changed successfully";
|
||||
}
|
||||
else {
|
||||
qDebug() << "Router::routeAdd: failed CreateIpForwardEntry(), Error:" << ip << dwStatus;
|
||||
}
|
||||
|
||||
if (dwStatus == NO_ERROR) success_count++;
|
||||
}
|
||||
|
||||
|
||||
// Free resources
|
||||
if (pIpForwardTable)
|
||||
free(pIpForwardTable);
|
||||
|
||||
qDebug() << "Router::routeAddList finished, success: " << success_count << "/" << ips.size();
|
||||
return success_count;
|
||||
}
|
||||
|
||||
bool RouterWin::clearSavedRoutes()
|
||||
{
|
||||
if (ipForwardRows.isEmpty()) return true;
|
||||
|
||||
qDebug() << "forward rows size:" << ipForwardRows.size();
|
||||
|
||||
// Declare and initialize variables
|
||||
PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
|
||||
DWORD dwSize = 0;
|
||||
BOOL bOrder = FALSE;
|
||||
DWORD dwStatus = 0;
|
||||
|
||||
// Find out how big our buffer needs to be.
|
||||
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
|
||||
if (dwStatus == ERROR_INSUFFICIENT_BUFFER) {
|
||||
// Allocate the memory for the table
|
||||
if (!(pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc(dwSize))) {
|
||||
qDebug() << "Router::clearSavedRoutes : Malloc failed. Out of memory";
|
||||
return false;
|
||||
}
|
||||
// Now get the table.
|
||||
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
|
||||
}
|
||||
|
||||
if (dwStatus != ERROR_SUCCESS) {
|
||||
qDebug() << "Router::clearSavedRoutes : getIpForwardTable failed";
|
||||
if (pIpForwardTable)
|
||||
free(pIpForwardTable);
|
||||
return false;
|
||||
}
|
||||
|
||||
int removed_count = 0;
|
||||
for (int i = 0; i < ipForwardRows.size(); ++i) {
|
||||
dwStatus = DeleteIpForwardEntry(&ipForwardRows[i]);
|
||||
|
||||
if (dwStatus != ERROR_SUCCESS) {
|
||||
qDebug() << "Router::clearSavedRoutes : Could not delete old row" << i;
|
||||
}
|
||||
else removed_count++;
|
||||
}
|
||||
|
||||
if (pIpForwardTable)
|
||||
free(pIpForwardTable);
|
||||
|
||||
qDebug() << "Router::clearSavedRoutes : removed routes:" << removed_count << "of" << ipForwardRows.size();
|
||||
ipForwardRows.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RouterWin::routeDelete(const QString &ip)
|
||||
{
|
||||
qDebug().noquote() << QString("ROUTE DELETE, IP: %1").arg(ip);
|
||||
|
||||
QProcess p;
|
||||
p.setProcessChannelMode(QProcess::MergedChannels);
|
||||
QString command = QString("route delete %1")
|
||||
.arg(ip);
|
||||
|
||||
p.start(command);
|
||||
p.waitForFinished();
|
||||
qDebug().noquote() << "OUTPUT route delete: " + p.readAll();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RouterWin::flushDns()
|
||||
{
|
||||
QProcess p;
|
||||
p.setProcessChannelMode(QProcess::MergedChannels);
|
||||
QString command = QString("ipconfig /flushdns");
|
||||
|
||||
p.start(command);
|
||||
p.waitForFinished();
|
||||
//qDebug().noquote() << "OUTPUT ipconfig /flushdns: " + p.readAll();
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#ifndef ROUTERWIN_H
|
||||
#define ROUTERWIN_H
|
||||
|
||||
#include <QTimer>
|
||||
#include <QString>
|
||||
#include <QSettings>
|
||||
#include <QHash>
|
||||
#include <QDebug>
|
||||
#include <QObject>
|
||||
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <WinSock2.h> //includes Windows.h
|
||||
#include <WS2tcpip.h>
|
||||
|
||||
|
||||
#include <iphlpapi.h>
|
||||
#include <IcmpAPI.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
typedef uint8_t u8_t ;
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#endif //Q_OS_WIN
|
||||
|
||||
|
||||
/**
|
||||
* @brief The Router class - General class for handling ip routing
|
||||
*/
|
||||
class RouterWin : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static RouterWin& Instance();
|
||||
|
||||
bool routeAdd(const QString &ip, const QString &gw, QString mask = QString());
|
||||
int routeAddList(const QString &gw, const QStringList &ips);
|
||||
bool clearSavedRoutes();
|
||||
bool routeDelete(const QString &ip);
|
||||
void flushDns();
|
||||
|
||||
public slots:
|
||||
|
||||
private:
|
||||
RouterWin() {}
|
||||
RouterWin(RouterWin const &) = delete;
|
||||
RouterWin& operator= (RouterWin const&) = delete;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
QList<MIB_IPFORWARDROW> ipForwardRows;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // ROUTERWIN_H
|
||||
@@ -25,10 +25,12 @@ SOURCES = \
|
||||
|
||||
win32 {
|
||||
HEADERS += \
|
||||
tapcontroller_win.h
|
||||
tapcontroller_win.h \
|
||||
router_win.h
|
||||
|
||||
SOURCES += \
|
||||
tapcontroller_win.cpp
|
||||
tapcontroller_win.cpp \
|
||||
router_win.cpp
|
||||
|
||||
LIBS += \
|
||||
-luser32 \
|
||||
@@ -40,6 +42,14 @@ LIBS += \
|
||||
-lgdi32
|
||||
}
|
||||
|
||||
macx {
|
||||
HEADERS += \
|
||||
router_mac.h
|
||||
|
||||
SOURCES += \
|
||||
router_mac.cpp
|
||||
}
|
||||
|
||||
include(../src/qtservice.pri)
|
||||
|
||||
#CONFIG(release, debug|release) {
|
||||
|
||||