Imported existing sources

This commit is contained in:
2020-05-08 15:35:19 +02:00
parent 755507b86b
commit 0d2496221a
10 changed files with 597 additions and 41 deletions

103
.gitignore vendored
View File

@@ -1,52 +1,73 @@
# C++ objects and libs
*.slo
*.lo
*.o
# This file is used to ignore files which are generated
# ----------------------------------------------------------------------------
*~
*.autosave
*.a
*.la
*.lai
*.core
*.moc
*.o
*.obj
*.orig
*.rej
*.so
*.so.*
*.dll
*.dylib
# Qt-es
object_script.*.Release
object_script.*.Debug
*_plugin_import.cpp
*_pch.h.cpp
*_resource.rc
*.qm
.#*
*.*#
core
!core/
tags
.DS_Store
.directory
*.debug
Makefile*
*.prl
*.app
moc_*.cpp
ui_*.h
qrc_*.cpp
Thumbs.db
*.res
*.rc
/.qmake.cache
/.qmake.stash
*.pro.user
*.pro.user.*
*.qbs.user
*.qbs.user.*
*.moc
moc_*.cpp
moc_*.h
qrc_*.cpp
ui_*.h
*.qmlc
*.jsc
Makefile*
*build-*
*.qm
*.prl
# Qt unit tests
target_wrapper.*
# qtcreator generated files
*.pro.user*
# QtCreator
*.autosave
# xemacs temporary files
*.flc
# QtCreator Qml
*.qmlproject.user
*.qmlproject.user.*
# Vim temporary files
.*.swp
# QtCreator CMake
CMakeLists.txt.user*
# Visual Studio generated files
*.ib_pdb_index
*.idb
*.ilk
*.pdb
*.sln
*.suo
*.vcproj
*vcproj.*.*.user
*.ncb
*.sdf
*.opensdf
*.vcxproj
*vcxproj.*
# QtCreator 4.8< compilation database
compile_commands.json
# MinGW generated files
*.Debug
*.Release
# Python byte code
*.pyc
# Binaries
# --------
*.dll
*.exe
# QtCreator local machine specific files for imported projects
*creator.user*

21
avivpn.pro Normal file
View File

@@ -0,0 +1,21 @@
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++17
DEFINES += QT_DEPRECATED_WARNINGS QT_DISABLE_DEPRECATED_BEFORE=0x060000
SOURCES += \
entry.cpp \
main.cpp \
mainwindow.cpp \
vpnmodel.cpp
HEADERS += \
entry.h \
mainwindow.h \
vpnmodel.h
FORMS += \
mainwindow.ui

119
entry.cpp Normal file
View File

@@ -0,0 +1,119 @@
#include "entry.h"
#include <QDir>
#include <QDebug>
Entry::Entry(const QString &name, const QString &tunnel, bool sudo, QObject *parent) :
QObject{parent}, m_name{name}, m_tunnel{tunnel}, m_sudo{sudo}
{
connect(&m_process, &QProcess::errorOccurred, this, &Entry::errorOccurred);
connect(&m_process, &QProcess::stateChanged, this, &Entry::stateChanged);
connect(&m_process, &QProcess::readyReadStandardOutput, this, &Entry::readyReadStandardOutput);
connect(&m_process, &QProcess::readyReadStandardError, this, &Entry::readyReadStandardError);
}
Entry::~Entry()
{
qDebug() << m_name;
m_process.terminate();
if (!m_process.waitForFinished(250))
m_process.kill();
}
Qt::CheckState Entry::state() const
{
switch (m_process.state())
{
case QProcess::Starting:
return Qt::PartiallyChecked;
case QProcess::Running:
return Qt::Checked;
case QProcess::NotRunning:
return Qt::Unchecked;
}
}
void Entry::toggle()
{
if (m_process.state() == QProcess::NotRunning)
start();
else
stop();
}
void Entry::start()
{
qDebug() << m_name;
m_logOutput.clear();
m_process.start(binaryName(), arguments());
}
void Entry::stop()
{
qDebug() << m_name;
m_process.terminate();
}
QString Entry::binaryName() const
{
return m_sudo ? "sudo" : "ssh";
}
QStringList Entry::arguments() const
{
QStringList args {
"-v", // Verbose mode. Causes ssh to print debugging messages about its progress.
"-N", // Do not execute a remote command. This is useful for just forwarding ports.
"-T", // Disable pseudo-terminal allocation.
"-oServerAliveInterval=60",
"-oExitOnForwardFailure=yes",
m_tunnel,
"pc178"
};
if (m_sudo)
{
const QDir sshDir(QDir::home().absoluteFilePath(".ssh"));
args.insert(0, "ssh");
args.insert(1, "-F");
args.insert(2, sshDir.absoluteFilePath("config"));
args.insert(3, "-i");
args.insert(4, sshDir.absoluteFilePath("id_rsa"));
}
return args;
}
void Entry::errorOccurred(QProcess::ProcessError error)
{
qDebug() << m_name << error;
}
void Entry::stateChanged(QProcess::ProcessState state)
{
qDebug() << m_name << state;
emit dataChanged();
}
void Entry::readyReadStandardOutput()
{
m_process.setReadChannel(QProcess::StandardOutput);
while (m_process.canReadLine())
{
const auto line = m_process.readLine();
//qDebug() << m_name << line;
m_logOutput.append(line);
}
}
void Entry::readyReadStandardError()
{
m_process.setReadChannel(QProcess::StandardError);
while (m_process.canReadLine())
{
const auto line = m_process.readLine();
//qDebug() << m_name << line;
m_logOutput.append(line);
}
}

