diff --git a/kylin-nm.pro b/kylin-nm.pro index 4999ff17..6e946347 100644 --- a/kylin-nm.pro +++ b/kylin-nm.pro @@ -5,6 +5,7 @@ CONFIG += ordered \ SUBDIRS = \ plugins/plugin.pro \ + src-vpn/src-vpn.pro \ src \ TRANSLATIONS += \ diff --git a/src-vpn/frontend/frontend.pri b/src-vpn/frontend/frontend.pri new file mode 100644 index 00000000..35d7edbe --- /dev/null +++ b/src-vpn/frontend/frontend.pri @@ -0,0 +1,7 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/vpnobject.h + +SOURCES += \ + $$PWD/vpnobject.cpp diff --git a/src-vpn/frontend/vpnobject.cpp b/src-vpn/frontend/vpnobject.cpp new file mode 100644 index 00000000..8959d72a --- /dev/null +++ b/src-vpn/frontend/vpnobject.cpp @@ -0,0 +1,102 @@ +#include "vpnobject.h" +#include +#include + +#include "ukuistylehelper/ukuistylehelper.h" + +#define MAINWINDOW_WIDTH 420 +#define MAINWINDOW_HEIGHT 476 + +vpnObject::vpnObject(QMainWindow *parent) : QMainWindow(parent) +{ + initUI(); + initTrayIcon(); + initDbusConnnect(); + this->setFixedSize(MAINWINDOW_WIDTH, MAINWINDOW_HEIGHT); +} + +vpnObject::~vpnObject() +{ + if (m_vpnGsettings != nullptr) { + delete m_vpnGsettings; + m_vpnGsettings = nullptr; + } +} + +void vpnObject::initUI() +{ + vpnWidget = new QWidget(this); + QPalette pal = qApp->palette(); +// pal.set + vpnWidget->setPalette(pal); + this->setCentralWidget(vpnWidget); +} + +void vpnObject::initTrayIcon() +{ + m_vpnTrayIcon = new QSystemTrayIcon(this); + m_vpnTrayIcon->setToolTip(QString(tr("vpn tool"))); + m_vpnTrayIcon->setIcon(QIcon::fromTheme("ukui-vpn-symbolic")); + m_vpnTrayIcon->setVisible(true); + initVpnIconVisible(); + connect(m_vpnTrayIcon, &QSystemTrayIcon::activated, this, &vpnObject::onTrayIconActivated); +} + +void vpnObject::initVpnIconVisible() +{ + if (QGSettings::isSchemaInstalled(QByteArray(GSETTINGS_VPNICON_VISIBLE))) { + m_vpnGsettings = new QGSettings(QByteArray(GSETTINGS_VPNICON_VISIBLE)); + if (m_vpnGsettings->keys().contains(QString(VISIBLE))) { + m_vpnTrayIcon->setVisible(m_vpnGsettings->get(VISIBLE).toBool()); + connect(m_vpnGsettings, &QGSettings::changed, this, [=]() { + m_vpnTrayIcon->setVisible(m_vpnGsettings->get(VISIBLE).toBool()); + }); + } + } +} + +/** + * @brief vpnObject::onTrayIconActivated 点击托盘图标的槽函数 + */ +void vpnObject::onTrayIconActivated(QSystemTrayIcon::ActivationReason reason) +{ + switch(reason) { + case QSystemTrayIcon::Trigger: + if(this->isActiveWindow()) { + this->hide(); + } else { + onShowMainWindow(); + } + break; + + default: + break; + } +} + +void vpnObject::onShowMainWindow() +{ + //去除窗管标题栏,传入参数为QWidget* + kdk::UkuiStyleHelper::self()->removeHeader(this); + this->show(); + this->raise(); + this->activateWindow(); +} + +void vpnObject::initDbusConnnect() +{ + //模式切换 + QDBusConnection::sessionBus().connect(QString("com.kylin.statusmanager.interface"), + QString("/"), + QString("com.kylin.statusmanager.interface"), + QString("mode_change_signal"), this, SLOT(onTabletModeChanged(bool))); +} + +void vpnObject::onTabletModeChanged(bool mode) +{ + qDebug() << "TabletMode change" << mode; + Q_UNUSED(mode) + //模式切换时,隐藏主界面 + this->hide(); +} + diff --git a/src-vpn/frontend/vpnobject.h b/src-vpn/frontend/vpnobject.h new file mode 100644 index 00000000..01e1feea --- /dev/null +++ b/src-vpn/frontend/vpnobject.h @@ -0,0 +1,60 @@ +#ifndef VPNOBJECT_H +#define VPNOBJECT_H + +#include +#include +#include +#include +#include +#include +#include + +#define VISIBLE "visible" +#define GSETTINGS_VPNICON_VISIBLE "org.ukui.kylin-nm.vpnicon" +//const QByteArray GSETTINGS_VPNICON_VISIBLE = "org.ukui.kylin-nm.vpnicon"; + + +class vpnObject : public QMainWindow +{ + Q_OBJECT +public: + explicit vpnObject(QMainWindow *parent = nullptr); + ~vpnObject(); + +private: + void initUI(); + void initTrayIcon(); + void initVpnIconVisible(); + void initDbusConnnect(); + + +private: + QWidget * vpnWidget = nullptr; + + QSystemTrayIcon * m_vpnTrayIcon = nullptr; + QGSettings * m_vpnGsettings; //VPN配置文件 + QGSettings * StyleSettings = nullptr; + double tran =1; + + QDBusInterface * m_positionInterface = nullptr; + bool m_isShowInCenter = false; + +public Q_SLOTS: + void onShowMainWindow(); + +private Q_SLOTS: + void onTrayIconActivated(QSystemTrayIcon::ActivationReason reason); + void onTabletModeChanged(bool mode); + + +Q_SIGNALS: + void vpnAdd(QStringList info); + void vpnRemove(QString dbusPath); + void vpnUpdate(QStringList info); + void vpnActiveConnectionStateChanged(QString uuid, int status); + void activateFailed(QString errorMessage); + void deactivateFailed(QString errorMessage); + void mainWindowVisibleChanged(const bool &visible); +}; + +#endif // VPNOBJECT_H diff --git a/src-vpn/kylin-vpn-dbus/kylin-vpn-dbus.pri b/src-vpn/kylin-vpn-dbus/kylin-vpn-dbus.pri new file mode 100644 index 00000000..3a21e81e --- /dev/null +++ b/src-vpn/kylin-vpn-dbus/kylin-vpn-dbus.pri @@ -0,0 +1,8 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/kylinvpndbus.h \ + +SOURCES += \ + $$PWD/kylinvpndbus.cpp \ + diff --git a/src-vpn/kylin-vpn-dbus/kylinvpndbus.cpp b/src-vpn/kylin-vpn-dbus/kylinvpndbus.cpp new file mode 100644 index 00000000..1f92b3c5 --- /dev/null +++ b/src-vpn/kylin-vpn-dbus/kylinvpndbus.cpp @@ -0,0 +1,41 @@ +#include "kylinvpndbus.h" + +KylinVpnDbus::KylinVpnDbus(QObject *parent) : QObject(parent) +{ + qDBusRegisterMetaType >(); +} + +void KylinVpnDbus::getVirtualList(QVector &vector) +{ + +} + +void KylinVpnDbus::deleteVpn(const QString &connUuid) +{ + +} + +void KylinVpnDbus::activateVpn(const QString &connUuid) +{ + +} + +void KylinVpnDbus::deactivateVpn(const QString &connUuid) +{ + +} + +void KylinVpnDbus::showKylinVpn() +{ + +} + +void KylinVpnDbus::showVpnAddWidget() +{ + +} + +void KylinVpnDbus::showDetailPage(const QString &connUuid) +{ + +} diff --git a/src-vpn/kylin-vpn-dbus/kylinvpndbus.h b/src-vpn/kylin-vpn-dbus/kylinvpndbus.h new file mode 100644 index 00000000..f4880a43 --- /dev/null +++ b/src-vpn/kylin-vpn-dbus/kylinvpndbus.h @@ -0,0 +1,44 @@ +#ifndef KYLINVPNDBUS_H +#define KYLINVPNDBUS_H + +#include +#include +#include +#include +#include + +#include + +class KylinVpnDbus : public QObject +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "com.kylin.kylinvpntest") +public: + explicit KylinVpnDbus(QObject *parent = nullptr); + ~KylinVpnDbus() = default; + +public Q_SLOTS: + Q_NOREPLY void getVirtualList(QVector &vector); + //删除 + Q_NOREPLY void deleteVpn(const QString &connUuid); + //连接 + Q_NOREPLY void activateVpn(const QString& connUuid); + //断开 + Q_NOREPLY void deactivateVpn(const QString& connUuid); + + void showKylinVpn(); + + Q_NOREPLY void showVpnAddWidget(); + Q_NOREPLY void showDetailPage(const QString& connUuid); + +Q_SIGNALS: + void vpnAdd(QStringList info); + void vpnRemove(QString dbusPath); + void vpnUpdate(QStringList info); + void vpnActiveConnectionStateChanged(QString uuid, int status); + + void activateFailed(QString errorMessage); + void deactivateFailed(QString errorMessage); +}; + +#endif // KYLINVPNDBUS_H diff --git a/src-vpn/kylin-vpn.desktop b/src-vpn/kylin-vpn.desktop new file mode 100644 index 00000000..464e2869 --- /dev/null +++ b/src-vpn/kylin-vpn.desktop @@ -0,0 +1,18 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Kylin VPN +Name[zh_CN]=麒麟VPN设置工具 +Name[zh_HK]=麒麟VPN設置工具 +Name[zh_TW]=麒麟VPN設置工具 +Icon=gnome-dev-ethernet +Comment=Beautiful Network Config Applet +Comment[zh_CN]=麒麟VPN设置工具,提供查看和简单设置功能,拥有美观的界面和舒适的操作. +Keywords=applet;vpn;network;network-manager; +Exec=/usr/bin/kylin-vpn +StartupNotify=false +Terminal=false +Type=Application +OnlyShowIn=UKUI +X-UKUI-AutoRestart=true +NoDisplay=true +X-UKUI-Autostart-Phase=Application diff --git a/src-vpn/main.cpp b/src-vpn/main.cpp new file mode 100644 index 00000000..9eee5492 --- /dev/null +++ b/src-vpn/main.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program 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, or (at your option) + * any later version. + * + * This program 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 this program; if not, see +#include +#include "qt-single-application.h" +#include +#include +#include +#include +#include +#include +#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) +#include "xatom-helper.h" +#endif + +#include "vpnobject.h" + +#define LOG_IDENT "kylin_vpn" + +const QString QT_TRANSLATE_FILE = "/usr/share/qt5/translations/qt_zh_CN.qm"; + +void messageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) +{ + QByteArray localMsg = msg.toLocal8Bit(); + QByteArray currentDateTime = QDateTime::currentDateTime().toString().toLocal8Bit(); + + bool showDebug = true; + QString logFilePath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/.config/ukui/kylin-nm.log"; + FILE *log_file = nullptr; + + if (showDebug) { + log_file = fopen(logFilePath.toLocal8Bit().constData(), "a+"); + } + + const char *file = context.file ? context.file : ""; + const char *function = context.function ? context.function : ""; + switch (type) { + case QtDebugMsg: + if (!log_file) { + break; + } + fprintf(log_file, "Debug: %s: %s (%s:%u, %s)\n", currentDateTime.constData(), localMsg.constData(), file, context.line, function); + break; + case QtInfoMsg: + fprintf(log_file? log_file: stdout, "Info: %s: %s (%s:%u, %s)\n", currentDateTime.constData(), localMsg.constData(), file, context.line, function); + break; + case QtWarningMsg: + fprintf(log_file? log_file: stderr, "Warning: %s: %s (%s:%u, %s)\n", currentDateTime.constData(), localMsg.constData(), file, context.line, function); + break; + case QtCriticalMsg: + fprintf(log_file? log_file: stderr, "Critical: %s: %s (%s:%u, %s)\n", currentDateTime.constData(), localMsg.constData(), file, context.line, function); + break; + case QtFatalMsg: + fprintf(log_file? log_file: stderr, "Fatal: %s: %s (%s:%u, %s)\n", currentDateTime.constData(), localMsg.constData(), file, context.line, function); + break; + } + + if (log_file) + fclose(log_file); +} + +int main(int argc, char *argv[]) +{ + initUkuiLog4qt("kylin-vpn"); + + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + + QString id = QString("kylin-vpn"+ QLatin1String(getenv("DISPLAY"))); + QtSingleApplication a(id, argc, argv); + + QApplication::setQuitOnLastWindowClosed(false); + +// QThread thread; +// KyNetworkResourceManager *p_networkResource = KyNetworkResourceManager::getInstance(); +// p_networkResource->moveToThread(&thread); +// QObject::connect(&thread, SIGNAL(started()), p_networkResource, SLOT(onInitNetwork())); +// thread.start(); + + // Internationalization + QString locale = QLocale::system().name(); + QTranslator trans_global; + qDebug() << "QLocale " << QLocale(); + if (trans_global.load(QLocale(), "kylin-vpn", "_", ":/translations/")) + { + a.installTranslator(&trans_global); + qDebug()<<"Translations load success"; + } else { + qWarning() << "Translations load fail"; + } + + QTranslator qtBaseTranslator; + if (qtBaseTranslator.load(QLocale(), "qt", "_", "/usr/share/qt5/translations/")) + { + a.installTranslator(&qtBaseTranslator); + qDebug()<<"QtBase Translations load success"; + } else { + qWarning() << "QtBase Translations load fail"; + } + +// while (!p_networkResource->NetworkManagerIsInited()) { +// ::usleep(1000); +// } + + vpnObject vpnobject; + a.setActivationWindow(&vpnobject); + vpnobject.setProperty("useStyleWindowManager", false); //禁用拖动 + a.setWindowIcon(QIcon::fromTheme("ukui-vpn-symbolic")); + + auto connection = QDBusConnection::sessionBus(); + KylinVpnDbus dbusObject(&vpnobject); + if (!connection.registerService("com.kylin.kylinvpntest") + || !connection.registerObject("/com/kylin/kylinvpntest", &dbusObject, + QDBusConnection::ExportAllSlots | QDBusConnection :: ExportAllSignals)) { + qCritical() << "QDbus register service failed reason:" << connection.lastError(); + } + + return a.exec(); +} diff --git a/src-vpn/org.ukui.kylin-vpn.switch.gschema.xml b/src-vpn/org.ukui.kylin-vpn.switch.gschema.xml new file mode 100644 index 00000000..47add632 --- /dev/null +++ b/src-vpn/org.ukui.kylin-vpn.switch.gschema.xml @@ -0,0 +1,9 @@ + + + + false + vpnicon visible + vpnicon visible.true is visible,false is invisible. + + + diff --git a/src-vpn/singleapplication/qt-local-peer.cpp b/src-vpn/singleapplication/qt-local-peer.cpp new file mode 100644 index 00000000..4a62537c --- /dev/null +++ b/src-vpn/singleapplication/qt-local-peer.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies) +** 2020 KylinSoft Co., Ltd. +** Contact: http://www.qt-project.org/legal +** +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** +****************************************************************************/ + + +#include "qt-local-peer.h" +#include +#include +#include + +#if defined(Q_OS_UNIX) +#include +#include +#include +#endif + +namespace QtLP_Private { +#include "qt-locked-file.cpp" +#include "qt-locked-file-unix.cpp" +} + +const char* QtLocalPeer::ack = "ack"; + +QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId) + : QObject(parent), id(appId) { + QString prefix = id; + if(id.isEmpty()) { + id = QCoreApplication::applicationFilePath(); +#if defined(Q_OS_WIN) + id = id.toLower(); +#endif + prefix = id.section(QLatin1Char('/'), -1); //完整路径按‘/’分隔后取最后一个字段 + } + prefix.remove(QRegExp("[^a-zA-Z]")); //去掉名称中的非字母 + prefix.truncate(6); //取前六位 + + QByteArray idc = id.toUtf8(); + quint16 idNum = qChecksum(idc.constData(), idc.size()); + socketName = QLatin1String("qtsingleapp-") + prefix + + QLatin1Char('-') + QString::number(idNum, 16); + +#if defined(Q_OS_WIN) + if(!pProcessIdToSessionId) { + QLibrary lib("kernel32"); + pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId"); + } + if(pProcessIdToSessionId) { + DWORD sessionId = 0; + pProcessIdToSessionId(GetCurrentProcessId(), &sessionId); + socketName += QLatin1Char('-') + QString::number(sessionId, 16); + } +#else + socketName += QLatin1Char('-') + QString::number(::getuid(), 16); +#endif + + server = new QLocalServer(this); + QString lockName = QDir(QDir::tempPath()).absolutePath() + + QLatin1Char('/') + socketName + + QLatin1String("-lockfile"); //tmp目录下的锁文件 + lockFile.setFileName(lockName); + lockFile.open(QIODevice::ReadWrite); +} + + + +bool QtLocalPeer::isClient() { + if(lockFile.isLocked()) + return false; + + if(!lockFile.lock(QtLP_Private::QtLockedFile::WriteLock, false)) + return true; + + //由于文件锁的存在,仅当本进程第一次启动时能执行到此并使server进行监听和关联槽函数 + bool res = server->listen(socketName); +#if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(4,5,0)) + // ### Workaround + if(!res && server->serverError() == QAbstractSocket::AddressInUseError) { + QFile::remove(QDir::cleanPath(QDir::tempPath()) + QLatin1Char('/') + socketName); + res = server->listen(socketName); + } +#endif + if(!res) + qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString())); + QObject::connect(server, &QLocalServer::newConnection, this, &QtLocalPeer::receiveConnection); + return false; +} + + +bool QtLocalPeer::sendMessage(const QString &message, int timeout) { + if(!isClient()) + return false; + + QLocalSocket socket; + bool connOk = false; + for(int i = 0; i < 2; i++) { + // Try twice, in case the other instance is just starting up + socket.connectToServer(socketName); + connOk = socket.waitForConnected(timeout / 2); + if(connOk || i) + break; + int ms = 250; +#if defined(Q_OS_WIN) + Sleep(DWORD(ms)); +#else + struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; + nanosleep(&ts, NULL); +#endif + } + if(!connOk) + return false; + + QByteArray uMsg(message.toUtf8()); + QDataStream ds(&socket); + ds.writeBytes(uMsg.constData(), uMsg.size()); + bool res = socket.waitForBytesWritten(timeout); + if(res) { + res &= socket.waitForReadyRead(timeout); // wait for ack + if(res) + res &= (socket.read(qstrlen(ack)) == ack); + } + return res; +} + +/** + * @brief QtLocalPeer::receiveConnection 当新进程启动时,会尝试连接此进程server,server接收到newConnection信号并触发此槽函数 + */ +void QtLocalPeer::receiveConnection() { + QLocalSocket* socket = server->nextPendingConnection(); //获取新进程的socket + if(!socket) + return; + + while(true) { + if(socket->state() == QLocalSocket::UnconnectedState) { + qWarning("QtLocalPeer: Peer disconnected"); + delete socket; + return; + } + if(socket->bytesAvailable() >= qint64(sizeof(quint32))) + break; + socket->waitForReadyRead(); + } + + QDataStream ds(socket); + QByteArray uMsg; + quint32 remaining; + ds >> remaining; + uMsg.resize(remaining); + int got = 0; + char* uMsgBuf = uMsg.data(); + do { + got = ds.readRawData(uMsgBuf, remaining); + remaining -= got; + uMsgBuf += got; + } while(remaining && got >= 0 && socket->waitForReadyRead(2000)); + if(got < 0) { + qWarning("QtLocalPeer: Message reception failed %s", socket->errorString().toLatin1().constData()); + delete socket; + return; + } + QString message(QString::fromUtf8(uMsg)); + socket->write(ack, qstrlen(ack)); + socket->waitForBytesWritten(1000); + socket->waitForDisconnected(1000); // make sure client reads ack + delete socket; + Q_EMIT messageReceived(message); //获取新进程的启动信息并作为信号发送给前端 +} diff --git a/src-vpn/singleapplication/qt-local-peer.h b/src-vpn/singleapplication/qt-local-peer.h new file mode 100644 index 00000000..883aec2a --- /dev/null +++ b/src-vpn/singleapplication/qt-local-peer.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies) +** 2020 KylinSoft Co., Ltd. +** Contact: http://www.qt-project.org/legal +** +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** +****************************************************************************/ + +#ifndef QTLOCALPEER_H +#define QTLOCALPEER_H + +#include +#include +#include + +#include "qt-locked-file.h" + +class QtLocalPeer : public QObject { + Q_OBJECT + +public: + QtLocalPeer(QObject *parent = 0, const QString &appId = QString()); + bool isClient(); + bool sendMessage(const QString &message, int timeout); + QString applicationId() const { + return id; + } + +Q_SIGNALS: + void messageReceived(const QString &message); + +protected Q_SLOTS: + void receiveConnection(); + +protected: + QString id; + QString socketName; + QLocalServer* server; + QtLP_Private::QtLockedFile lockFile; + +private: + static const char* ack; +}; + +#endif // QTLOCALPEER_H diff --git a/src-vpn/singleapplication/qt-locked-file-unix.cpp b/src-vpn/singleapplication/qt-locked-file-unix.cpp new file mode 100644 index 00000000..51472520 --- /dev/null +++ b/src-vpn/singleapplication/qt-locked-file-unix.cpp @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies) +** 2020 KylinSoft Co., Ltd. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +#include "qt-locked-file.h" + +bool QtLockedFile::lock(LockMode mode, bool block) { + if(!isOpen()) { + qWarning("QtLockedFile::lock(): file is not opened"); + return false; + } + + if(mode == NoLock) + return unlock(); + + if(mode == m_lock_mode) + return true; + + if(m_lock_mode != NoLock) + unlock(); + + struct flock fl; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + fl.l_type = (mode == ReadLock) ? F_RDLCK : F_WRLCK; + int cmd = block ? F_SETLKW : F_SETLK; + int ret = fcntl(handle(), cmd, &fl); + + if(ret == -1) { + if(errno != EINTR && errno != EAGAIN) + qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); + return false; + } + + + m_lock_mode = mode; + return true; +} + + +bool QtLockedFile::unlock() { + if(!isOpen()) { + qWarning("QtLockedFile::unlock(): file is not opened"); + return false; + } + + if(!isLocked()) + return true; + + struct flock fl; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + fl.l_type = F_UNLCK; + int ret = fcntl(handle(), F_SETLKW, &fl); + + if(ret == -1) { + qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); + return false; + } + + m_lock_mode = NoLock; + return true; +} + +QtLockedFile::~QtLockedFile() { + if(isOpen()) + unlock(); +} + diff --git a/src-vpn/singleapplication/qt-locked-file.cpp b/src-vpn/singleapplication/qt-locked-file.cpp new file mode 100644 index 00000000..707b4cfa --- /dev/null +++ b/src-vpn/singleapplication/qt-locked-file.cpp @@ -0,0 +1,189 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies) +** 2020 KylinSoft Co., Ltd. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qt-locked-file.h" + +/*! + \class QtLockedFile + + \brief The QtLockedFile class extends QFile with advisory locking + functions. + + A file may be locked in read or write mode. Multiple instances of + \e QtLockedFile, created in multiple processes running on the same + machine, may have a file locked in read mode. Exactly one instance + may have it locked in write mode. A read and a write lock cannot + exist simultaneously on the same file. + + The file locks are advisory. This means that nothing prevents + another process from manipulating a locked file using QFile or + file system functions offered by the OS. Serialization is only + guaranteed if all processes that access the file use + QLockedFile. Also, while holding a lock on a file, a process + must not open the same file again (through any API), or locks + can be unexpectedly lost. + + The lock provided by an instance of \e QtLockedFile is released + whenever the program terminates. This is true even when the + program crashes and no destructors are called. +*/ + +/*! \enum QtLockedFile::LockMode + + This enum describes the available lock modes. + + \value ReadLock A read lock. + \value WriteLock A write lock. + \value NoLock Neither a read lock nor a write lock. +*/ + +/*! + Constructs an unlocked \e QtLockedFile object. This constructor + behaves in the same way as \e QFile::QFile(). + + \sa QFile::QFile() +*/ +QtLockedFile::QtLockedFile() + : QFile() { +#ifdef Q_OS_WIN + wmutex = 0; + rmutex = 0; +#endif + m_lock_mode = NoLock; +} + +/*! + Constructs an unlocked QtLockedFile object with file \a name. This + constructor behaves in the same way as \e QFile::QFile(const + QString&). + + \sa QFile::QFile() +*/ +QtLockedFile::QtLockedFile(const QString &name) + : QFile(name) { +#ifdef Q_OS_WIN + wmutex = 0; + rmutex = 0; +#endif + m_lock_mode = NoLock; +} + +/*! + Opens the file in OpenMode \a mode. + + This is identical to QFile::open(), with the one exception that the + Truncate mode flag is disallowed. Truncation would conflict with the + advisory file locking, since the file would be modified before the + write lock is obtained. If truncation is required, use resize(0) + after obtaining the write lock. + + Returns true if successful; otherwise false. + + \sa QFile::open(), QFile::resize() +*/ +bool QtLockedFile::open(OpenMode mode) { + if(mode & QIODevice::Truncate) { + qWarning("QtLockedFile::open(): Truncate mode not allowed."); + return false; + } + return QFile::open(mode); +} + +/*! + Returns \e true if this object has a in read or write lock; + otherwise returns \e false. + + \sa lockMode() +*/ +bool QtLockedFile::isLocked() const { + return m_lock_mode != NoLock; +} + +/*! + Returns the type of lock currently held by this object, or \e + QtLockedFile::NoLock. + + \sa isLocked() +*/ +QtLockedFile::LockMode QtLockedFile::lockMode() const { + return m_lock_mode; +} + +/*! + \fn bool QtLockedFile::lock(LockMode mode, bool block = true) + + Obtains a lock of type \a mode. The file must be opened before it + can be locked. + + If \a block is true, this function will block until the lock is + aquired. If \a block is false, this function returns \e false + immediately if the lock cannot be aquired. + + If this object already has a lock of type \a mode, this function + returns \e true immediately. If this object has a lock of a + different type than \a mode, the lock is first released and then a + new lock is obtained. + + This function returns \e true if, after it executes, the file is + locked by this object, and \e false otherwise. + + \sa unlock(), isLocked(), lockMode() +*/ + +/*! + \fn bool QtLockedFile::unlock() + + Releases a lock. + + If the object has no lock, this function returns immediately. + + This function returns \e true if, after it executes, the file is + not locked by this object, and \e false otherwise. + + \sa lock(), isLocked(), lockMode() +*/ + +/*! + \fn QtLockedFile::~QtLockedFile() + + Destroys the \e QtLockedFile object. If any locks were held, they + are released. +*/ diff --git a/src-vpn/singleapplication/qt-locked-file.h b/src-vpn/singleapplication/qt-locked-file.h new file mode 100644 index 00000000..332d648e --- /dev/null +++ b/src-vpn/singleapplication/qt-locked-file.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies) +** 2020 KylinSoft Co., Ltd. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTLOCKEDFILE_H +#define QTLOCKEDFILE_H + +#include +#ifdef Q_OS_WIN +#include +#endif + +#if defined(Q_OS_WIN) +# if !defined(QT_QTLOCKEDFILE_EXPORT) && !defined(QT_QTLOCKEDFILE_IMPORT) +# define QT_QTLOCKEDFILE_EXPORT +# elif defined(QT_QTLOCKEDFILE_IMPORT) +# if defined(QT_QTLOCKEDFILE_EXPORT) +# undef QT_QTLOCKEDFILE_EXPORT +# endif +# define QT_QTLOCKEDFILE_EXPORT __declspec(dllimport) +# elif defined(QT_QTLOCKEDFILE_EXPORT) +# undef QT_QTLOCKEDFILE_EXPORT +# define QT_QTLOCKEDFILE_EXPORT __declspec(dllexport) +# endif +#else +# define QT_QTLOCKEDFILE_EXPORT +#endif + +namespace QtLP_Private { + +class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile { +public: + enum LockMode { NoLock = 0, ReadLock, WriteLock }; + + QtLockedFile(); + QtLockedFile(const QString &name); + ~QtLockedFile(); + + bool open(OpenMode mode); + + bool lock(LockMode mode, bool block = true); + bool unlock(); + bool isLocked() const; + LockMode lockMode() const; + +private: +#ifdef Q_OS_WIN + Qt::HANDLE wmutex; + Qt::HANDLE rmutex; + QVector rmutexes; + QString mutexname; + + Qt::HANDLE getMutexHandle(int idx, bool doCreate); + bool waitMutex(Qt::HANDLE mutex, bool doBlock); + +#endif + LockMode m_lock_mode; +}; +} +#endif diff --git a/src-vpn/singleapplication/qt-single-application.cpp b/src-vpn/singleapplication/qt-single-application.cpp new file mode 100644 index 00000000..4f3b8d65 --- /dev/null +++ b/src-vpn/singleapplication/qt-single-application.cpp @@ -0,0 +1,351 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies) +** 2020 KylinSoft Co., Ltd. +** Contact: http://www.qt-project.org/legal +** +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** +****************************************************************************/ + + +#include "qt-single-application.h" +#include "qt-local-peer.h" +#include +#include +#include +#include +#include +#include +#include "../frontend/vpnobject.h" + + +/*! + \class QtSingleApplication qtsingleapplication.h + \brief The QtSingleApplication class provides an API to detect and + communicate with running instances of an application. + + This class allows you to create applications where only one + instance should be running at a time. I.e., if the user tries to + launch another instance, the already running instance will be + activated instead. Another usecase is a client-server system, + where the first started instance will assume the role of server, + and the later instances will act as clients of that server. + + By default, the full path of the executable file is used to + determine whether two processes are instances of the same + application. You can also provide an explicit identifier string + that will be compared instead. + + The application should create the QtSingleApplication object early + in the startup phase, and call isRunning() to find out if another + instance of this application is already running. If isRunning() + returns false, it means that no other instance is running, and + this instance has assumed the role as the running instance. In + this case, the application should continue with the initialization + of the application user interface before entering the event loop + with exec(), as normal. + + The messageReceived() signal will be emitted when the running + application receives messages from another instance of the same + application. When a message is received it might be helpful to the + user to raise the application so that it becomes visible. To + facilitate this, QtSingleApplication provides the + setActivationWindow() function and the activateWindow() slot. + + If isRunning() returns true, another instance is already + running. It may be alerted to the fact that another instance has + started by using the sendMessage() function. Also data such as + startup parameters (e.g. the name of the file the user wanted this + new instance to open) can be passed to the running instance with + this function. Then, the application should terminate (or enter + client mode). + + If isRunning() returns true, but sendMessage() fails, that is an + indication that the running instance is frozen. + + Here's an example that shows how to convert an existing + application to use QtSingleApplication. It is very simple and does + not make use of all QtSingleApplication's functionality (see the + examples for that). + + \code + // Original + int main(int argc, char **argv) + { + QApplication app(argc, argv); + + MyMainWidget mmw; + mmw.show(); + return app.exec(); + } + + // Single instance + int main(int argc, char **argv) + { + QtSingleApplication app(argc, argv); + + if (app.isRunning()) + return !app.sendMessage(someDataString); + + MyMainWidget mmw; + app.setActivationWindow(&mmw); + mmw.show(); + return app.exec(); + } + \endcode + + Once this QtSingleApplication instance is destroyed (normally when + the process exits or crashes), when the user next attempts to run the + application this instance will not, of course, be encountered. The + next instance to call isRunning() or sendMessage() will assume the + role as the new running instance. + + For console (non-GUI) applications, QtSingleCoreApplication may be + used instead of this class, to avoid the dependency on the QtGui + library. + + \sa QtSingleCoreApplication +*/ + + +void QtSingleApplication::sysInit(const QString &appId) { + m_activateWindow = 0; + m_peer = new QtLocalPeer(this, appId); + connect(m_peer, &QtLocalPeer::messageReceived, this, &QtSingleApplication::messageReceived); +} + + +/*! + Creates a QtSingleApplication object. The application identifier + will be QCoreApplication::applicationFilePath(). \a argc, \a + argv, and \a GUIenabled are passed on to the QAppliation constructor. + + If you are creating a console application (i.e. setting \a + GUIenabled to false), you may consider using + QtSingleCoreApplication instead. +*/ + +QtSingleApplication::QtSingleApplication(int &argc, char **argv, bool GUIenabled) + : QApplication(argc, argv, GUIenabled) { + sysInit(); +} + + +/*! + Creates a QtSingleApplication object with the application + identifier \a appId. \a argc and \a argv are passed on to the + QAppliation constructor. +*/ + +QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv) + : QApplication(argc, argv) { + sysInit(appId); +} + +#if QT_VERSION < 0x050000 + +/*! + Creates a QtSingleApplication object. The application identifier + will be QCoreApplication::applicationFilePath(). \a argc, \a + argv, and \a type are passed on to the QAppliation constructor. +*/ +QtSingleApplication::QtSingleApplication(int &argc, char **argv, Type type) + : QApplication(argc, argv, type) { + sysInit(); +} + + +# if defined(Q_WS_X11) +/*! + Special constructor for X11, ref. the documentation of + QApplication's corresponding constructor. The application identifier + will be QCoreApplication::applicationFilePath(). \a dpy, \a visual, + and \a cmap are passed on to the QApplication constructor. +*/ +QtSingleApplication::QtSingleApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE cmap) + : QApplication(dpy, visual, cmap) { + sysInit(); +} + +/*! + Special constructor for X11, ref. the documentation of + QApplication's corresponding constructor. The application identifier + will be QCoreApplication::applicationFilePath(). \a dpy, \a argc, \a + argv, \a visual, and \a cmap are passed on to the QApplication + constructor. +*/ +QtSingleApplication::QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) + : QApplication(dpy, argc, argv, visual, cmap) { + sysInit(); +} + +/*! + Special constructor for X11, ref. the documentation of + QApplication's corresponding constructor. The application identifier + will be \a appId. \a dpy, \a argc, \a + argv, \a visual, and \a cmap are passed on to the QApplication + constructor. +*/ +QtSingleApplication::QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) + : QApplication(dpy, argc, argv, visual, cmap) { + sysInit(appId); +} +# endif // Q_WS_X11 +#endif // QT_VERSION < 0x050000 + + +/*! + Returns true if another instance of this application is running; + otherwise false. + + This function does not find instances of this application that are + being run by a different user (on Windows: that are running in + another session). + + \sa sendMessage() +*/ + +bool QtSingleApplication::isRunning() { + return m_peer->isClient(); +} + + +/*! + Tries to send the text \a message to the currently running + instance. The QtSingleApplication object in the running instance + will emit the messageReceived() signal when it receives the + message. + + This function returns true if the message has been sent to, and + processed by, the current instance. If there is no instance + currently running, or if the running instance fails to process the + message within \a timeout milliseconds, this function return false. + + \sa isRunning(), messageReceived() +*/ +bool QtSingleApplication::sendMessage(const QString &message, int timeout) { + return m_peer->sendMessage(message, timeout); +} + + +/*! + Returns the application identifier. Two processes with the same + identifier will be regarded as instances of the same application. +*/ +QString QtSingleApplication::id() const { + return m_peer->applicationId(); +} + + +/*! + Sets the activation window of this application to \a aw. The + activation window is the widget that will be activated by + activateWindow(). This is typically the application's main window. + + If \a activateOnMessage is true (the default), the window will be + activated automatically every time a message is received, just prior + to the messageReceived() signal being emitted. + + \sa activateWindow(), messageReceived() +*/ + +void QtSingleApplication::setActivationWindow(QWidget* aw, bool activateOnMessage) { + m_activateWindow = aw; + if (activateOnMessage) + connect(m_peer, &QtLocalPeer::messageReceived, this, &QtSingleApplication::activateWindow); + else + disconnect(m_peer, &QtLocalPeer::messageReceived, this, &QtSingleApplication::activateWindow); +} + + +/*! + Returns the applications activation window if one has been set by + calling setActivationWindow(), otherwise returns 0. + + \sa setActivationWindow() +*/ +QWidget* QtSingleApplication::activationWindow() const { + return m_activateWindow; +} + + +/*! + De-minimizes, raises, and activates this application's activation window. + This function does nothing if no activation window has been set. + + This is a convenience function to show the user that this + application instance has been activated when he has tried to start + another instance. + + This function should typically be called in response to the + messageReceived() signal. By default, that will happen + automatically, if an activation window has been set. + + \sa setActivationWindow(), messageReceived(), initialize() +*/ +void QtSingleApplication::activateWindow() { + if (m_activateWindow) { + if(this->applicationState() & Qt::ApplicationInactive) + { + m_activateWindow->setWindowState(m_activateWindow->windowState() & ~Qt::WindowMinimized); + m_activateWindow->raise(); + m_activateWindow->showNormal(); + m_activateWindow->activateWindow(); + } + else { + m_activateWindow->setWindowState(m_activateWindow->windowState() & Qt::WindowMinimized); + m_activateWindow->hide(); + } + } +} + + +/*! + \fn void QtSingleApplication::messageReceived(const QString& message) + + This signal is emitted when the current instance receives a \a + message from another instance of this application. + + \sa sendMessage(), setActivationWindow(), activateWindow() +*/ + + +/*! + \fn void QtSingleApplication::initialize(bool dummy = true) + + \obsolete +*/ diff --git a/src-vpn/singleapplication/qt-single-application.h b/src-vpn/singleapplication/qt-single-application.h new file mode 100644 index 00000000..084b9b7c --- /dev/null +++ b/src-vpn/singleapplication/qt-single-application.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies) +** 2020 KylinSoft Co., Ltd. +** Contact: http://www.qt-project.org/legal +** +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** +****************************************************************************/ + +#ifndef QTSINGLEAPPLICATION_H +#define QTSINGLEAPPLICATION_H + +#include + +class QtLocalPeer; + +#if defined(Q_OS_WIN) +# if !defined(QT_QTSINGLEAPPLICATION_EXPORT) && !defined(QT_QTSINGLEAPPLICATION_IMPORT) +# define QT_QTSINGLEAPPLICATION_EXPORT +# elif defined(QT_QTSINGLEAPPLICATION_IMPORT) +# if defined(QT_QTSINGLEAPPLICATION_EXPORT) +# undef QT_QTSINGLEAPPLICATION_EXPORT +# endif +# define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllimport) +# elif defined(QT_QTSINGLEAPPLICATION_EXPORT) +# undef QT_QTSINGLEAPPLICATION_EXPORT +# define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllexport) +# endif +#else +# define QT_QTSINGLEAPPLICATION_EXPORT +#endif + +class QT_QTSINGLEAPPLICATION_EXPORT QtSingleApplication : public QApplication { + Q_OBJECT + +public: + QtSingleApplication(int &argc, char **argv, bool GUIenabled = true); + QtSingleApplication(const QString &id, int &argc, char **argv); +#if QT_VERSION < 0x050000 + QtSingleApplication(int &argc, char **argv, Type type); +# if defined(Q_WS_X11) + QtSingleApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); + QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap = 0); + QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); +# endif // Q_WS_X11 +#endif // QT_VERSION < 0x050000 + + bool isRunning(); + QString id() const; + + void setActivationWindow(QWidget* aw, bool activateOnMessage = true); + QWidget* activationWindow() const; + + // Obsolete: + void initialize(bool dummy = true) { + isRunning(); + Q_UNUSED(dummy) + } + +public Q_SLOTS: + bool sendMessage(const QString &message, int timeout = 5000); + void activateWindow(); + + +Q_SIGNALS: + void messageReceived(const QString &message); + + +private: + void sysInit(const QString &appId = QString()); + QtLocalPeer *m_peer; + QWidget *m_activateWindow; +}; + +#endif // QTSINGLEAPPLICATION_H diff --git a/src-vpn/singleapplication/qt-single-application.pri b/src-vpn/singleapplication/qt-single-application.pri new file mode 100644 index 00000000..0a4ca9ae --- /dev/null +++ b/src-vpn/singleapplication/qt-single-application.pri @@ -0,0 +1,27 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD +QT *= network +greaterThan(QT_MAJOR_VERSION, 4): QT *= widgets + +qtsingleapplication-uselib:!qtsingleapplication-buildlib { + LIBS += -L$$QTSINGLEAPPLICATION_LIBDIR -l$$QTSINGLEAPPLICATION_LIBNAME +} else { + SOURCES += + HEADERS += +} + +win32 { + contains(TEMPLATE, lib):contains(CONFIG, shared):DEFINES += QT_QTSINGLEAPPLICATION_EXPORT + else:qtsingleapplication-uselib:DEFINES += QT_QTSINGLEAPPLICATION_IMPORT +} + +HEADERS += \ + $$PWD/qt-local-peer.h \ + $$PWD/qt-locked-file.h \ + $$PWD/qt-single-application.h + +SOURCES += \ + $$PWD/qt-local-peer.cpp \ + $$PWD/qt-locked-file-unix.cpp \ + $$PWD/qt-single-application.cpp \ + $$PWD/qt-locked-file.cpp diff --git a/src-vpn/src-vpn.pro b/src-vpn/src-vpn.pro new file mode 100644 index 00000000..f635152d --- /dev/null +++ b/src-vpn/src-vpn.pro @@ -0,0 +1,74 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2018-10-19T15:29:47 +# +#------------------------------------------------- + +QT += core gui x11extras dbus KWindowSystem svg concurrent network + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = kylin-vpn +TEMPLATE = app + +CONFIG += c++14 qt warn_on link_pkgconfig no_keywords +#CONFIG += release + +PKGCONFIG +=gio-2.0 glib-2.0 gio-unix-2.0 libnm libnma libsecret-1 gtk+-3.0 gsettings-qt libcap kysdk-qtwidgets kysdk-waylandhelper +PKGCONFIG +=kysdk-sysinfo + +INCLUDEPATH += /usr/include/KF5/NetworkManagerQt + +LIBS += -L/usr/lib/ -lgsettings-qt -lX11 -lKF5NetworkManagerQt -lukui-log4qt +#LIBS += -lkysec + +target.path = /usr/bin +target.source += $$TARGET +desktop.path = /etc/xdg/autostart/ +desktop.files = kylin-vpn.desktop +gschema.files = org.ukui.kylin-vpn.switch.gschema.xml +gschema.path = /usr/share/glib-2.0/schemas/ + +INSTALLS += target \ + desktop \ + gschema \ + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which has been marked as deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if you use deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +# QMAKE_CXXFLAGS += -Wno-unused-parameter +QMAKE_CPPFLAGS *= $(shell dpkg-buildflags --get CPPFLAGS) +QMAKE_CFLAGS *= $(shell dpkg-buildflags --get CFLAGS) +QMAKE_CXXFLAGS *= $(shell dpkg-buildflags --get CXXFLAGS) +QMAKE_LFLAGS *= $(shell dpkg-buildflags --get LDFLAGS) + +include(singleapplication/qt-single-application.pri) +include(frontend/frontend.pri) +include(kylin-vpn-dbus/kylin-vpn-dbus.pri) + +RESOURCES += \ + vpnqrc.qrc + +SOURCES += \ + main.cpp + +unix { + UI_DIR = .ui + MOC_DIR = .moc + OBJECTS_DIR = .obj +} + +DISTFILES += \ + org.ukui.kylin-vpn.switch.gschema.xml + +TRANSLATIONS += \ + translations/kylin-vpn_zh_CN.ts \ + translations/kylin-vpn_bo_CN.ts diff --git a/src-vpn/translations/kylin-vpn_bo_CN.qm b/src-vpn/translations/kylin-vpn_bo_CN.qm new file mode 100644 index 00000000..be651eed --- /dev/null +++ b/src-vpn/translations/kylin-vpn_bo_CN.qm @@ -0,0 +1 @@ + + + + + SinglePage + + + Settings + + + + + Kylin VPN + + + + + kylin vpn applet desktop message + + + + + VpnListItem + + + Not connected + + + + + + Disconnect + + + + + + Connect + + + + + VpnPage + + + VPN + + + + + VPN Settings + + + + + vpnAddPage + + + create VPN + + + + + VPN Type + + + + + VPN Name + + + + + + Required + + + + + VPN Server + + + + + VPN + + + + + Auto Connection + + + + + Confirm + + + + + Cancel + + + + + vpnConfigPage + + + VPN Type + + + + + VpnDetail + + + + VPN + + + + + IPv4 + + + + + IPv6 + + + + + Advanced + + + + + vpnObject + + + vpn tool + + + + diff --git a/src-vpn/translations/kylin-vpn_zh_CN.qm b/src-vpn/translations/kylin-vpn_zh_CN.qm new file mode 100644 index 00000000..75c3ce3e Binary files /dev/null and b/src-vpn/translations/kylin-vpn_zh_CN.qm differ diff --git a/src-vpn/translations/kylin-vpn_zh_CN.ts b/src-vpn/translations/kylin-vpn_zh_CN.ts new file mode 100644 index 00000000..29c04c79 --- /dev/null +++ b/src-vpn/translations/kylin-vpn_zh_CN.ts @@ -0,0 +1,835 @@ + + + + + SinglePage + + + Settings + 设置 + + + + Kylin VPN + VPN工具 + + + + kylin vpn applet desktop message + vpn配置桌面提示 + + + + VpnAdvancedPage + + + MPPE encryption algorithm: + MPPE加密算法: + + + + Use Stateful encryption + 使用有状态加密 + + + + Send PPP echo packets + 发送PPP回显包 + + + + Authentication Mode: + 认证方式: + + + + PAP authentication + PAP认证 + + + + CHAP authentication + CHAP认证 + + + + MSCHAP authentication + MSCHAP认证 + + + + MSCHAP2 authentication + MSCHAP2认证 + + + + EAP authentication + EAP认证 + + + + Compression Mode: + 压缩方式: + + + + Allow BSD data compression + 允许BSD压缩 + + + + Allow Default data compression + 允许Default压缩 + + + + Allow TCP header compression + 允许TCP头压缩 + + + + Use protocol field compression negotiation + 使用协议域压缩协商 + + + + Use Address/Control compression + 使用地址/控制压缩 + + + + All Available + 任意 + + + + 128-bit + 128位 + + + + 40-bit + 40位 + + + + Use custom gateway port + 使用自定义网关端口 + + + + Use compression + 使用压缩 + + + + Use a TCP connection + 使用TCP连接 + + + + Set virtual device type + 设置虚拟设备类型 + + + + Set virtual device name + 设置虚拟设备名称 + + + + Limit TCP Maximum Segment Size(MSS) + 限制TCP最大段尺寸(MSS) + + + + Randomize remote hosts + 随机化远程主机 + + + + IPv6 tun link + IPv6 tun连接 + + + + Specify ping interval + 指定Ping周期 + + + + Specify exit or restart ping + 指定退出或重启的Ping + + + + Specify max routes + 指定路由上限 + + + + Infinite retry on error + 出错时无限重试 + + + + Use custom key size + 使用自定义密钥大小 + + + + Choose + 选择 + + + + Use custom renegotiation interval + 使用自定义重协商间隔 + + + + Use custom tunnel Maximum Transmission Umit(MTU) + 使用自定义隧道最大单元传输(MTU) + + + + Use custom UDP fragment size + 使用自定义UDP分片大小 + + + + Accept authenticated packets from any address (Float) + 接受来自任何地址(Float)已通过身份验证的数据包 + + + + Subject Match + 主题匹配 + + + + Key File + 密钥文件 + + + + Key Direction + 密钥方向 + + + + Server Address + 服务器地址 + + + + Port + 端口 + + + + Proxy USername + 代理用户名 + + + + Proxy Password + 代理密码 + + + + General + 常规 + + + + TLS settings + TLS设置 + + + + Server Certificate Check + 服务器证书检验 + + + + Use the previous authentication end (server) certificate + 使用前面验证端(服务器)证书 + + + + Verify peer (server) certificate nsCertType specification + 验证对等点(服务器)证书nsCertType指定 + + + + Mode + 模式 + + + + Proxies + 代理 + + + + Proxy Type + 代理类型 + + + + Security + 安全 + + + + HMAC Authentication + HMAC认证 + + + + Input content + 输入内容 + + + + No + + + + + Self-adaption + 自适应 + + + + Automatic + 自动 + + + + Exit + 退出 + + + + Restart + 重启 + + + + Don't verify certificate identification + 不验证证书标识 + + + + Verify the entire subject exactly + 确切地验证整个主题 + + + + Verify name exactly + 精确验证名称 + + + + Verify name by prefix + 按前缀验证名称 + + + + + Server + 服务器 + + + + + Client + 客户端 + + + + + + None + + + + + TLS-Certification + TLS-认证 + + + + TLS-Encryption + TLS-加密 + + + + Not Required + 不需要 + + + + Default + 默认 + + + + Options: + 选项: + + + + Request an inner IP address + 请求内部IP地址 + + + + Enforce UDP encapsulation + 强制UDP封装 + + + + Use IP compression + 使用IP压缩 + + + + Enable custom password suggestions + 启用自定义密码建议 + + + + VpnConfigPage + + + Type + 类型 + + + + Name + 名称 + + + + Static Key + 静态密钥 + + + + Local IP + 本地IP地址 + + + + Remote IP + 远程IP地址 + + + + PIN Code + PIN码 + + + + + + Password + 密码 + + + + NT Domain + NT域 + + + + Server Address + 服务器地址 + + + + Authentication Mode + 认证方式 + + + + CA Certificate + CA证书 + + + + User Certificate + 用户证书 + + + + Key Direction + 密钥方向 + + + + Private Key + 私钥 + + + + Private Key Password + 私有密钥密码 + + + + + Password Options + 密码选项 + + + + Username + 用户名 + + + + Notice: +If key direction is used, it must be opposite to the VPN side used. If '1' is used, the connection must use '0'. If you are not sure which value to use, please contact your system administrator. + 注意: +如果使用了密钥方向,它必须和使用的VPN端相反。如果使用了“1”,连接必须要使用“0”。如果不确定使用哪个值,请联系您的系统管理员。 + + + + + + + Choose + 选择 + + + + None + + + + + + Save password only for this user + 仅对当前用户保存密码 + + + + + Save password for all users + 为所有用户保存密码 + + + + + Ask password every time + 每次都询问 + + + + + Don't require a password + 不需要密码 + + + + + Required + 必填 + + + + Certificate(TLS) + 证书(TLS) + + + + Static key + 静态密钥 + + + + Password and certificate(TLS) + 密码和证书(TLS) + + + + Certificate/Private key + 证书/私钥 + + + + Certificate/ssh-agent + 证书/ssh-agent + + + + Smart card + 智能卡 + + + + Choose a private key + 选择私钥 + + + + + Key Files (*.key *.pem *.der *.p12 *.pfx) + 私钥文件(*.key *.pem *.der *.p12 *.pfx) + + + + Choose a CA certificate + 选择CA证书 + + + + + CA Files (*.pem *.der *.p12 *.crt *.cer *.pfx) + CA文件 (*.pem *.der *.p12 *.crt *.cer *.pfx) + + + + Choose a User certificate + 选择用户证书 + + + + Choose a Static key + 选择静态密钥 + + + + VpnIpv4Page + + + IPv4 Config + IPv4配置 + + + + Address + 地址 + + + + Netmask + 子网掩码 + + + + Default Gateway + 默认网关 + + + + DNS Server + DNS服务器 + + + + Search Domain + 搜索域 + + + + DHCP Client ID + DHCP客户端ID + + + + Auto(DHCP) + 自动(DHCP) + + + + Manual + 手动 + + + + VpnIpv6Page + + + IPv6 Config + IPv6配置 + + + + Address + 地址 + + + + Netmask + 子网掩码 + + + + Default Gateway + 默认网关 + + + + DNS Server + DNS服务器 + + + + Search Domain + 搜索域 + + + + Auto(DHCP) + 自动(DHCP) + + + + Manual + 手动 + + + + VpnListItem + + + Not connected + 未连接 + + + + + Disconnect + 断开 + + + + + Connect + 连接 + + + + VpnPage + + + VPN + + + + + VPN Settings + VPN设置 + + + + vpnAddPage + + + create VPN + 创建VPN + + + + VPN Type + VPN类型 + + + + VPN Name + VPN名称 + + + + + Required + 必填 + + + + VPN Server + 服务器地址 + + + + VPN + + + + + Auto Connection + 自动连接 + + + + Confirm + 确定 + + + + Cancel + 取消 + + + + vpnConfigPage + + VPN Type + VPN类型 + + + + VpnDetail + + + + VPN + + + + + IPv4 + + + + + + IPv6 + + + + + Advanced + 高级 + + + + Auto Connection + 自动连接 + + + + Cancel + 取消 + + + + Confirm + 确定 + + + + vpnObject + + + vpn tool + VPN工具 + + + diff --git a/src-vpn/vpnqrc.qrc b/src-vpn/vpnqrc.qrc new file mode 100644 index 00000000..13fa41d8 --- /dev/null +++ b/src-vpn/vpnqrc.qrc @@ -0,0 +1,6 @@ + + + translations/kylin-vpn_zh_CN.qm + translations/kylin-vpn_bo_CN.qm + +