48
entry.h Normal file
View File

@@ -0,0 +1,48 @@
#pragma once
#include <QObject>
#include <QProcess>
class Entry : public QObject
{
Q_OBJECT
public:
Entry(const QString &name, const QString &tunnel, bool sudo, QObject *parent = nullptr);
~Entry() override;
const QString &name() const { return m_name; }
const QString &tunnel() const { return m_tunnel; }
bool sudo() const { return m_sudo; }
const QString &logOutput() const { return m_logOutput; }
Qt::CheckState state() const;
void toggle();
void start();
void stop();
QString binaryName() const;
QStringList arguments() const;
signals:
void dataChanged();
private slots:
void errorOccurred(QProcess::ProcessError error);
void stateChanged(QProcess::ProcessState state);
void readyReadStandardOutput();
void readyReadStandardError();
private:
const QString m_name;
const QString m_tunnel;
const bool m_sudo;
bool m_readyReceived{false};
QProcess m_process;
QString m_logOutput;
};

25
main.cpp Normal file
View File

@@ -0,0 +1,25 @@
#include <QApplication>
#include <QDebug>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
qSetMessagePattern(QStringLiteral("%{time dd.MM.yyyy HH:mm:ss.zzz} "
"["
"%{if-debug}D%{endif}"
"%{if-info}I%{endif}"
"%{if-warning}W%{endif}"
"%{if-critical}C%{endif}"
"%{if-fatal}F%{endif}"
"] "
"%{function}(): "
"%{message}"));
MainWindow mainWindow;
mainWindow.show();
return app.exec();
}

46
mainwindow.cpp Normal file
View File

@@ -0,0 +1,46 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMenu>
#include <QDialog>
#include <QVBoxLayout>
#include <QPlainTextEdit>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow{parent},
m_ui{std::make_unique<Ui::MainWindow>()}
{
m_ui->setupUi(this);
m_ui->treeView->setModel(&m_model);
connect(m_ui->treeView, &QWidget::customContextMenuRequested, [&view=*m_ui->treeView,&model=m_model](const QPoint &pos){
const auto index = view.indexAt(pos);
if (!index.isValid())
return;
const auto &entry = model.getEntry(index);
QMenu menu;
const auto showLogAction = menu.addAction("Show log");
const auto selectedAction = menu.exec(view.viewport()->mapToGlobal(pos));
if (selectedAction == showLogAction)
{
QDialog dialog;
QVBoxLayout layout;
QPlainTextEdit widget;
QFont font = widget.document()->defaultFont();
font.setFamily("Courier New");
widget.document()->setDefaultFont(font);
widget.setLineWrapMode(QPlainTextEdit::NoWrap);
widget.setReadOnly(true);
widget.setPlainText(entry.logOutput());
layout.addWidget(&widget);
dialog.setLayout(&layout);
dialog.exec();
}
});
}
MainWindow::~MainWindow() = default;

23
mainwindow.h Normal file
View File

@@ -0,0 +1,23 @@
#pragma once
#include <memory>
#include <QMainWindow>
#include "vpnmodel.h"
namespace Ui { class MainWindow; }
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow() override;
private:
std::unique_ptr<Ui::MainWindow> m_ui;
VpnModel m_model;
};

63
mainwindow.ui Normal file
View File

@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="startAllButton">
<property name="text">
<string>Start all</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="stopAllButton">
<property name="text">
<string>Stop all</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTreeView" name="treeView">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

142
vpnmodel.cpp Normal file
View File

@@ -0,0 +1,142 @@
#include "vpnmodel.h"
#include <QDebug>
#include <QFont>
#include <iterator>
namespace {
enum {
ColumnName,
ColumnCommand,
NumberOfColumns
};
}
VpnModel::VpnModel(QObject *parent) :
QAbstractTableModel{parent}
{
for (Entry &entry : m_entries)
connect(&entry, &Entry::dataChanged, this, &VpnModel::entryChanged);
}
int VpnModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return std::size(m_entries);
}
int VpnModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return NumberOfColumns;
}
QVariant VpnModel::data(const QModelIndex &index, int role) const
{
const auto &entry = m_entries[index.row()];
switch (index.column())
{
case ColumnName:
switch (Qt::ItemDataRole(role))
{
case Qt::DisplayRole:
case Qt::EditRole:
return entry.name();
case Qt::CheckStateRole:
return entry.state();
case Qt::FontRole:
QFont font;
font.setBold(true);
return font;
}
break;
case ColumnCommand:
switch (Qt::ItemDataRole(role))
{
case Qt::DisplayRole:
case Qt::EditRole:
return entry.binaryName() + ' ' + entry.arguments().join(' ');
}
break;
}
return {};
}
QVariant VpnModel::headerData(int section, Qt::Orientation orientation, int role) const
{
switch (orientation)
{
case Qt::Horizontal:
switch (section)
{
case ColumnName:
switch (Qt::ItemDataRole(role))
{
case Qt::DisplayRole:
case Qt::EditRole:
return tr("Name");
}
break;
case ColumnCommand:
switch (Qt::ItemDataRole(role))
{
case Qt::DisplayRole:
case Qt::EditRole:
return tr("Command");
}
break;
}
break;
}
return {};
}
bool VpnModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
auto &entry = m_entries[index.row()];
switch (index.column())
{
case ColumnName:
switch (role)
{
case Qt::CheckStateRole:
entry.toggle();
break;
}
break;
}
return false;
}
Qt::ItemFlags VpnModel::flags(const QModelIndex &index) const
{
auto flags = QAbstractTableModel::flags(index);
if (index.column() == 0)
flags |= Qt::ItemIsUserCheckable | Qt::ItemIsAutoTristate;
return flags;
}
void VpnModel::entryChanged()
{
const auto *sender_ptr = sender();
const auto iter = std::find_if(std::cbegin(m_entries), std::cend(m_entries), [sender_ptr](const Entry &entry){
return &entry == sender_ptr;
});
if (iter == std::cend(m_entries))
{
qCritical() << "unknown sender" << sender_ptr;
return;
}
const auto row = std::distance(std::cbegin(m_entries), iter);
const auto index = createIndex(row, 0);
emit dataChanged(index, index, { Qt::CheckStateRole });
}

48
vpnmodel.h Normal file
View File

@@ -0,0 +1,48 @@
#pragma once
#include <QAbstractTableModel>
#include <array>
#include "entry.h"
class VpnModel : public QAbstractTableModel
{
Q_OBJECT
public:
VpnModel(QObject *parent = nullptr);
Entry &getEntry(const QModelIndex &index) { return getEntry(index.row()); }
const Entry &getEntry(const QModelIndex &index) const { return getEntry(index.row()); }
Entry &getEntry(int row) { return m_entries[row]; }
const Entry &getEntry(int row) const { return m_entries[row]; }
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
private slots:
void entryChanged();
private:
std::array<Entry, 10> m_entries {
Entry { "Bamboo", "-L 127.1.0.1:2233:bamboo.avibit.com:2233", false },
Entry { "Bitbucket", "-L 127.2.0.1:2233:bitbucket.avibit.com:2233", false },
Entry { "Crucible", "-L 127.3.0.1:2233:crucible.avibit.com:2233", false },
Entry { "Confluence", "-L 127.4.0.1:2233:confluence.avibit.com:2233", false },
Entry { "Jira", "-L 127.5.0.1:2233:jira.avibit.com:2233", false },
Entry { "SVN", "-L 3690:svn.avibit.com:3690", false },
Entry { "Bitbucket SSH", "-L 127.2.0.1:7999:bitbucket.avibit.com:7999", false },
Entry { "Timetool", "-L 8080:timetool.avibit.com:8080", false },
//Entry { "Mirror", "-L 80:mirror.avibit.com:80", true },
Entry { "Intranet", "-L 80:intranet.avibit.com:80", true },
Entry { "Sonarqube", "-L 9000:localhost:9000", false }
};
};