diff --git a/AdaptixClient/CMakeLists.txt b/AdaptixClient/CMakeLists.txt index 90c3112a..14a1220a 100644 --- a/AdaptixClient/CMakeLists.txt +++ b/AdaptixClient/CMakeLists.txt @@ -21,6 +21,7 @@ find_package(Qt6 Network WebSockets Sql + Qml ) include_directories( @@ -95,7 +96,6 @@ SET( HEADERS Headers/UI/Dialogs/DialogSyncPacket.h Headers/UI/Widgets/ListenersWidget.h Headers/UI/Dialogs/DialogListener.h - Headers/Client/WidgetBuilder.h Headers/UI/Widgets/SessionsTableWidget.h Headers/UI/Dialogs/DialogAgent.h Headers/UI/Widgets/ConsoleWidget.h @@ -131,6 +131,17 @@ SET( HEADERS Headers/Workers/DownloaderWorker.h Headers/Workers/UploaderWorker.h Headers/UI/Dialogs/DialogUploader.h + Headers/Client/AxScript/AxScriptManager.h + Headers/Client/AxScript/AxScriptEngine.h + Headers/Client/AxScript/BridgeApp.h + Headers/Client/AxScript/BridgeEvent.h + Headers/Client/AxScript/BridgeForm.h + Headers/Client/AxScript/BridgeMenu.h + Headers/Client/AxScript/AxCommandWrappers.h + Headers/Client/AxScript/AxElementWrappers.h + Headers/UI/Widgets/CredentialsWidget.h + Headers/UI/Dialogs/DialogCredential.h + Headers/UI/Widgets/AxConsoleWidget.h ) add_executable(AdaptixClient @@ -159,7 +170,6 @@ add_executable(AdaptixClient Source/Client/ProcessSyncPacket.cpp Source/UI/Widgets/ListenersWidget.cpp Source/UI/Dialogs/DialogListener.cpp - Source/Client/WidgetBuilder.cpp Source/UI/Widgets/SessionsTableWidget.cpp Source/UI/Dialogs/DialogAgent.cpp Source/UI/Widgets/ConsoleWidget.cpp @@ -194,6 +204,17 @@ add_executable(AdaptixClient Source/Workers/DownloaderWorker.cpp Source/Workers/UploaderWorker.cpp Source/UI/Dialogs/DialogUploader.cpp + Source/Client/AxScript/AxScriptManager.cpp + Source/Client/AxScript/AxScriptEngine.cpp + Source/Client/AxScript/BridgeApp.cpp + Source/Client/AxScript/BridgeEvent.cpp + Source/Client/AxScript/BridgeForm.cpp + Source/Client/AxScript/BridgeMenu.cpp + Source/Client/AxScript/AxCommandWrappers.cpp + Source/Client/AxScript/AxElementWrappers.cpp + Source/UI/Widgets/CredentialsWidget.cpp + Source/UI/Dialogs/DialogCredential.cpp + Source/UI/Widgets/AxConsoleWidget.cpp ) target_compile_definitions(AdaptixClient PRIVATE QT_DEPRECATED_WARNINGS ) @@ -207,6 +228,7 @@ if(UNIX) Qt6::Network Qt6::WebSockets Qt6::Sql + Qt6::Qml OpenSSL::Crypto pthread dl @@ -220,6 +242,7 @@ elseif(APPLE) Qt6::Network Qt6::WebSockets Qt6::Sql + Qt6::Qml OpenSSL::Crypto pthread dl @@ -233,6 +256,7 @@ else() Qt6::Network Qt6::WebSockets Qt6::Sql + Qt6::Qml wsock32 ws2_32 crypt32 iphlpapi netapi32 version winmm userenv ) diff --git a/AdaptixClient/Headers/Agent/Agent.h b/AdaptixClient/Headers/Agent/Agent.h index 0ec9caf1..cb4d9137 100644 --- a/AdaptixClient/Headers/Agent/Agent.h +++ b/AdaptixClient/Headers/Agent/Agent.h @@ -3,6 +3,7 @@ #include +class Commander; class ConsoleWidget; class BrowserFilesWidget; class BrowserProcessWidget; @@ -17,13 +18,13 @@ public: AdaptixWidget* adaptixWidget = nullptr; AgentData data = {}; - BrowsersConfig browsers = {}; QImage imageActive = QImage(); QImage imageInactive = QImage(); - QString connType = QString(); - QString parentId = QString(); + QString connType = QString(); + QString listenerType = QString(); + QString parentId = QString(); QVector childsId; AgentTableWidgetItem* item_Id = nullptr; @@ -44,6 +45,7 @@ public: GraphItem* graphItem = nullptr; QImage graphImage = QImage(); + Commander* commander = nullptr; ConsoleWidget* Console = nullptr; BrowserFilesWidget* FileBrowser = nullptr; BrowserProcessWidget* ProcessBrowser = nullptr; @@ -60,19 +62,13 @@ public: void MarkItem(const QString &mark); void SetColor(const QString &color) const; void UpdateImage(); - QString TasksStop(const QStringList &tasks) const; + QString TasksCancel(const QStringList &tasks) const; QString TasksDelete(const QStringList &tasks) const; void SetParent(const PivotData &pivotData); void UnsetParent(const PivotData &pivotData); void AddChild(const PivotData &pivotData); void RemoveChild(const PivotData &pivotData); - - QString BrowserDisks() const; - QString BrowserProcess() const; - QString BrowserList(const QString &path) const; - QString BrowserUpload(const QString &path, const QString &content) const; - QString BrowserDownload(const QString &path) const; }; -#endif //ADAPTIXCLIENT_AGENT_H +#endif diff --git a/AdaptixClient/Headers/Agent/AgentTableWidgetItem.h b/AdaptixClient/Headers/Agent/AgentTableWidgetItem.h index f41f4e5f..df53e029 100644 --- a/AdaptixClient/Headers/Agent/AgentTableWidgetItem.h +++ b/AdaptixClient/Headers/Agent/AgentTableWidgetItem.h @@ -17,4 +17,4 @@ public: void SetColor(QColor bg, QColor fg); }; -#endif //ADAPTIXCLIENT_TABLEWIDGETITEMAGENT_H +#endif diff --git a/AdaptixClient/Headers/Agent/Commander.h b/AdaptixClient/Headers/Agent/Commander.h index e072ad3e..d85138f1 100644 --- a/AdaptixClient/Headers/Agent/Commander.h +++ b/AdaptixClient/Headers/Agent/Commander.h @@ -1,19 +1,24 @@ #ifndef ADAPTIXCLIENT_COMMANDER_H #define ADAPTIXCLIENT_COMMANDER_H -#include +#include +#include +#include +#include +#include +#include +#include struct Argument { - QString type; - QString name; - bool required; - bool flag; - QString mark; - QString description; - QString defaultValue; - bool defaultUsed; - bool valid; + QString type; + QString name; + bool required; + bool flag; + QString mark; + QString description; + bool defaultUsed; + QVariant defaultValue; }; struct Command @@ -24,61 +29,66 @@ struct Command QString example; QList args; QList subcommands; - QString exec; + bool is_pre_hook; + QJSValue pre_hook; }; -struct Constant +struct CommandsGroup { - QString Name; - QMap Map; + QString groupName; + QString filepath; + QList commands; + QJSEngine* engine; }; -struct ExtModule +struct PostHook { - QString Name; - QString FilePath; - QList Commands; - QMap Constants; + bool isSet; + QString engineName; + QJSValue hook; }; struct CommanderResult { - bool output; - QString message; - bool error; + bool error; + bool output; + QString message; + QJsonObject data; + bool is_pre_hook; + PostHook post_hook; }; -class BofPacker -{ -public: - QByteArray data; - void Pack(const QString &type, const QJsonValue &jsonValue); - QString Build() const; -}; -class Commander + +class Commander : public QObject { - QList commands; - QMap extModules; +Q_OBJECT + + QString agentType; + QString listenerType; QString error; - Constant ParseConstant(QJsonObject jsonObject); - Command ParseCommand(QJsonObject jsonObject); - Argument ParseArgument(const QString &argString); - CommanderResult ProcessCommand(AgentData agentData, Command command, QStringList args, ExtModule extMod); - QString ProcessExecExtension(const AgentData &agentData, ExtModule extMod, QString ExecString, QList args, QJsonObject jsonObj); + CommandsGroup regCommandsGroup; + QVector axCommandsGroup; + + QString ProcessPreHook(QJSEngine *engine, const Command &command, const QString &agentId, const QString &cmdline, const QJsonObject &jsonObj, QStringList args); + CommanderResult ProcessCommand(Command command, QStringList args, QJsonObject jsonObj); CommanderResult ProcessHelp(QStringList commandParts); public: explicit Commander(); - ~Commander(); + ~Commander() override; + + void AddRegCommands(const CommandsGroup &group); + void AddAxCommands(const CommandsGroup &group); + void RemoveAxCommands(const QString &filepath); - bool AddRegCommands(const QByteArray &jsonData); - bool AddExtModule(const QString &filepath, const QString &extName, QList extCommands, QList extConstants); - void RemoveExtModule(const QString &filepath); QString GetError(); QStringList GetCommands(); - CommanderResult ProcessInput(AgentData agentData, QString input); + CommanderResult ProcessInput(QString agentId, QString cmdline); + +signals: + void commandsUpdated(); }; -#endif //ADAPTIXCLIENT_COMMANDER_H +#endif diff --git a/AdaptixClient/Headers/Agent/Task.h b/AdaptixClient/Headers/Agent/Task.h index 8325bd5c..2f25e3d4 100644 --- a/AdaptixClient/Headers/Agent/Task.h +++ b/AdaptixClient/Headers/Agent/Task.h @@ -28,4 +28,4 @@ public: void Update(QJsonObject jsonObjTaskData); }; -#endif //TASK_H +#endif diff --git a/AdaptixClient/Headers/Agent/TaskTableWidgetItem.h b/AdaptixClient/Headers/Agent/TaskTableWidgetItem.h index a84cde38..a4c37ecf 100644 --- a/AdaptixClient/Headers/Agent/TaskTableWidgetItem.h +++ b/AdaptixClient/Headers/Agent/TaskTableWidgetItem.h @@ -14,4 +14,4 @@ public: ~TaskTableWidgetItem() override; }; -#endif //ADAPTIXCLIENT_TABLEWIDGETITEMTASK_H +#endif diff --git a/AdaptixClient/Headers/Client/AuthProfile.h b/AdaptixClient/Headers/Client/AuthProfile.h index 539fc1ee..7f746d6a 100644 --- a/AdaptixClient/Headers/Client/AuthProfile.h +++ b/AdaptixClient/Headers/Client/AuthProfile.h @@ -34,4 +34,4 @@ public: void SetRefreshToken(const QString &token); }; -#endif //ADAPTIXCLIENT_AUTHPROFILE_H \ No newline at end of file +#endif \ No newline at end of file diff --git a/AdaptixClient/Headers/Client/AxScript/AxCommandWrappers.h b/AdaptixClient/Headers/Client/AxScript/AxCommandWrappers.h new file mode 100644 index 00000000..f367c1c8 --- /dev/null +++ b/AdaptixClient/Headers/Client/AxScript/AxCommandWrappers.h @@ -0,0 +1,67 @@ +#ifndef AXCOMMANDWRAPPERS_H +#define AXCOMMANDWRAPPERS_H + +#include +#include +#include +#include + +class AxCommandWrappers : public QObject { +Q_OBJECT + Command command; + +public: + explicit AxCommandWrappers(const QString &name, const QString &description, const QString &example, const QString &message, QObject* parent = nullptr); + + Command getCommand() const; + + Q_INVOKABLE void addSubCommands(const QJSValue& array); + + Q_INVOKABLE void addArgBool(const QString &flag, const QString &description = ""); + Q_INVOKABLE void addArgBool(const QString &flag, const QString &description, const QJSValue &value); + + Q_INVOKABLE void addArgInt(const QString &name, bool required = false, const QString &description = ""); + Q_INVOKABLE void addArgInt(const QString &name, const QString &description, const QJSValue &value); + Q_INVOKABLE void addArgFlagInt(const QString &flag, const QString &name, bool required = false, const QString &description = ""); + Q_INVOKABLE void addArgFlagInt(const QString &flag, const QString &name, const QString &description, const QJSValue &value); + + Q_INVOKABLE void addArgString(const QString &name, bool required = false, const QString &description = ""); + Q_INVOKABLE void addArgString(const QString &name, const QString &description, const QJSValue &value); + Q_INVOKABLE void addArgFlagString(const QString &flag, const QString &name, bool required = false, const QString &description = ""); + Q_INVOKABLE void addArgFlagString(const QString &flag, const QString &name, const QString &description, const QJSValue &value); + + Q_INVOKABLE void addArgFile(const QString &name, bool required = false, const QString &description = ""); + Q_INVOKABLE void addArgFlagFile(const QString &flag, const QString &name, bool required = false, const QString &description = ""); + + Q_INVOKABLE void setPreHook(const QJSValue& handler); + +signals: + void scriptError(const QString &msg); +}; + + + + + +class AxCommandGroupWrapper : public QObject { +Q_OBJECT + QObject* parent; + QString name; + QList commands; + QJSEngine* engine; + +public: + explicit AxCommandGroupWrapper(QJSEngine* engine, QObject* parent = nullptr); + + void SetParams(const QString &name, const QJSValue& array); + QString getName() const; + QList getCommands() const; + QJSEngine* getEngine() const; + + Q_INVOKABLE void add(const QJSValue& array); + +signals: + void scriptError(const QString &msg); +}; + +#endif diff --git a/AdaptixClient/Headers/Client/AxScript/AxElementWrappers.h b/AdaptixClient/Headers/Client/AxScript/AxElementWrappers.h new file mode 100644 index 00000000..f491d5e2 --- /dev/null +++ b/AdaptixClient/Headers/Client/AxScript/AxElementWrappers.h @@ -0,0 +1,794 @@ +#ifndef AXELEMENTWRAPPERS_H +#define AXELEMENTWRAPPERS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class AxScriptEngine; + +inline const QMap FIELD_MAP_CREDS = { + {"username", "Username"}, + {"password", "Password"}, + {"realm", "Realm"}, + {"type", "Type"}, + {"tag", "Tag"}, + {"date", "Date"}, + {"storage", "Storage"}, + {"agent_id", "Agent"}, + {"host", "Host"} +}; + +/// ABSTRACT + +class AbstractAxLayout { +public: + virtual ~AbstractAxLayout() = default; + virtual QLayout* layout() const = 0; +}; + + +class AbstractAxMenuItem : public QObject { +Q_OBJECT +public: + explicit AbstractAxMenuItem(QObject* parent = nullptr) : QObject(parent) {} + virtual void setContext(QVariantList context) = 0; +}; + + +class AbstractAxElement { +public: + virtual ~AbstractAxElement() = default; + virtual QVariant jsonMarshal() const = 0; + virtual void jsonUnmarshal(const QVariant& value) = 0; +}; + + +class AbstractAxSelector { +public: + virtual QJSValue selected_data() const = 0; +}; + + +class AbstractAxVisualElement { +public: + virtual ~AbstractAxVisualElement() = default; + + virtual QWidget* widget() const = 0; + virtual void setEnabled(const bool enable) const = 0; + virtual void setVisible(const bool enable) const = 0; + virtual bool getEnabled() const = 0; + virtual bool getVisible() const = 0; +}; + + +// /// BYTES +// +// class AxByteArrayWrapper : public QObject { +// Q_OBJECT +// QByteArray data; +// +// public: +// explicit AxByteArrayWrapper(QString str, QObject* parent = nullptr); +// +// Q_INVOKABLE QString toHex() const; +// Q_INVOKABLE QString toBase64() const; +// Q_INVOKABLE QString toString() const; +// Q_INVOKABLE QVariantList toIntArray() const; +// Q_INVOKABLE int at(int index) const; +// Q_INVOKABLE void clear(); +// Q_INVOKABLE int size() const; +// Q_INVOKABLE bool contains(QObject* other) const; +// +// Q_INVOKABLE int compare(QObject* other) const; +// Q_INVOKABLE void trimmed(); +// Q_INVOKABLE void prepend(QObject* value); +// Q_INVOKABLE void append(QObject* value); +// Q_INVOKABLE void insert(int index, QObject* value); +// Q_INVOKABLE AxByteArrayWrapper* slice(int start, int length = -1) const; +// }; + + + +// MENU + +class AxActionWrapper : public AbstractAxMenuItem { +Q_OBJECT + QJSValue handler; + QPointer pAction; + QJSEngine* engine; + +public: + explicit AxActionWrapper(const QString& text, const QJSValue& handler, QJSEngine* engine, QObject* parent = nullptr); + QAction* action() const; + void setContext(QVariantList context) override; + + void triggerWithContext(const QVariantList& arg) const; +}; + + +class AxSeparatorWrapper : public AbstractAxMenuItem { +Q_OBJECT + QPointer pAction; + +public: + explicit AxSeparatorWrapper(QObject* parent = nullptr); + QAction* action() const; + void setContext(QVariantList context) override; +}; + + +class AxMenuWrapper : public AbstractAxMenuItem { +Q_OBJECT + QPointer pMenu; + QList items; + +public: + explicit AxMenuWrapper(const QString& title, QObject* parent = nullptr); + QMenu* menu() const; + void setContext(QVariantList context) override; + + Q_INVOKABLE void addItem(AbstractAxMenuItem* item); +}; + + + +/// LAYOUT + +class AxBoxLayoutWrapper : public QObject, public AbstractAxLayout { +Q_OBJECT + QBoxLayout* boxLayout; + +public: + explicit AxBoxLayoutWrapper(QBoxLayout::Direction dir, QObject* parent = nullptr); + + QBoxLayout* layout() const override; + + Q_INVOKABLE void addWidget(QObject* widgetWrapper) const; +}; + + + +class AxGridLayoutWrapper : public QObject, public AbstractAxLayout { +Q_OBJECT + QGridLayout* gridLayout; + +public: + explicit AxGridLayoutWrapper(QObject* parent = nullptr); + + QGridLayout* layout() const override; + + Q_INVOKABLE void addWidget(QObject* widgetWrapper, int row, int col, int rowSpan = 1, int colSpan = 1) const; +}; + + + +/// LINE + +class AxLineWrapper : public QObject, public AbstractAxVisualElement { +Q_OBJECT + QFrame* line; + +public: + explicit AxLineWrapper(QFrame::Shape dir, QObject* parent = nullptr); + + QFrame* widget() const override; + Q_INVOKABLE void setEnabled(const bool enable) const override { widget()->setEnabled(enable); } + Q_INVOKABLE void setVisible(const bool enable) const override { widget()->setVisible(enable); } + Q_INVOKABLE bool getEnabled() const override { return widget()->isEnabled(); } + Q_INVOKABLE bool getVisible() const override { return widget()->isVisible(); } +}; + + + +/// SPACER + +class AxSpacerWrapper : public QObject { +Q_OBJECT + QSpacerItem* spacer; + +public: + explicit AxSpacerWrapper(int w, int h, QSizePolicy::Policy hData, QSizePolicy::Policy vData, QObject* parent = nullptr); + + QSpacerItem* widget() const; +}; + + + +/// TEXTLINE + +class AxTextLineWrapper : public QObject, public AbstractAxElement, public AbstractAxVisualElement { +Q_OBJECT + QLineEdit* lineedit; + +public: + explicit AxTextLineWrapper(QLineEdit* edit, QObject* parent = nullptr); + + QVariant jsonMarshal() const override; + void jsonUnmarshal(const QVariant& value) override; + + QLineEdit* widget() const override; + Q_INVOKABLE void setEnabled(const bool enable) const override { widget()->setEnabled(enable); } + Q_INVOKABLE void setVisible(const bool enable) const override { widget()->setVisible(enable); } + Q_INVOKABLE bool getEnabled() const override { return widget()->isEnabled(); } + Q_INVOKABLE bool getVisible() const override { return widget()->isVisible(); } + + Q_INVOKABLE QString text() const; + Q_INVOKABLE void setText(const QString& text) const; + Q_INVOKABLE void setPlaceholder(const QString& text) const; + Q_INVOKABLE void setReadOnly(const bool& readonly) const; + +signals: + void textChanged(const QString &text); + void textEdited(const QString &text); + void returnPressed(); + void editingFinished(); +}; + + + +/// COMBO + +class AxComboBoxWrapper : public QObject, public AbstractAxElement, public AbstractAxVisualElement { +Q_OBJECT + QComboBox* comboBox; + +public: + explicit AxComboBoxWrapper(QComboBox* comboBox, QObject* parent = nullptr); + + QVariant jsonMarshal() const override; + void jsonUnmarshal(const QVariant& value) override; + + QComboBox* widget() const override; + Q_INVOKABLE void setEnabled(const bool enable) const override { widget()->setEnabled(enable); } + Q_INVOKABLE void setVisible(const bool enable) const override { widget()->setVisible(enable); } + Q_INVOKABLE bool getEnabled() const override { return widget()->isEnabled(); } + Q_INVOKABLE bool getVisible() const override { return widget()->isVisible(); } + + Q_INVOKABLE void addItem(const QString& text) const; + Q_INVOKABLE void addItems(const QJSValue& array) const; + Q_INVOKABLE void setItems(const QJSValue& array) const; + Q_INVOKABLE void clear() const; + Q_INVOKABLE QString currentText() const; + Q_INVOKABLE void setCurrentIndex(int index) const; + Q_INVOKABLE int currentIndex() const; + +signals: + void currentTextChanged(const QString &text); + void currentIndexChanged(int index); +}; + + + +/// SPIN + +class AxSpinBoxWrapper : public QObject, public AbstractAxElement, public AbstractAxVisualElement { +Q_OBJECT + QSpinBox* spin; + +public: + explicit AxSpinBoxWrapper(QSpinBox* spin, QObject* parent = nullptr); + + QVariant jsonMarshal() const override; + void jsonUnmarshal(const QVariant& value) override; + + QSpinBox* widget() const override; + Q_INVOKABLE void setEnabled(const bool enable) const override { widget()->setEnabled(enable); } + Q_INVOKABLE void setVisible(const bool enable) const override { widget()->setVisible(enable); } + Q_INVOKABLE bool getEnabled() const override { return widget()->isEnabled(); } + Q_INVOKABLE bool getVisible() const override { return widget()->isVisible(); } + + Q_INVOKABLE int value() const; + Q_INVOKABLE void setValue(int value) const; + Q_INVOKABLE void setRange(int min, int max) const; + +signals: + void valueChanged(int); +}; + + + +/// DATE + +class AxDateEditWrapper : public QObject, public AbstractAxElement, public AbstractAxVisualElement { +Q_OBJECT + QDateEdit* dateedit; + +public: + explicit AxDateEditWrapper(QDateEdit* edit, const QString &format, QObject* parent = nullptr); + + QVariant jsonMarshal() const override; + void jsonUnmarshal(const QVariant& value) override; + + QDateEdit* widget() const override; + Q_INVOKABLE void setEnabled(const bool enable) const override { widget()->setEnabled(enable); } + Q_INVOKABLE void setVisible(const bool enable) const override { widget()->setVisible(enable); } + Q_INVOKABLE bool getEnabled() const override { return widget()->isEnabled(); } + Q_INVOKABLE bool getVisible() const override { return widget()->isVisible(); } + + Q_INVOKABLE QString dateString() const; + Q_INVOKABLE void setDateString(const QString& date) const; +}; + + + +/// TEXTEDIT + +class AxTimeEditWrapper : public QObject, public AbstractAxElement, public AbstractAxVisualElement { +Q_OBJECT + QTimeEdit* timeedit; + +public: + explicit AxTimeEditWrapper(QTimeEdit* edit, const QString &format, QObject* parent = nullptr); + + QVariant jsonMarshal() const override; + void jsonUnmarshal(const QVariant& value) override; + + QTimeEdit* widget() const override; + Q_INVOKABLE void setEnabled(const bool enable) const override { widget()->setEnabled(enable); } + Q_INVOKABLE void setVisible(const bool enable) const override { widget()->setVisible(enable); } + Q_INVOKABLE bool getEnabled() const override { return widget()->isEnabled(); } + Q_INVOKABLE bool getVisible() const override { return widget()->isVisible(); } + + Q_INVOKABLE QString timeString() const; + Q_INVOKABLE void setTimeString(const QString& time) const; +}; + + + +/// TEXTMULTI + +class AxTextMultiWrapper : public QObject, public AbstractAxElement, public AbstractAxVisualElement { +Q_OBJECT + QPlainTextEdit* textedit; + +public: + explicit AxTextMultiWrapper(QPlainTextEdit* edit, QObject* parent = nullptr); + + QVariant jsonMarshal() const override; + void jsonUnmarshal(const QVariant& value) override; + + QPlainTextEdit* widget() const override; + Q_INVOKABLE void setEnabled(const bool enable) const override { widget()->setEnabled(enable); } + Q_INVOKABLE void setVisible(const bool enable) const override { widget()->setVisible(enable); } + Q_INVOKABLE bool getEnabled() const override { return widget()->isEnabled(); } + Q_INVOKABLE bool getVisible() const override { return widget()->isVisible(); } + + Q_INVOKABLE QString text() const; + Q_INVOKABLE void setText(const QString& text) const; + Q_INVOKABLE void appendText(const QString& text) const; + Q_INVOKABLE void setPlaceholder(const QString& text) const; + Q_INVOKABLE void setReadOnly(const bool& readonly) const; +}; + + + +/// CHECK + +class AxCheckBoxWrapper : public QObject, public AbstractAxElement, public AbstractAxVisualElement { +Q_OBJECT + QCheckBox* check; + +public: + explicit AxCheckBoxWrapper(QCheckBox* box, QObject* parent = nullptr); + + QVariant jsonMarshal() const override; + void jsonUnmarshal(const QVariant& value) override; + + QCheckBox* widget() const override; + Q_INVOKABLE void setEnabled(const bool enable) const override { widget()->setEnabled(enable); } + Q_INVOKABLE void setVisible(const bool enable) const override { widget()->setVisible(enable); } + Q_INVOKABLE bool getEnabled() const override { return widget()->isEnabled(); } + Q_INVOKABLE bool getVisible() const override { return widget()->isVisible(); } + + Q_INVOKABLE bool isChecked() const; + Q_INVOKABLE void setChecked(bool checked) const; + +signals: + void stateChanged(); +}; + + + +/// LABEL + +class AxLabelWrapper : public QObject, public AbstractAxVisualElement { +Q_OBJECT + QLabel* label; + +public: + explicit AxLabelWrapper(QLabel* label, QObject* parent = nullptr); + + QLabel* widget() const override; + Q_INVOKABLE void setEnabled(const bool enable) const override { widget()->setEnabled(enable); } + Q_INVOKABLE void setVisible(const bool enable) const override { widget()->setVisible(enable); } + Q_INVOKABLE bool getEnabled() const override { return widget()->isEnabled(); } + Q_INVOKABLE bool getVisible() const override { return widget()->isVisible(); } + + Q_INVOKABLE void setText(const QString& text) const; + Q_INVOKABLE QString text() const; +}; + + + +/// TAB + +class AxTabWrapper : public QObject, public AbstractAxVisualElement { +Q_OBJECT + QTabWidget* tabs; + +public: + explicit AxTabWrapper(QTabWidget* tabs, QObject* parent = nullptr); + + QTabWidget* widget() const override; + Q_INVOKABLE void setEnabled(const bool enable) const override { widget()->setEnabled(enable); } + Q_INVOKABLE void setVisible(const bool enable) const override { widget()->setVisible(enable); } + Q_INVOKABLE bool getEnabled() const override { return widget()->isEnabled(); } + Q_INVOKABLE bool getVisible() const override { return widget()->isVisible(); } + + Q_INVOKABLE void addTab(QObject* wrapper, const QString &title) const; +}; + + + +/// TABLE + +class AxTableWidgetWrapper : public QObject, public AbstractAxElement, public AbstractAxVisualElement { +Q_OBJECT +public: + QTableWidget* table; + QJSEngine* engine; + + explicit AxTableWidgetWrapper(const QJSValue &headers, QTableWidget* tableWidget, QJSEngine* jsEngine, QObject* parent = nullptr); + + QVariant jsonMarshal() const override; + void jsonUnmarshal(const QVariant& value) override; + + QTableWidget* widget() const override; + Q_INVOKABLE void setEnabled(const bool enable) const override { widget()->setEnabled(enable); } + Q_INVOKABLE void setVisible(const bool enable) const override { widget()->setVisible(enable); } + Q_INVOKABLE bool getEnabled() const override { return widget()->isEnabled(); } + Q_INVOKABLE bool getVisible() const override { return widget()->isVisible(); } + + Q_INVOKABLE void addColumn(const QString &header) const; + Q_INVOKABLE void setColumns(const QJSValue &headers) const; + Q_INVOKABLE void addItem(const QJSValue &items) const; + Q_INVOKABLE int rowCount() const; + Q_INVOKABLE int columnCount() const; + Q_INVOKABLE void setRowCount(int rows); + Q_INVOKABLE void setColumnCount(int cols); + Q_INVOKABLE int currentRow() const; + Q_INVOKABLE int currentColumn() const; + Q_INVOKABLE void setSortingEnabled(bool enable); + Q_INVOKABLE void resizeToContent(int column); + Q_INVOKABLE QString text(int row, int column) const; + Q_INVOKABLE void setText(int row, int column, const QString &text) const; + Q_INVOKABLE void setReadOnly(bool read); + Q_INVOKABLE void hideColumn(int column); + Q_INVOKABLE void setHeadersVisible(bool enable); + Q_INVOKABLE void setColumnAlign(int column, const QString &align); + Q_INVOKABLE void clear(); + Q_INVOKABLE QJSValue selectedRows(); + +signals: + void cellChanged(int row, int column); + void cellClicked(int row, int column); + void cellDoubleClicked(int row, int column); +}; + + + +/// LIST + +class AxListWidgetWrapper : public QObject, public AbstractAxElement, public AbstractAxVisualElement { +Q_OBJECT + QListWidget* list; + QJSEngine* engine; + + bool readonly = false; + +public: + explicit AxListWidgetWrapper(QListWidget* widget, QJSEngine* engine, QObject* parent = nullptr); + + QVariant jsonMarshal() const override; + void jsonUnmarshal(const QVariant& value) override; + + QListWidget* widget() const override; + Q_INVOKABLE void setEnabled(const bool enable) const override { widget()->setEnabled(enable); } + Q_INVOKABLE void setVisible(const bool enable) const override { widget()->setVisible(enable); } + Q_INVOKABLE bool getEnabled() const override { return widget()->isEnabled(); } + Q_INVOKABLE bool getVisible() const override { return widget()->isVisible(); } + + Q_INVOKABLE QJSValue items(); + Q_INVOKABLE void addItem(const QString& text); + Q_INVOKABLE void addItems(const QJSValue &items); + Q_INVOKABLE void removeItem(int index); + Q_INVOKABLE QString itemText(int index) const; + Q_INVOKABLE void setItemText(int index, const QString& text); + Q_INVOKABLE void clear(); + Q_INVOKABLE int count() const; + Q_INVOKABLE int currentRow() const; + Q_INVOKABLE void setCurrentRow(int row); + Q_INVOKABLE QJSValue selectedRows() const; + Q_INVOKABLE void setReadOnly(bool readonly); + +signals: + void currentTextChanged(const QString ¤tText); + void currentRowChanged(int currentRow); + void itemClickedText(const QString& text); + void itemDoubleClickedText(const QString& text); +}; + + + +/// BUTTON + +class AxButtonWrapper : public QObject, public AbstractAxVisualElement { +Q_OBJECT + QPushButton* button; + +public: + explicit AxButtonWrapper(QPushButton* btn, QObject* parent = nullptr); + + QPushButton* widget() const override; + Q_INVOKABLE void setEnabled(const bool enable) const override { widget()->setEnabled(enable); } + Q_INVOKABLE void setVisible(const bool enable) const override { widget()->setVisible(enable); } + Q_INVOKABLE bool getEnabled() const override { return widget()->isEnabled(); } + Q_INVOKABLE bool getVisible() const override { return widget()->isVisible(); } + +signals: + void clicked(); +}; + + + +/// GROUPBOX + + +class AxGroupBoxWrapper : public QObject, public AbstractAxElement, public AbstractAxVisualElement { +Q_OBJECT + QGroupBox* groupBox; + +public: + explicit AxGroupBoxWrapper(const bool checkable, QGroupBox* box, QObject* parent = nullptr); + + QVariant jsonMarshal() const override; + void jsonUnmarshal(const QVariant& value) override; + + QGroupBox* widget() const override; + Q_INVOKABLE void setEnabled(const bool enable) const override { widget()->setEnabled(enable); } + Q_INVOKABLE void setVisible(const bool enable) const override { widget()->setVisible(enable); } + Q_INVOKABLE bool getEnabled() const override { return widget()->isEnabled(); } + Q_INVOKABLE bool getVisible() const override { return widget()->isVisible(); } + + Q_INVOKABLE void setTitle(const QString& title); + Q_INVOKABLE bool isCheckable() const; + Q_INVOKABLE void setCheckable(bool checkable); + Q_INVOKABLE bool isChecked() const; + Q_INVOKABLE void setChecked(bool checked); + Q_INVOKABLE void setPanel(QObject* panel) const; + +signals: + void clicked(bool checked = false); +}; + + + +/// SCROLLAREA + +class AxScrollAreaWrapper : public QObject, public AbstractAxVisualElement { +Q_OBJECT + QScrollArea* scrollArea; + +public: + explicit AxScrollAreaWrapper(QScrollArea* area, QObject* parent = nullptr); + + QScrollArea* widget() const override; + Q_INVOKABLE void setEnabled(const bool enable) const override { widget()->setEnabled(enable); } + Q_INVOKABLE void setVisible(const bool enable) const override { widget()->setVisible(enable); } + Q_INVOKABLE bool getEnabled() const override { return widget()->isEnabled(); } + Q_INVOKABLE bool getVisible() const override { return widget()->isVisible(); } + + Q_INVOKABLE void setPanel(QObject* panel) const; + Q_INVOKABLE void setWidgetResizable(bool resizable); +}; + + +/// SPLITTER + +class AxSplitterWrapper : public QObject, public AbstractAxVisualElement { +Q_OBJECT + QSplitter* splitter; + +public: + explicit AxSplitterWrapper(QSplitter* splitter, QObject* parent = nullptr); + + QSplitter* widget() const override; + Q_INVOKABLE void setEnabled(const bool enable) const override { widget()->setEnabled(enable); } + Q_INVOKABLE void setVisible(const bool enable) const override { widget()->setVisible(enable); } + Q_INVOKABLE bool getEnabled() const override { return widget()->isEnabled(); } + Q_INVOKABLE bool getVisible() const override { return widget()->isVisible(); } + + Q_INVOKABLE void addPage(QObject* w); + Q_INVOKABLE void setSizes(const QVariantList& sizes); + +signals: + void splitterMoved(int pos, int index); +}; + + + +/// STACK + +class AxStackedWidgetWrapper : public QObject, public AbstractAxVisualElement { +Q_OBJECT + QStackedWidget* stack; + +public: + explicit AxStackedWidgetWrapper(QStackedWidget* widget, QObject* parent = nullptr); + + QStackedWidget* widget() const override; + Q_INVOKABLE void setEnabled(const bool enable) const override { widget()->setEnabled(enable); } + Q_INVOKABLE void setVisible(const bool enable) const override { widget()->setVisible(enable); } + Q_INVOKABLE bool getEnabled() const override { return widget()->isEnabled(); } + Q_INVOKABLE bool getVisible() const override { return widget()->isVisible(); } + + Q_INVOKABLE int addPage(QObject* page); + Q_INVOKABLE int insertPage(int index, QObject* page); + Q_INVOKABLE void removePage(int index); + Q_INVOKABLE void setCurrentIndex(int index); + Q_INVOKABLE int currentIndex() const; + Q_INVOKABLE int count() const; + +signals: + void currentChanged(int index); +}; + + + +/// PANEL + +class AxPanelWrapper : public QObject, public AbstractAxVisualElement { +Q_OBJECT + QWidget* panel; + +public: + explicit AxPanelWrapper(QWidget* w, QObject* parent = nullptr); + + QWidget* widget() const override; + Q_INVOKABLE void setEnabled(const bool enable) const override { widget()->setEnabled(enable); } + Q_INVOKABLE void setVisible(const bool enable) const override { widget()->setVisible(enable); } + Q_INVOKABLE bool getEnabled() const override { return widget()->isEnabled(); } + Q_INVOKABLE bool getVisible() const override { return widget()->isVisible(); } + + Q_INVOKABLE void setLayout(QObject* layoutWrapper) const; +}; + + + +/// CONTAINER + +class AxContainerWrapper : public QObject { +Q_OBJECT + QJSEngine* engine; + QMap widgets; + +public: + explicit AxContainerWrapper(QJSEngine* jsEngine, QObject* parent = nullptr); + + Q_INVOKABLE void put(const QString& id, QObject* wrapper); + Q_INVOKABLE QObject* get(const QString& id); + Q_INVOKABLE bool contains(const QString& id) const; + Q_INVOKABLE void remove(const QString& id); + Q_INVOKABLE QString toJson(); + Q_INVOKABLE void fromJson(const QString& jsonString); + Q_INVOKABLE QJSValue toProperty(); + Q_INVOKABLE void fromProperty(const QJSValue& obj); +}; + + + +/// DIALOG + +class AxDialogWrapper : public QObject { +Q_OBJECT + QDialog* dialog; + QVBoxLayout* layout; + QLayout* userLayout = nullptr; + QDialogButtonBox* buttons; + +public: + explicit AxDialogWrapper(const QString& title, QWidget* parent = nullptr); + + Q_INVOKABLE void setLayout(QObject* layoutWrapper); + Q_INVOKABLE void setSize(int w, int h) const; + Q_INVOKABLE bool exec() const; + Q_INVOKABLE void close() const; + Q_INVOKABLE void setButtonsText(const QString& ok_text, const QString& cancel_text) const; +}; + + + +/// SELECTOR FILE + +class AxSelectorFile : public QObject, public AbstractAxElement, public AbstractAxVisualElement { + Q_OBJECT + FileSelector* selector; + +public: + explicit AxSelectorFile(FileSelector* selector, QObject* parent = nullptr); + + QVariant jsonMarshal() const override; + void jsonUnmarshal(const QVariant& value) override; + + FileSelector* widget() const override; + Q_INVOKABLE void setEnabled(const bool enable) const override { widget()->setEnabled(enable); } + Q_INVOKABLE void setVisible(const bool enable) const override { widget()->setVisible(enable); } + Q_INVOKABLE bool getEnabled() const override { return widget()->isEnabled(); } + Q_INVOKABLE bool getVisible() const override { return widget()->isVisible(); } + + Q_INVOKABLE void setPlaceholder(const QString& text) const; +}; + + + +/// SELECTOR CREDENTIALS + +class AxDialogCreds : public QDialog { +Q_OBJECT + QVBoxLayout* mainLayout = nullptr; + QTableWidget* tableWidget = nullptr; + QHBoxLayout* bottomLayout = nullptr; + QPushButton* chooseButton = nullptr; + QSpacerItem* spacer_1 = nullptr; + QSpacerItem* spacer_2 = nullptr; + + QWidget* searchWidget = nullptr; + QHBoxLayout* searchLayout = nullptr; + QLineEdit* searchLineEdit = nullptr; + ClickableLabel* hideButton = nullptr; + + QVector table_headers; + QVector > credList; + QMap allData; + QVector selectedData; + +public: + explicit AxDialogCreds(const QJSValue &headers, QVector vecCreds, QTableWidget* tableWidget, QPushButton* button, QWidget* parent = nullptr); + + QVector data(); + +public slots: + void onClicked(); + void handleSearch(); + void clearSearch(); +}; + +class AxSelectorCreds : public QObject { +Q_OBJECT + AxDialogCreds* dialog; + AxScriptEngine* scriptEngine; + QMap creds; + +public: + explicit AxSelectorCreds(const QJSValue &headers, QTableWidget* tableWidget, QPushButton* button, AxScriptEngine* jsEngine, QWidget* parent = nullptr); + + Q_INVOKABLE void setSize(int w, int h) const; + Q_INVOKABLE QJSValue exec() const; + Q_INVOKABLE void close() const; +}; + +#endif diff --git a/AdaptixClient/Headers/Client/AxScript/AxScriptEngine.h b/AdaptixClient/Headers/Client/AxScript/AxScriptEngine.h new file mode 100644 index 00000000..13cc155a --- /dev/null +++ b/AdaptixClient/Headers/Client/AxScript/AxScriptEngine.h @@ -0,0 +1,95 @@ +#ifndef AXSCRIPTENGINE_H +#define AXSCRIPTENGINE_H + +#include +#include +#include +#include +#include +#include + +class BridgeApp; +class BridgeForm; +class BridgeEvent; +class BridgeMenu; +class AbstractAxMenuItem; +class AxScriptManager; + +struct AxEvent { + QJSValue handler; + QString event_id; + QSet agents; + QSet listenerts; + QSet os; + QJSEngine* jsEngine; +}; + +struct AxMenuItem { + AbstractAxMenuItem* menu; + QSet agents; + QSet listenerts; + QSet os; +}; + +struct ScriptContext { + QString name; + QJSValue scriptObject; + QList objects; + QList actions; + + QList eventFileBroserDisks; + QList eventFileBroserList; + QList eventFileBroserUpload; + QList eventProcessBrowserList; + + QList menuSessionMain; + QList menuSessionAgent; + QList menuSessionBrowser; + QList menuSessionAccess; + QList menuFileBrowser; + QList menuProcessBrowser; + QList menuDownloadRunning; + QList menuDownloadFinished; + QList menuTasks; + QList menuTasksJob; +}; + +class AxScriptEngine : public QObject { +Q_OBJECT + AxScriptManager* scriptManager; + + std::unique_ptr jsEngine; + std::unique_ptr bridgeApp; + std::unique_ptr bridgeForm; + std::unique_ptr bridgeEvent; + std::unique_ptr bridgeMenu; + +public: + ScriptContext context; + + explicit AxScriptEngine(AxScriptManager* script_manager, const QString &name = "", QObject *parent = nullptr); + ~AxScriptEngine() override; + + QJSEngine* engine() const; + BridgeApp* app() const; + BridgeForm* form() const; + BridgeEvent* event() const; + BridgeMenu* menu() const; + + AxScriptManager* manager() const; + + void registerObject(QObject* obj); + void registerAction(QAction* action); + void registerEvent(const QString &type, const QJSValue &handler, const QSet &list_agents, const QSet &list_os, const QSet &list_listeners, const QString &id); + void removeEvent(const QString &id); + void registerMenu(const QString &type, AbstractAxMenuItem* menu, const QSet &list_agents, const QSet &list_os, const QSet &list_listeners); + bool execute(const QString &code); + + QList getEvents(const QString &type); + QList getMenuItems(const QString &type); + +public slots: + void engineError(const QString &message); +}; + +#endif diff --git a/AdaptixClient/Headers/Client/AxScript/AxScriptManager.h b/AdaptixClient/Headers/Client/AxScript/AxScriptManager.h new file mode 100644 index 00000000..ef9596e1 --- /dev/null +++ b/AdaptixClient/Headers/Client/AxScript/AxScriptManager.h @@ -0,0 +1,105 @@ +#ifndef AXSCRIPTMANAGER_H +#define AXSCRIPTMANAGER_H + +#include +#include +#include +#include +#include + +struct ExtensionFile; +struct AxMenuItem; +struct AxEvent; +class AxScriptEngine; +class AdaptixWidget; +class Agent; + +struct DataMenuFileBrowser { + QString agentId; + QString path; + QString name; + QString type; +}; + +struct DataMenuProcessBrowser { + QString agentId; + QString pid; + QString ppid; + QString arch; + QString session_id; + QString context; + QString process; +}; + +struct DataMenuDownload { + QString agentId; + QString fileId; + QString path; + QString state; +}; + +class AxScriptManager : public QObject { +Q_OBJECT + AdaptixWidget* adaptixWidget = nullptr; + AxScriptEngine* mainScript = nullptr; + QMap scripts; + QMap listeners_scripts; + QMap agents_scripts; + +public: + AxScriptManager(AdaptixWidget* main_widget, QObject *parent = nullptr); + ~AxScriptManager() override; + + QJSEngine* MainScriptEngine(); + void ResetMain(); + void Clear(); + + QJSEngine* GetEngine(const QString &name); + AdaptixWidget* GetAdaptix() const; + QMap GetAgents() const; + QVector GetCredentials() const; + QStringList GetInterfaces() const; + + QStringList ListenerScriptList(); + void ListenerScriptAdd(const QString &name, const QString &ax_script); + QJSEngine* ListenerScriptEngine(const QString &name); + + QStringList AgentScriptList(); + void AgentScriptAdd(const QString &name, const QString &ax_script); + QJSEngine* AgentScriptEngine(const QString &name); + QJSValue AgentScriptExecute(const QString &name, const QString &code); + + QStringList ScriptList(); + bool ScriptAdd(ExtensionFile* ext); + void ScriptRemove(const ExtensionFile &ext); + + void GlobalScriptLoad(const QString &path); + void GlobalScriptUnload(const QString &path); + + void RegisterCommandsGroup(const CommandsGroup &group, const QStringList &listeners, const QStringList &agents, const QList &os); + void RemoveEvent(const QString &event_id); + QList FilterMenuItems(const QStringList &agentIds, const QString &menuType); + QList FilterEvents(const QString &agentId, const QString &eventType); + + void AppAgentSetColor(const QStringList &agents, const QString &background, const QString &foreground, const bool reset); + void AppAgentSetImpersonate(const QString &id, const QString &impersonate, const bool elevated); + void AppAgentSetMark(const QStringList &agents, const QString &mark); + void AppAgentSetTag(const QStringList &agents, const QString &tag); + + int AddMenuSession(QMenu* menu, const QString &menuType, QStringList agentIds); + int AddMenuFileBrowser(QMenu* menu, QVector files); + int AddMenuProcessBrowser(QMenu* menu, QVector processes); + int AddMenuDownload(QMenu* menu, const QString &menuType, QVector files); + int AddMenuTask(QMenu* menu, const QString &menuType, const QStringList &tasks); + +public slots: + void consolePrintMessage(const QString &message); + void consolePrintError(const QString &message); + + void emitFileBrowserDisks(const QString &agentId); + void emitFileBrowserList(const QString &agentId, const QString &path); + void emitFileBrowserUpload(const QString &agentId, const QString &path, const QString &localFilename); + void emitProcessBrowserList(const QString &agentId); +}; + +#endif diff --git a/AdaptixClient/Headers/Client/AxScript/BridgeApp.h b/AdaptixClient/Headers/Client/AxScript/BridgeApp.h new file mode 100644 index 00000000..41ae2a66 --- /dev/null +++ b/AdaptixClient/Headers/Client/AxScript/BridgeApp.h @@ -0,0 +1,70 @@ +#ifndef BRIDGEAPP_H +#define BRIDGEAPP_H + +#include +#include +#include + +class AxScriptEngine; +class Command; + +class BridgeApp : public QObject { +Q_OBJECT + AxScriptEngine* scriptEngine; + QWidget* widget; + +public: + explicit BridgeApp(AxScriptEngine* scriptEngine, QObject* parent = nullptr); + ~BridgeApp() override; + AxScriptEngine* GetScriptEngine() const; + +public slots: + QJSValue agents() const; + QJSValue agent_info(const QString &id, const QString &property) const; + void agent_set_color(const QJSValue& agents, const QString &background, const QString &foreground, const bool reset); + void agent_set_impersonate(const QString &id, const QString &impersonate, const bool elevated); + void agent_set_mark(const QJSValue& agents, const QString &mark); + void agent_set_tag(const QJSValue& agents, const QString &tag); + QString arch(const QString &id) const; + QString bof_pack(const QString &types, const QJSValue &args); + void copy_to_clipboard(const QString &text); + void console_message(const QString &id, const QString &message, const QString &type = "", const QString &text = ""); + QJSValue credentials() const; + void credentials_add(const QString &username, const QString &password, const QString &realm = "", const QString &type = "password", const QString &tag = "", const QString &storage = "manual", const QString &host = ""); + QObject* create_command(const QString &name, const QString &description, const QString &example = "", const QString &message = ""); + QObject* create_commands_group(const QString &name, const QJSValue& array); + void execute_alias(const QString &id, const QString &cmdline, const QString &command, const QString &message = "", const QJSValue &hook = QJSValue()) const; + void execute_browser(const QString &id, const QString &command) const; + void execute_command(const QString &id, const QString &command, const QJSValue &hook = QJSValue()) const; + QString file_basename(const QString &path) const; + bool file_exists(const QString &path) const; + QString file_read(QString path) const; + QString format_time(const QString &format, const int &time) const; + QJSValue interfaces() const; + bool is64(const QString &id) const; + bool isadmin(const QString &id) const; + void log(const QString &text); + void log_error(const QString &text); + void open_agent_console(const QString &id); + void open_access_tunnel(const QString &id, bool socks4, bool socks5, bool lportfwd, bool rportfwd); + void open_browser_files(const QString &id); + void open_browser_process(const QString &id); + void open_remote_terminal(const QString &id); + QString prompt_open_file(const QString &caption = "Select file", const QString &filter = QString()); + QString prompt_open_dir(const QString &caption = "Select directory"); + QString prompt_save_file(const QString &filename, const QString &caption = "Select file", const QString &filter = QString()); + void register_commands_group(QObject* obj, const QJSValue& agents, const QJSValue& os, const QJSValue& listeners); + void script_import(const QString &path); + void script_load(const QString &path); + void script_unload(const QString &path); + QString script_dir(); + void show_message(const QString &title, const QString &text); + int ticks(); + +signals: + void consoleMessage(const QString &msg); + void consoleError(const QString &msg); + void engineError(const QString &msg); +}; + +#endif diff --git a/AdaptixClient/Headers/Client/AxScript/BridgeEvent.h b/AdaptixClient/Headers/Client/AxScript/BridgeEvent.h new file mode 100644 index 00000000..60901a68 --- /dev/null +++ b/AdaptixClient/Headers/Client/AxScript/BridgeEvent.h @@ -0,0 +1,32 @@ +#ifndef BRIDGEEVENT_H +#define BRIDGEEVENT_H + +#include +#include +#include +#include + +class AxScriptEngine; + +class BridgeEvent : public QObject { +Q_OBJECT + AxScriptEngine* scriptEngine; + +public: + explicit BridgeEvent(AxScriptEngine* scriptEngine, QObject* parent = nullptr); + ~BridgeEvent() override; + + void reg(const QString &event, const QString &type, const QJSValue &handler, const QJSValue &agents, const QJSValue &os, const QJSValue &listeners, const QString &event_id); + +public slots: + void on_filebrowser_disks(const QJSValue &handler, const QJSValue &agents, const QJSValue &os = QJSValue(), const QJSValue &listeners = QJSValue(), const QString &event_id = ""); + void on_filebrowser_list(const QJSValue &handler, const QJSValue &agents, const QJSValue &os = QJSValue(), const QJSValue &listeners = QJSValue(), const QString &event_id = ""); + void on_filebrowser_upload(const QJSValue &handler, const QJSValue &agents, const QJSValue &os = QJSValue(), const QJSValue &listeners = QJSValue(), const QString &event_id = ""); + void on_processbrowser_list(const QJSValue &handler, const QJSValue &agents, const QJSValue &os = QJSValue(), const QJSValue &listeners = QJSValue(), const QString &event_id = ""); + void remove(const QString &event_id); + +signals: + void scriptError(const QString &msg); +}; + +#endif diff --git a/AdaptixClient/Headers/Client/AxScript/BridgeForm.h b/AdaptixClient/Headers/Client/AxScript/BridgeForm.h new file mode 100644 index 00000000..e68c8a0b --- /dev/null +++ b/AdaptixClient/Headers/Client/AxScript/BridgeForm.h @@ -0,0 +1,112 @@ +#ifndef BRIDGEFORM_H +#define BRIDGEFORM_H + +#include +#include +#include +#include + +class AxScriptEngine; + +class BridgeForm : public QObject { +Q_OBJECT + AxScriptEngine* scriptEngine; + QWidget* widget; + +public: + BridgeForm(AxScriptEngine* scriptEngine, QObject* parent = nullptr); + ~BridgeForm() override; + +public slots: + void connect(QObject* sender, const QString& signal, const QJSValue& handler); + + /// Elements + QObject* create_vlayout(); + QObject* create_hlayout(); + QObject* create_gridlayout(); + QObject* create_vline(); + QObject* create_hline(); + QObject* create_vspacer(); + QObject* create_hspacer(); + QObject* create_label(const QString &text = ""); + QObject* create_textline(const QString &text = ""); + QObject* create_combo(); + QObject* create_check(const QString& label= ""); + QObject* create_spin(); + QObject* create_dateline(const QString& format = "dd.MM.yyyy"); + QObject* create_timeline(const QString& format = "HH:mm:ss"); + QObject* create_button(const QString& text= ""); + QObject* create_textmulti(const QString& text= ""); + QObject* create_list(); + QObject* create_table(const QJSValue &headers); + + QObject* create_tabs(); + QObject* create_groupbox(const QString& title = "", const bool checkable = false); + QObject* create_hsplitter(); + QObject* create_vsplitter(); + QObject* create_scrollarea(); + QObject* create_panel(); + QObject* create_stack(); + QObject* create_container(); + QObject* create_dialog(const QString &title) const; + + QObject* create_selector_file(); + QObject* create_selector_credentials(const QJSValue &headers) const; + +signals: + void scriptError(const QString &msg); +}; + + + + + +class SignalProxy : public QObject { +Q_OBJECT +public: + QJSEngine* engine; + QJSValue handler; + + explicit SignalProxy(QJSEngine* engine, QJSValue handler, QObject* parent = nullptr) : QObject(parent), engine(engine), handler(std::move(handler)) {} + +public slots: + void call() const { + if (handler.isCallable()) + handler.call(); + } + + void callWithArg(const bool &arg) const { + if (handler.isCallable()) { + QJSValueList args; + args << engine->toScriptValue(arg); + handler.call(args); + } + } + + void callWithArg(const int &arg) const { + if (handler.isCallable()) { + QJSValueList args; + args << engine->toScriptValue(arg); + handler.call(args); + } + } + + void callWithArg(const QString& arg) const { + if (handler.isCallable()) { + QJSValueList args; + args << engine->toScriptValue(arg); + handler.call(args); + } + } + + void callWithArgs(const int &arg1, const int &arg2) const { + if (handler.isCallable()) { + QJSValueList args; + args << engine->toScriptValue(arg1); + args << engine->toScriptValue(arg2); + handler.call(args); + } + } +}; + +#endif diff --git a/AdaptixClient/Headers/Client/AxScript/BridgeMenu.h b/AdaptixClient/Headers/Client/AxScript/BridgeMenu.h new file mode 100644 index 00000000..4cc9cea2 --- /dev/null +++ b/AdaptixClient/Headers/Client/AxScript/BridgeMenu.h @@ -0,0 +1,48 @@ +#ifndef BRIDGEMENU_H +#define BRIDGEMENU_H + +#include +#include + +class AxScriptEngine; +class AbstractAxMenuItem; +class AxActionWrapper; +class AxMenuWrapper; +class AxSeparatorWrapper; + +class BridgeMenu : public QObject { +Q_OBJECT + AxScriptEngine* scriptEngine; + QWidget* widget; + QList menuItems; + + QList items() const; + void clear(); + +public: + explicit BridgeMenu(AxScriptEngine* scriptEngine, QObject* parent = nullptr); + ~BridgeMenu() override; + + void reg(const QString &type, AbstractAxMenuItem *item, const QJSValue &agents, const QJSValue &os, const QJSValue &listeners); + +public slots: + AxActionWrapper* create_action(const QString& text, const QJSValue& handler); + AxMenuWrapper* create_menu(const QString& title); + AxSeparatorWrapper* create_separator(); + + void add_session_main(AbstractAxMenuItem* item, const QJSValue &agents, const QJSValue &os = QJSValue(), const QJSValue &listeners = QJSValue()); + void add_session_agent(AbstractAxMenuItem* item, const QJSValue &agents, const QJSValue &os = QJSValue(), const QJSValue &listeners = QJSValue()); + void add_session_browser(AbstractAxMenuItem* item, const QJSValue &agents, const QJSValue &os = QJSValue(), const QJSValue &listeners = QJSValue()); + void add_session_access(AbstractAxMenuItem* item, const QJSValue &agents, const QJSValue &os = QJSValue(), const QJSValue &listeners = QJSValue()); + + void add_filebrowser(AbstractAxMenuItem* item, const QJSValue &agents, const QJSValue &os = QJSValue(), const QJSValue &listeners = QJSValue()); + void add_processbrowser(AbstractAxMenuItem* item, const QJSValue &agents, const QJSValue &os = QJSValue(), const QJSValue &listeners = QJSValue()); + + void add_downloads_running(AbstractAxMenuItem* item, const QJSValue &agents, const QJSValue &os = QJSValue(), const QJSValue &listeners = QJSValue()); + void add_downloads_finished(AbstractAxMenuItem* item, const QJSValue &agents, const QJSValue &os = QJSValue(), const QJSValue &listeners = QJSValue()); + + void add_tasks(AbstractAxMenuItem* item, const QJSValue &agents, const QJSValue &os = QJSValue(), const QJSValue &listeners = QJSValue()); + void add_tasks_job(AbstractAxMenuItem* item, const QJSValue &agents, const QJSValue &os = QJSValue(), const QJSValue &listeners = QJSValue()); +}; + +#endif diff --git a/AdaptixClient/Headers/Client/Extender.h b/AdaptixClient/Headers/Client/Extender.h index 56a62de3..baa67dab 100644 --- a/AdaptixClient/Headers/Client/Extender.h +++ b/AdaptixClient/Headers/Client/Extender.h @@ -6,23 +6,29 @@ class DialogExtender; class MainAdaptix; -class Extender +class Extender : public QObject { +Q_OBJECT MainAdaptix* mainAdaptix = nullptr; public: explicit Extender(MainAdaptix* m); - ~Extender(); + ~Extender() override; DialogExtender* dialogExtender = nullptr; QMap extenderFiles; void LoadFromDB(); - void LoadFromFile(QString path, bool enabled); - void SetExtension(const ExtensionFile &extFile ); + void LoadFromFile(const QString &path, bool enabled); + void SetExtension(ExtensionFile extFile ); void EnableExtension(const QString &path); void DisableExtension(const QString &path); void RemoveExtension(const QString &path); + +public slots: + void syncedOnReload(const QString &project); + void loadGlobalScript(const QString &path); + void unloadGlobalScript(const QString &path); }; -#endif //ADAPTIXCLIENT_EXTENDER_H +#endif diff --git a/AdaptixClient/Headers/Client/Requestor.h b/AdaptixClient/Headers/Client/Requestor.h index 2bb0697b..40161e38 100644 --- a/AdaptixClient/Headers/Client/Requestor.h +++ b/AdaptixClient/Headers/Client/Requestor.h @@ -9,7 +9,7 @@ QJsonObject HttpReq(const QString &sUrl, const QByteArray &jsonData, const QStri QJsonObject HttpReqTimeout( int timeout, const QString &sUrl, const QByteArray &jsonData, const QString &token ); -/// CLIENT +///CLIENT bool HttpReqLogin(AuthProfile* profile); @@ -19,7 +19,7 @@ bool HttpReqJwtUpdate(AuthProfile* profile); bool HttpReqGetOTP(const QString &type, const QString &objectId, AuthProfile profile, QString* message, bool* ok); -/// LISTENER +///LISTENER bool HttpReqListenerStart(const QString &listenerName, const QString &configType, const QString &configData, AuthProfile profile, QString* message, bool* ok ); @@ -27,13 +27,11 @@ bool HttpReqListenerEdit(const QString &listenerName, const QString &configType, bool HttpReqListenerStop(const QString &listenerName, const QString &listenerType, AuthProfile profile, QString* message, bool* ok ); -/// AGENT +///AGENT -bool HttpReqAgentGenerate(const QString &listenerName, const QString &listenerType, const QString &agentName, const QString &os, const QString &configData, AuthProfile profile, QString* message, bool* ok ); +bool HttpReqAgentGenerate(const QString &listenerName, const QString &listenerType, const QString &agentName, const QString &configData, AuthProfile profile, QString* message, bool* ok ); -bool HttpReqAgentCommand(const QString &agentName, const QString &agentId, const QString &cmdLine, const QString &data, AuthProfile profile, QString* message, bool* ok ); - -bool HttpReqAgentExit( QStringList agentsId, AuthProfile profile, QString* message, bool* ok ); +bool HttpReqAgentCommand(const QByteArray &jsonData, AuthProfile profile, QString* message, bool* ok ); bool HttpReqConsoleRemove( QStringList agentsId, AuthProfile profile, QString* message, bool* ok ); @@ -45,26 +43,20 @@ bool HttpReqAgentSetMark( QStringList agentsId, const QString &mark, AuthProfile bool HttpReqAgentSetColor( QStringList agentsId, const QString &background, const QString &foreground, bool reset, AuthProfile profile, QString* message, bool* ok ); -bool HttpReqTaskStop(const QString &agentId, QStringList tasksId, AuthProfile profile, QString* message, bool* ok ); +bool HttpReqAgentSetImpersonate(const QString &agentId, const QString &impersonate, bool elevated, AuthProfile profile, QString* message, bool* ok ); + +///TASK + +bool HttpReqTaskCancel(const QString &agentId, QStringList tasksId, AuthProfile profile, QString* message, bool* ok ); bool HttpReqTasksDelete(const QString &agentId, QStringList tasksId, AuthProfile profile, QString* message, bool* ok ); +bool HttpReqTasksHook(const QByteArray &jsonData, AuthProfile profile, QString* message, bool* ok); + ///DOWNLOAD -bool HttpReqDownloadStart(const QString &agentId, const QString &path, AuthProfile profile, QString* message, bool* ok ); - bool HttpReqDownloadAction(const QString &action, const QString &fileId, AuthProfile profile, QString* message, bool* ok ); -///BROWSER - -bool HttpReqBrowserDisks(const QString &agentId, AuthProfile profile, QString* message, bool* ok ); - -bool HttpReqBrowserProcess(const QString &agentId, AuthProfile profile, QString* message, bool* ok ); - -bool HttpReqBrowserList(const QString &agentId, const QString &path, AuthProfile profile, QString* message, bool* ok ); - -bool HttpReqBrowserUpload(const QString &agentId, const QString &path, const QString &content, AuthProfile profile, QString* message, bool* ok ); - ///TUNNEL bool HttpReqTunnelStartServer(const QString &tunnelType, const QByteArray &jsonData, AuthProfile profile, QString* message, bool* ok); @@ -73,10 +65,18 @@ bool HttpReqTunnelStop(const QString &tunnelId, AuthProfile profile, QString* me bool HttpReqTunnelSetInfo(const QString &tunnelId, const QString &info, AuthProfile profile, QString* message, bool* ok ); -/// SCREEN +///SCREEN bool HttpReqScreenSetNote( QStringList scrensId, const QString ¬e, AuthProfile profile, QString* message, bool* ok ); bool HttpReqScreenRemove( QStringList scrensId, AuthProfile profile, QString* message, bool* ok ); -#endif //ADAPTIXCLIENT_REQUESTOR_H +///CREDS + +bool HttpReqCredentialsCreate(const QByteArray &jsonData, AuthProfile profile, QString* message, bool* ok); + +bool HttpReqCredentialsEdit(const QByteArray &jsonData, AuthProfile profile, QString* message, bool* ok); + +bool HttpReqCredentialsRemove(const QString &credsId, AuthProfile profile, QString* message, bool* ok); + +#endif diff --git a/AdaptixClient/Headers/Client/Settings.h b/AdaptixClient/Headers/Client/Settings.h index 507d892c..514f5f25 100644 --- a/AdaptixClient/Headers/Client/Settings.h +++ b/AdaptixClient/Headers/Client/Settings.h @@ -23,4 +23,4 @@ public: void SaveToDB() const; }; -#endif //ADAPTIXCLIENT_SETTINGS_H +#endif diff --git a/AdaptixClient/Headers/Client/Storage.h b/AdaptixClient/Headers/Client/Storage.h index a747b6fd..38b37555 100644 --- a/AdaptixClient/Headers/Client/Storage.h +++ b/AdaptixClient/Headers/Client/Storage.h @@ -47,4 +47,4 @@ public: static void UpdateSettingsTasks(const SettingsData &settingsData); }; -#endif //ADAPTIXCLIENT_STORAGE_H \ No newline at end of file +#endif \ No newline at end of file diff --git a/AdaptixClient/Headers/Client/TunnelEndpoint.h b/AdaptixClient/Headers/Client/TunnelEndpoint.h index b99f8287..19b86de4 100644 --- a/AdaptixClient/Headers/Client/TunnelEndpoint.h +++ b/AdaptixClient/Headers/Client/TunnelEndpoint.h @@ -48,4 +48,4 @@ private slots: void onStartSocks5AuthChannel(); }; -#endif //TUNNELENDPOINT_H +#endif diff --git a/AdaptixClient/Headers/Client/WidgetBuilder.h b/AdaptixClient/Headers/Client/WidgetBuilder.h deleted file mode 100644 index 39ac8168..00000000 --- a/AdaptixClient/Headers/Client/WidgetBuilder.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef ADAPTIXCLIENT_WIDGETBUILDER_H -#define ADAPTIXCLIENT_WIDGETBUILDER_H - -#include - -class WidgetBuilder -{ - QWidget* widget = nullptr; - - QMap widgetMap; - QVector lpVector; - QJsonObject qJsonObject; - QString error; - -public: - bool valid = false; - - explicit WidgetBuilder(const QByteArray& jsonData); - ~WidgetBuilder(); - - void BuildWidget(bool editable); - QLayout* BuildLayout(QString layoutType, QJsonObject rootObj, bool editable); - QString GetError(); - QWidget* GetWidget() const; - void ClearWidget() const; - void FillData(const QString &jsonString); - QString CollectData(); -}; - -#endif //ADAPTIXCLIENT_WIDGETBUILDER_H \ No newline at end of file diff --git a/AdaptixClient/Headers/MainAdaptix.h b/AdaptixClient/Headers/MainAdaptix.h index 3c189105..5c398d61 100644 --- a/AdaptixClient/Headers/MainAdaptix.h +++ b/AdaptixClient/Headers/MainAdaptix.h @@ -31,4 +31,4 @@ public: extern MainAdaptix* GlobalClient; -#endif //ADAPTIXCLIENT_MAINADAPTIX_H +#endif diff --git a/AdaptixClient/Headers/UI/Dialogs/DialogAgent.h b/AdaptixClient/Headers/UI/Dialogs/DialogAgent.h index b5717104..7b82b10a 100644 --- a/AdaptixClient/Headers/UI/Dialogs/DialogAgent.h +++ b/AdaptixClient/Headers/UI/Dialogs/DialogAgent.h @@ -5,6 +5,8 @@ #include #include +class AxContainerWrapper; + class DialogAgent : public QDialog { QGridLayout* mainGridLayout = nullptr; @@ -18,8 +20,6 @@ class DialogAgent : public QDialog QLineEdit* listenerInput = nullptr; QLabel* agentLabel = nullptr; QComboBox* agentCombobox = nullptr; - QLabel* osLabel = nullptr; - QComboBox* osCombobox = nullptr; QPushButton* buttonLoad = nullptr; QPushButton* buttonSave = nullptr; QPushButton* closeButton = nullptr; @@ -27,30 +27,30 @@ class DialogAgent : public QDialog QGroupBox* agentConfigGroupbox = nullptr; QStackedWidget* configStackWidget = nullptr; - QVector regAgents; - QMap> agentsOs; - AuthProfile authProfile; QString listenerName; QString listenerType; + QStringList agents; + QMap widgets; + QMap containers; + void createUI(); public: explicit DialogAgent(const QString &listenerName, const QString &listenerType); ~DialogAgent() override; - void AddExAgents(const QVector ®Agents); + void AddExAgents(const QStringList &agents, const QMap &widgets, const QMap &containers); void SetProfile(const AuthProfile &profile); void Start(); protected slots: - void changeConfig(const QString &fn); - void changeOs(const QString &os); void onButtonLoad(); void onButtonSave(); + void changeConfig(const QString &agentName); void onButtonGenerate(); void onButtonClose(); }; -#endif //ADAPTIXCLIENT_DIALOGAGENT_H +#endif diff --git a/AdaptixClient/Headers/UI/Dialogs/DialogConnect.h b/AdaptixClient/Headers/UI/Dialogs/DialogConnect.h index 9064cc79..edf72ab1 100644 --- a/AdaptixClient/Headers/UI/Dialogs/DialogConnect.h +++ b/AdaptixClient/Headers/UI/Dialogs/DialogConnect.h @@ -49,4 +49,4 @@ private slots: }; -#endif //ADAPTIXCLIENT_DIALOGCONNECT_H +#endif diff --git a/AdaptixClient/Headers/UI/Dialogs/DialogCredential.h b/AdaptixClient/Headers/UI/Dialogs/DialogCredential.h new file mode 100644 index 00000000..eaed96d5 --- /dev/null +++ b/AdaptixClient/Headers/UI/Dialogs/DialogCredential.h @@ -0,0 +1,51 @@ +#ifndef DIALOGCREDENTIAL_H +#define DIALOGCREDENTIAL_H + +#include + +class DialogCredential : public QDialog +{ + QGridLayout* mainGridLayout = nullptr; + QLabel* usernameLabel = nullptr; + QLineEdit* usernameInput = nullptr; + QLabel* passwordLabel = nullptr; + QLineEdit* passwordInput = nullptr; + QLabel* realmLabel = nullptr; + QLineEdit* realmInput = nullptr; + QLabel* typeLabel = nullptr; + QComboBox* typeCombo = nullptr; + QLabel* tagLabel = nullptr; + QLineEdit* tagInput = nullptr; + QLabel* storageLabel = nullptr; + QComboBox* storageCombo = nullptr; + QLabel* hostLabel = nullptr; + QLineEdit* hostInput = nullptr; + QHBoxLayout* hLayoutBottom = nullptr; + QSpacerItem* spacer_1 = nullptr; + QSpacerItem* spacer_2 = nullptr; + QPushButton* createButton = nullptr; + QPushButton* cancelButton = nullptr; + + bool valid = false; + QString message = ""; + QString credsId = ""; + CredentialData data = {}; + + void createUI(); + +public: + explicit DialogCredential(); + ~DialogCredential() override; + + void StartDialog(); + void SetEditmode(const CredentialData &credentialData); + bool IsValid() const; + QString GetMessage() const; + CredentialData GetCredData() const; + +protected slots: + void onButtonCreate(); + void onButtonCancel(); +}; + +#endif diff --git a/AdaptixClient/Headers/UI/Dialogs/DialogDownloader.h b/AdaptixClient/Headers/UI/Dialogs/DialogDownloader.h index 2eb55a1a..d1eacd85 100644 --- a/AdaptixClient/Headers/UI/Dialogs/DialogDownloader.h +++ b/AdaptixClient/Headers/UI/Dialogs/DialogDownloader.h @@ -17,7 +17,7 @@ Q_OBJECT QLabel* speedLabel = nullptr; QLabel* statusLabel = nullptr; QLabel* labelPath = nullptr; - QLineEdit* lineeditPash = nullptr; + QLineEdit* lineeditPath = nullptr; QThread* workerThread; DownloaderWorker* worker; @@ -27,4 +27,4 @@ public: ~DialogDownloader() override; }; -#endif //DIALOGDOWNLOADER_H +#endif diff --git a/AdaptixClient/Headers/UI/Dialogs/DialogExtender.h b/AdaptixClient/Headers/UI/Dialogs/DialogExtender.h index 7fce3c87..2d0b31de 100644 --- a/AdaptixClient/Headers/UI/Dialogs/DialogExtender.h +++ b/AdaptixClient/Headers/UI/Dialogs/DialogExtender.h @@ -12,8 +12,11 @@ Q_OBJECT Extender* extender = nullptr; QGridLayout* layout = nullptr; QTableWidget* tableWidget = nullptr; + QSplitter* splitter = nullptr; QTextEdit* textComment = nullptr; QPushButton* buttonClose = nullptr; + QSpacerItem* spacer1 = nullptr; + QSpacerItem* spacer2 = nullptr; void createUI(); @@ -35,4 +38,4 @@ public slots: void onRowSelect(int row, int column) const; }; -#endif //ADAPTIXCLIENT_DIALOGEXTENDER_H +#endif diff --git a/AdaptixClient/Headers/UI/Dialogs/DialogListener.h b/AdaptixClient/Headers/UI/Dialogs/DialogListener.h index 4256006c..3e5c2eff 100644 --- a/AdaptixClient/Headers/UI/Dialogs/DialogListener.h +++ b/AdaptixClient/Headers/UI/Dialogs/DialogListener.h @@ -4,7 +4,7 @@ #include #include -class WidgetBuilder; +class AxContainerWrapper; class DialogListener : public QDialog { @@ -26,17 +26,20 @@ class DialogListener : public QDialog QGroupBox* listenerConfigGroupbox = nullptr; QStackedWidget* configStackWidget = nullptr; - QMap listenersUI; - AuthProfile authProfile; - bool editMode = false; + QStringList listeners; + QMap widgets; + QMap containers; + + AuthProfile authProfile; + bool editMode = false; void createUI(); public: - explicit DialogListener(); + explicit DialogListener(QWidget *parent = nullptr); ~DialogListener() override; - void AddExListeners(const QMap &listeners); + void AddExListeners(const QStringList &listeners, const QMap &widgets, const QMap &containers); void SetProfile(const AuthProfile &profile); void Start(); void SetEditMode(const QString &name); @@ -49,4 +52,4 @@ protected slots: void onButtonCancel(); }; -#endif //ADAPTIXCLIENT_DIALOGLISTENER_H \ No newline at end of file +#endif \ No newline at end of file diff --git a/AdaptixClient/Headers/UI/Dialogs/DialogSettings.h b/AdaptixClient/Headers/UI/Dialogs/DialogSettings.h index c63eeecb..46333174 100644 --- a/AdaptixClient/Headers/UI/Dialogs/DialogSettings.h +++ b/AdaptixClient/Headers/UI/Dialogs/DialogSettings.h @@ -72,4 +72,4 @@ public slots: void onClose(); }; -#endif //ADAPTIXCLIENT_DIALOGSETTINGS_H +#endif \ No newline at end of file diff --git a/AdaptixClient/Headers/UI/Dialogs/DialogSyncPacket.h b/AdaptixClient/Headers/UI/Dialogs/DialogSyncPacket.h index 8b5d5475..558350d0 100644 --- a/AdaptixClient/Headers/UI/Dialogs/DialogSyncPacket.h +++ b/AdaptixClient/Headers/UI/Dialogs/DialogSyncPacket.h @@ -37,4 +37,4 @@ public: void finish() const; }; -#endif //ADAPTIXCLIENT_DIALOGSYNCPACKET_H \ No newline at end of file +#endif \ No newline at end of file diff --git a/AdaptixClient/Headers/UI/Dialogs/DialogTunnel.h b/AdaptixClient/Headers/UI/Dialogs/DialogTunnel.h index 127389eb..63af47ab 100644 --- a/AdaptixClient/Headers/UI/Dialogs/DialogTunnel.h +++ b/AdaptixClient/Headers/UI/Dialogs/DialogTunnel.h @@ -20,7 +20,7 @@ class DialogTunnel : public QDialog QPushButton* buttonCreate = nullptr; QSpacerItem* horizontalSpacer_1 = nullptr; QSpacerItem* horizontalSpacer_2 = nullptr; - //Socks5 + QWidget* socks5Widget = nullptr; QGridLayout* socks5GridLayout = nullptr; QLabel* socks5LocalAddrLabel = nullptr; @@ -31,13 +31,13 @@ class DialogTunnel : public QDialog QLineEdit* socks5AuthUserInput = nullptr; QLabel* socks5AuthPassLabel = nullptr; QLineEdit* socks5AuthPassInput = nullptr; - //Socks4 + QWidget* socks4Widget = nullptr; QGridLayout* socks4GridLayout = nullptr; QLabel* socks4LocalAddrLabel = nullptr; QLineEdit* socks4LocalAddrInput = nullptr; QSpinBox* socks4LocalPortSpin = nullptr; - //LPF + QWidget* lpfWidget = nullptr; QGridLayout* lpfGridLayout = nullptr; QLabel* lpfLocalAddrLabel = nullptr; @@ -46,7 +46,7 @@ class DialogTunnel : public QDialog QLabel* lpfTargetAddrLabel = nullptr; QLineEdit* lpfTargetAddrInput = nullptr; QSpinBox* lpfTargetPortSpin = nullptr; - //RPF + QWidget* rpfWidget = nullptr; QGridLayout* rpfGridLayout = nullptr; QLabel* rpfPortLabel = nullptr; @@ -65,10 +65,9 @@ class DialogTunnel : public QDialog void createUI(); public: - explicit DialogTunnel(); + explicit DialogTunnel(const QString &agentId, bool s4, bool s5, bool lpf, bool rpf); ~DialogTunnel() override; - void SetSettings(const QString &agentId, bool s5, bool s4, bool lpf, bool rpf); void StartDialog(); bool IsValid() const; QString GetMessage() const; @@ -83,4 +82,4 @@ protected slots: void onButtonCancel(); }; -#endif //DIALOGTUNNEL_H +#endif diff --git a/AdaptixClient/Headers/UI/Dialogs/DialogUploader.h b/AdaptixClient/Headers/UI/Dialogs/DialogUploader.h index d83327d9..59203857 100644 --- a/AdaptixClient/Headers/UI/Dialogs/DialogUploader.h +++ b/AdaptixClient/Headers/UI/Dialogs/DialogUploader.h @@ -26,4 +26,4 @@ public: ~DialogUploader() override; }; -#endif // DIALOGUPLOADER_H +#endif diff --git a/AdaptixClient/Headers/UI/Graph/GraphItem.h b/AdaptixClient/Headers/UI/Graph/GraphItem.h index 0db24049..6bc1bf47 100644 --- a/AdaptixClient/Headers/UI/Graph/GraphItem.h +++ b/AdaptixClient/Headers/UI/Graph/GraphItem.h @@ -71,4 +71,4 @@ protected: void mouseMoveEvent( QGraphicsSceneMouseEvent* event ) override; }; -#endif //ADAPTIXCLIENT_GRAPHITEM_H +#endif diff --git a/AdaptixClient/Headers/UI/Graph/GraphItemLink.h b/AdaptixClient/Headers/UI/Graph/GraphItemLink.h index a294a18e..a6448cd5 100644 --- a/AdaptixClient/Headers/UI/Graph/GraphItemLink.h +++ b/AdaptixClient/Headers/UI/Graph/GraphItemLink.h @@ -32,4 +32,4 @@ protected: static void paintLineText( QPainter* painter, double angle, const QLineF &line, const QString &text, QColor textColor ); }; -#endif //ADAPTIXCLIENT_GRAPHITEMLINK_H +#endif diff --git a/AdaptixClient/Headers/UI/Graph/GraphScene.h b/AdaptixClient/Headers/UI/Graph/GraphScene.h index feac5a0b..69bf8460 100644 --- a/AdaptixClient/Headers/UI/Graph/GraphScene.h +++ b/AdaptixClient/Headers/UI/Graph/GraphScene.h @@ -20,4 +20,4 @@ private: void contextMenuEvent( QGraphicsSceneContextMenuEvent *event ) override; }; -#endif //ADAPTIXCLIENT_GRAPHSCENE_H +#endif diff --git a/AdaptixClient/Headers/UI/Graph/LayoutTreeLeft.h b/AdaptixClient/Headers/UI/Graph/LayoutTreeLeft.h index 19daf110..64e4a5e2 100644 --- a/AdaptixClient/Headers/UI/Graph/LayoutTreeLeft.h +++ b/AdaptixClient/Headers/UI/Graph/LayoutTreeLeft.h @@ -29,4 +29,4 @@ public: static void executeShifts( GraphItem* item ); }; -#endif //ADAPTIXCLIENT_LAYOUTTREELEFT_H +#endif diff --git a/AdaptixClient/Headers/UI/Graph/SessionsGraph.h b/AdaptixClient/Headers/UI/Graph/SessionsGraph.h index 410fb0f5..a65be596 100644 --- a/AdaptixClient/Headers/UI/Graph/SessionsGraph.h +++ b/AdaptixClient/Headers/UI/Graph/SessionsGraph.h @@ -43,4 +43,4 @@ protected: void timerEvent( QTimerEvent* event ) override; }; -#endif //ADAPTIXCLIENT_SESSIONSGRAPH_H +#endif diff --git a/AdaptixClient/Headers/UI/MainUI.h b/AdaptixClient/Headers/UI/MainUI.h index 4f37df36..25d6a7a3 100644 --- a/AdaptixClient/Headers/UI/MainUI.h +++ b/AdaptixClient/Headers/UI/MainUI.h @@ -11,7 +11,7 @@ class MainUI : public QMainWindow { QTabWidget* mainuiTabWidget = nullptr; - QMap AdaptixProjects; + QVector AdaptixProjects; public: explicit MainUI(); @@ -19,13 +19,15 @@ public: static void onNewProject(); void onCloseProject(); + void onAxScriptConsole(); - static void onExtender(); + static void onScriptManager(); static void onSettings(); void AddNewProject(AuthProfile* profile, QThread* channelThread, WebSocketWorker* channelWsWorker); - void AddNewExtension(const ExtensionFile &extFile); + bool AddNewExtension(ExtensionFile *extFile); + bool SyncExtension(const QString &Project, ExtensionFile *extFile); void RemoveExtension(const ExtensionFile &extFile); void UpdateSessionsTableColumns(); @@ -36,4 +38,4 @@ protected: void closeEvent(QCloseEvent *event) override; }; -#endif //ADAPTIXCLIENT_MAINUI_H +#endif diff --git a/AdaptixClient/Headers/UI/Widgets/AdaptixWidget.h b/AdaptixClient/Headers/UI/Widgets/AdaptixWidget.h index bf8726d3..d5da3218 100644 --- a/AdaptixClient/Headers/UI/Widgets/AdaptixWidget.h +++ b/AdaptixClient/Headers/UI/Widgets/AdaptixWidget.h @@ -1,35 +1,34 @@ #ifndef ADAPTIXCLIENT_ADAPTIXWIDGET_H #define ADAPTIXCLIENT_ADAPTIXWIDGET_H +#include #include +#include class Task; class Agent; -class Commander; class LastTickWorker; class WebSocketWorker; class SessionsTableWidget; class SessionsGraph; +class AxConsoleWidget; class LogsWidget; class ListenersWidget; class DownloadsWidget; class ScreenshotsWidget; +class CredentialsWidget; class TasksWidget; class TunnelsWidget; class TunnelEndpoint; -class WidgetBuilder; class DialogSyncPacket; class AuthProfile; +class AxScriptManager; typedef struct RegAgentConfig { - QString agentName; - QString watermark; - QString listenerName; - QString operatingSystem; - QString handlerId; - WidgetBuilder* builder; + QString name; + QString listenerType; + int os; Commander* commander; - BrowsersConfig browsers; bool valid; } RegAgentConfig; @@ -77,6 +76,9 @@ public: QThread* TickThread = nullptr; LastTickWorker* TickWorker = nullptr; + AxScriptManager* ScriptManager = nullptr; + + AxConsoleWidget* AxConsoleTab = nullptr; LogsWidget* LogsTab = nullptr; ListenersWidget* ListenersTab = nullptr; SessionsTableWidget* SessionsTablePage = nullptr; @@ -84,41 +86,62 @@ public: TunnelsWidget* TunnelsTab = nullptr; DownloadsWidget* DownloadsTab = nullptr; ScreenshotsWidget* ScreenshotsTab = nullptr; + CredentialsWidget* CredentialsTab = nullptr; TasksWidget* TasksTab = nullptr; - QMap> Commanders; /// agentName -> (handlerId -> commander) - QMap> AgentBrowserConfigs; /// agentName -> (handlerId -> BrowserConfigs) QVector RegisterAgents; - QMap RegisterListeners; /// listenerName -> builder QVector Listeners; QVector Tunnels; QMap Downloads; QMap Screenshots; + QVector Credentials; QMap Pivots; QVector TasksVector; QMap TasksMap; QVector AgentsVector; QMap AgentsMap; - QMap Extensions; + QMap PostHooksJS; QMap ClientTunnels; + QStringList addresses; explicit AdaptixWidget(AuthProfile* authProfile, QThread* channelThread, WebSocketWorker* channelWsWorker); ~AdaptixWidget() override; AuthProfile* GetProfile() const; - void RegisterListenerConfig(const QString &fn, const QString &ui); - void RegisterAgentConfig(const QString &agentName, const QString &watermark, const QString &handlersJson, const QString &listenersJson); - void ClearAdaptix(); - RegAgentConfig GetRegAgent(const QString &agentName, const QString &listenerName, int os); void AddTab(QWidget* tab, const QString &title, const QString &icon = "" ) const; void RemoveTab(int index) const; - void AddExtension(ExtensionFile ext); + bool AddExtension(ExtensionFile* ext); void RemoveExtension(const ExtensionFile &ext); void Close(); + void ClearAdaptix(); + + void RegisterListenerConfig(const QString &fn, const QString &ax_script); + void RegisterAgentConfig(const QString &agentName, const QString &ax_script, const QStringList &listeners); + QList GetAgentNames(const QString &listenerType) const; + RegAgentConfig GetRegAgent(const QString &agentName, const QString &listenerName, int os); + QList GetCommanders(const QStringList &listeners, const QStringList &agents, const QList &os) const; + QList GetCommandersAll() const; + + void PostHookProcess(QJsonObject jsonHookObj); + + void LoadConsoleUI(const QString &AgentId); + void LoadTasksOutput() const; + void LoadFileBrowserUI(const QString &AgentId); + void LoadProcessBrowserUI(const QString &AgentId); + void LoadTerminalUI(const QString &AgentId); + void ShowTunnelCreator(const QString &AgentId, bool socks4, bool socks5, bool lportfwd, bool rportfwd); signals: void SyncedSignal(); + void SyncedOnReloadSignal(QString project); + void LoadGlobalScriptSignal(QString path); + void UnloadGlobalScriptSignal(QString path); + + void eventFileBrowserDisks(QString agentId); + void eventFileBrowserList(QString agentId, QString path); + void eventFileBrowserUpload(QString agentId, QString path, QString localFilename); + void eventProcessBrowserList(QString agentId); public slots: void ChannelClose() const; @@ -128,18 +151,14 @@ public slots: void SetSessionsTableUI() const; void SetGraphUI() const; void SetTasksUI() const; + void LoadAxConsoleUI() const; void LoadLogsUI() const; void LoadListenersUI() const; void LoadTunnelsUI() const; void LoadDownloadsUI() const; void LoadScreenshotsUI() const; - void LoadTasksOutput() const; + void LoadCredentialsUI() const; void OnReconnect(); - void LoadConsoleUI(const QString &AgentId); - void LoadFileBrowserUI(const QString &AgentId); - void LoadProcessBrowserUI(const QString &AgentId); - void LoadTerminalUI(const QString &AgentId); - }; -#endif //ADAPTIXCLIENT_ADAPTIXWIDGET_H \ No newline at end of file +#endif \ No newline at end of file diff --git a/AdaptixClient/Headers/UI/Widgets/AxConsoleWidget.h b/AdaptixClient/Headers/UI/Widgets/AxConsoleWidget.h new file mode 100644 index 00000000..bf0a75aa --- /dev/null +++ b/AdaptixClient/Headers/UI/Widgets/AxConsoleWidget.h @@ -0,0 +1,61 @@ +#ifndef AXCONSOLEWIDGET_H +#define AXCONSOLEWIDGET_H + +#include + +class AdaptixWidget; +class AxScriptManager; +class TextEditConsole; +class ClickableLabel; +class KPH_ConsoleInput; + +class AxConsoleWidget : public QWidget +{ + AdaptixWidget* adaptixWidget = nullptr; + AxScriptManager* scriptManager = nullptr; + + QGridLayout* MainGridLayout = nullptr; + QLabel* CmdLabel = nullptr; + QLineEdit* InputLineEdit = nullptr; + TextEditConsole* OutputTextEdit = nullptr; + QPushButton* ResetButton = nullptr; + + QWidget* searchWidget = nullptr; + QHBoxLayout* searchLayout = nullptr; + ClickableLabel* prevButton = nullptr; + ClickableLabel* nextButton = nullptr; + QLabel* searchLabel = nullptr; + QLineEdit* searchLineEdit = nullptr; + ClickableLabel* hideButton = nullptr; + QSpacerItem* spacer = nullptr; + QShortcut* shortcutSearch = nullptr; + + int currentIndex = -1; + QVector allSelections; + + KPH_ConsoleInput* kphInputLineEdit = nullptr; + + void createUI(); + void findAndHighlightAll(const QString& pattern); + void highlightCurrent() const; + +public: + explicit AxConsoleWidget(AxScriptManager* m, AdaptixWidget* w); + ~AxConsoleWidget() override; + + void OutputClear() const; + void InputFocus() const; + void AddToHistory(const QString& command); + void PrintMessage(const QString& message); + void PrintError(const QString& message); + +public slots: + void processInput(); + void toggleSearchPanel(); + void handleSearch(); + void handleSearchBackward(); + void handleShowHistory(); + void onResetScript(); +}; + +#endif diff --git a/AdaptixClient/Headers/UI/Widgets/BrowserFilesWidget.h b/AdaptixClient/Headers/UI/Widgets/BrowserFilesWidget.h index 9368b304..66983233 100644 --- a/AdaptixClient/Headers/UI/Widgets/BrowserFilesWidget.h +++ b/AdaptixClient/Headers/UI/Widgets/BrowserFilesWidget.h @@ -90,10 +90,9 @@ public slots: void onParent(); void onReload() const; void onUpload() const; - void actionDownload() const; void handleTableDoubleClicked(const QModelIndex &index); void handleTreeDoubleClicked(QTreeWidgetItem* item, int column); void handleTableMenu(const QPoint &pos); }; -#endif //ADAPTIXCLIENT_BROWSERFILESWIDGET_H \ No newline at end of file +#endif \ No newline at end of file diff --git a/AdaptixClient/Headers/UI/Widgets/BrowserProcessWidget.h b/AdaptixClient/Headers/UI/Widgets/BrowserProcessWidget.h index c13a8138..142ef562 100644 --- a/AdaptixClient/Headers/UI/Widgets/BrowserProcessWidget.h +++ b/AdaptixClient/Headers/UI/Widgets/BrowserProcessWidget.h @@ -64,4 +64,4 @@ public slots: void onTreeSelect() const; }; -#endif //ADAPTIXCLIENT_BROWSERPROCESSWIDGET_H +#endif diff --git a/AdaptixClient/Headers/UI/Widgets/ConsoleWidget.h b/AdaptixClient/Headers/UI/Widgets/ConsoleWidget.h index 26c0b98e..6982d7bf 100644 --- a/AdaptixClient/Headers/UI/Widgets/ConsoleWidget.h +++ b/AdaptixClient/Headers/UI/Widgets/ConsoleWidget.h @@ -4,9 +4,10 @@ #include #include #include +#include class Agent; -class Commander; +class AdaptixWidget; #define CONSOLE_OUT_LOCAL 1 #define CONSOLE_OUT_LOCAL_INFO 2 @@ -15,9 +16,12 @@ class Commander; #define CONSOLE_OUT_INFO 5 #define CONSOLE_OUT_ERROR 6 #define CONSOLE_OUT_SUCCESS 7 +#define CONSOLE_OUT 10 class ConsoleWidget : public QWidget { + AdaptixWidget* adaptixWidget = nullptr; + QGridLayout* MainGridLayout = nullptr; QLabel* CmdLabel = nullptr; QLabel* InfoLabel = nullptr; @@ -35,6 +39,7 @@ class ConsoleWidget : public QWidget ClickableLabel* hideButton = nullptr; QSpacerItem* spacer = nullptr; QShortcut* shortcutSearch = nullptr; + KPH_SearchInput* searchInput = nullptr; bool userSelectedCompletion = false; int currentIndex = -1; @@ -49,10 +54,11 @@ class ConsoleWidget : public QWidget void highlightCurrent() const; public: - explicit ConsoleWidget(Agent* a, Commander* c); + explicit ConsoleWidget(AdaptixWidget* w, Agent* a, Commander* c); ~ConsoleWidget() override; - void UpgradeCompleter() const; + void ProcessCmdResult(const QString &commandLine, const CommanderResult &cmdResult, bool UI); + void InputFocus() const; void AddToHistory(const QString& command); void SetInput(const QString &command); @@ -62,6 +68,7 @@ public: void ConsoleOutputPrompt( qint64 timestamp, const QString &taskId, const QString &user, const QString &commandLine ) const; public slots: + void upgradeCompleter() const; void processInput(); void onCompletionSelected(const QString &selectedText); void toggleSearchPanel(); @@ -70,4 +77,4 @@ public slots: void handleShowHistory(); }; -#endif //ADAPTIXCLIENT_CONSOLEWIDGET_H \ No newline at end of file +#endif \ No newline at end of file diff --git a/AdaptixClient/Headers/UI/Widgets/CredentialsWidget.h b/AdaptixClient/Headers/UI/Widgets/CredentialsWidget.h new file mode 100644 index 00000000..b810ccc3 --- /dev/null +++ b/AdaptixClient/Headers/UI/Widgets/CredentialsWidget.h @@ -0,0 +1,49 @@ +#ifndef CREDENTIALSWIDGET_H +#define CREDENTIALSWIDGET_H + +#include + +class AdaptixWidget; +class ClickableLabel; + +class CredentialsWidget : public QWidget +{ + AdaptixWidget* adaptixWidget = nullptr; + QGridLayout* mainGridLayout = nullptr; + QTableWidget* tableWidget = nullptr; + QShortcut* shortcutSearch = nullptr; + + QWidget* searchWidget = nullptr; + QHBoxLayout* searchLayout = nullptr; + QLineEdit* inputFilter = nullptr; + ClickableLabel* hideButton = nullptr; + + void createUI(); + bool filterItem(const CredentialData &credentials) const; + void addTableItem(const CredentialData &newCredentials) const; + +public: + explicit CredentialsWidget(AdaptixWidget* w); + ~CredentialsWidget() override; + + void Clear() const; + void AddCredentialsItem(const CredentialData &newCredentials) const; + void EditCredentialsItem(const CredentialData &newCredentials) const; + void RemoveCredentialsItem(const QString &credId) const; + + void SetData() const; + void ClearTableContent() const; + + void CredentialsAdd(const QString &username, const QString &password, const QString &realm, const QString &type, const QString &tag, const QString &storage, const QString &host); + +public slots: + void toggleSearchPanel() const; + void onFilterUpdate() const; + void handleCredentialsMenu( const QPoint &pos ) const; + void onCreateCreds(); + void onEditCreds() const; + void onRemoveCreds() const; + void onExportCreds() const; +}; + +#endif \ No newline at end of file diff --git a/AdaptixClient/Headers/UI/Widgets/DownloadsWidget.h b/AdaptixClient/Headers/UI/Widgets/DownloadsWidget.h index 8c9595aa..b67857ee 100644 --- a/AdaptixClient/Headers/UI/Widgets/DownloadsWidget.h +++ b/AdaptixClient/Headers/UI/Widgets/DownloadsWidget.h @@ -3,16 +3,18 @@ #include +class AdaptixWidget; + class DownloadsWidget : public QWidget { - QWidget* mainWidget = nullptr; - QTableWidget* tableWidget = nullptr; - QGridLayout* mainGridLayout = nullptr; + AdaptixWidget* adaptixWidget = nullptr; + QTableWidget* tableWidget = nullptr; + QGridLayout* mainGridLayout = nullptr; void createUI(); public: - DownloadsWidget(QWidget* w); + DownloadsWidget(AdaptixWidget* w); ~DownloadsWidget() override; void Clear() const; @@ -26,9 +28,6 @@ public slots: void actionSyncCurl() const; void actionSyncWget() const; void actionDelete() const; - void actionResume() const; - void actionPause() const; - void actionCancel() const; }; -#endif //ADAPTIXCLIENT_DOWNLOADSWIDGET_H \ No newline at end of file +#endif \ No newline at end of file diff --git a/AdaptixClient/Headers/UI/Widgets/ListenersWidget.h b/AdaptixClient/Headers/UI/Widgets/ListenersWidget.h index 9de2eaf2..d4ac1d0a 100644 --- a/AdaptixClient/Headers/UI/Widgets/ListenersWidget.h +++ b/AdaptixClient/Headers/UI/Widgets/ListenersWidget.h @@ -3,16 +3,18 @@ #include +class AdaptixWidget; + class ListenersWidget : public QWidget { - QWidget* mainWidget = nullptr; - QGridLayout* mainGridLayout = nullptr; - QTableWidget* tableWidget = nullptr; + AdaptixWidget* adaptixWidget = nullptr; + QGridLayout* mainGridLayout = nullptr; + QTableWidget* tableWidget = nullptr; void createUI(); public: - explicit ListenersWidget( QWidget* w ); + explicit ListenersWidget(AdaptixWidget* w); ~ListenersWidget() override; void Clear() const; @@ -22,10 +24,10 @@ public: public slots: void handleListenersMenu( const QPoint &pos ) const; - void createListener() const; - void editListener() const; - void removeListener() const; - void generateAgent() const; + void onCreateListener() const; + void onEditListener() const; + void onRemoveListener() const; + void onGenerateAgent() const; }; -#endif //ADAPTIXCLIENT_LISTENERSWIDGET_H \ No newline at end of file +#endif \ No newline at end of file diff --git a/AdaptixClient/Headers/UI/Widgets/LogsWidget.h b/AdaptixClient/Headers/UI/Widgets/LogsWidget.h index c0a2a1fd..9309e676 100644 --- a/AdaptixClient/Headers/UI/Widgets/LogsWidget.h +++ b/AdaptixClient/Headers/UI/Widgets/LogsWidget.h @@ -47,4 +47,4 @@ public slots: void handleSearchBackward(); }; -#endif //ADAPTIXCLIENT_LOGSWIDGET_H +#endif diff --git a/AdaptixClient/Headers/UI/Widgets/ScreenshotsWidget.h b/AdaptixClient/Headers/UI/Widgets/ScreenshotsWidget.h index 226bb35f..b8dc28c2 100644 --- a/AdaptixClient/Headers/UI/Widgets/ScreenshotsWidget.h +++ b/AdaptixClient/Headers/UI/Widgets/ScreenshotsWidget.h @@ -59,4 +59,4 @@ public slots: void actionDownload() const; }; -#endif //SCREENSHOTSWIDGET_H \ No newline at end of file +#endif \ No newline at end of file diff --git a/AdaptixClient/Headers/UI/Widgets/SessionsTableWidget.h b/AdaptixClient/Headers/UI/Widgets/SessionsTableWidget.h index abc29872..2d267cd4 100644 --- a/AdaptixClient/Headers/UI/Widgets/SessionsTableWidget.h +++ b/AdaptixClient/Headers/UI/Widgets/SessionsTableWidget.h @@ -5,10 +5,12 @@ #include class Agent; +class AdaptixWidget; class SessionsTableWidget : public QWidget { - QWidget* mainWidget = nullptr; + AdaptixWidget* adaptixWidget = nullptr; + QGridLayout* mainGridLayout = nullptr; QTableWidget* tableWidget = nullptr; QMenu* menuSessions = nullptr; @@ -44,7 +46,7 @@ public: int ColumnSleep = 14; int ColumnCount = 15; - explicit SessionsTableWidget( QWidget* w ); + explicit SessionsTableWidget( AdaptixWidget* w ); ~SessionsTableWidget() override; void AddAgentItem(Agent* newAgent) const; @@ -58,18 +60,13 @@ public: public slots: void toggleSearchPanel() const; + void onFilterUpdate() const; void handleTableDoubleClicked( const QModelIndex &index ) const; void handleSessionsTableMenu(const QPoint &pos ); - void onFilterUpdate() const; void actionConsoleOpen() const; void actionExecuteCommand(); void actionTasksBrowserOpen() const; - void actionTerminalOpen() const; - void actionFileBrowserOpen() const; - void actionProcessBrowserOpen() const; - void actionCreateTunnel() const; - void actionAgentExit() const; void actionMarkActive() const; void actionMarkInactive() const; void actionItemColor() const; @@ -82,4 +79,4 @@ public slots: void actionItemsShowAll() const; }; -#endif //ADAPTIXCLIENT_SESSIONSTABLEWIDGET_H +#endif diff --git a/AdaptixClient/Headers/UI/Widgets/TasksWidget.h b/AdaptixClient/Headers/UI/Widgets/TasksWidget.h index c4f8d8c6..b8320cc5 100644 --- a/AdaptixClient/Headers/UI/Widgets/TasksWidget.h +++ b/AdaptixClient/Headers/UI/Widgets/TasksWidget.h @@ -5,6 +5,7 @@ #include class Task; +class AdaptixWidget; class TaskOutputWidget : public QWidget { @@ -26,10 +27,10 @@ public: class TasksWidget : public QWidget { - QWidget* mainWidget = nullptr; - QGridLayout* mainGridLayout = nullptr; - QTableWidget* tableWidget = nullptr; - QShortcut* shortcutSearch = nullptr; + AdaptixWidget* adaptixWidget = nullptr; + QGridLayout* mainGridLayout = nullptr; + QTableWidget* tableWidget = nullptr; + QShortcut* shortcutSearch = nullptr; QWidget* searchWidget = nullptr; QHBoxLayout* searchLayout = nullptr; @@ -59,7 +60,7 @@ public: TaskOutputWidget* taskOutputConsole = nullptr; - explicit TasksWidget( QWidget* w ); + explicit TasksWidget( AdaptixWidget* w ); ~TasksWidget() override; void AddTaskItem(Task* newTask) const; @@ -80,8 +81,8 @@ public slots: void actionCopyTaskId() const; void actionCopyCmd() const; void actionOpenConsole() const; - void actionStop() const; + void actionCancel() const; void actionDelete() const; }; -#endif //ADAPTIXCLIENT_TASKSWIDGET_H +#endif diff --git a/AdaptixClient/Headers/UI/Widgets/TerminalWidget.h b/AdaptixClient/Headers/UI/Widgets/TerminalWidget.h index dad8c9b1..482d6aee 100644 --- a/AdaptixClient/Headers/UI/Widgets/TerminalWidget.h +++ b/AdaptixClient/Headers/UI/Widgets/TerminalWidget.h @@ -56,4 +56,4 @@ public slots: void recvDataFromSocket(const QByteArray &msg); }; -#endif //TERMINALWIDGET_H +#endif diff --git a/AdaptixClient/Headers/UI/Widgets/TunnelsWidget.h b/AdaptixClient/Headers/UI/Widgets/TunnelsWidget.h index adb9d4cd..c708e042 100644 --- a/AdaptixClient/Headers/UI/Widgets/TunnelsWidget.h +++ b/AdaptixClient/Headers/UI/Widgets/TunnelsWidget.h @@ -26,4 +26,4 @@ public slots: void actionStopTunnel() const; }; -#endif //TUNNELSWIDGET_H +#endif diff --git a/AdaptixClient/Headers/Utils/Convert.h b/AdaptixClient/Headers/Utils/Convert.h index 471009e4..d9b36d5f 100644 --- a/AdaptixClient/Headers/Utils/Convert.h +++ b/AdaptixClient/Headers/Utils/Convert.h @@ -37,4 +37,4 @@ QString GenerateRandomString(const int length, const QString &setName); QString GenerateHash(const QString &algorithm, int length, const QString &inputString); -#endif //ADAPTIXCLIENT_CONVERT_H \ No newline at end of file +#endif \ No newline at end of file diff --git a/AdaptixClient/Headers/Utils/CustomElements.h b/AdaptixClient/Headers/Utils/CustomElements.h index 1b10fa82..5e711a8b 100644 --- a/AdaptixClient/Headers/Utils/CustomElements.h +++ b/AdaptixClient/Headers/Utils/CustomElements.h @@ -3,6 +3,24 @@ #include +class ListDelegate : public QStyledItemDelegate { +public: + using QStyledItemDelegate::QStyledItemDelegate; + + QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override { + QLineEdit* editor = new QLineEdit(parent); + editor->setContentsMargins(1, 1, 1, 1); + return editor; + } + + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override { + QSize size = QStyledItemDelegate::sizeHint(option, index); + return QSize(size.width(), size.height() + 10); + } +}; + + + class PaddingDelegate : public QStyledItemDelegate { public: explicit PaddingDelegate(QObject* parent = nullptr) : QStyledItemDelegate(parent) {} @@ -18,13 +36,10 @@ public: if (hasBg) painter->fillRect(optFull.rect, bgBrush); + optFull.state &= ~QStyle::State_HasFocus; + QStyledItemDelegate::paint(painter, optFull, index); } - - // QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override { - // QSize originalSize = QStyledItemDelegate::sizeHint(option, index); - // return QSize(originalSize.width() + 30, originalSize.height()); - // } }; @@ -121,4 +136,4 @@ signals: void ctx_history(); }; -#endif //ADAPTIXCLIENT_CUSTOMELEMENTS_H +#endif diff --git a/AdaptixClient/Headers/Utils/FileSystem.h b/AdaptixClient/Headers/Utils/FileSystem.h index bad5b968..82b220d5 100644 --- a/AdaptixClient/Headers/Utils/FileSystem.h +++ b/AdaptixClient/Headers/Utils/FileSystem.h @@ -28,4 +28,4 @@ QString GetParentPathUnix(const QString& path); QIcon GetFileSystemIcon(int type, bool used); -#endif //ADAPTIXCLIENT_FILESYSTEM_H +#endif diff --git a/AdaptixClient/Headers/Utils/KeyPressHandler.h b/AdaptixClient/Headers/Utils/KeyPressHandler.h index babfca90..d5990b61 100644 --- a/AdaptixClient/Headers/Utils/KeyPressHandler.h +++ b/AdaptixClient/Headers/Utils/KeyPressHandler.h @@ -3,6 +3,34 @@ #include +class KPH_SearchInput : public QObject +{ +Q_OBJECT + QLineEdit* inputLineEdit; + +public: + KPH_SearchInput(QLineEdit *input, QObject *parent = nullptr) : QObject(parent), inputLineEdit(input) { + inputLineEdit->installEventFilter(this); + } + +signals: + void escPressed(); + +protected: + bool eventFilter(QObject *watched, QEvent *event) override { + if (watched == inputLineEdit && event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(event); + + if ( keyEvent->key() == Qt::Key_Escape ) { + emit escPressed(); + } + } + return QObject::eventFilter(watched, event); + } +}; + + + class KPH_ConsoleInput : public QObject { Q_OBJECT @@ -66,6 +94,11 @@ protected: return true; } + if (keyEvent->key() == Qt::Key_L && (keyEvent->modifiers() & Qt::ControlModifier)) { + outputTextEdit->clear(); + return true; + } + if (keyEvent->key() == Qt::Key_Tab) { if (keyEvent->modifiers() & Qt::ControlModifier) { QString filePath = QFileDialog::getOpenFileName(nullptr, "Select file"); @@ -157,4 +190,4 @@ protected: } }; -#endif //KEYPRESSHANDLER_H +#endif diff --git a/AdaptixClient/Headers/Utils/Logs.h b/AdaptixClient/Headers/Utils/Logs.h index 4f5b77f8..aabe07c9 100644 --- a/AdaptixClient/Headers/Utils/Logs.h +++ b/AdaptixClient/Headers/Utils/Logs.h @@ -16,4 +16,4 @@ void MessageError(const QString &message ); void MessageSuccess(const QString &message ); -#endif //ADAPTIXCLIENT_LOGS_H +#endif diff --git a/AdaptixClient/Headers/Workers/DownloaderWorker.h b/AdaptixClient/Headers/Workers/DownloaderWorker.h index 66d6f2a8..d53d7918 100644 --- a/AdaptixClient/Headers/Workers/DownloaderWorker.h +++ b/AdaptixClient/Headers/Workers/DownloaderWorker.h @@ -41,4 +41,4 @@ public slots: void onError(QNetworkReply::NetworkError); }; -#endif //DOWNLOADERWORKER_H +#endif diff --git a/AdaptixClient/Headers/Workers/LastTickWorker.h b/AdaptixClient/Headers/Workers/LastTickWorker.h index 60ff354c..9552bec8 100644 --- a/AdaptixClient/Headers/Workers/LastTickWorker.h +++ b/AdaptixClient/Headers/Workers/LastTickWorker.h @@ -21,4 +21,4 @@ public slots: void updateLastItems() const; }; -#endif //ADAPTIXCLIENT_LASTTICKWORKER_H +#endif diff --git a/AdaptixClient/Headers/Workers/TerminalWorker.h b/AdaptixClient/Headers/Workers/TerminalWorker.h index 8554585d..30d42388 100644 --- a/AdaptixClient/Headers/Workers/TerminalWorker.h +++ b/AdaptixClient/Headers/Workers/TerminalWorker.h @@ -37,4 +37,4 @@ private slots: void onWsError(QAbstractSocket::SocketError error); }; -#endif //TERMINALWORKER_H +#endif diff --git a/AdaptixClient/Headers/Workers/TunnelWorker.h b/AdaptixClient/Headers/Workers/TunnelWorker.h index f8bb1088..74dfed5c 100644 --- a/AdaptixClient/Headers/Workers/TunnelWorker.h +++ b/AdaptixClient/Headers/Workers/TunnelWorker.h @@ -35,4 +35,4 @@ private slots: void onWsError(QAbstractSocket::SocketError error); }; -#endif //TUNNELWORKER_H +#endif diff --git a/AdaptixClient/Headers/Workers/UploaderWorker.h b/AdaptixClient/Headers/Workers/UploaderWorker.h index 60d8a638..e839c193 100644 --- a/AdaptixClient/Headers/Workers/UploaderWorker.h +++ b/AdaptixClient/Headers/Workers/UploaderWorker.h @@ -40,4 +40,4 @@ public slots: void onError(QNetworkReply::NetworkError); }; -#endif //UPLOADERWORKER_H +#endif diff --git a/AdaptixClient/Headers/Workers/WebSocketWorker.h b/AdaptixClient/Headers/Workers/WebSocketWorker.h index 2e7e9c75..89c937bc 100644 --- a/AdaptixClient/Headers/Workers/WebSocketWorker.h +++ b/AdaptixClient/Headers/Workers/WebSocketWorker.h @@ -34,4 +34,4 @@ signals: void websocket_closed(); }; -#endif //ADAPTIXCLIENT_WEBSOCKETWORKER_H \ No newline at end of file +#endif \ No newline at end of file diff --git a/AdaptixClient/Headers/main.h b/AdaptixClient/Headers/main.h index 94ff0a34..f05685d3 100644 --- a/AdaptixClient/Headers/main.h +++ b/AdaptixClient/Headers/main.h @@ -67,7 +67,7 @@ #include #include -#define FRAMEWORK_VERSION "Adaptix Framework v0.6" +#define FRAMEWORK_VERSION "Adaptix Framework v0.7" /////////// @@ -109,6 +109,7 @@ #define TYPE_AGENT_TASK_UPDATE 0x4a #define TYPE_AGENT_TASK_SEND 0x4b #define TYPE_AGENT_TASK_REMOVE 0x4c +#define TYPE_AGENT_TASK_HOOK 0x4d #define TYPE_DOWNLOAD_CREATE 0x51 #define TYPE_DOWNLOAD_UPDATE 0x52 @@ -134,6 +135,10 @@ #define TYPE_PIVOT_CREATE 0x71 #define TYPE_PIVOT_DELETE 0x72 +#define TYPE_CREDS_CREATE 0x81 +#define TYPE_CREDS_EDIT 0x82 +#define TYPE_CREDS_DELETE 0x83 + ////////// #define DOWNLOAD_STATE_RUNNING 0x1 @@ -163,25 +168,6 @@ ////////// -typedef struct BrowsersConfig { - bool RemoteTerminal; - bool FileBrowser; - bool FileBrowserDisks; - bool FileBrowserDownload; - bool FileBrowserUpload; - bool ProcessBrowser; - bool DownloadsCancel; - bool DownloadsResume; - bool DownloadsPause; - bool TasksJobKill; - bool SessionsMenuExit; - bool SessionsMenuTunnels; - bool Socks4; - bool Socks5; - bool Lportfwd; - bool Rportfwd; -} BrowsersConfig; - typedef struct SettingsData { QString MainTheme; QString FontFamily; @@ -205,7 +191,7 @@ typedef struct SettingsData { typedef struct ListenerData { QString ListenerName; - QString ListenerType; + QString ListenerFullName; QString BindHost; QString BindPort; QString AgentAddresses; @@ -267,6 +253,20 @@ typedef struct ScreenData QByteArray Content; } ScreenData; +typedef struct CredentialData +{ + QString CredId; + QString Username; + QString Password; + QString Realm; + QString Type; + QString Tag; + QString Date; + QString Storage; + QString AgentId; + QString Host; +} CredentialData; + typedef struct TunnelData { QString TunnelId; @@ -313,12 +313,13 @@ typedef struct ExtensionFile { QString Name; QString FilePath; + QString Code; QString Description; - QString Comment; + QString Message; bool Enabled; + bool NoSave; bool Valid; - QVector ExConstants; QMap > ExCommands; } ExtensionFile; diff --git a/AdaptixClient/Libs/Konsole/Emulation.cpp b/AdaptixClient/Libs/Konsole/Emulation.cpp index 8bbb8ea2..b4af312d 100644 --- a/AdaptixClient/Libs/Konsole/Emulation.cpp +++ b/AdaptixClient/Libs/Konsole/Emulation.cpp @@ -92,7 +92,6 @@ void Emulation::setScreen(int n) { Screen *old = _currentScreen; _currentScreen = _screen[n & 1]; if (_currentScreen != old) { - // tell all windows onto this emulation to switch to the newly active screen for (ScreenWindow *window : std::as_const(_windows)) window->setScreen(_currentScreen); checkScreenInUse(); @@ -146,8 +145,6 @@ QString Emulation::keyBindings() const { return _keyTranslator->name(); } -// process application unicode input to terminal -// this is a trivial scanner void Emulation::receiveChar(wchar_t c) { c &= 0xff; switch (c) { @@ -175,7 +172,7 @@ void Emulation::receiveChar(wchar_t c) { void Emulation::sendKeyEvent(QKeyEvent *ev, bool) { emit stateSet(NOTIFYNORMAL); - if (!ev->text().isEmpty()) { // A block of text + if (!ev->text().isEmpty()) { emit sendData(ev->text().toUtf8().constData(), ev->text().length()); } } @@ -197,11 +194,9 @@ void Emulation::receiveData(const char *text, int length) { for (int i = 0; i < length; i++) { if (text[i] == '\030') { - // ZRQINIT 0 Request receive init if ((length - i - 1 > 3) && (strncmp(text + i + 1, "B00", 3) == 0)) { emit zmodemSendDetected(); } - // ZRINIT 1 Receive init if ((length - i - 1 > 5) && (strncmp(text + i + 1, "B0100", 5) == 0)) { emit zmodemRecvDetected(); } @@ -237,7 +232,6 @@ void Emulation::writeToStream(TerminalCharacterDecoder *_decoder, int startLine, } int Emulation::lineCount() const { - // sum number of lines currently on _screen plus number of lines in history return _currentScreen->getLines() + _currentScreen->getHistLines(); } @@ -316,7 +310,7 @@ uint ExtendedCharTable::createExtendedChar(uint* unicodePoints , ushort length) const uint initialHash = hash; bool triedCleaningSolution = false; - while (extendedCharTable.contains(hash) && hash != 0) { // 0 has a special meaning for chars so we don't use it + while (extendedCharTable.contains(hash) && hash != 0) { if (extendedCharMatch(hash, unicodePoints, length)) { return hash; } else { @@ -342,7 +336,6 @@ uint ExtendedCharTable::createExtendedChar(uint* unicodePoints , ushort length) } } } else { - qWarning() << "Using all the extended char hashes, going to miss this extended character"; return 0; } } @@ -374,7 +367,6 @@ ExtendedCharTable::ExtendedCharTable() { } ExtendedCharTable::~ExtendedCharTable() { - // free all allocated character buffers QHashIterator iter(extendedCharTable); while (iter.hasNext()) { iter.next(); @@ -382,5 +374,4 @@ ExtendedCharTable::~ExtendedCharTable() { } } -// global instance ExtendedCharTable ExtendedCharTable::instance; diff --git a/AdaptixClient/Libs/Konsole/Emulation.h b/AdaptixClient/Libs/Konsole/Emulation.h index 75bedfb0..e47ade4d 100644 --- a/AdaptixClient/Libs/Konsole/Emulation.h +++ b/AdaptixClient/Libs/Konsole/Emulation.h @@ -143,18 +143,18 @@ protected: LocaleCodec = 0, Utf8Codec = 1 }; - void setCodec(EmulationCodec codec); // codec number, 0 = locale, 1=utf8 + void setCodec(EmulationCodec codec); QList _windows; - Screen* _currentScreen; // pointer to the screen which is currently active, + Screen* _currentScreen; Screen* _screen[2]; QStringEncoder _fromUtf16; QStringDecoder _toUtf16; - const KeyboardTranslator* _keyTranslator; // the keyboard layout + const KeyboardTranslator* _keyTranslator; bool _enableHandleCtrlC; @@ -181,4 +181,4 @@ private: QByteArray dupCache; }; -#endif // EMULATION_H \ No newline at end of file +#endif \ No newline at end of file diff --git a/AdaptixClient/Libs/Konsole/Screen.cpp b/AdaptixClient/Libs/Konsole/Screen.cpp index a2bf05fb..7bab568d 100644 --- a/AdaptixClient/Libs/Konsole/Screen.cpp +++ b/AdaptixClient/Libs/Konsole/Screen.cpp @@ -136,7 +136,7 @@ void Screen::nextLine() { void Screen::eraseChars(int n) { if (n == 0) - n = 1; // Default + n = 1; int p = qMax(0, qMin(cuX + n - 1, columns - 1)); clearImage(loc(cuX, cuY), loc(p, cuY), ' '); } @@ -161,7 +161,7 @@ void Screen::deleteChars(int n) { void Screen::insertChars(int n) { if (n == 0) - n = 1; // Default + n = 1; if (screenLines[cuY].size() < cuX) screenLines[cuY].resize(cuX); @@ -249,16 +249,14 @@ void Screen::resizeImage(int new_lines, int new_columns) { if ((new_lines == lines) && (new_columns == columns)) return; - if (cuY > new_lines - 1) { // attempt to preserve focus and lines - _bottomMargin = lines - 1; // FIXME: margin lost + if (cuY > new_lines - 1) { + _bottomMargin = lines - 1; for (int i = 0; i < cuY - (new_lines - 1); i++) { addHistLine(); scrollUp(0, 1); } } - // create new screen lines and copy from old to new - ImageLine *newScreenLines = new ImageLine[new_lines + 1]; for (int i = 0; i < qMin(lines, new_lines + 1); i++) newScreenLines[i] = screenLines[i]; @@ -279,7 +277,6 @@ void Screen::resizeImage(int new_lines, int new_columns) { cuX = qMin(cuX, columns - 1); cuY = qMin(cuY, lines - 1); - // FIXME: try to keep values, evtl. _topMargin = 0; _bottomMargin = lines - 1; initTabStops(); @@ -329,7 +326,7 @@ void Screen::reverseRendition(Character &p) const { CharacterColor b = p.backgroundColor; p.foregroundColor = b; - p.backgroundColor = f; // p->r &= ~RE_TRANSPARENT; + p.backgroundColor = f; } void Screen::updateEffectiveRendition() { @@ -359,7 +356,6 @@ void Screen::copyFromHistory(Character *dest, int startLine, int count) const { for (int column = length; column < columns; column++) dest[destLineOffset + column] = defaultChar; - // invert selected text if (selBegin != -1) { for (int column = 0; column < columns; column++) { if (isSelected(column, line)) { @@ -384,7 +380,6 @@ void Screen::copyFromScreen(Character *dest, int startLine, int count) const { dest[destIndex] = screenLines[srcIndex / columns].value( srcIndex % columns, defaultChar); - // invert selected text if (selBegin != -1 && isSelected(column, line + history->getLines())) reverseRendition(dest[destIndex]); } @@ -405,23 +400,17 @@ void Screen::getImage(Character *dest, int size, int startLine, qBound(0, history->getLines() - startLine, mergedLines); const int linesInScreenBuffer = mergedLines - linesInHistoryBuffer; - // copy lines from history buffer if (linesInHistoryBuffer > 0) copyFromHistory(dest, startLine, linesInHistoryBuffer); - // copy lines from screen buffer if (linesInScreenBuffer > 0) - copyFromScreen(dest + linesInHistoryBuffer * columns, - startLine + linesInHistoryBuffer - history->getLines(), - linesInScreenBuffer); + copyFromScreen(dest + linesInHistoryBuffer * columns, startLine + linesInHistoryBuffer - history->getLines(), linesInScreenBuffer); - // invert display when in screen mode if (getMode(MODE_Screen)) { for (int i = 0; i < mergedLines * columns; i++) - reverseRendition(dest[i]); // for reverse display + reverseRendition(dest[i]); } - // mark the character at the current cursor position int cursorIndex = loc(cuX, cuY + linesInHistoryBuffer); if (getMode(MODE_Cursor) && cursorIndex < columns * mergedLines) dest[cursorIndex].rendition |= RE_CURSOR; @@ -440,16 +429,13 @@ QVector Screen::getLineProperties(int startLine, QVector result(mergedLines); int index = 0; - // copy properties for lines in history for (int line = startLine; line < startLine + linesInHistory; line++) { - // TODO Support for line properties other than wrapped lines if (history->isWrappedLine(line)) { result[index] = (LineProperty)(result[index] | LINE_WRAPPED); } index++; } - // copy properties for lines in screen buffer const int firstScreenLine = startLine + linesInHistory - history->getLines(); for (int line = firstScreenLine; line < firstScreenLine + linesInScreen; line++) { @@ -462,13 +448,13 @@ QVector Screen::getLineProperties(int startLine, void Screen::reset(bool clearScreen) { setMode(MODE_Wrap); - saveMode(MODE_Wrap); // wrap at end of margin + saveMode(MODE_Wrap); resetMode(MODE_Origin); - saveMode(MODE_Origin); // position refers to [1,1] + saveMode(MODE_Origin); resetMode(MODE_Insert); - saveMode(MODE_Insert); // overstroke - setMode(MODE_Cursor); // cursor visible - resetMode(MODE_Screen); // screen not inverse + saveMode(MODE_Insert); + setMode(MODE_Cursor); + resetMode(MODE_Screen); resetMode(MODE_NewLine); _topMargin = 0; @@ -487,13 +473,12 @@ void Screen::clear() { } void Screen::backspace() { - cuX = qMin(columns - 1, cuX); // nowrap! + cuX = qMin(columns - 1, cuX); cuX = qMax(0, cuX - 1); if (screenLines[cuY].size() < cuX + 1) screenLines[cuY].resize(cuX + 1); -#if 0 // TODO: implement when unicode_width is fixed, we need more - // backspace/cursorMove +#if 0 wchar_t c = 0; if(cuX <= 0) { if(cuY > 0) { @@ -509,7 +494,7 @@ void Screen::backspace() { int ow = CharWidth::unicode_width(c,false); int w = CharWidth::unicode_width(c,true); if(w == 2 && ow == 1) { - cuX = qMin(columns-1,cuX); // nowrap! + cuX = qMin(columns-1,cuX); cuX = qMax(0,cuX-1); if (screenLines[cuY].size() < cuX+1) @@ -520,7 +505,6 @@ void Screen::backspace() { } void Screen::tab(int n) { - // note that TAB is a format effector (does not write ' '); if (n == 0) n = 1; while ((n > 0) && (cuX < columns - 1)) { @@ -532,7 +516,6 @@ void Screen::tab(int n) { } void Screen::backtab(int n) { - // note that TAB is a format effector (does not write ' '); if (n == 0) n = 1; while ((n > 0) && (cuX > 0)) { @@ -557,9 +540,6 @@ void Screen::changeTabStop(bool set) { void Screen::initTabStops() { tabStops.resize(columns); - // Arrg! The 1st tabstop has to be one longer than the other. - // i.e. the kids start counting from 0 instead of 1. - // Other programs might behave correctly. Be aware. for (int i = 0; i < columns; i++) tabStops[i] = (i % 8 == 0 && i != 0); } @@ -574,17 +554,11 @@ void Screen::checkSelection(int from, int to) { if (selBegin == -1) return; int scr_TL = loc(0, history->getLines()); - // Clear entire selection if it overlaps region [from, to] if ((selBottomRight >= (from + scr_TL)) && (selTopLeft <= (to + scr_TL))) clearSelection(); } void Screen::displayCharacter(wchar_t c) { - // Note that VT100 does wrapping BEFORE putting the character. - // This has impact on the assumption of valid cursor positions. - // We indicate the fact that a newline has to be triggered by - // putting the cursor one right to the last column of the screen. - int w = CharWidth::unicode_width(c); if (w < 0) return; @@ -592,7 +566,6 @@ void Screen::displayCharacter(wchar_t c) { if (w == 0) { if (QChar(c).category() != QChar::Mark_NonSpacing) return; - // Find previous "real character" to try to combine with int charToCombineWithX = qMin(cuX, screenLines[cuY].length()); int charToCombineWithY = cuY; bool previousChar = true; @@ -600,16 +573,13 @@ void Screen::displayCharacter(wchar_t c) { if (charToCombineWithX > 0) { --charToCombineWithX; } else if (charToCombineWithY > 0 && lineProperties.at(charToCombineWithY - 1) & LINE_WRAPPED) { - // Try previous line --charToCombineWithY; charToCombineWithX = screenLines[charToCombineWithY].length() - 1; } else { - // Give up previousChar = false; break; } - // Failsafe if (charToCombineWithX < 0) { previousChar = false; break; @@ -632,7 +602,7 @@ void Screen::displayCharacter(wchar_t c) { Q_ASSERT(oldChars); if (oldChars && extendedCharLength < 8) { Q_ASSERT(extendedCharLength > 1); - Q_ASSERT(extendedCharLength < 65535); // redundant due to above check + Q_ASSERT(extendedCharLength < 65535); auto chars = std::make_unique(extendedCharLength + 1); std::copy_n(oldChars, extendedCharLength, chars.get()); chars[extendedCharLength] = c; @@ -652,7 +622,6 @@ notcombine: } } - // ensure current line vector has enough elements int size = screenLines[cuY].size(); if (size < cuX + w) { screenLines[cuY].resize(cuX + w); @@ -663,7 +632,6 @@ notcombine: lastPos = loc(cuX, cuY); - // check if selection is still valid. checkSelection(lastPos, lastPos); Character ¤tChar = screenLines[cuY][cuX]; @@ -702,7 +670,7 @@ void Screen::compose(const QString & /*compose*/) { QChar c(image[lastPos].character); compose.prepend(c); - //compose.compose(); ### FIXME! + compose.compose(); ### FIXME! image[lastPos].character = compose[0].unicode();*/ } @@ -713,9 +681,9 @@ void Screen::resetScrolledLines() { _scrolledLines = 0; } void Screen::scrollUp(int n) { if (n == 0) - n = 1; // Default + n = 1; if (_topMargin == 0) - addHistLine(); // history.history + addHistLine(); scrollUp(_topMargin, n); } @@ -733,7 +701,6 @@ void Screen::scrollUp(int from, int n) { _lastScrolledRegion = QRect(0, _topMargin, columns - 1, (_bottomMargin - _topMargin)); - // FIXME: make sure `topMargin', `bottomMargin', `from', `n' is in bounds. moveImage(loc(0, from), loc(0, from + n), loc(columns, _bottomMargin)); clearImage(loc(0, _bottomMargin - n + 1), loc(columns - 1, _bottomMargin), ' '); @@ -741,14 +708,13 @@ void Screen::scrollUp(int from, int n) { void Screen::scrollDown(int n) { if (n == 0) - n = 1; // Default + n = 1; scrollDown(_topMargin, n); } void Screen::scrollDown(int from, int n) { _scrolledLines += n; - // FIXME: make sure `topMargin', `bottomMargin', `from', `n' is in bounds. if (n <= 0) return; if (from > _bottomMargin) @@ -767,15 +733,15 @@ void Screen::setCursorYX(int y, int x) { void Screen::setCursorX(int x) { if (x == 0) - x = 1; // Default - x -= 1; // Adjust + x = 1; + x -= 1; cuX = qMax(0, qMin(columns - 1, x)); } void Screen::setCursorY(int y) { if (y == 0) - y = 1; // Default - y -= 1; // Adjust + y = 1; + y -= 1; cuY = qMax(0, qMin(lines - 1, y + (getMode(MODE_Origin) ? _topMargin : 0))); } @@ -836,9 +802,7 @@ QString Screen::getScreenText(int row1, int col1, int row2, int col2, int mode) void Screen::clearImage(int loca, int loce, char c) { int scr_TL = loc(0, history->getLines()); - // FIXME: check positions - // Clear entire selection if it overlaps region to be moved... if ((selBottomRight > (loca + scr_TL)) && (selTopLeft < (loce + scr_TL))) { clearSelection(); } @@ -848,8 +812,6 @@ void Screen::clearImage(int loca, int loce, char c) { Character clearCh(c, currentForeground, currentBackground, DEFAULT_RENDITION); - // if the character being used to clear the area is the same as the - // default character, the affected lines can simply be shrunk. bool isDefaultCh = (clearCh == Character()); for (int y = topLine; y <= bottomLine; y++) { @@ -878,11 +840,6 @@ void Screen::moveImage(int dest, int sourceBegin, int sourceEnd) { int lines = (sourceEnd - sourceBegin) / columns; - // move screen image and line properties: - // the source and destination areas of the image may overlap, - // so it matters that we do the copy in the right order - - // forwards if dest < sourceBegin or backwards otherwise. - //(search the web for 'memmove implementation' for details) if (dest < sourceBegin) { for (int i = 0; i <= lines; i++) { screenLines[(dest / columns) + i] = @@ -900,31 +857,30 @@ void Screen::moveImage(int dest, int sourceBegin, int sourceEnd) { } if (lastPos != -1) { - int diff = dest - sourceBegin; // Scroll by this amount + int diff = dest - sourceBegin; lastPos += diff; if ((lastPos < 0) || (lastPos >= (lines * columns))) lastPos = -1; } - // Adjust selection to follow scroll. if (selBegin != -1) { bool beginIsTL = (selBegin == selTopLeft); - int diff = dest - sourceBegin; // Scroll by this amount + int diff = dest - sourceBegin; int scr_TL = loc(0, history->getLines()); - int srca = sourceBegin + scr_TL; // Translate index from screen to global - int srce = sourceEnd + scr_TL; // Translate index from screen to global + int srca = sourceBegin + scr_TL; + int srce = sourceEnd + scr_TL; int desta = srca + diff; int deste = srce + diff; if ((selTopLeft >= srca) && (selTopLeft <= srce)) selTopLeft += diff; else if ((selTopLeft >= desta) && (selTopLeft <= deste)) - selBottomRight = -1; // Clear selection (see below) + selBottomRight = -1; if ((selBottomRight >= srca) && (selBottomRight <= srce)) selBottomRight += diff; else if ((selBottomRight >= desta) && (selBottomRight <= deste)) - selBottomRight = -1; // Clear selection (see below) + selBottomRight = -1; if (selBottomRight < 0) { clearSelection(); @@ -949,7 +905,6 @@ void Screen::clearToBeginOfScreen() { } void Screen::clearEntireScreen() { - // Add entire screen to history for (int i = 0; i < (lines - 1); i++) { addHistLine(); scrollUp(0, 1); @@ -958,10 +913,6 @@ void Screen::clearEntireScreen() { clearImage(loc(0, 0), loc(columns - 1, lines - 1), ' '); } -/*! fill screen with 'E' - This is to aid screen alignment - */ - void Screen::helpAlign() { clearImage(loc(0, 0), loc(columns - 1, lines - 1), 'E'); } @@ -1070,7 +1021,6 @@ void Screen::setSelectionEnd(const int x, const int y) { selBottomRight = endPos; } - // Normalize the selection in column mode if (blockSelectionMode) { int topRow = selTopLeft / columns; int topColumn = selTopLeft % columns; @@ -1138,12 +1088,6 @@ void Screen::writeToStream(TerminalCharacterDecoder *decoder, int startIndex, const bool appendNewLine = (y != bottom); int copied = copyLineToStream(y, start, count, decoder, appendNewLine, preserveLineBreaks); - - // if the selection goes beyond the end of the last line then - // append a new line character. - // - // this makes it possible to 'select' a trailing new line character after - // the text on a line. if (y == bottom && copied < count) { Character newLineChar('\n'); decoder->decodeLine(&newLineChar, 1, 0); @@ -1151,14 +1095,7 @@ void Screen::writeToStream(TerminalCharacterDecoder *decoder, int startIndex, } } -int Screen::copyLineToStream(int line, int start, int count, - TerminalCharacterDecoder *decoder, - bool appendNewLine, - bool preserveLineBreaks) const { - // buffer to hold characters for decoding - // the buffer is static to avoid initialising every - // element on each call to copyLineToStream - //(which is unnecessary since all elements will be overwritten anyway) +int Screen::copyLineToStream(int line, int start, int count, TerminalCharacterDecoder *decoder, bool appendNewLine, bool preserveLineBreaks) const { static const int MAX_CHARS = 1024; static Character characterBuffer[MAX_CHARS]; @@ -1166,23 +1103,17 @@ int Screen::copyLineToStream(int line, int start, int count, LineProperty currentLineProperties = 0; - // determine if the line is in the history buffer or the screen image if (line < history->getLines()) { const int lineLength = history->getLineLen(line); - // ensure that start position is before end of line start = qMin(start, qMax(0, lineLength - 1)); - // retrieve line from history buffer. It is assumed - // that the history buffer does not store trailing white space - // at the end of the line, so it does not need to be trimmed here if (count == -1) { count = lineLength - start; } else { count = qMin(start + count, lineLength) - start; } - // safety checks Q_ASSERT(start >= 0); Q_ASSERT(count >= 0); Q_ASSERT((start + count) <= history->getLineLen(line)); @@ -1202,30 +1133,24 @@ int Screen::copyLineToStream(int line, int start, int count, Character *data = screenLines[screenLine].data(); int length = screenLines[screenLine].count(); - // retrieve line from screen image for (int i = start; i < qMin(start + count, length); i++) { characterBuffer[i - start] = data[i]; } - // count cannot be any greater than length count = qBound(0, count, length >= start ? length - start : 0); Q_ASSERT(screenLine < lineProperties.count()); currentLineProperties |= lineProperties[screenLine]; } - // add new line character at end - const bool omitLineBreak = - (currentLineProperties & LINE_WRAPPED) || !preserveLineBreaks; + const bool omitLineBreak = (currentLineProperties & LINE_WRAPPED) || !preserveLineBreaks; if (!omitLineBreak && appendNewLine && (count + 1 < MAX_CHARS)) { characterBuffer[count] = '\n'; count++; } - // decode line and write to text stream - decoder->decodeLine((Character *)characterBuffer, count, - currentLineProperties); + decoder->decodeLine((Character *)characterBuffer, count, currentLineProperties); return count; } @@ -1236,8 +1161,6 @@ void Screen::writeLinesToStream(TerminalCharacterDecoder *decoder, int fromLine, } void Screen::addHistLine() { - // add line to history buffer - // we have to take care about scrolling, too... if (hasScroll()) { int oldHistLines = history->getLines(); @@ -1249,12 +1172,9 @@ void Screen::addHistLine() { bool beginIsTL = (selBegin == selTopLeft); - // If the history is full, increment the count - // of dropped lines if (newHistLines == oldHistLines) _droppedLines++; - // Adjust selection for the new point of reference if (newHistLines > oldHistLines) { if (selBegin != -1) { selTopLeft += columns; @@ -1263,7 +1183,6 @@ void Screen::addHistLine() { } if (selBegin != -1) { - // Scroll selection in history up int top_BR = loc(0, 1 + newHistLines); if (selTopLeft < top_BR) diff --git a/AdaptixClient/Libs/Konsole/ScreenWindow.cpp b/AdaptixClient/Libs/Konsole/ScreenWindow.cpp index 6628f392..9dc0ee05 100644 --- a/AdaptixClient/Libs/Konsole/ScreenWindow.cpp +++ b/AdaptixClient/Libs/Konsole/ScreenWindow.cpp @@ -172,8 +172,6 @@ void ScreenWindow::scrollTo(int line) { const int delta = line - _currentLine; _currentLine = line; - // keep track of number of lines scrolled by, - // this can be reset by calling resetScrollCount() _scrollCount += delta; _bufferNeedsUpdate = true; @@ -216,10 +214,8 @@ void ScreenWindow::notifyOutputChanged() { void ScreenWindow::handleCommandFromKeyboard( KeyboardTranslator::Command command) { - // Keyboard-based navigation bool update = false; - // EraseCommand is handled in Vt102Emulation if (command & KeyboardTranslator::ScrollPageUpCommand) { scrollBy(ScreenWindow::ScrollPages, -1); update = true; diff --git a/AdaptixClient/Libs/Konsole/TerminalDisplay.cpp b/AdaptixClient/Libs/Konsole/TerminalDisplay.cpp index 900328b7..cfe876c1 100644 --- a/AdaptixClient/Libs/Konsole/TerminalDisplay.cpp +++ b/AdaptixClient/Libs/Konsole/TerminalDisplay.cpp @@ -42,19 +42,16 @@ const ColorEntry base_color_table[TABLE_COLORS] = { - // Fixme: could add faint colors here, also. - // normal ColorEntry(QColor(0x00, 0x00, 0x00), false), - ColorEntry(QColor(0xB2, 0xB2, 0xB2), true), // Dfore, Dback + ColorEntry(QColor(0xB2, 0xB2, 0xB2), true), ColorEntry(QColor(0x00, 0x00, 0x00), false), - ColorEntry(QColor(0xB2, 0x18, 0x18), false), // Black, Red + ColorEntry(QColor(0xB2, 0x18, 0x18), false), ColorEntry(QColor(0x18, 0xB2, 0x18), false), - ColorEntry(QColor(0xB2, 0x68, 0x18), false), // Green, Yellow + ColorEntry(QColor(0xB2, 0x68, 0x18), false), ColorEntry(QColor(0x18, 0x18, 0xB2), false), - ColorEntry(QColor(0xB2, 0x18, 0xB2), false), // Blue, Magenta + ColorEntry(QColor(0xB2, 0x18, 0xB2), false), ColorEntry(QColor(0x18, 0xB2, 0xB2), false), - ColorEntry(QColor(0xB2, 0xB2, 0xB2), false), // Cyan, White - // intensiv + ColorEntry(QColor(0xB2, 0xB2, 0xB2), false), ColorEntry(QColor(0x00, 0x00, 0x00), false), ColorEntry(QColor(0xFF, 0xFF, 0xFF), true), ColorEntry(QColor(0x68, 0x68, 0x68), false), @@ -67,13 +64,8 @@ const ColorEntry base_color_table[TABLE_COLORS] = ColorEntry(QColor(0xFF, 0xFF, 0xFF), false) }; -// scroll increment used when dragging selection at top/bottom of window. - -// static bool TerminalDisplay::_antialiasText = true; -// we use this to force QPainter to display text in LTR mode -// more information can be found in: http://unicode.org/reports/tr9/ const QChar LTR_OVERRIDE_CHAR(0x202D); /* ------------------------------------------------------------------------- */ @@ -90,18 +82,12 @@ const QChar LTR_OVERRIDE_CHAR(0x202D); IBMPC (rgb) Black Blue Green Cyan Red Magenta Yellow White */ -// using global statics for the unclutter feature makes tracking the override cursor simple -// there's only one cursor to override and only one terminal relevant for that at any time -// gs_deadSpot serves as flag and also allows a position check - it doesn't matter that this isn't -// correct when checking the position across instances as its only purpose is to catch judder when -// the user doesn't really touch the mouse - once the mouse moves we'll quickly be out of the deadzone static QPoint gs_deadSpot(-1,-1); static QPoint gs_futureDeadSpot; std::shared_ptr TerminalDisplay::_hideMouseTimer; ScreenWindow *TerminalDisplay::screenWindow() const { return _screenWindow; } void TerminalDisplay::setScreenWindow(ScreenWindow *window) { - // disconnect existing screen window if any if (_screenWindow) { disconnect(_screenWindow, nullptr, this, nullptr); } @@ -109,9 +95,6 @@ void TerminalDisplay::setScreenWindow(ScreenWindow *window) { _screenWindow = window; if (window) { - // TODO: Determine if this is an issue. - // #warning "The order here is not specified - does it matter whether - // updateImage or updateLineProperties comes first?" connect(_screenWindow, &ScreenWindow::outputChanged, this, &TerminalDisplay::updateLineProperties); connect(_screenWindow, &ScreenWindow::outputChanged, this, @@ -136,7 +119,6 @@ void TerminalDisplay::setBackgroundColor(const QColor &color) { p.setColor(backgroundRole(), color); setPalette(p); - // Avoid propagating the palette change to the scroll bar _scrollBar->setPalette(QApplication::palette()); update(); @@ -190,10 +172,6 @@ void TerminalDisplay::fontChange(const QFont &) { QFontMetrics fm(font()); _fontHeight = fm.height() + _lineSpacing; - // waba TerminalDisplay 1.123: - // "Base character width on widest ASCII character. This prevents too wide - // characters in the presence of double wide (e.g. Japanese) characters." - // Get the width from representative normal width characters _fontWidth = qRound((double)fm.horizontalAdvance(QLatin1String(REPCHAR)) / (double)qstrlen(REPCHAR)); @@ -217,9 +195,6 @@ void TerminalDisplay::fontChange(const QFont &) { emit changedFontMetricSignal(_fontHeight, _fontWidth); propagateSize(); - // We will run paint event testing procedure. - // Although this operation will destroy the original content, - // the content will be drawn again after the test. _drawTextTestFlag = true; update(); } @@ -232,7 +207,6 @@ void TerminalDisplay::calDrawTextAdditionHeight(QPainter &painter) { painter.drawText(test_rect, Qt::AlignBottom, LTR_OVERRIDE_CHAR + QLatin1String("Mq"), &feedback_rect); - // qDebug() << "test_rect:" << test_rect << "feeback_rect:" << feedback_rect; painter.restore(); _drawTextAdditionHeight = qMax(0, (feedback_rect.height() - _fontHeight) / 2); @@ -243,30 +217,16 @@ void TerminalDisplay::calDrawTextAdditionHeight(QPainter &painter) { void TerminalDisplay::setVTFont(const QFont &f) { QFont font = f; - // Check if font is not fixed pitch and print a warning if (!QFontInfo(font).fixedPitch()) { - // qDebug() << "Using a variable-width font in the terminal. This may cause - // performance degradation and display/alignment errors."; } - // hint that text should be drawn without anti-aliasing. - // depending on the user's font configuration, this may not be respected if (!_antialiasText) font.setStyleStrategy(QFont::NoAntialias); - // experimental optimization. Konsole assumes that the terminal is using a - // mono-spaced font, in which case kerning information should have no effect. - // Disabling kerning saves some computation when rendering text. font.setKerning(false); - // QFont::ForceIntegerMetrics has been removed. - // Set full hinting instead to ensure the letters are aligned properly. font.setHintingPreference(QFont::PreferFullHinting); - // "Draw intense colors in bold font" feature needs to use different font - // weights. StyleName property, when set, doesn't allow weight changes. Since - // all properties (weight, stretch, italic, etc) are stored in QFont - // independently, in almost all cases styleName is not needed. font.setStyleName(QString()); QWidget::setFont(font); @@ -274,9 +234,7 @@ void TerminalDisplay::setVTFont(const QFont &f) { fontChange(font); } -void TerminalDisplay::setFont(const QFont &) { - // ignore font change request if not coming from konsole itself -} +void TerminalDisplay::setFont(const QFont &) {} /* ------------------------------------------------------------------------- */ /* */ @@ -308,40 +266,25 @@ TerminalDisplay::TerminalDisplay(QWidget *parent) _cursorShape(Emulation::KeyboardCursorShape::BlockCursor), mMotionAfterPasting(NoMoveScreenWindow), _leftBaseMargin(1), _topBaseMargin(1), _drawLineChars(true),_mouseAutohideDelay(-1) { - // variables for draw text _drawTextAdditionHeight = 0; _drawTextTestFlag = false; - // terminal applications are not designed with Right-To-Left in mind, - // so the layout is forced to Left-To-Right setLayoutDirection(Qt::LeftToRight); - // The offsets are not yet calculated. - // Do not calculate these too often to be more smoothly when resizing - // konsole in opaque mode. _topMargin = _topBaseMargin; _leftMargin = _leftBaseMargin; - // create scroll bar for scrolling output up and down - // set the scroll bar's slider to occupy the whole area of the scroll bar - // initially _scrollBar = new ScrollBar(this); QString style_sheet = qApp->styleSheet(); _scrollBar->setStyleSheet(style_sheet); - // since the contrast with the terminal background may not be enough, - // the scrollbar should be auto-filled if not transient if (!_scrollBar->style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, _scrollBar)) _scrollBar->setAutoFillBackground(true); setScroll(0, 0); _scrollBar->setCursor(Qt::ArrowCursor); connect(_scrollBar, &QScrollBar::valueChanged, this, &TerminalDisplay::scrollBarPositionChanged); - // qtermwidget: we have to hide it here due the - // _scrollbarLocation==NoScrollBar check in - // TerminalDisplay::setScrollBarPosition(ScrollBarPosition position) _scrollBar->hide(); - // setup timers for blinking cursor and text _blinkTimer = new QTimer(this); connect(_blinkTimer, &QTimer::timeout, this, &TerminalDisplay::blinkEvent); _blinkCursorTimer = new QTimer(this); @@ -353,19 +296,15 @@ TerminalDisplay::TerminalDisplay(QWidget *parent) setColorTable(base_color_table); setMouseTracking(true); - // Enable drag and drop - setAcceptDrops(true); // attempt + setAcceptDrops(true); dragInfo.state = diNone; setFocusPolicy(Qt::WheelFocus); - // enable input method support setAttribute(Qt::WA_InputMethodEnabled, true); setInputMethodHints(Qt::ImhSensitiveData | Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhMultiLine); - // this is an important optimization, it tells Qt - // that TerminalDisplay will handle repainting its entire area. setAttribute(Qt::WA_OpaquePaintEvent); _gridLayout = new QGridLayout(this); @@ -379,29 +318,10 @@ TerminalDisplay::TerminalDisplay(QWidget *parent) _lockbackgroundImage = QPixmap(10, 10); _lockbackgroundImage.fill(Qt::gray); - // _backgroundVideoPlayer = new QMediaPlayer; - // _backgroundVideoSink = new QVideoSink; - // _backgroundVideoPlayer->setLoops(QMediaPlayer::Infinite); - // _backgroundVideoPlayer->setVideoOutput(_backgroundVideoSink); - // connect(_backgroundVideoSink, &QVideoSink::videoFrameChanged, this, [&](const QVideoFrame &frame) { - // _backgroundVideoFrame = QPixmap::fromImage(frame.toImage()); - // update(); - // }); - new AutoScrollHandler(this); } TerminalDisplay::~TerminalDisplay() { - // if (_backgroundVideoPlayer->playbackState() == QMediaPlayer::PlayingState) { - // _backgroundVideoPlayer->stop(); - // } - // delete _backgroundVideoPlayer; - // delete _backgroundVideoSink; - // if (_backgroundMovie != nullptr) { - // _backgroundMovie->stop(); - // QObject::disconnect(_backgroundMovie, nullptr, this, nullptr); - // delete _backgroundMovie; - // } disconnect(_blinkTimer); disconnect(_blinkCursorTimer); if (_hideMouseTimer) @@ -495,7 +415,6 @@ static const quint32 LineChars[] = { }; static void drawLineChar(QPainter &paint, int x, int y, int w, int h, uint8_t code) { - // Calculate cell midpoints, end points. int cx = x + w / 2; int cy = y + h / 2; int ex = x + w - 1; @@ -503,7 +422,6 @@ static void drawLineChar(QPainter &paint, int x, int y, int w, int h, uint8_t co quint32 toDraw = LineChars[code]; - // Top _lines: if (toDraw & TopL) paint.drawLine(cx - 1, y, cx - 1, cy - 2); if (toDraw & TopC) @@ -511,7 +429,6 @@ static void drawLineChar(QPainter &paint, int x, int y, int w, int h, uint8_t co if (toDraw & TopR) paint.drawLine(cx + 1, y, cx + 1, cy - 2); - // Bot _lines: if (toDraw & BotL) paint.drawLine(cx - 1, cy + 2, cx - 1, ey); if (toDraw & BotC) @@ -519,7 +436,6 @@ static void drawLineChar(QPainter &paint, int x, int y, int w, int h, uint8_t co if (toDraw & BotR) paint.drawLine(cx + 1, cy + 2, cx + 1, ey); - // Left _lines: if (toDraw & LeftT) paint.drawLine(x, cy - 1, cx - 2, cy - 1); if (toDraw & LeftC) @@ -527,7 +443,6 @@ static void drawLineChar(QPainter &paint, int x, int y, int w, int h, uint8_t co if (toDraw & LeftB) paint.drawLine(x, cy + 1, cx - 2, cy + 1); - // Right _lines: if (toDraw & RightT) paint.drawLine(cx + 2, cy - 1, ex, cy - 1); if (toDraw & RightC) @@ -535,7 +450,6 @@ static void drawLineChar(QPainter &paint, int x, int y, int w, int h, uint8_t co if (toDraw & RightB) paint.drawLine(cx + 2, cy + 1, ex, cy + 1); - // Intersection points. if (toDraw & Int11) paint.drawPoint(cx - 1, cy - 1); if (toDraw & Int12) @@ -559,61 +473,58 @@ static void drawLineChar(QPainter &paint, int x, int y, int w, int h, uint8_t co } static void drawOtherChar(QPainter &paint, int x, int y, int w, int h, uchar code) { - // Calculate cell midpoints, end points. const int cx = x + w / 2; const int cy = y + h / 2; const int ex = x + w - 1; const int ey = y + h - 1; - // Double dashes if (0x4C <= code && code <= 0x4F) { const int xHalfGap = qMax(w / 15, 1); const int yHalfGap = qMax(h / 15, 1); switch (code) { - case 0x4D: // BOX DRAWINGS HEAVY DOUBLE DASH HORIZONTAL + case 0x4D: paint.drawLine(x, cy - 1, cx - xHalfGap - 1, cy - 1); paint.drawLine(x, cy + 1, cx - xHalfGap - 1, cy + 1); paint.drawLine(cx + xHalfGap, cy - 1, ex, cy - 1); paint.drawLine(cx + xHalfGap, cy + 1, ex, cy + 1); /* Falls through. */ - case 0x4C: // BOX DRAWINGS LIGHT DOUBLE DASH HORIZONTAL + case 0x4C: paint.drawLine(x, cy, cx - xHalfGap - 1, cy); paint.drawLine(cx + xHalfGap, cy, ex, cy); break; - case 0x4F: // BOX DRAWINGS HEAVY DOUBLE DASH VERTICAL + case 0x4F: paint.drawLine(cx - 1, y, cx - 1, cy - yHalfGap - 1); paint.drawLine(cx + 1, y, cx + 1, cy - yHalfGap - 1); paint.drawLine(cx - 1, cy + yHalfGap, cx - 1, ey); paint.drawLine(cx + 1, cy + yHalfGap, cx + 1, ey); /* Falls through. */ - case 0x4E: // BOX DRAWINGS LIGHT DOUBLE DASH VERTICAL + case 0x4E: paint.drawLine(cx, y, cx, cy - yHalfGap - 1); paint.drawLine(cx, cy + yHalfGap, cx, ey); break; } } - // Rounded corner characters else if (0x6D <= code && code <= 0x70) { const int r = w * 3 / 8; const int d = 2 * r; switch (code) { - case 0x6D: // BOX DRAWINGS LIGHT ARC DOWN AND RIGHT + case 0x6D: paint.drawLine(cx, cy + r, cx, ey); paint.drawLine(cx + r, cy, ex, cy); paint.drawArc(cx, cy, d, d, 90 * 16, 90 * 16); break; - case 0x6E: // BOX DRAWINGS LIGHT ARC DOWN AND LEFT + case 0x6E: paint.drawLine(cx, cy + r, cx, ey); paint.drawLine(x, cy, cx - r, cy); paint.drawArc(cx - d, cy, d, d, 0 * 16, 90 * 16); break; - case 0x6F: // BOX DRAWINGS LIGHT ARC UP AND LEFT + case 0x6F: paint.drawLine(cx, y, cx, cy - r); paint.drawLine(x, cy, cx - r, cy); paint.drawArc(cx - d, cy - d, d, d, 270 * 16, 90 * 16); break; - case 0x70: // BOX DRAWINGS LIGHT ARC UP AND RIGHT + case 0x70: paint.drawLine(cx, y, cx, cy - r); paint.drawLine(cx + r, cy, ex, cy); paint.drawArc(cx, cy - d, d, d, 180 * 16, 90 * 16); @@ -621,16 +532,15 @@ static void drawOtherChar(QPainter &paint, int x, int y, int w, int h, uchar cod } } - // Diagonals else if (0x71 <= code && code <= 0x73) { switch (code) { - case 0x71: // BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT + case 0x71: paint.drawLine(ex, y, x, ey); break; - case 0x72: // BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT + case 0x72: paint.drawLine(x, y, ex, ey); break; - case 0x73: // BOX DRAWINGS LIGHT DIAGONAL CROSS + case 0x73: paint.drawLine(ex, y, x, ey); paint.drawLine(x, y, ex, ey); break; @@ -704,10 +614,7 @@ QTermWidget::KeyboardCursorShape TerminalDisplay::keyboardCursorShape() const { void TerminalDisplay::setKeyboardCursorColor(bool useForegroundColor, const QColor &color) { if (useForegroundColor) - _cursorColor = QColor(); // an invalid color means that - // the foreground color of the - // current character should - // be used + _cursorColor = QColor(); else _cursorColor = color; @@ -724,8 +631,6 @@ void TerminalDisplay::setBackgroundPixmap(QPixmap *backgroundImage) { if (backgroundImage != nullptr) { setAttribute(Qt::WA_OpaquePaintEvent, false); } else { - // if (_backgroundMovie == nullptr && (!_backgroundVideoPlayer->playbackState() == QMediaPlayer::PlayingState) && _backgroundImage.isNull()) - // setAttribute(Qt::WA_OpaquePaintEvent, true); } } @@ -737,8 +642,6 @@ void TerminalDisplay::setBackgroundImage(const QString &backgroundImage) { setAttribute(Qt::WA_OpaquePaintEvent, false); } else { _backgroundImage = QPixmap(); - // if (_backgroundMovie == nullptr && (!_backgroundVideoPlayer->playbackState() == QMediaPlayer::PlayingState) && !_backgroundPixmapRef) - // setAttribute(Qt::WA_OpaquePaintEvent, true); } } @@ -748,24 +651,8 @@ void TerminalDisplay::setBackgroundMovie(const QString &backgroundImage) { movie = new QMovie(backgroundImage); } if (movie && movie->isValid()) { - // if (_backgroundMovie != nullptr) { - // _backgroundMovie->stop(); - // QObject::disconnect(_backgroundMovie, nullptr, this, nullptr); - // delete _backgroundMovie; - // } - // _backgroundMovie = movie; - // QObject::connect(_backgroundMovie, &QMovie::frameChanged, this, [&] { update(); }); setAttribute(Qt::WA_OpaquePaintEvent, false); - // _backgroundMovie->start(); } else { - // if (_backgroundMovie != nullptr) { - // _backgroundMovie->stop(); - // QObject::disconnect(_backgroundMovie, nullptr, this, nullptr); - // delete _backgroundMovie; - // } - // _backgroundMovie = nullptr; - // if (_backgroundImage.isNull() && (!_backgroundVideoPlayer->playbackState() == QMediaPlayer::PlayingState) && !_backgroundPixmapRef) - // setAttribute(Qt::WA_OpaquePaintEvent, true); if (movie) delete movie; } @@ -773,15 +660,9 @@ void TerminalDisplay::setBackgroundMovie(const QString &backgroundImage) { void TerminalDisplay::setBackgroundVideo(const QString &backgroundVideo) { if (!backgroundVideo.isEmpty()) { - // _backgroundVideoPlayer->setSource(QUrl::fromLocalFile(backgroundVideo)); - // _backgroundVideoPlayer->play(); setAttribute(Qt::WA_OpaquePaintEvent, false); } else { - // _backgroundVideoPlayer->stop(); - // _backgroundVideoPlayer->setSource(QUrl()); _backgroundVideoFrame = QPixmap(); - // if (_backgroundMovie == nullptr && _backgroundImage.isNull() && !_backgroundPixmapRef) - // setAttribute(Qt::WA_OpaquePaintEvent, true); } } @@ -793,12 +674,6 @@ void TerminalDisplay::drawBackground(QPainter &painter, const QRect &rect, const QColor &backgroundColor, bool useOpacitySetting) { QPixmap currentBackgroundImage = _backgroundImage; - // if (_backgroundMovie != nullptr) { - // currentBackgroundImage = _backgroundMovie->currentPixmap(); - // } - // if (_backgroundVideoPlayer->playbackState() == QMediaPlayer::PlayingState) { - // currentBackgroundImage = _backgroundVideoFrame; - // } if (useOpacitySetting) { QColor color(backgroundColor); if (currentBackgroundImage.isNull()) { @@ -828,13 +703,9 @@ void TerminalDisplay::drawCursor(QPainter &painter, const QRect &rect, painter.setPen(foregroundColor); if (_cursorShape == Emulation::KeyboardCursorShape::BlockCursor) { - // draw the cursor outline, adjusting the area so that - // it is draw entirely inside 'rect' float penWidth = qMax(1, painter.pen().width()); if (preedit) { - // with is single character, so the cursor width should be the same as - // the character width cursorRect.setWidth(_fontWidth); } @@ -844,8 +715,6 @@ void TerminalDisplay::drawCursor(QPainter &painter, const QRect &rect, painter.fillRect(cursorRect, _cursorColor.isValid() ? _cursorColor : foregroundColor); if (!_cursorColor.isValid()) { - // invert the colour used to draw the text to ensure that the - // character at the cursor position is readable invertCharacterColor = true; } } @@ -863,15 +732,12 @@ void TerminalDisplay::drawCharacters(QPainter &painter, const QRect &rect, const Character *style, bool invertCharacterColor, bool tooWide) { - // don't draw text which is currently blinking if (_blinking && (style->rendition & RE_BLINK)) return; - // don't draw concealed characters if (style->rendition & RE_CONCEAL) return; - // setup bold and underline bool useBold = ((style->rendition & RE_BOLD) && _boldIntense) || font().bold(); const bool useUnderline = @@ -895,7 +761,6 @@ void TerminalDisplay::drawCharacters(QPainter &painter, const QRect &rect, painter.setFont(font); } - // setup pen const CharacterColor &textColor = (invertCharacterColor ? style->backgroundColor : style->foregroundColor); const QColor color = textColor.color(_colorTable); @@ -905,10 +770,6 @@ void TerminalDisplay::drawCharacters(QPainter &painter, const QRect &rect, painter.setPen(color); } - // FIXME: Here is a hack to solve the East Asian language symbol - // "“‘" rendering issue. - // But it is not a good solution. We should find a better way to solve - // this issue. int font_width = _charWidth->string_font_width(text); int width = CharWidth::string_unicode_width(text); if (_fix_quardCRT_issue33 && font_width != width) { @@ -921,13 +782,6 @@ void TerminalDisplay::drawCharacters(QPainter &painter, const QRect &rect, } else { if (_charWidth->font_width(line_char) != CharWidth::unicode_width(line_char)) { - // https://github.com/QQxiaoming/quardCRT/issues/33#issuecomment-2044020900 - // | left | center | right | - // | ------------ | ------------ | ------------ | - // | L'’' U+2019 | L'×' U+00D7 | L'‘' U+2018 | - // | L'”' U+201D | L'÷' U+00F7 | L'“' U+201C | - // | | L'‖' U+2016 | L'‚' U+201A | - // | | | L'‛' U+201B | const QList right_chars = {0x201C, 0x2018, 0x201A, 0x201B}; const QList center_chars = {0x00D7, 0x00F7, 0x2016}; const QList left_chars = {0x201D, 0x2019, 0x2580, 0x2584, 0x2588}; @@ -974,14 +828,9 @@ void TerminalDisplay::drawCharacters(QPainter &painter, const QRect &rect, } } } else { - // draw text if (isLineCharString(text)) { drawLineCharString(painter, rect.x(), rect.y(), text, style); } else { - // Force using LTR as the document layout for the terminal area, because - // there is no use cases for RTL emulator and RTL terminal application. - // - // This still allows RTL characters to be rendered in the RTL way. painter.setLayoutDirection(Qt::LeftToRight); if (_bidiEnabled) { @@ -1009,8 +858,6 @@ void TerminalDisplay::drawTextFragment(QPainter &painter, const QRect &rect, bool isSelection) { painter.save(); - // when the selected text is not opaque, the text is drawn with inverted - // colors but else the text is drawn with the normal colors if (_selectedTextOpacity < 1.0) { if (isSelection) { CharacterColor f = style->foregroundColor; @@ -1020,24 +867,19 @@ void TerminalDisplay::drawTextFragment(QPainter &painter, const QRect &rect, } } - // setup painter const QColor foregroundColor = style->foregroundColor.color(_colorTable); const QColor backgroundColor = style->backgroundColor.color(_colorTable); - // draw background if different from the display's background color if (backgroundColor != _colorTable[DEFAULT_BACK_COLOR].color) { drawBackground(painter, rect, backgroundColor, false /* do not use transparency */); } - // draw cursor shape if the current character is the cursor - // this may alter the foreground and background colors bool invertCharacterColor = false; if (style->rendition & RE_CURSOR) drawCursor(painter, rect, foregroundColor, backgroundColor, invertCharacterColor); - // draw text drawCharacters(painter, rect, text, style, invertCharacterColor, tooWide); painter.restore(); @@ -1071,55 +913,26 @@ void TerminalDisplay::setCursorPos(const int curx, const int cury) { int xpos, ypos; ypos = _topMargin + tLy + _fontHeight*(cury-1) + _fontAscent; xpos = _leftMargin + tLx + _fontWidth*curx; - //setMicroFocusHint(xpos, ypos, 0, _fontHeight); //### ??? - // fprintf(stderr, "x/y = %d/%d\txpos/ypos = %d/%d\n", curx, cury, xpos, ypos); _cursorLine = cury; _cursorCol = curx; } #endif -// scrolls the image by 'lines', down if lines > 0 or up otherwise. -// -// the terminal emulation keeps track of the scrolling of the character -// image as it receives input, and when the view is updated, it calls -// scrollImage() with the final scroll amount. this improves performance -// because scrolling the display is much cheaper than re-rendering all the text -// for the part of the image which has moved up or down. Instead only new lines -// have to be drawn void TerminalDisplay::scrollImage(int lines, const QRect &screenWindowRegion) { - // if the flow control warning is enabled this will interfere with the - // scrolling optimizations and cause artifacts. the simple solution here - // is to just disable the optimization whilst it is visible if (_outputSuspendedLabel && _outputSuspendedLabel->isVisible()) return; - // constrain the region to the display - // the bottom of the region is capped to the number of lines in the display's - // internal image - 2, so that the height of 'region' is strictly less - // than the height of the internal image. QRect region = screenWindowRegion; region.setBottom(qMin(region.bottom(), this->_lines - 2)); - // return if there is nothing to do if (lines == 0 || _image == nullptr || !region.isValid() || (region.top() + abs(lines)) >= region.bottom() || this->_lines <= region.height()) return; - // hide terminal size label to prevent it being scrolled if (_resizeWidget && _resizeWidget->isVisible()) _resizeWidget->hide(); - // Note: With Qt 4.4 the left edge of the scrolled area must be at 0 - // to get the correct (newly exposed) part of the widget repainted. - // - // The right edge must be before the left edge of the scroll bar to - // avoid triggering a repaint of the entire widget, the distance is - // given by SCROLLBAR_CONTENT_GAP - // - // Set the QT_FLUSH_PAINT environment variable to '1' before starting the - // application to monitor repainting. - // int scrollBarWidth = _scrollBar->isHidden() ? 0 : _scrollBar->style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, _scrollBar) @@ -1144,35 +957,27 @@ void TerminalDisplay::scrollImage(int lines, const QRect &screenWindowRegion) { Q_ASSERT(linesToMove > 0); Q_ASSERT(bytesToMove > 0); - // scroll internal image if (lines > 0) { - // check that the memory areas that we are going to move are valid Q_ASSERT((char *)lastCharPos + bytesToMove < (char *)(_image + (this->_lines * this->_columns))); Q_ASSERT((lines * this->_columns) < _imageSize); - // scroll internal image down memmove(firstCharPos, lastCharPos, bytesToMove); - // set region of display to scroll scrollRect.setTop(top); } else { - // check that the memory areas that we are going to move are valid Q_ASSERT((char *)firstCharPos + bytesToMove < (char *)(_image + (this->_lines * this->_columns))); - // scroll internal image up memmove(lastCharPos, firstCharPos, bytesToMove); - // set region of the display to scroll scrollRect.setTop(top + abs(lines) * _fontHeight); } scrollRect.setHeight(linesToMove * _fontHeight); Q_ASSERT(scrollRect.isValid() && !scrollRect.isEmpty()); - // scroll the display vertically to match internal _image scroll(0, _fontHeight * (-lines), scrollRect); } @@ -1221,11 +1026,6 @@ void TerminalDisplay::processFilters() { QRegion preUpdateHotSpots = hotSpotRegion(); - // use _screenWindow->getImage() here rather than _image because - // other classes may call processFilters() when this display's - // ScreenWindow emits a scrolled() signal - which will happen before - // updateImage() is called on the display and therefore _image is - // out of date at this point _filterChain->setImage( _screenWindow->getImage(), _screenWindow->windowLines(), _screenWindow->windowColumns(), _screenWindow->getLineProperties()); @@ -1240,16 +1040,10 @@ void TerminalDisplay::updateImage() { if (!_screenWindow) return; - // optimization - scroll the existing image where possible and - // avoid expensive text drawing for parts of the image that - // can simply be moved up or down scrollImage(_screenWindow->scrollCount(), _screenWindow->scrollRegion()); _screenWindow->resetScrollCount(); if (!_image) { - // Create _image. - // The emitted changedContentSizeSignal also leads to getImage being - // recreated, so do this first. updateImageSize(); } @@ -1269,9 +1063,9 @@ void TerminalDisplay::updateImage() { int tLy = tL.y(); _hasBlinker = false; - CharacterColor cf; // undefined - CharacterColor _clipboard; // undefined - int cr = -1; // undefined + CharacterColor cf; + CharacterColor _clipboard; + int cr = -1; const int linesToUpdate = qMin(this->_lines, qMax(0, lines)); const int columnsToUpdate = qMin(this->_columns, qMax(0, columns)); @@ -1280,18 +1074,12 @@ void TerminalDisplay::updateImage() { char *dirtyMask = new char[columnsToUpdate + 2]; QRegion dirtyRegion; - // debugging variable, this records the number of lines that are found to - // be 'dirty' ( ie. have changed from the old _image to the new _image ) and - // which therefore need to be repainted for (y = 0; y < linesToUpdate; ++y) { const Character *currentLine = &_image[y * this->_columns]; const Character *const newLine = &newimg[y * columns]; bool updateLine = false; - // The dirty mask indicates which characters need repainting. We also - // mark surrounding neighbours dirty, in case the character exceeds - // its cell boundaries memset(dirtyMask, 0, columnsToUpdate + 2); for (x = 0; x < columnsToUpdate; ++x) { @@ -1301,21 +1089,18 @@ void TerminalDisplay::updateImage() { } QFontMetrics fm(font()); - if (!_resizing) // not while _resizing, we're expecting a paintEvent + if (!_resizing) for (x = 0; x < columnsToUpdate; ++x) { if ((newLine[x].rendition & RE_BLINK) != 0) { _hasBlinker = true; } - // Start drawing if this character or the next one differs. - // We also take the next one into account to handle the situation - // where characters exceed their cell width. if (dirtyMask[x]) { wchar_t c = newLine[x + 0].character; if (!c) continue; int p = 0; - disstrU[p++] = c; // fontMap(c); + disstrU[p++] = c; bool lineDraw = isLineChar(newLine[x+0]); bool doubleWidth = (x + 1 == columnsToUpdate) ? false @@ -1332,7 +1117,7 @@ void TerminalDisplay::updateImage() { const Character &ch = newLine[x + len]; if (!ch.character) - continue; // Skip trailing part of multi-col chars. + continue; bool nextIsDoubleWidth = (x + len + 1 == columnsToUpdate) @@ -1354,7 +1139,7 @@ void TerminalDisplay::updateImage() { break; } - disstrU[p++] = c; // fontMap(c); + disstrU[p++] = c; } std::wstring unistr(disstrU, p); @@ -1372,20 +1157,13 @@ void TerminalDisplay::updateImage() { } } - // both the top and bottom halves of double height _lines must always be - // redrawn although both top and bottom halves contain the same characters, - // only the top one is actually drawn. if (_lineProperties.count() > y) { if ((_lineProperties[y] & LINE_DOUBLEHEIGHT) != 0) { updateLine = true; } } - // if the characters on the line are different in the old and the new _image - // then this line must be repainted. if (updateLine) { - // add the area occupied by this line to the region which needs to be - // repainted QRect dirtyRect = QRect(_leftMargin + tLx, _topMargin + tLy + _fontHeight * y, _fontWidth * columnsToUpdate, _fontHeight); @@ -1393,14 +1171,10 @@ void TerminalDisplay::updateImage() { dirtyRegion |= dirtyRect; } - // replace the line of characters in the old _image with the - // current line of the new _image memcpy((void *)currentLine, (const void *)newLine, columnsToUpdate * sizeof(Character)); } - // if the new _image is smaller than the previous _image, then ensure that the - // area outside the new _image is cleared if (linesToUpdate < _usedLines) { dirtyRegion |= QRect(_leftMargin + tLx, _topMargin + tLy + _fontHeight * linesToUpdate, @@ -1419,7 +1193,6 @@ void TerminalDisplay::updateImage() { dirtyRegion |= _inputMethodData.previousPreeditRect; - // update the parts of the display which have changed update(dirtyRegion); if (_hasBlinker && !_blinkTimer->isActive()) @@ -1466,8 +1239,6 @@ void TerminalDisplay::setBlinkingCursor(bool blink) { _hasBlinkingCursor = blink; if (blink && !_blinkCursorTimer->isActive() && hasFocus()) { - // QApplication::cursorFlashTime() may be negative, and a too fast - // blinking is not good. Also, see TerminalDisplay::keyPressEvent. _blinkCursorTimer->start(std::max(QApplication::cursorFlashTime(), 1000) / 2); } @@ -1493,9 +1264,6 @@ void TerminalDisplay::setBlinkingTextEnabled(bool blink) { } void TerminalDisplay::focusOutEvent(QFocusEvent *) { - // trigger a repaint of the cursor so that it is both visible (in case - // it was hidden during blinking) - // and drawn in a focused out state _cursorBlinking = false; updateCursor(); _blinkCursorTimer->stop(); @@ -1505,13 +1273,10 @@ void TerminalDisplay::focusOutEvent(QFocusEvent *) { _blinkTimer->stop(); - // This signal should be emitted only in the end - // because the focus may change in response to it. emit termLostFocus(); } void TerminalDisplay::focusInEvent(QFocusEvent *) { if (_hasBlinkingCursor) { - // see TerminalDisplay::setBlinkingCursor _blinkCursorTimer->start(std::max(QApplication::cursorFlashTime(), 1000) / 2); } updateCursor(); @@ -1519,15 +1284,12 @@ void TerminalDisplay::focusInEvent(QFocusEvent *) { if (_hasBlinker) _blinkTimer->start(TEXT_BLINK_DELAY); - // This signal should be emitted only in the end - // because the focus may change in response to it. emit termGetFocus(); } void TerminalDisplay::enterEvent(QEnterEvent* event) { if (gs_deadSpot.x() < 0 && _hideMouseTimer - // NOTE: scrollBar->underMouse() doesn't work here && !_scrollBar->rect().contains(_scrollBar->mapFromParent(event->position().toPoint()))) { gs_futureDeadSpot = event->position().toPoint(); @@ -1551,12 +1313,6 @@ void TerminalDisplay::paintEvent(QPaintEvent *pe) { QRect cr = contentsRect(); QPixmap currentBackgroundImage = _backgroundImage; - // if (_backgroundMovie != nullptr) { - // currentBackgroundImage = _backgroundMovie->currentPixmap(); - // } - // if (_backgroundVideoPlayer->playbackState() == QMediaPlayer::PlayingState) { - // currentBackgroundImage = _backgroundVideoFrame; - // } if (!currentBackgroundImage.isNull()) { QColor background = _colorTable[DEFAULT_BACK_COLOR].color; @@ -1573,10 +1329,9 @@ void TerminalDisplay::paintEvent(QPaintEvent *pe) { paint.save(); paint.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); - if (_backgroundMode == Stretch) { // scale the image without keeping its - // proportions to fill the screen + if (_backgroundMode == Stretch) { paint.drawPixmap(cr, currentBackgroundImage, currentBackgroundImage.rect()); - } else if (_backgroundMode == Zoom) { // zoom in/out the image to fit it + } else if (_backgroundMode == Zoom) { QRect r = currentBackgroundImage.rect(); qreal wRatio = static_cast(cr.width()) / r.width(); qreal hRatio = static_cast(cr.height()) / r.height(); @@ -1590,8 +1345,7 @@ void TerminalDisplay::paintEvent(QPaintEvent *pe) { r.moveCenter(cr.center()); paint.drawPixmap(r, currentBackgroundImage, currentBackgroundImage.rect()); - } else if (_backgroundMode == Fit) { // if the image is bigger than the - // terminal, zoom it out to fit it + } else if (_backgroundMode == Fit) { QRect r = currentBackgroundImage.rect(); qreal wRatio = static_cast(cr.width()) / r.width(); qreal hRatio = static_cast(cr.height()) / r.height(); @@ -1610,11 +1364,11 @@ void TerminalDisplay::paintEvent(QPaintEvent *pe) { r.moveCenter(cr.center()); paint.drawPixmap(r, currentBackgroundImage, currentBackgroundImage.rect()); } else if (_backgroundMode == - Center) { // center the image without scaling/zooming + Center) { QRect r = currentBackgroundImage.rect(); r.moveCenter(cr.center()); paint.drawPixmap(r.topLeft(), currentBackgroundImage); - } else if (_backgroundMode == Tile) { // tile the image + } else if (_backgroundMode == Tile) { QPixmap scaled = currentBackgroundImage; qreal wRatio = static_cast(cr.width()) / currentBackgroundImage.width(); @@ -1641,7 +1395,7 @@ void TerminalDisplay::paintEvent(QPaintEvent *pe) { x = 0; y += scaled.height(); } - } else // if (_backgroundMode == None) + } else { paint.drawPixmap(0, 0, currentBackgroundImage); } @@ -1717,8 +1471,6 @@ void TerminalDisplay::drawInputMethodPreeditString(QPainter &painter, const QRec FilterChain *TerminalDisplay::filterChain() const { return _filterChain; } void TerminalDisplay::paintFilters(QPainter &painter) { - // get color of character under mouse and use it to draw - // lines for filters QPoint cursorPos = mapFromGlobal(QCursor::pos()); int cursorLine; int cursorColumn; @@ -1734,9 +1486,6 @@ void TerminalDisplay::paintFilters(QPainter &painter) { painter.setPen(QPen(cursorCharacter.foregroundColor.color(colorTable()))); - // iterate over hotspots identified by the display's currently active filters - // and draw appropriate visuals to indicate the presence of the hotspot - QList spots = _filterChain->hotSpots(); QListIterator iter(spots); while (iter.hasNext()) { @@ -1774,11 +1523,7 @@ void TerminalDisplay::paintFilters(QPainter &painter) { for (int line = spot->startLine(); line <= spot->endLine(); line++) { int startColumn = 0; - int endColumn = _columns - 1; // TODO use number of _columns which are - // actually occupied on this line rather - // than the width of the display in _columns - - // ignore whitespace at the end of the lines + int endColumn = _columns - 1; do { if (endColumn <= 0) break; @@ -1790,8 +1535,6 @@ void TerminalDisplay::paintFilters(QPainter &painter) { endColumn--; } while (true); - // increment here because the column which we want to set 'endColumn' to - // is the first whitespace character at the end of the line endColumn++; if (line == spot->startLine()) @@ -1799,35 +1542,20 @@ void TerminalDisplay::paintFilters(QPainter &painter) { if (line == spot->endLine()) endColumn = spot->endColumn(); - // subtract one pixel from - // the right and bottom so that - // we do not overdraw adjacent - // hotspots - // - // subtracting one pixel from all sides also prevents an edge case where - // moving the mouse outside a link could still leave it underlined - // because the check below for the position of the cursor - // finds it on the border of the target area QRect r; r.setCoords(startColumn * _fontWidth + 1 + leftMargin, line * _fontHeight + 1 + _topBaseMargin, endColumn * _fontWidth - 1 + leftMargin, (line + 1) * _fontHeight - 1 + _topBaseMargin); - // Underline link hotspots if (spot->type() == Filter::HotSpot::Link) { QFontMetrics metrics(font()); - // find the baseline (which is the invisible line that the characters in - // the font sit on, with some having tails dangling below) int baseline = r.bottom() - metrics.descent(); - // find the position of the underline below that int underlinePos = baseline + metrics.underlinePos(); if (region.contains(mapFromGlobal(QCursor::pos()))) { painter.drawLine(r.left(), underlinePos, r.right(), underlinePos); } } - // Marker hotspots simply have a transparent rectanglular shape - // drawn on top of them else if (spot->type() == Filter::HotSpot::Marker) { QColor markerColor = spot->color(); markerColor.setAlpha(120); @@ -1837,17 +1565,12 @@ void TerminalDisplay::paintFilters(QPainter &painter) { } } -// NOTE: This should be called only when "_fixedFont" is set to "false" (temporarily). int TerminalDisplay::textWidth(const int startColumn, const int length, const int line) const { QFontMetrics fm(font()); int result = 0; for (int column = 0; column < length; column++) { auto c = _image[loc(startColumn + column, line)]; - // Take care of double-column characters and those with small widths. - // Exclude line characters, as some of them are ambiguous ('A') [1] - // [1] http://www.unicode.org/Public/UCD/latest/ucd/EastAsianWidth.txt if (_fixedFont_original && !isLineChar(c)) { - // c == 0 may happen here after a double-column character result += fm.horizontalAdvance(QLatin1Char(REPCHAR[0])); } else { result += fm.horizontalAdvance(QChar(static_cast(c.character))); @@ -1886,18 +1609,15 @@ void TerminalDisplay::drawContents(QPainter &paint, const QRect &rect) { quint32 c = _image[loc(lux, y)].character; int x = lux; if (!c && x) - x--; // Search for start of multi-column character + x--; for (; x <= rlx; x++) { int len = 1; int p = 0; - // reset our buffer to the number of columns int bufferSize = numberOfColumns; unistr.resize(bufferSize); - // is this a single character or a sequence of characters ? if (_image[loc(x, y)].rendition & RE_EXTENDED_CHAR) { - // sequence of characters ushort extendedCharLength = 0; uint* chars = ExtendedCharTable::instance .lookupExtendedChar(_image[loc(x,y)].character,extendedCharLength); @@ -1911,11 +1631,10 @@ void TerminalDisplay::drawContents(QPainter &paint, const QRect &rect) { } } } else { - // single character c = _image[loc(x, y)].character; if (c) { Q_ASSERT(p < bufferSize); - unistr[p++] = c; // fontMap(c); + unistr[p++] = c; } } @@ -1942,11 +1661,10 @@ void TerminalDisplay::drawContents(QPainter &paint, const QRect &rect) { !(_fixedFont && (nxtC = _image[loc(x+len,y)].character) && (nxtCharWidth = fm.horizontalAdvance(QString::fromWCharArray((const wchar_t *)(&nxtC), 1))) < _fontWidth) && !bigWidth && !(_fixedFont && !nxtDoubleWidth && nxtC && nxtCharWidth > _fontWidth) && - isLineChar(_image[loc(x+len,y)]) == lineDraw) // Assignment! + isLineChar(_image[loc(x+len,y)]) == lineDraw) { c = _image[loc(x+len,y)].character; if (_image[loc(x+len,y)].rendition & RE_EXTENDED_CHAR) { - // sequence of characters ushort extendedCharLength = 0; const uint* chars = ExtendedCharTable::instance.lookupExtendedChar(c, extendedCharLength); if (chars) { @@ -1959,26 +1677,23 @@ void TerminalDisplay::drawContents(QPainter &paint, const QRect &rect) { } } } else { - // single character if (c) { Q_ASSERT( p < bufferSize ); - unistr[p++] = c; //fontMap(c); + unistr[p++] = c; } } - if (doubleWidth) // assert((_image[loc(x+len,y)+1].character == 0)), see - // above if condition - len++; // Skip trailing part of multi-column character + if (doubleWidth) + len++; len++; } if ((x + len < _usedColumns) && (!_image[loc(x + len, y)].character)) - len++; // Adjust for trailing part of multi-column character + len++; bool save__fixedFont = _fixedFont; if (lineDraw) _fixedFont = false; unistr.resize(p); - // Create a text scaling matrix for double width and double height lines. QTransform textScale; if (y < _lineProperties.size()) { @@ -1989,33 +1704,19 @@ void TerminalDisplay::drawContents(QPainter &paint, const QRect &rect) { textScale.scale(1, 2); } - // Apply text scaling matrix. paint.setWorldTransform(textScale, true); - // calculate the area in which the text will be drawn QRect textArea = calculateTextArea(tLx, tLy, x, y, len); - // move the calculated area to take account of scaling applied to the - // painter. the position of the area from the origin (0,0) is scaled by - // the opposite of whatever transformation has been applied to the - // painter. this ensures that painting does actually start from - // textArea.topLeft() (instead of textArea.topLeft() * painter-scale) textArea.moveTopLeft(textScale.inverted().map(textArea.topLeft())); - // paint text fragment drawTextFragment(paint, textArea, unistr, &_image[loc(x, y)], tooWide, _screenWindow->isSelected(x, y)); _fixedFont = save__fixedFont; - // reset back to single-width, single-height _lines paint.setWorldTransform(textScale.inverted(), true); if (y < _lineProperties.size() - 1) { - // double-height _lines are represented by two adjacent _lines - // containing the same characters - // both _lines will have the LINE_DOUBLEHEIGHT attribute. - // If the current line has the LINE_DOUBLEHEIGHT attribute, - // we can therefore skip the next line if (_lineProperties[y] & LINE_DOUBLEHEIGHT) y++; } @@ -2031,9 +1732,6 @@ void TerminalDisplay::blinkEvent() { _blinking = !_blinking; - // TODO: Optimize to only repaint the areas of the widget - // where there is blinking text - // rather than repainting the whole widget. update(); } @@ -2082,7 +1780,6 @@ void TerminalDisplay::updateImageSize() { makeImage(); - // copy the old image to reduce flicker int lines = qMin(oldlin, _lines); int columns = qMin(oldcol, _columns); @@ -2102,19 +1799,13 @@ void TerminalDisplay::updateImageSize() { if (_resizing) { if (_showResizeNotificationEnabled) showResizeNotification(); - emit changedContentSizeSignal(_contentHeight, _contentWidth); // expose resizeEvent + emit changedContentSizeSignal(_contentHeight, _contentWidth); emit changedContentCountSignal(_lines, _columns); } _resizing = false; } -// showEvent and hideEvent are reimplemented here so that it appears to other -// classes that the display has been resized when the display is hidden or -// shown. -// -// TODO: Perhaps it would be better to have separate signals for show and hide -// instead of using the same signal as the one for a content size change void TerminalDisplay::showEvent(QShowEvent *) { emit changedContentSizeSignal(_contentHeight, _contentWidth); } @@ -2129,10 +1820,6 @@ void TerminalDisplay::scrollBarPositionChanged(int) { _screenWindow->scrollTo(_scrollBar->value()); - // if the thumb has been moved to the bottom of the _scrollBar then set - // the display to automatically track new output, - // that is, scroll down automatically - // to how new _lines as they are added const bool atEndOfOutput = (_scrollBar->value() == _scrollBar->maximum()); _screenWindow->setTrackOutput(atEndOfOutput); @@ -2140,11 +1827,6 @@ void TerminalDisplay::scrollBarPositionChanged(int) { } void TerminalDisplay::setScroll(int cursor, int slines) { - // update _scrollBar if the range or value has changed, - // otherwise return - // - // setting the range or value of a _scrollBar will always trigger - // a repaint, so it should be avoided if it is not necessary if (_scrollBar->minimum() == 0 && _scrollBar->maximum() == (slines - _lines) && _scrollBar->value() == cursor) { @@ -2212,18 +1894,13 @@ void TerminalDisplay::mousePressEvent(QMouseEvent *ev) { _lineSelectionMode = false; _wordSelectionMode = false; - emit isBusySelecting(true); // Keep it steady... - // Drag only when the Control key is hold + emit isBusySelecting(true); bool selected = false; - // The receiver of the testIsSelected() signal will adjust - // 'selected' accordingly. - // emit testIsSelected(pos.x(), pos.y(), selected); selected = _screenWindow->isSelected(pos.x(), pos.y()); if ((!_ctrlDrag || ev->modifiers() & Qt::ControlModifier) && selected) { - // The user clicked inside selected text if ((_mouseMarks) && (ev->modifiers() & Qt::ShiftModifier)) { _screenWindow->clearSelection(); if (shiftSelectionStartX == -1 && shiftSelectionStartY == -1) { @@ -2242,7 +1919,6 @@ void TerminalDisplay::mousePressEvent(QMouseEvent *ev) { dragInfo.start = ev->pos(); } } else { - // No reason to ever start a drag event dragInfo.state = diNone; _preserveLineBreaks = !((ev->modifiers() & Qt::ControlModifier) && @@ -2253,7 +1929,6 @@ void TerminalDisplay::mousePressEvent(QMouseEvent *ev) { if (_mouseMarks) { if (ev->modifiers() & Qt::ShiftModifier) { if (_screenWindow->isClearSelection()) { - // check if (shiftSelectionStartX == -1 && shiftSelectionStartY == -1) { shiftSelectionStartX = pos.x(); shiftSelectionStartY = pos.y(); @@ -2279,19 +1954,17 @@ void TerminalDisplay::mousePressEvent(QMouseEvent *ev) { _screenWindow->clearSelection(); shiftSelectionStartX = -1; shiftSelectionStartY = -1; - // emit clearSelectionSignal(); pos.ry() += _scrollBar->value(); _iPntSel = _pntSel = pos; - _actSel = 1; // left mouse button pressed but nothing selected yet. + _actSel = 1; } } else { if (ev->modifiers() & Qt::ShiftModifier) { _screenWindow->clearSelection(); - // emit clearSelectionSignal(); pos.ry() += _scrollBar->value(); _iPntSel = _pntSel = pos; - _actSel = 1; // left mouse button pressed but nothing selected yet. + _actSel = 1; } else { emit mouseSignal( 0, charColumn + 1, @@ -2336,15 +2009,15 @@ QList TerminalDisplay::filterActions(const QPoint &position) { void TerminalDisplay::hideStaleMouse() const { - if (gs_deadSpot.x() > -1) // we already have a dead spot + if (gs_deadSpot.x() > -1) return; - if (gs_futureDeadSpot.x() < 0) // that's not expected nor gonna end well + if (gs_futureDeadSpot.x() < 0) return; - if (!underMouse()) // we don't care about the mouse + if (!underMouse()) return; - if (QApplication::activeWindow() && QApplication::activeWindow() != window()) // some other app window has the focus + if (QApplication::activeWindow() && QApplication::activeWindow() != window()) return; - if (_scrollBar->underMouse()) // the mouse is over the scrollbar + if (_scrollBar->underMouse()) return; gs_deadSpot = gs_futureDeadSpot; QApplication::setOverrideCursor(Qt::BlankCursor); @@ -2370,7 +2043,6 @@ void TerminalDisplay::autoHideMouseAfter(int delay) } void TerminalDisplay::mouseMoveEvent(QMouseEvent *ev) { - // unclutter if (_mouseAutohideDelay > -1) { if (gs_deadSpot.x() > -1 && (ev->pos() - gs_deadSpot).manhattanLength() > 8) { @@ -2393,8 +2065,6 @@ void TerminalDisplay::mouseMoveEvent(QMouseEvent *ev) { getCharacterPosition(ev->position().toPoint(), charLine, charColumn); - // handle filters - // change link hot-spot appearance on mouse-over Filter::HotSpot *spot = _filterChain->hotSpotAt(charLine, charColumn); if (spot && spot->type() == Filter::HotSpot::Link) { QRegion previousHotspotArea = _mouseOverHotspotArea; @@ -2427,38 +2097,19 @@ void TerminalDisplay::mouseMoveEvent(QMouseEvent *ev) { } update(_mouseOverHotspotArea | previousHotspotArea); - // if (_mouseOverHotspotArea.contains(ev->pos())) { - // if (spot && spot->type() == Filter::HotSpot::Link && - // spot->hasClickAction()) { - // QPoint globalPos = mapToGlobal(ev->pos()); - // QToolTip::showText(globalPos, spot->clickActionToolTip()); - // if (!_ctrlDrag && ev->modifiers() & Qt::ControщаlModifier) { - // setCursor(QCursor(Qt::PointingHandCursor)); - // } else { - // setCursor(QCursor(_mouseMarks ? Qt::IBeamCursor : Qt::ArrowCursor)); - // } - // } else { - // setCursor(QCursor(_mouseMarks ? Qt::IBeamCursor : Qt::ArrowCursor)); - // } - // } else { + QToolTip::hideText(); setCursor(QCursor(_mouseMarks ? Qt::IBeamCursor : Qt::ArrowCursor)); - // } } else if (!_mouseOverHotspotArea.isEmpty()) { update(_mouseOverHotspotArea); - // set hotspot area to an invalid rectangle _mouseOverHotspotArea = QRegion(); QToolTip::hideText(); setCursor(QCursor(_mouseMarks ? Qt::IBeamCursor : Qt::ArrowCursor)); } - // for auto-hiding the cursor, we need mouseTracking if (ev->buttons() == Qt::NoButton) return; - // if the terminal is interested in mouse movements - // then emit a mouse movement signal, unless the shift - // key is being held down, which overrides this. if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) { int button = 3; if (ev->buttons() & Qt::LeftButton) @@ -2476,32 +2127,24 @@ void TerminalDisplay::mouseMoveEvent(QMouseEvent *ev) { } if (dragInfo.state == diPending) { - // we had a mouse down, but haven't confirmed a drag yet - // if the mouse has moved sufficiently, we will confirm - - // int distance = KGlobalSettings::dndEventDelay(); int distance = QApplication::startDragDistance(); if (ev->position().x() > dragInfo.start.x() + distance || ev->position().x() < dragInfo.start.x() - distance || ev->position().y() > dragInfo.start.y() + distance || ev->position().y() < dragInfo.start.y() - distance) { - // we've left the drag square, we can start a real drag operation now - emit isBusySelecting(false); // Ok.. we can breath again. + emit isBusySelecting(false); _screenWindow->clearSelection(); doDrag(); } return; } else if (dragInfo.state == diDragging) { - // this isn't technically needed because mouseMoveEvent is suppressed during - // Qt drag operations, replaced by dragMoveEvent return; } if (_actSel == 0) return; - // don't extend selection while pasting if (ev->buttons() & Qt::MiddleButton) return; @@ -2514,22 +2157,16 @@ void TerminalDisplay::extendSelection(const QPoint &position) { if (!_screenWindow) return; - // if ( !contentsRect().contains(ev->pos()) ) return; QPoint tL = contentsRect().topLeft(); int tLx = tL.x(); int tLy = tL.y(); int scroll = _scrollBar->value(); - // we're in the process of moving the mouse with the left button pressed - // the mouse cursor will kept caught within the bounds of the text in - // this widget. - int linesBeyondWidget = 0; QRect textBounds(tLx + _leftMargin, tLy + _topMargin, _usedColumns * _fontWidth - 1, _usedLines * _fontHeight - 1); - // Adjust position within text area bounds. QPoint oldpos = pos; pos.setX(qBound(textBounds.left(), pos.x(), textBounds.right())); @@ -2538,12 +2175,12 @@ void TerminalDisplay::extendSelection(const QPoint &position) { if (oldpos.y() > textBounds.bottom()) { linesBeyondWidget = (oldpos.y() - textBounds.bottom()) / _fontHeight; _scrollBar->setValue(_scrollBar->value() + linesBeyondWidget + - 1); // scrollforward + 1); } if (oldpos.y() < textBounds.top()) { linesBeyondWidget = (textBounds.top() - oldpos.y()) / _fontHeight; _scrollBar->setValue(_scrollBar->value() - linesBeyondWidget - - 1); // history + 1); } int charColumn = 0; @@ -2552,7 +2189,7 @@ void TerminalDisplay::extendSelection(const QPoint &position) { QPoint here = QPoint( charColumn, - charLine); // QPoint((pos.x()-tLx-_leftMargin+(_fontWidth/2))/_fontWidth,(pos.y()-tLy-_topMargin)/_fontHeight); + charLine); QPoint ohere; QPoint _iPntSelCorr = _iPntSel; _iPntSelCorr.ry() -= _scrollBar->value(); @@ -2561,7 +2198,6 @@ void TerminalDisplay::extendSelection(const QPoint &position) { bool swapping = false; if (_wordSelectionMode) { - // Extend to word boundaries int i; QChar selClass; @@ -2573,7 +2209,6 @@ void TerminalDisplay::extendSelection(const QPoint &position) { _pntSelCorr.x() < _iPntSelCorr.x())); swapping = left_not_right != old_left_not_right; - // Find left (left_not_right ? from here : from start) QPoint left = left_not_right ? here : _iPntSelCorr; i = loc(left.x(), left.y()); if (i >= 0 && i <= _imageSize) { @@ -2592,7 +2227,6 @@ void TerminalDisplay::extendSelection(const QPoint &position) { } } - // Find left (left_not_right ? from start : from here) QPoint right = left_not_right ? _iPntSelCorr : here; i = loc(right.x(), right.y()); if (i >= 0 && i <= _imageSize) { @@ -2611,7 +2245,6 @@ void TerminalDisplay::extendSelection(const QPoint &position) { } } - // Pick which is start (ohere) and which is extension (here) if (left_not_right) { here = left; ohere = right; @@ -2623,7 +2256,6 @@ void TerminalDisplay::extendSelection(const QPoint &position) { } if (_lineSelectionMode) { - // Extend to complete line bool above_not_below = (here.y() < _iPntSelCorr.y()); QPoint above = above_not_below ? here : _iPntSelCorr; @@ -2638,7 +2270,6 @@ void TerminalDisplay::extendSelection(const QPoint &position) { above.setX(0); below.setX(_usedColumns - 1); - // Pick which is start (ohere) and which is extension (here) if (above_not_below) { here = above; ohere = below; @@ -2667,10 +2298,8 @@ void TerminalDisplay::extendSelection(const QPoint &position) { _pntSelCorr.x() < _iPntSelCorr.x())); swapping = left_not_right != old_left_not_right; - // Find left (left_not_right ? from here : from start) QPoint left = left_not_right ? here : _iPntSelCorr; - // Find left (left_not_right ? from start : from here) QPoint right = left_not_right ? _iPntSelCorr : here; if (right.x() > 0 && !_columnSelectionMode) { i = loc(right.x(), right.y()); @@ -2684,12 +2313,11 @@ void TerminalDisplay::extendSelection(const QPoint &position) { if (right.x() < _usedColumns-1) right = left_not_right ? _iPntSelCorr : here; else - right.rx()++; // will be balanced later because of offset=-1; + right.rx()++; }*/ } } - // Pick which is start (ohere) and which is extension (here) if (left_not_right) { here = left; ohere = right; @@ -2702,10 +2330,10 @@ void TerminalDisplay::extendSelection(const QPoint &position) { } if ((here == _pntSelCorr) && (scroll == _scrollBar->value())) - return; // not moved + return; if (here == ohere) - return; // It's not left, it's not right. + return; if (_actSel < 2 || swapping) { if (_columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode) { @@ -2715,7 +2343,7 @@ void TerminalDisplay::extendSelection(const QPoint &position) { } } - _actSel = 2; // within selection + _actSel = 2; _pntSel = here; _pntSel.ry() += _scrollBar->value(); @@ -2737,9 +2365,7 @@ void TerminalDisplay::mouseReleaseEvent(QMouseEvent *ev) { if (ev->button() == Qt::LeftButton) { emit isBusySelecting(false); if (dragInfo.state == diPending) { - // We had a drag event pending but never confirmed. Kill selection _screenWindow->clearSelection(); - // emit clearSelectionSignal(); } else { if (_actSel > 1) { setSelection(_screenWindow->selectedText(_preserveLineBreaks)); @@ -2747,10 +2373,6 @@ void TerminalDisplay::mouseReleaseEvent(QMouseEvent *ev) { _actSel = 0; - // FIXME: emits a release event even if the mouse is - // outside the range. The procedure used in `mouseMoveEvent' - // applies here, too. - if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) emit mouseSignal( 0, charColumn + 1, @@ -2789,11 +2411,6 @@ void TerminalDisplay::getCharacterPosition(const QPointF &widgetPoint, if (column < 0) column = 0; - // the column value returned can be equal to _usedColumns, which - // is the position just after the last character displayed in a line. - // - // this is required so that the user can select characters in the right-most - // column (or left-most for right-to-left input) if (column > _usedColumns) column = _usedColumns; } @@ -2825,11 +2442,8 @@ void TerminalDisplay::mouseDoubleClickEvent(QMouseEvent *ev) { QPoint pos(charColumn, charLine); - // pass on double click as two clicks. if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) { - // Send just _ONE_ click event, since the first click of the double click - // was already sent by the click handler - emit mouseSignal(0, pos.x() + 1,pos.y() + 1 + _scrollBar->value() - _scrollBar->maximum(), 0); // left button + emit mouseSignal(0, pos.x() + 1,pos.y() + 1 + _scrollBar->value() - _scrollBar->maximum(), 0); return; } @@ -2842,10 +2456,8 @@ void TerminalDisplay::mouseDoubleClickEvent(QMouseEvent *ev) { _wordSelectionMode = true; - // find word boundaries... QChar selClass = charClass(_image[i]); { - // find the start of the word int x = bgnSel.x(); while (((x > 0) || (bgnSel.y() > 0 && (_lineProperties[bgnSel.y() - 1] & LINE_WRAPPED))) && charClass(_image[i-1]) == selClass ) { @@ -2861,7 +2473,6 @@ void TerminalDisplay::mouseDoubleClickEvent(QMouseEvent *ev) { bgnSel.setX(x); _screenWindow->setSelectionStart(bgnSel.x(), bgnSel.y(), false); - // find the end of the word i = loc(endSel.x(), endSel.y()); x = endSel.x(); while (((x < _usedColumns - 1) || @@ -2879,14 +2490,13 @@ void TerminalDisplay::mouseDoubleClickEvent(QMouseEvent *ev) { endSel.setX(x); - // In word selection mode don't select @ (64) if at end of word. if (QChar(_image[i].character) == QLatin1Char('@') && endSel.x() - bgnSel.x() > 0 && (_image[i].rendition & RE_EXTENDED_CHAR) == 0) { endSel.setX( x - 1 ); } - _actSel = 2; // within selection + _actSel = 2; _screenWindow->setSelectionEnd(endSel.x(), endSel.y()); @@ -2903,20 +2513,10 @@ void TerminalDisplay::wheelEvent(QWheelEvent *ev) { return; if (_mouseMarks && _scrollBar->maximum() > 0) { - // If the program running in the terminal is not interested in - // Mouse events, send the event to the scrollbar if the slider - // has room to move _scrollBar->event(ev); } else if (_mouseMarks && !_isPrimaryScreen) { - // assume that each Up / Down key event will cause the terminal application - // to scroll by one line. - // - // to get a reasonable scrolling speed, scroll by one line for every 5 - // degrees of mouse wheel rotation. Mouse wheels typically move in steps of - // 15 degrees, giving a scroll of 3 lines int key = ev->angleDelta().y() > 0 ? Qt::Key_Up : Qt::Key_Down; - // QWheelEvent::angleDelta().y() gives rotation in eighths of a degree int wheelDegrees = ev->angleDelta().y() / 8; int linesToScroll = abs(wheelDegrees) / 5; @@ -2925,7 +2525,6 @@ void TerminalDisplay::wheelEvent(QWheelEvent *ev) { for (int i = 0; i < linesToScroll; i++) emit keyPressedSignal(&keyScrollEvent, false); } else if (!_mouseMarks) { - // terminal program wants notification of mouse activity int charLine; int charColumn; getCharacterPosition(ev->position(), charLine, charColumn); @@ -2951,14 +2550,13 @@ void TerminalDisplay::mouseTripleClickEvent(QMouseEvent *ev) { _lineSelectionMode = true; _wordSelectionMode = false; - _actSel = 2; // within selection - emit isBusySelecting(true); // Keep it steady... + _actSel = 2; + emit isBusySelecting(true); while (_iPntSel.y() > 0 && (_lineProperties[_iPntSel.y() - 1] & LINE_WRAPPED)) _iPntSel.ry()--; if (_tripleClickMode == SelectForwardsFromCursor) { - // find word boundary start int i = loc(_iPntSel.x(), _iPntSel.y()); QChar selClass = charClass(_image[i]); int x = _iPntSel.x(); @@ -2995,8 +2593,7 @@ void TerminalDisplay::mouseTripleClickEvent(QMouseEvent *ev) { bool TerminalDisplay::focusNextPrevChild(bool next) { if (next) - return false; // This disables changing the active part in konqueror - // when pressing Tab + return false; return QWidget::focusNextPrevChild(next); } @@ -3057,7 +2654,6 @@ void TerminalDisplay::emitSelection(bool useXselection, bool appendReturn) { if (!_screenWindow) return; - // Paste Clipboard by simulating keypress events QString text = QApplication::clipboard()->text( useXselection ? QClipboard::Selection : QClipboard::Clipboard); if (!text.isEmpty()) { @@ -3076,26 +2672,17 @@ void TerminalDisplay::emitSelection(bool useXselection, bool appendReturn) { bracketText(text); - // appendReturn is intentionally handled _after_ enclosing texts with - // brackets as that feature is used to allow execution of commands - // immediately after paste. Ref: https://bugs.kde.org/show_bug.cgi?id=16179 - // Ref: - // https://github.com/KDE/konsole/commit/83d365f2ebfe2e659c1e857a2f5f247c556ab571 if (appendReturn) { text.append(QLatin1Char('\r')); } QKeyEvent e(QEvent::KeyPress, 0, Qt::NoModifier, text); - emit keyPressedSignal(&e, true); // expose as a big fat keypress event + emit keyPressedSignal(&e, true); _screenWindow->clearSelection(); switch (mMotionAfterPasting) { case MoveStartScreenWindow: - // Temporarily stop tracking output, or pasting contents triggers - // ScreenWindow::notifyOutputChanged() and the latter scrolls the - // terminal to the last line. It will be re-enabled when needed - // (e.g., scrolling to the last line). _screenWindow->setTrackOutput(false); _screenWindow->scrollTo(0); break; @@ -3168,8 +2755,6 @@ void TerminalDisplay::setTrimPastedTrailingNewlines( void TerminalDisplay::setFlowControlWarningEnabled(bool enable) { _flowControlWarningEnabled = enable; - // if the dialog is currently visible and the flow control warning has - // been disabled then hide the dialog if (!enable) outputSuspended(false); } @@ -3181,11 +2766,8 @@ void TerminalDisplay::setMotionAfterPasting(MotionAfterPasting action) { int TerminalDisplay::motionAfterPasting() { return mMotionAfterPasting; } void TerminalDisplay::keyPressEvent(QKeyEvent *event) { - _actSel = 0; // Key stroke implies a screen update, so TerminalDisplay won't - // know where the current selection is. - + _actSel = 0; if (_hasBlinkingCursor) { - // see TerminalDisplay::setBlinkingCursor _blinkCursorTimer->start(std::max(QApplication::cursorFlashTime(), 1000) / 2); if (_cursorBlinking) blinkCursorEvent(); @@ -3220,11 +2802,9 @@ QVariant TerminalDisplay::inputMethodQuery(Qt::InputMethodQuery query) const { return font(); break; case Qt::ImCursorPosition: - // return the cursor position within the current line return cursorPos.x(); break; case Qt::ImSurroundingText: { - // return the text from the current line QString lineText; QTextStream stream(&lineText); PlainTextDecoder decoder; @@ -3249,9 +2829,6 @@ QVariant TerminalDisplay::inputMethodQuery(Qt::InputMethodQuery query) const { bool TerminalDisplay::handleShortcutOverrideEvent(QKeyEvent *keyEvent) { int modifiers = keyEvent->modifiers(); - // When a possible shortcut combination is pressed, - // emit the overrideShortcutCheck() signal to allow the host - // to decide whether the terminal should override it or not. if (modifiers != Qt::NoModifier) { int modifierCount = 0; unsigned int currentModifier = Qt::ShiftModifier; @@ -3271,11 +2848,8 @@ bool TerminalDisplay::handleShortcutOverrideEvent(QKeyEvent *keyEvent) { } } - // Override any of the following shortcuts because - // they are needed by the terminal int keyCode = keyEvent->key() | modifiers; switch (keyCode) { - // list is taken from the QLineEdit::event() code case Qt::Key_Tab: case Qt::Key_Delete: case Qt::Key_Home: @@ -3314,9 +2888,6 @@ void TerminalDisplay::bell() { if (_bellMode == NoBell) return; - // limit the rate at which bells can occur - //...mainly for sound effects where rapid bells in sequence - // produce a horrible noise if (_allowBell) { _allowBell = false; QTimer::singleShot(500, this, &TerminalDisplay::enableBell); @@ -3345,7 +2916,6 @@ void TerminalDisplay::swapColorTable() { } void TerminalDisplay::clearImage() { - // We initialize _image[_imageSize] too. See makeImage() for (int i = 0; i <= _imageSize; i++) { _image[i].character = ' '; _image[i].foregroundColor = @@ -3387,11 +2957,9 @@ void TerminalDisplay::calcGeometry() { contentsRect().height() - 2 * _topBaseMargin + /* mysterious */ 1; if (!_isFixedSize) { - // ensure that display is always at least one column wide _columns = qMax(1, _contentWidth / _fontWidth); _usedColumns = qMin(_usedColumns, _columns); - // ensure that display is always at least one line high _lines = qMax(1, _contentHeight / _fontHeight); _usedLines = qMin(_usedLines, _lines); } @@ -3400,22 +2968,16 @@ void TerminalDisplay::calcGeometry() { void TerminalDisplay::makeImage() { calcGeometry(); - // confirm that array will be of non-zero size, since the painting code - // assumes a non-zero array length Q_ASSERT(_lines > 0 && _columns > 0); Q_ASSERT(_usedLines <= _lines && _usedColumns <= _columns); _imageSize = _lines * _columns; - // We over-commit one character so that we can be more relaxed in dealing with - // certain boundary conditions: _image[_imageSize] is a valid but unused - // position _image = new Character[_imageSize + 1]; clearImage(); } -// calculate the needed size, this must be synced with calcGeometry() void TerminalDisplay::setSize(int columns, int lines) { int scrollBarWidth = (_scrollBar->isHidden() || @@ -3438,7 +3000,6 @@ void TerminalDisplay::setSize(int columns, int lines) { void TerminalDisplay::setFixedSize(int cols, int lins) { _isFixedSize = true; - // ensure that display is at least one line by one column in size _columns = qMax(1, cols); _lines = qMax(1, lins); _usedColumns = qMin(_usedColumns, _columns); @@ -3462,16 +3023,11 @@ void TerminalDisplay::dragEnterEvent(QDragEnterEvent *event) { } void TerminalDisplay::dropEvent(QDropEvent *event) { - // KUrl::List urls = KUrl::List::fromMimeData(event->mimeData()); QList urls = event->mimeData()->urls(); QString dropText; if (!urls.isEmpty()) { - // TODO/FIXME: escape or quote pasted things if necessary... - qDebug() << "TerminalDisplay: handling urls. It can be broken. Report any " - "errors, please"; for (int i = 0; i < urls.count(); i++) { - // KUrl url = KIO::NetAccess::mostLocalUrl( urls[i] , 0 ); QUrl url = urls[i]; QString urlText; @@ -3481,11 +3037,6 @@ void TerminalDisplay::dropEvent(QDropEvent *event) { else urlText = url.toString(); - // in future it may be useful to be able to insert file names with - // drag-and-drop without quoting them (this only affects paths with spaces - // in) - // urlText = KShell::quoteArg(urlText); - QChar q(QLatin1Char('\'')); dropText += q + QString(urlText).replace(q, QLatin1String("'\\''")) + q; dropText += QLatin1Char(' '); @@ -3515,18 +3066,10 @@ void TerminalDisplay::doDrag() { mimeData->setText(QApplication::clipboard()->text(QClipboard::Selection)); dragInfo.dragObject->setMimeData(mimeData); dragInfo.dragObject->exec(Qt::CopyAction); - // Don't delete the QTextDrag object. Qt will delete it when it's done with - // it. } void TerminalDisplay::outputSuspended(bool suspended) { - // create the label when this function is first called if (!_outputSuspendedLabel) { - // This label includes a link to an English language website - // describing the 'flow control' (Xon/Xoff) feature found in almost - // all terminal emulators. - // If there isn't a suitable article available in the target language the - // link can simply be removed. _outputSuspendedLabel = new QLabel( tr("Output has been " "suspended" @@ -3535,14 +3078,12 @@ void TerminalDisplay::outputSuspended(bool suspended) { this); QPalette palette(_outputSuspendedLabel->palette()); - // KColorScheme::adjustBackground(palette,KColorScheme::NeutralBackground); _outputSuspendedLabel->setPalette(palette); _outputSuspendedLabel->setAutoFillBackground(true); _outputSuspendedLabel->setBackgroundRole(QPalette::Base); _outputSuspendedLabel->setFont(QApplication::font()); _outputSuspendedLabel->setContentsMargins(5, 5, 5, 5); - // enable activation of "Xon/Xoff" link in label _outputSuspendedLabel->setTextInteractionFlags( Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard); _outputSuspendedLabel->setOpenExternalLinks(true); @@ -3561,7 +3102,7 @@ uint TerminalDisplay::lineSpacing() const { return _lineSpacing; } void TerminalDisplay::setLineSpacing(uint i) { _lineSpacing = i; - setVTFont(font()); // Trigger an update. + setVTFont(font()); } int TerminalDisplay::margin() const { return _topBaseMargin; } @@ -3645,7 +3186,6 @@ ScrollBar::ScrollBar(QWidget* parent) : QScrollBar(parent) {} void ScrollBar::enterEvent(QEnterEvent* event) { - // show the mouse cursor that was auto-hidden if (gs_deadSpot.x() > -1) { gs_deadSpot = QPoint(-1,-1); diff --git a/AdaptixClient/Libs/Konsole/TerminalDisplay.h b/AdaptixClient/Libs/Konsole/TerminalDisplay.h index 08cfbecc..8ac43f4b 100644 --- a/AdaptixClient/Libs/Konsole/TerminalDisplay.h +++ b/AdaptixClient/Libs/Konsole/TerminalDisplay.h @@ -322,7 +322,6 @@ protected: void mouseTripleClickEvent(QMouseEvent* ev); - // reimplemented void inputMethodEvent ( QInputMethodEvent* event ) override; QVariant inputMethodQuery( Qt::InputMethodQuery query ) const override; @@ -337,7 +336,7 @@ protected slots: private slots: void swapColorTable(); - void tripleClickTimeout(); // resets possibleTripleClick + void tripleClickTimeout(); private: @@ -384,7 +383,7 @@ private: bool isLineChar(Character c) const; bool isLineCharString(const std::wstring& string) const; - void hideStaleMouse() const; // conditionally hides the mouse cursor + void hideStaleMouse() const; QPointer _screenWindow; @@ -393,28 +392,28 @@ private: QGridLayout* _gridLayout; CharWidth *_charWidth; - bool _fixedFont; // has fixed pitch - bool _fixedFont_original; // used only in textWidth() - int _fontHeight; // height - int _fontWidth; // width - int _fontAscent; // ascend - bool _boldIntense; // Whether intense colors should be rendered with bold font - int _drawTextAdditionHeight; // additional height to prevent font trancation - bool _drawTextTestFlag; // indicate it is a testing or not + bool _fixedFont; + bool _fixedFont_original; + int _fontHeight; + int _fontWidth; + int _fontAscent; + bool _boldIntense; + int _drawTextAdditionHeight; + bool _drawTextTestFlag; - int _leftMargin; // offset - int _topMargin; // offset + int _leftMargin; + int _topMargin; - int _lines; // the number of lines that can be displayed in the widget - int _columns; // the number of columns that can be displayed in the widget + int _lines; + int _columns; - int _usedLines; // the number of lines that are actually being used, this will be less + int _usedLines; - int _usedColumns; // the number of columns that are actually being used, this will be less + int _usedColumns; int _contentHeight; int _contentWidth; - Character* _image; // [lines][columns] + Character* _image; int _imageSize; QVector _lineProperties; @@ -431,10 +430,10 @@ private: bool _disabledBracketedPasteMode; bool _showResizeNotificationEnabled; - QPoint _iPntSel; // initial selection point - QPoint _pntSel; // current selection point - QPoint _tripleSelBegin; // help avoid flicker - int _actSel; // selection state + QPoint _iPntSel; + QPoint _pntSel; + QPoint _tripleSelBegin; + int _actSel; bool _wordSelectionMode; bool _lineSelectionMode; bool _preserveLineBreaks; @@ -446,23 +445,22 @@ private: QString _wordCharacters; int _bellMode; - bool _blinking; // hide text in paintEvent - bool _hasBlinker; // has characters to blink - bool _cursorBlinking; // hide cursor in paintEvent - bool _hasBlinkingCursor; // has blinking cursor enabled - bool _allowBlinkingText; // allow text to blink - bool _ctrlDrag; // require Ctrl key for drag + bool _blinking; + bool _hasBlinker; + bool _cursorBlinking; + bool _hasBlinkingCursor; + bool _allowBlinkingText; + bool _ctrlDrag; TripleClickMode _tripleClickMode; - bool _isFixedSize; //Columns / lines are locked. - QTimer* _blinkTimer; // active when hasBlinker - QTimer* _blinkCursorTimer; // active when hasBlinkingCursor + bool _isFixedSize; + QTimer* _blinkTimer; + QTimer* _blinkCursorTimer; static std::shared_ptr _hideMouseTimer; - //QMenu* _drop; QString _dropText; int _dndFileCount; - bool _possibleTripleClick; // is set in mouseDoubleClickEvent and deleted + bool _possibleTripleClick; QLabel* _resizeWidget; QTimer* _resizeTimer; @@ -473,7 +471,7 @@ private: uint _lineSpacing; - bool _colorsInverted; // true during visual bell + bool _colorsInverted; QSize _size; @@ -481,9 +479,6 @@ private: QPixmap *_backgroundPixmapRef = nullptr; QPixmap _backgroundImage; - // QMovie *_backgroundMovie = nullptr; - // QMediaPlayer* _backgroundVideoPlayer; - // QVideoSink* _backgroundVideoSink; QPixmap _backgroundVideoFrame; bool _isLocked; QPixmap _lockbackgroundImage; @@ -510,7 +505,7 @@ private: }; InputMethodData _inputMethodData; - static bool _antialiasText; // do we antialias or not + static bool _antialiasText; static const int TEXT_BLINK_DELAY = 500; @@ -521,7 +516,7 @@ private: int _mouseAutohideDelay; - int _preeditColorIndex = 16; //Color4Intense + int _preeditColorIndex = 16; int shiftSelectionStartX = -1; int shiftSelectionStartY = -1; @@ -595,4 +590,4 @@ private: QDialogButtonBox *buttonBox; }; -#endif // TERMINALDISPLAY_H +#endif diff --git a/AdaptixClient/Libs/Konsole/Vt102Emulation.cpp b/AdaptixClient/Libs/Konsole/Vt102Emulation.cpp index 08d79959..cfdbf1cc 100644 --- a/AdaptixClient/Libs/Konsole/Vt102Emulation.cpp +++ b/AdaptixClient/Libs/Konsole/Vt102Emulation.cpp @@ -64,8 +64,6 @@ void Vt102Emulation::reset() { technical reference of this program. */ -// Tokens ------------------------------------------------------------------ -- - /* Since the tokens are the central notion if this section, we've put them in front. They provide the syntactical elements used to represent the @@ -120,8 +118,6 @@ void Vt102Emulation::reset() { #define MAX_ARGUMENT 4096 -// Tokenizer --------------------------------------------------------------- -- - /* The tokenizer's state The state is represented by the buffer (tokenBuffer, tokenBufferPos), @@ -152,15 +148,13 @@ void Vt102Emulation::addToCurrentToken(wchar_t cc) { tokenBufferPos = qMin(tokenBufferPos + 1, MAX_TOKEN_LENGTH - 1); } -// Character Class flags used while decoding -#define CTL 1 // Control character -#define CHR 2 // Printable character -#define CPN 4 // TODO: Document me -#define DIG 8 // Digit -#define SCS 16 // TODO: Document me -#define GRP 32 // TODO: Document me -#define CPS 64 // Character which indicates end of window resize - // escape sequence '\e[8;;t' +#define CTL 1 +#define CHR 2 +#define CPN 4 +#define DIG 8 +#define SCS 16 +#define GRP 32 +#define CPS 64 void Vt102Emulation::initTokenizer() { int i; @@ -173,7 +167,6 @@ void Vt102Emulation::initTokenizer() { charClass[i] |= CHR; for (s = (quint8 *)"@ABCDEFGHILMPSTXZbcdfry"; *s; ++s) charClass[*s] |= CPN; - // resize = \e[8;;t for (s = (quint8 *)"t"; *s; ++s) charClass[*s] |= CPS; for (s = (quint8 *)"0123456789"; *s; ++s) @@ -216,40 +209,32 @@ void Vt102Emulation::initTokenizer() { #define egt() (p >= 3 && s[2] == '>') #define esp() (p == 4 && s[3] == ' ') #define Xpe (tokenBufferPos >= 2 && tokenBuffer[1] == ']') -#define Xte (Xpe && (cc == 7 || (prevCC == 27 && cc == 92))) // 27, 92 => "\e\\" (ST, String Terminator) +#define Xte (Xpe && (cc == 7 || (prevCC == 27 && cc == 92))) #define ces(C) (cc < 256 && (charClass[cc] & (C)) == (C) && !Xte) #define CNTL(c) ((c) - '@') #define ESC 27 #define DEL 127 -// process an incoming unicode character void Vt102Emulation::receiveChar(wchar_t cc) { if ((cc == L'\r') || (cc == L'\n')) dupDisplayCharacter(cc); if (cc == DEL) - return; // VT100: ignore. + return; if (ces(CTL)) { - // ignore control characters in the text part of Xpe (aka OSC) "ESC]" - // escape sequences; this matches what XTERM docs say if (Xpe) { prevCC = cc; return; } - // DEC HACK ALERT! Control Characters are allowed *within* esc sequences in - // VT100 This means, they do neither a resetTokenizer() nor a pushToToken(). - // Some of them, do of course. Guess this originates from a weakly layered - // handling of the X-on X-off protocol, which comes really below this level. if (cc == CNTL('X') || cc == CNTL('Z') || cc == ESC) - resetTokenizer(); // VT100: CAN or SUB + resetTokenizer(); if (cc != ESC) { processToken(TY_CTL(cc + '@'), 0, 0); return; } } - // advance the state addToCurrentToken(cc); wchar_t *s = tokenBuffer; @@ -319,7 +304,6 @@ void Vt102Emulation::receiveChar(wchar_t cc) { return; } - // resize = \e[8;;t if (eps(CPS)) { processToken(TY_CSI_PS(cc, argv[0]), argv[1], argv[2]); resetTokenizer(); @@ -343,18 +327,15 @@ void Vt102Emulation::receiveChar(wchar_t cc) { if (epp()) processToken(TY_CSI_PR(cc, argv[i]), 0, 0); else if (egt()) - processToken(TY_CSI_PG(cc), 0, 0); // spec. case for ESC]>0c or ESC]>c + processToken(TY_CSI_PG(cc), 0, 0); else if (cc == 'm' && argc - i >= 4 && (argv[i] == 38 || argv[i] == 48) && argv[i + 1] == 2) { - // ESC[ ... 48;2;;; ... m -or- ESC[ ... - // 38;2;;; ... m i += 2; processToken(TY_CSI_PS(cc, argv[i - 2]), COLOR_SPACE_RGB, (argv[i] << 16) | (argv[i + 1] << 8) | argv[i + 2]); i += 2; } else if (cc == 'm' && argc - i >= 2 && (argv[i] == 38 || argv[i] == 48) && argv[i + 1] == 5) { - // ESC[ ... 48;5; ... m -or- ESC[ ... 38;5; ... m i += 2; processToken(TY_CSI_PS(cc, argv[i - 2]), COLOR_SPACE_256, argv[i]); } else @@ -362,7 +343,6 @@ void Vt102Emulation::receiveChar(wchar_t cc) { } resetTokenizer(); } else { - // VT52 Mode if (lec(1, 0, ESC)) return; if (les(1, 0, CHR)) { @@ -409,13 +389,6 @@ void Vt102Emulation::processOSC() { } switch (command) { - /* - * Operating System Controls https://www.xfree86.org/current/ctlseqs.html - * - * Ps = 0 → Change Icon Name and Window Title to Pt - * Ps = 1 → Change Icon Name to Pt - * Ps = 2 → Change Window Title to Pt - */ case 0: case 1: case 2: { @@ -424,8 +397,6 @@ void Vt102Emulation::processOSC() { processWindowAttributeChange(command, newValue); break; } - // Ps = 52 → Manipulate Selection Data. These controls may be disabled using - // the allowWindowOps resource. case 52: { /* The first, Pc , may contain any character from the set c p s 0 1 2 3 4 5 * 6 7 . It is used to construct a list of selection parameters for @@ -485,12 +456,8 @@ void Vt102Emulation::updateTitle() { } void Vt102Emulation::doTitleChanged(int what, const QString &caption) { - // set to true if anything is actually changed (eg. old _nameTitle != new - // _nameTitle ) bool modified = false; - // (btw: what=0 changes _userTitle and icon, what=1 only icon, what=2 only - // _nameTitle if ((what == 0) || (what == 2)) { _isTitleChanged = true; if (_userTitle != caption) { @@ -510,7 +477,7 @@ void Vt102Emulation::doTitleChanged(int what, const QString &caption) { if (what == 11) { QString colorString = caption.section(QLatin1Char(';'), 0, 0); QColor backColor = QColor(colorString); - if (backColor.isValid()) { // change color via \033]11;Color\007 + if (backColor.isValid()) { if (backColor != _modifiedBackground) { _modifiedBackground = backColor; emit changeBackgroundColorRequest(backColor); @@ -533,7 +500,6 @@ void Vt102Emulation::doTitleChanged(int what, const QString &caption) { emit openUrlRequest(cwd); } - // change icon via \033]32;Icon\007 if (what == 32) { _isTitleChanged = true; if (_iconName != caption) { @@ -553,8 +519,6 @@ void Vt102Emulation::doTitleChanged(int what, const QString &caption) { } } -// Interpreting Codes --------------------------------------------------------- - /* Now that the incoming character stream is properly tokenized, meaning is assigned to them. These are either operations of @@ -576,9 +540,7 @@ void Vt102Emulation::processToken(int token, wchar_t p, int q) { case TY_CHR(): _currentScreen->displayCharacter(p); dupDisplayCharacter(p); - break; // UTF16 - - // 127 DEL : ignored on input + break; case TY_CTL('@'): /* NUL: ignored */ break; @@ -592,46 +554,46 @@ void Vt102Emulation::processToken(int token, wchar_t p, int q) { break; case TY_CTL('E'): reportAnswerBack(); - break; // VT100 + break; case TY_CTL('F'): /* ACK: ignored */ break; case TY_CTL('G'): emit stateSet(NOTIFYBELL); - break; // VT100 + break; case TY_CTL('H'): _currentScreen->backspace(); - break; // VT100 + break; case TY_CTL('I'): _currentScreen->tab(); - break; // VT100 + break; case TY_CTL('J'): _currentScreen->newLine(); - break; // VT100 + break; case TY_CTL('K'): _currentScreen->newLine(); - break; // VT100 + break; case TY_CTL('L'): _currentScreen->newLine(); - break; // VT100 + break; case TY_CTL('M'): _currentScreen->toStartOfLine(); - break; // VT100 + break; case TY_CTL('N'): useCharset(1); - break; // VT100 + break; case TY_CTL('O'): useCharset(0); - break; // VT100 + break; case TY_CTL('P'): /* DLE: ignored */ break; case TY_CTL('Q'): /* DC1: XON continue */ - break; // VT100 + break; case TY_CTL('R'): /* DC2: ignored */ break; case TY_CTL('S'): /* DC3: XOFF halt */ - break; // VT100 + break; case TY_CTL('T'): /* DC4: ignored */ break; case TY_CTL('U'): /* NAK: ignored */ @@ -643,13 +605,13 @@ void Vt102Emulation::processToken(int token, wchar_t p, int q) { case TY_CTL('X'): _currentScreen->displayCharacter(0x2592); dupDisplayCharacter(0x2592); - break; // VT100 + break; case TY_CTL('Y'): /* EM : ignored */ break; case TY_CTL('Z'): _currentScreen->displayCharacter(0x2592); dupDisplayCharacter(0x2592); - break; // VT100 + break; case TY_CTL('['): /* ESC: cannot be seen here. */ break; case TY_CTL('\\'): /* FS : ignored */ @@ -663,16 +625,16 @@ void Vt102Emulation::processToken(int token, wchar_t p, int q) { case TY_ESC('D'): _currentScreen->index(); - break; // VT100 + break; case TY_ESC('E'): _currentScreen->nextLine(); - break; // VT100 + break; case TY_ESC('H'): _currentScreen->changeTabStop(true); - break; // VT100 + break; case TY_ESC('M'): _currentScreen->reverseIndex(); - break; // VT100 + break; case TY_ESC('Z'): reportTerminalType(); break; @@ -701,52 +663,52 @@ void Vt102Emulation::processToken(int token, wchar_t p, int q) { break; case TY_ESC('<'): setMode(MODE_Ansi); - break; // VT100 + break; case TY_ESC_CS('(', '0'): setCharset(0, '0'); - break; // VT100 + break; case TY_ESC_CS('(', 'A'): setCharset(0, 'A'); - break; // VT100 + break; case TY_ESC_CS('(', 'B'): setCharset(0, 'B'); - break; // VT100 + break; case TY_ESC_CS(')', '0'): setCharset(1, '0'); - break; // VT100 + break; case TY_ESC_CS(')', 'A'): setCharset(1, 'A'); - break; // VT100 + break; case TY_ESC_CS(')', 'B'): setCharset(1, 'B'); - break; // VT100 + break; case TY_ESC_CS('*', '0'): setCharset(2, '0'); - break; // VT100 + break; case TY_ESC_CS('*', 'A'): setCharset(2, 'A'); - break; // VT100 + break; case TY_ESC_CS('*', 'B'): setCharset(2, 'B'); - break; // VT100 + break; case TY_ESC_CS('+', '0'): setCharset(3, '0'); - break; // VT100 + break; case TY_ESC_CS('+', 'A'): setCharset(3, 'A'); - break; // VT100 + break; case TY_ESC_CS('+', 'B'): setCharset(3, 'B'); - break; // VT100 + break; case TY_ESC_CS('%', 'G'): /*No longer updating codec*/ - break; // LINUX + break; case TY_ESC_CS('%', '@'): /*No longer updating codec*/ - break; // LINUX + break; case TY_ESC_DE('3'): /* Double height line, top half */ _currentScreen->setLineProperty(LINE_DOUBLEWIDTH, true); @@ -768,13 +730,11 @@ void Vt102Emulation::processToken(int token, wchar_t p, int q) { _currentScreen->helpAlign(); break; - // resize = \e[8;;t case TY_CSI_PS('t', 8): setImageSize(p /*lines */, q /* columns */); emit imageResizeRequest(QSize(q, p)); break; - // change tab text color : \e[28;t color: 0-16,777,215 case TY_CSI_PS('t', 28): emit changeTabTextColorRequest(p); break; @@ -802,10 +762,10 @@ void Vt102Emulation::processToken(int token, wchar_t p, int q) { break; case TY_CSI_PS('g', 0): _currentScreen->changeTabStop(false); - break; // VT100 + break; case TY_CSI_PS('g', 3): _currentScreen->clearTabStops(); - break; // VT100 + break; case TY_CSI_PS('h', 4): _currentScreen->setMode(MODE_Insert); break; @@ -813,7 +773,7 @@ void Vt102Emulation::processToken(int token, wchar_t p, int q) { setMode(MODE_NewLine); break; case TY_CSI_PS('i', 0): /* IGNORE: attached printer */ - break; // VT100 + break; case TY_CSI_PS('l', 4): _currentScreen->resetMode(MODE_Insert); break; @@ -832,19 +792,19 @@ void Vt102Emulation::processToken(int token, wchar_t p, int q) { break; case TY_CSI_PS('m', 1): _currentScreen->setRendition(RE_BOLD); - break; // VT100 + break; case TY_CSI_PS('m', 2): _currentScreen->setRendition(RE_FAINT); break; case TY_CSI_PS('m', 3): _currentScreen->setRendition(RE_ITALIC); - break; // VT100 + break; case TY_CSI_PS('m', 4): _currentScreen->setRendition(RE_UNDERLINE); - break; // VT100 + break; case TY_CSI_PS('m', 5): _currentScreen->setRendition(RE_BLINK); - break; // VT100 + break; case TY_CSI_PS('m', 7): _currentScreen->setRendition(RE_REVERSE); break; @@ -858,11 +818,11 @@ void Vt102Emulation::processToken(int token, wchar_t p, int q) { _currentScreen->setRendition(RE_OVERLINE); break; case TY_CSI_PS('m', 10): /* IGNORED: mapping related */ - break; // LINUX + break; case TY_CSI_PS('m', 11): /* IGNORED: mapping related */ - break; // LINUX + break; case TY_CSI_PS('m', 12): /* IGNORED: mapping related */ - break; // LINUX + break; case TY_CSI_PS('m', 21): _currentScreen->resetRendition(RE_BOLD); break; @@ -872,7 +832,7 @@ void Vt102Emulation::processToken(int token, wchar_t p, int q) { break; case TY_CSI_PS('m', 23): _currentScreen->resetRendition(RE_ITALIC); - break; // VT100 + break; case TY_CSI_PS('m', 24): _currentScreen->resetRendition(RE_UNDERLINE); break; @@ -1015,21 +975,21 @@ void Vt102Emulation::processToken(int token, wchar_t p, int q) { reportCursorPosition(); break; case TY_CSI_PS('q', 0): /* IGNORED: LEDs off */ - break; // VT100 + break; case TY_CSI_PS('q', 1): /* IGNORED: LED1 on */ - break; // VT100 + break; case TY_CSI_PS('q', 2): /* IGNORED: LED2 on */ - break; // VT100 + break; case TY_CSI_PS('q', 3): /* IGNORED: LED3 on */ - break; // VT100 + break; case TY_CSI_PS('q', 4): /* IGNORED: LED4 on */ - break; // VT100 + break; case TY_CSI_PS('x', 0): reportTerminalParms(2); - break; // VT100 + break; case TY_CSI_PS('x', 1): reportTerminalParms(3); - break; // VT100 + break; case TY_CSI_PS_SP('q', 0): /* fall through */ case TY_CSI_PS_SP('q', 1): @@ -1056,28 +1016,28 @@ void Vt102Emulation::processToken(int token, wchar_t p, int q) { break; case TY_CSI_PN('A'): _currentScreen->cursorUp(p); - break; // VT100 + break; case TY_CSI_PN('B'): _currentScreen->cursorDown(p); - break; // VT100 + break; case TY_CSI_PN('C'): _currentScreen->cursorRight(p); - break; // VT100 + break; case TY_CSI_PN('D'): _currentScreen->cursorLeft(p); - break; // VT100 + break; case TY_CSI_PN('E'): _currentScreen->cursorNextLine(p); - break; // VT100 + break; case TY_CSI_PN('F'): _currentScreen->cursorPreviousLine(p); - break; // VT100 + break; case TY_CSI_PN('G'): _currentScreen->setCursorX(p); - break; // LINUX + break; case TY_CSI_PN('H'): _currentScreen->setCursorYX(p, q); - break; // VT100 + break; case TY_CSI_PN('I'): _currentScreen->tab(p); break; @@ -1107,222 +1067,207 @@ void Vt102Emulation::processToken(int token, wchar_t p, int q) { break; case TY_CSI_PN('c'): reportTerminalType(); - break; // VT100 + break; case TY_CSI_PN('d'): _currentScreen->setCursorY(p); - break; // LINUX + break; case TY_CSI_PN('f'): _currentScreen->setCursorYX(p, q); - break; // VT100 + break; case TY_CSI_PN('r'): setMargins(p, q); - break; // VT100 + break; case TY_CSI_PN('y'): /* IGNORED: Confidence test */ - break; // VT100 + break; case TY_CSI_PR('h', 1): setMode(MODE_AppCuKeys); - break; // VT100 + break; case TY_CSI_PR('l', 1): resetMode(MODE_AppCuKeys); - break; // VT100 + break; case TY_CSI_PR('s', 1): saveMode(MODE_AppCuKeys); - break; // FIXME + break; case TY_CSI_PR('r', 1): restoreMode(MODE_AppCuKeys); - break; // FIXME + break; case TY_CSI_PR('l', 2): resetMode(MODE_Ansi); - break; // VT100 + break; case TY_CSI_PR('h', 3): setMode(MODE_132Columns); - break; // VT100 + break; case TY_CSI_PR('l', 3): resetMode(MODE_132Columns); - break; // VT100 + break; case TY_CSI_PR('h', 4): /* IGNORED: soft scrolling */ - break; // VT100 + break; case TY_CSI_PR('l', 4): /* IGNORED: soft scrolling */ - break; // VT100 + break; case TY_CSI_PR('h', 5): _currentScreen->setMode(MODE_Screen); - break; // VT100 + break; case TY_CSI_PR('l', 5): _currentScreen->resetMode(MODE_Screen); - break; // VT100 + break; case TY_CSI_PR('h', 6): _currentScreen->setMode(MODE_Origin); - break; // VT100 + break; case TY_CSI_PR('l', 6): _currentScreen->resetMode(MODE_Origin); - break; // VT100 + break; case TY_CSI_PR('s', 6): _currentScreen->saveMode(MODE_Origin); - break; // FIXME + break; case TY_CSI_PR('r', 6): _currentScreen->restoreMode(MODE_Origin); - break; // FIXME + break; case TY_CSI_PR('h', 7): _currentScreen->setMode(MODE_Wrap); - break; // VT100 + break; case TY_CSI_PR('l', 7): _currentScreen->resetMode(MODE_Wrap); - break; // VT100 + break; case TY_CSI_PR('s', 7): _currentScreen->saveMode(MODE_Wrap); - break; // FIXME + break; case TY_CSI_PR('r', 7): _currentScreen->restoreMode(MODE_Wrap); - break; // FIXME + break; case TY_CSI_PR('h', 8): /* IGNORED: autorepeat on */ - break; // VT100 + break; case TY_CSI_PR('l', 8): /* IGNORED: autorepeat off */ - break; // VT100 + break; case TY_CSI_PR('s', 8): /* IGNORED: autorepeat on */ - break; // VT100 + break; case TY_CSI_PR('r', 8): /* IGNORED: autorepeat off */ - break; // VT100 + break; case TY_CSI_PR('h', 9): /* IGNORED: interlace */ - break; // VT100 + break; case TY_CSI_PR('l', 9): /* IGNORED: interlace */ - break; // VT100 + break; case TY_CSI_PR('s', 9): /* IGNORED: interlace */ - break; // VT100 + break; case TY_CSI_PR('r', 9): /* IGNORED: interlace */ - break; // VT100 + break; case TY_CSI_PR('h', 12): /* IGNORED: Cursor blink */ - break; // att610 + break; case TY_CSI_PR('l', 12): /* IGNORED: Cursor blink */ - break; // att610 + break; case TY_CSI_PR('s', 12): /* IGNORED: Cursor blink */ - break; // att610 + break; case TY_CSI_PR('r', 12): /* IGNORED: Cursor blink */ - break; // att610 + break; case TY_CSI_PR('h', 25): setMode(MODE_Cursor); - break; // VT100 + break; case TY_CSI_PR('l', 25): resetMode(MODE_Cursor); - break; // VT100 + break; case TY_CSI_PR('s', 25): saveMode(MODE_Cursor); - break; // VT100 + break; case TY_CSI_PR('r', 25): restoreMode(MODE_Cursor); - break; // VT100 + break; case TY_CSI_PR('h', 40): setMode(MODE_Allow132Columns); - break; // XTERM + break; case TY_CSI_PR('l', 40): resetMode(MODE_Allow132Columns); - break; // XTERM + break; case TY_CSI_PR('h', 41): /* IGNORED: obsolete more(1) fix */ - break; // XTERM + break; case TY_CSI_PR('l', 41): /* IGNORED: obsolete more(1) fix */ - break; // XTERM + break; case TY_CSI_PR('s', 41): /* IGNORED: obsolete more(1) fix */ - break; // XTERM + break; case TY_CSI_PR('r', 41): /* IGNORED: obsolete more(1) fix */ - break; // XTERM + break; case TY_CSI_PR('h', 47): setMode(MODE_AppScreen); - break; // VT100 + break; case TY_CSI_PR('l', 47): resetMode(MODE_AppScreen); - break; // VT100 + break; case TY_CSI_PR('s', 47): saveMode(MODE_AppScreen); - break; // XTERM + break; case TY_CSI_PR('r', 47): restoreMode(MODE_AppScreen); - break; // XTERM + break; case TY_CSI_PR('h', 67): /* IGNORED: DECBKM */ - break; // XTERM + break; case TY_CSI_PR('l', 67): /* IGNORED: DECBKM */ - break; // XTERM + break; case TY_CSI_PR('s', 67): /* IGNORED: DECBKM */ - break; // XTERM + break; case TY_CSI_PR('r', 67): /* IGNORED: DECBKM */ - break; // XTERM - - // XTerm defines the following modes: - // SET_VT200_MOUSE 1000 - // SET_VT200_HIGHLIGHT_MOUSE 1001 - // SET_BTN_EVENT_MOUSE 1002 - // SET_ANY_EVENT_MOUSE 1003 - // - - // Note about mouse modes: - // There are four mouse modes which xterm-compatible terminals can support - - // 1000,1001,1002,1003 Konsole currently supports mode 1000 (basic mouse - // press and release) and mode 1002 (dragging the mouse). - // TODO: Implementation of mouse modes 1001 (something called highlight - // tracking) and 1003 (a slight variation on dragging the mouse) - // + break; case TY_CSI_PR('h', 1000): setMode(MODE_Mouse1000); - break; // XTERM + break; case TY_CSI_PR('l', 1000): resetMode(MODE_Mouse1000); - break; // XTERM + break; case TY_CSI_PR('s', 1000): saveMode(MODE_Mouse1000); - break; // XTERM + break; case TY_CSI_PR('r', 1000): restoreMode(MODE_Mouse1000); - break; // XTERM + break; case TY_CSI_PR('h', 1001): /* IGNORED: hilite mouse tracking */ - break; // XTERM + break; case TY_CSI_PR('l', 1001): resetMode(MODE_Mouse1001); - break; // XTERM + break; case TY_CSI_PR('s', 1001): /* IGNORED: hilite mouse tracking */ - break; // XTERM + break; case TY_CSI_PR('r', 1001): /* IGNORED: hilite mouse tracking */ - break; // XTERM + break; case TY_CSI_PR('h', 1002): setMode(MODE_Mouse1002); - break; // XTERM + break; case TY_CSI_PR('l', 1002): resetMode(MODE_Mouse1002); - break; // XTERM + break; case TY_CSI_PR('s', 1002): saveMode(MODE_Mouse1002); - break; // XTERM + break; case TY_CSI_PR('r', 1002): restoreMode(MODE_Mouse1002); - break; // XTERM + break; case TY_CSI_PR('h', 1003): setMode(MODE_Mouse1003); - break; // XTERM + break; case TY_CSI_PR('l', 1003): resetMode(MODE_Mouse1003); - break; // XTERM + break; case TY_CSI_PR('s', 1003): saveMode(MODE_Mouse1003); - break; // XTERM + break; case TY_CSI_PR('r', 1003): restoreMode(MODE_Mouse1003); - break; // XTERM + break; case TY_CSI_PR('h', 1004): _reportFocusEvents = true; @@ -1333,155 +1278,150 @@ void Vt102Emulation::processToken(int token, wchar_t p, int q) { case TY_CSI_PR('h', 1005): setMode(MODE_Mouse1005); - break; // XTERM + break; case TY_CSI_PR('l', 1005): resetMode(MODE_Mouse1005); - break; // XTERM + break; case TY_CSI_PR('s', 1005): saveMode(MODE_Mouse1005); - break; // XTERM + break; case TY_CSI_PR('r', 1005): restoreMode(MODE_Mouse1005); - break; // XTERM + break; case TY_CSI_PR('h', 1006): setMode(MODE_Mouse1006); - break; // XTERM + break; case TY_CSI_PR('l', 1006): resetMode(MODE_Mouse1006); - break; // XTERM + break; case TY_CSI_PR('s', 1006): saveMode(MODE_Mouse1006); - break; // XTERM + break; case TY_CSI_PR('r', 1006): restoreMode(MODE_Mouse1006); - break; // XTERM + break; case TY_CSI_PR('h', 1015): setMode(MODE_Mouse1015); - break; // URXVT + break; case TY_CSI_PR('l', 1015): resetMode(MODE_Mouse1015); - break; // URXVT + break; case TY_CSI_PR('s', 1015): saveMode(MODE_Mouse1015); - break; // URXVT + break; case TY_CSI_PR('r', 1015): restoreMode(MODE_Mouse1015); - break; // URXVT + break; case TY_CSI_PR('h', 1034): /* IGNORED: 8bitinput activation */ - break; // XTERM + break; case TY_CSI_PR('h', 1047): setMode(MODE_AppScreen); - break; // XTERM + break; case TY_CSI_PR('l', 1047): _screen[1]->clearEntireScreen(); resetMode(MODE_AppScreen); - break; // XTERM + break; case TY_CSI_PR('s', 1047): saveMode(MODE_AppScreen); - break; // XTERM + break; case TY_CSI_PR('r', 1047): restoreMode(MODE_AppScreen); - break; // XTERM + break; - // FIXME: Unitoken: save translations123 case TY_CSI_PR('h', 1048): saveCursor(); - break; // XTERM + break; case TY_CSI_PR('l', 1048): restoreCursor(); - break; // XTERM + break; case TY_CSI_PR('s', 1048): saveCursor(); - break; // XTERM + break; case TY_CSI_PR('r', 1048): restoreCursor(); - break; // XTERM + break; - // FIXME: every once new sequences like this pop up in xterm. - // Here's a guess of what they could mean. case TY_CSI_PR('h', 1049): saveCursor(); _screen[1]->clearEntireScreen(); setMode(MODE_AppScreen); - break; // XTERM + break; case TY_CSI_PR('l', 1049): resetMode(MODE_AppScreen); restoreCursor(); - break; // XTERM + break; case TY_CSI_PR('h', 2004): setMode(MODE_BracketedPaste); - break; // XTERM + break; case TY_CSI_PR('l', 2004): resetMode(MODE_BracketedPaste); - break; // XTERM + break; case TY_CSI_PR('s', 2004): saveMode(MODE_BracketedPaste); - break; // XTERM + break; case TY_CSI_PR('r', 2004): restoreMode(MODE_BracketedPaste); - break; // XTERM + break; - // FIXME: weird DEC reset sequence case TY_CSI_PE('p'): /* IGNORED: reset ( ) */ break; - // FIXME: when changing between vt52 and ansi mode evtl do some resetting. case TY_VT52('A'): _currentScreen->cursorUp(1); - break; // VT52 + break; case TY_VT52('B'): _currentScreen->cursorDown(1); - break; // VT52 + break; case TY_VT52('C'): _currentScreen->cursorRight(1); - break; // VT52 + break; case TY_VT52('D'): _currentScreen->cursorLeft(1); - break; // VT52 + break; case TY_VT52('F'): setAndUseCharset(0, '0'); - break; // VT52 + break; case TY_VT52('G'): setAndUseCharset(0, 'B'); - break; // VT52 + break; case TY_VT52('H'): _currentScreen->setCursorYX(1, 1); - break; // VT52 + break; case TY_VT52('I'): _currentScreen->reverseIndex(); - break; // VT52 + break; case TY_VT52('J'): _currentScreen->clearToEndOfScreen(); - break; // VT52 + break; case TY_VT52('K'): _currentScreen->clearToEndOfLine(); - break; // VT52 + break; case TY_VT52('Y'): _currentScreen->setCursorYX(p - 31, q - 31); - break; // VT52 + break; case TY_VT52('Z'): reportTerminalType(); - break; // VT52 + break; case TY_VT52('<'): setMode(MODE_Ansi); - break; // VT52 + break; case TY_VT52('='): setMode(MODE_AppKeyPad); - break; // VT52 + break; case TY_VT52('>'): resetMode(MODE_AppKeyPad); - break; // VT52 + break; case TY_CSI_PG('c'): reportSecondaryAttributes(); - break; // VT100 + break; default: reportDecodingError(); @@ -1506,91 +1446,58 @@ void Vt102Emulation::sendString(const char *s, int length) { void Vt102Emulation::reportCursorPosition() { const size_t sz = 20; char tmp[sz]; - const size_t r = - snprintf(tmp, sz, "\033[%d;%dR", _currentScreen->getCursorY() + 1, - _currentScreen->getCursorX() + 1); - if (sz <= r) { - qWarning("Vt102Emulation::reportCursorPosition: Buffer too small\n"); - } + const size_t r = snprintf(tmp, sz, "\033[%d;%dR", _currentScreen->getCursorY() + 1, _currentScreen->getCursorX() + 1); + sendString(tmp); } void Vt102Emulation::reportTerminalType() { - // Primary device attribute response (Request was: ^[[0c or ^[[c (from TT321 - // Users Guide)) VT220: ^[[?63;1;2;3;6;7;8c (list deps on emul. - // capabilities) VT100: ^[[?1;2c VT101: ^[[?1;0c VT102: ^[[?6v if (getMode(MODE_Ansi)) - sendString("\033[?1;2c"); // I'm a VT100 + sendString("\033[?1;2c"); else - sendString("\033/Z"); // I'm a VT52 + sendString("\033/Z"); } void Vt102Emulation::reportSecondaryAttributes() { - // Secondary device attribute response (Request was: ^[[>0c or ^[[>c) if (getMode(MODE_Ansi)) - sendString("\033[>0;115;0c"); // Why 115? ;) + sendString("\033[>0;115;0c"); else - sendString("\033/Z"); // FIXME I don't think VT52 knows about it but kept - // for konsoles backward compatibility. + sendString("\033/Z"); } void Vt102Emulation::reportTerminalParms(int p) -// DECREPTPARM { const size_t sz = 100; char tmp[sz]; - const size_t r = - snprintf(tmp, sz, "\033[%d;1;1;112;112;1;0x", p); // not really true. - if (sz <= r) { - qWarning("Vt102Emulation::reportTerminalParms: Buffer too small\n"); - } + const size_t r = snprintf(tmp, sz, "\033[%d;1;1;112;112;1;0x", p); + sendString(tmp); } void Vt102Emulation::reportStatus() { - sendString("\033[0n"); // VT100. Device status report. 0 = Ready. + sendString("\033[0n"); } void Vt102Emulation::reportAnswerBack() { - // FIXME - Test this with VTTEST - // This is really obsolete VT100 stuff. const char *ANSWER_BACK = ""; sendString(ANSWER_BACK); } -/*! - `cx',`cy' are 1-based. - `cb' indicates the button pressed or released (0-2) or scroll event (4-5). - - eventType represents the kind of mouse action that occurred: - 0 = Mouse button press - 1 = Mouse drag - 2 = Mouse button release -*/ - void Vt102Emulation::sendMouseEvent(int cb, int cx, int cy, int eventType) { if (cx < 1 || cy < 1) return; - // With the exception of the 1006 mode, button release is encoded in cb. - // Note that if multiple extensions are enabled, the 1006 is used, so it's - // okay to check for only that. if (eventType == 2 && !getMode(MODE_Mouse1006)) cb = 3; - // normal buttons are passed as 0x20 + button, - // mouse wheel (buttons 4,5) as 0x5c + button if (cb >= 4) cb += 0x3c; - // Mouse motion handling if ((getMode(MODE_Mouse1002) || getMode(MODE_Mouse1003)) && eventType == 1) - cb += 0x20; // add 32 to signify motion event + cb += 0x20; char command[64]; command[0] = '\0'; - // Check the extensions in decreasing order of preference. Encoding the - // release event above assumes that 1006 comes first. if (getMode(MODE_Mouse1006)) { snprintf(command, sizeof(command), "\033[<%d;%d;%d%c", cb, cx, cy, eventType == 2 ? 'm' : 'M'); @@ -1598,9 +1505,6 @@ void Vt102Emulation::sendMouseEvent(int cb, int cx, int cy, int eventType) { snprintf(command, sizeof(command), "\033[%d;%d;%dM", cb + 0x20, cx, cy); } else if (getMode(MODE_Mouse1005)) { if (cx <= 2015 && cy <= 2015) { - // The xterm extension uses UTF-8 (up to 2 bytes) to encode - // coordinate+32, no matter what the locale is. We could easily - // convert manually, but QString can also do it for us. QChar coords[2]; coords[0] = static_cast(cx + 0x20); coords[1] = static_cast(cy + 0x20); @@ -1617,25 +1521,11 @@ void Vt102Emulation::sendMouseEvent(int cb, int cx, int cy, int eventType) { sendString(command); } -/** - * The focus lost event can be used by Vim (or other terminal applications) - * to recognize that the konsole window has lost focus. - * The escape sequence is also used by iTerm2. - * Vim needs the following plugin to be installed to convert the escape - * sequence into the FocusLost autocmd: https://github.com/sjl/vitality.vim - */ void Vt102Emulation::focusLost(void) { if (_reportFocusEvents) sendString("\033[O"); } -/** - * The focus gained event can be used by Vim (or other terminal applications) - * to recognize that the konsole window has gained focus again. - * The escape sequence is also used by iTerm2. - * Vim needs the following plugin to be installed to convert the escape - * sequence into the FocusGained autocmd: https://github.com/sjl/vitality.vim - */ void Vt102Emulation::focusGained(void) { if (_reportFocusEvents) sendString("\033[I"); @@ -1644,7 +1534,7 @@ void Vt102Emulation::focusGained(void) { void Vt102Emulation::sendText(const QString &text) { if (!text.isEmpty()) { QKeyEvent event(QEvent::KeyPress, 0, Qt::NoModifier, text); - sendKeyEvent(&event, false); // expose as a big fat keypress event + sendKeyEvent(&event, false); } } @@ -1652,7 +1542,6 @@ void Vt102Emulation::sendKeyEvent(QKeyEvent *event, bool fromPaste) { Qt::KeyboardModifiers modifiers = event->modifiers(); KeyboardTranslator::States states = KeyboardTranslator::NoState; - // get current states if (getMode(MODE_NewLine)) states |= KeyboardTranslator::NewLineState; if (getMode(MODE_Ansi)) @@ -1664,20 +1553,18 @@ void Vt102Emulation::sendKeyEvent(QKeyEvent *event, bool fromPaste) { if (getMode(MODE_AppKeyPad) && (modifiers & Qt::KeypadModifier)) states |= KeyboardTranslator::ApplicationKeypadState; - // check flow control state if (modifiers & KeyboardTranslator::CTRL_MOD) { switch (event->key()) { case Qt::Key_S: emit flowControlKeyPressed(true); break; case Qt::Key_Q: - case Qt::Key_C: // cancel flow control + case Qt::Key_C: emit flowControlKeyPressed(false); break; } } - // lookup key binding if (_keyTranslator) { KeyboardTranslator::Entry entry = _keyTranslator->findEntry(event->key(), modifiers, states); @@ -1708,13 +1595,8 @@ void Vt102Emulation::sendKeyEvent(QKeyEvent *event, bool fromPaste) { } #endif - // send result to terminal QByteArray textToSend; - // special handling for the Alt (aka. Meta) modifier. pressing - // Alt+[Character] results in Esc+[Character] being sent - // (unless there is an entry defined for this particular combination - // in the keyboard modifier) bool wantsAltModifier = entry.modifiers() & entry.modifierMask() & Qt::AltModifier; bool wantsMetaModifier = @@ -1738,7 +1620,6 @@ void Vt102Emulation::sendKeyEvent(QKeyEvent *event, bool fromPaste) { emit handleCommandFromKeyboard(entry.command()); } - // TODO command handling } else if (!entry.text().isEmpty()) { textToSend += entry.text(true, modifiers); } else if ((modifiers & KeyboardTranslator::CTRL_MOD) && @@ -1759,8 +1640,6 @@ void Vt102Emulation::sendKeyEvent(QKeyEvent *event, bool fromPaste) { } emit sendData(textToSend.constData(), textToSend.length()); } else { - // print an error message to the terminal if no key translator has been - // set QString translatorError = tr("No keyboard translator available. " "The information needed to convert key presses " @@ -1777,8 +1656,6 @@ void Vt102Emulation::sendKeyEvent(QKeyEvent *event, bool fromPaste) { /* */ /* ------------------------------------------------------------------------- */ -// Character Set Conversion ------------------------------------------------ -- - /* The processing contains a VT100 specific code translation layer. It's still in use and mainly responsible for the line drawing graphics. @@ -1797,12 +1674,9 @@ void Vt102Emulation::sendKeyEvent(QKeyEvent *event, bool fromPaste) { #define CHARSET _charset[_currentScreen == _screen[1]] -// Apply current character map. wchar_t Vt102Emulation::applyCharset(wchar_t c) { - // assert for i in [0..31] : vt100extended(vt100_graphics[i]) == i. const unsigned short vt100_graphics[32] = { - // 0/8 1/9 2/10 3/11 4/12 5/13 6/14 7/15 0x0020, 0x25C6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0xF800, 0xF801, 0x2500, 0xF803, 0xF804, 0x251c, 0x2524, 0x2534, @@ -1810,7 +1684,7 @@ wchar_t Vt102Emulation::applyCharset(wchar_t c) { if (CHARSET.graphic && 0x5f <= c && c <= 0x7e) return vt100_graphics[c - 0x5f]; if (CHARSET.pound && c == '#') - return 0xa3; // This mode is obsolete + return 0xa3; return c; } @@ -1831,7 +1705,7 @@ void Vt102Emulation::resetCharset(int scrno) { _charset[scrno].pound = false; } -void Vt102Emulation::setCharset(int n, int cs) // on both screens. +void Vt102Emulation::setCharset(int n, int cs) { _charset[0].charset[n & 3] = cs; useCharset(_charset[0].cu_cs); @@ -1847,7 +1721,7 @@ void Vt102Emulation::setAndUseCharset(int n, int cs) { void Vt102Emulation::useCharset(int n) { CHARSET.cu_cs = n & 3; CHARSET.graphic = (CHARSET.charset[n & 3] == '0'); - CHARSET.pound = (CHARSET.charset[n & 3] == 'A'); // This mode is obsolete + CHARSET.pound = (CHARSET.charset[n & 3] == 'A'); } void Vt102Emulation::setDefaultMargins() { @@ -1862,16 +1736,13 @@ void Vt102Emulation::setMargins(int t, int b) { void Vt102Emulation::saveCursor() { CHARSET.sa_graphic = CHARSET.graphic; - CHARSET.sa_pound = CHARSET.pound; // This mode is obsolete - // we are not clear about these - // sa_charset = charsets[cScreen->_charset]; - // sa_charset_num = cScreen->_charset; + CHARSET.sa_pound = CHARSET.pound; _currentScreen->saveCursor(); } void Vt102Emulation::restoreCursor() { CHARSET.graphic = CHARSET.sa_graphic; - CHARSET.pound = CHARSET.sa_pound; // This mode is obsolete + CHARSET.pound = CHARSET.sa_pound; _currentScreen->restoreCursor(); } @@ -1893,11 +1764,7 @@ void Vt102Emulation::restoreCursor() { We decided on the precise precise extend, somehow. */ -// "Mode" related part of the state. These are all booleans. - void Vt102Emulation::resetModes() { - // MODE_Allow132Columns is not reset here - // to match Xterm's behaviour (see Xterm's VTReset() function) resetMode(MODE_132Columns); saveMode(MODE_132Columns); @@ -2013,5 +1880,4 @@ char Vt102Emulation::eraseChar() const { void Vt102Emulation::reportDecodingError() { if (tokenBufferPos == 0 || (tokenBufferPos == 1 && (tokenBuffer[0] & 0xff) >= 32)) return; - //qDebug()<< "Undecodable sequence:" << QString::fromWCharArray(tokenBuffer, tokenBufferPos); } diff --git a/AdaptixClient/Libs/Konsole/Vt102Emulation.h b/AdaptixClient/Libs/Konsole/Vt102Emulation.h index 93aba0ff..f0ff902a 100644 --- a/AdaptixClient/Libs/Konsole/Vt102Emulation.h +++ b/AdaptixClient/Libs/Konsole/Vt102Emulation.h @@ -28,12 +28,12 @@ struct CharCodes { - char charset[4]; // - int cu_cs; // actual charset. - bool graphic; // Some VT100 tricks - bool pound ; // Some VT100 tricks - bool sa_graphic; // saved graphic - bool sa_pound; // saved pound + char charset[4]; + int cu_cs; + bool graphic; + bool pound ; + bool sa_graphic; + bool sa_pound; }; class Vt102Emulation : public Emulation @@ -87,9 +87,9 @@ private: void resetModes(); void resetTokenizer(); - #define MAX_TOKEN_LENGTH 100000 // Max length of tokens (e.g. window title) + #define MAX_TOKEN_LENGTH 100000 void addToCurrentToken(wchar_t cc); - wchar_t tokenBuffer[MAX_TOKEN_LENGTH]; //FIXME: overflow? + wchar_t tokenBuffer[MAX_TOKEN_LENGTH]; int tokenBufferPos; #define MAXARGS 15 void addDigit(int dig); @@ -140,12 +140,12 @@ private: bool _reportFocusEvents; QStringEncoder _toUtf8; - bool _isTitleChanged; ///< flag if the title/icon was changed by user + bool _isTitleChanged; QString _userTitle; - QString _iconText; // as set by: echo -en '\033]1;IconText\007 + QString _iconText; QString _nameTitle; QString _iconName; - QColor _modifiedBackground; // as set by: echo -en '\033]11;Color\007 + QColor _modifiedBackground; }; -#endif // VT102EMULATION_H +#endif diff --git a/AdaptixClient/Libs/Konsole/konsole.cpp b/AdaptixClient/Libs/Konsole/konsole.cpp index 51eab3e7..d4b17cf0 100644 --- a/AdaptixClient/Libs/Konsole/konsole.cpp +++ b/AdaptixClient/Libs/Konsole/konsole.cpp @@ -77,7 +77,6 @@ QTermWidget::QTermWidget(QWidget *messageParentWidget, QWidget *parent) connect( m_emulation, &Emulation::changeTabTextColorRequest, this, &QTermWidget::changeTabTextColorRequest); connect( m_emulation, &Emulation::cursorChanged, this, &QTermWidget::cursorChanged); - // That's OK, FilterChain's dtor takes care of UrlFilter. m_urlFilter = new UrlFilter(); connect(m_urlFilter, &UrlFilter::activated, this, &QTermWidget::urlActivated); m_terminalDisplay->filterChain()->addFilter(m_urlFilter); @@ -160,7 +159,6 @@ void QTermWidget::search(bool forwards, bool next) { new HistorySearch(m_emulation, regExp, forwards, startColumn, startLine, this); connect(historySearch, &HistorySearch::matchFound, this, [this](int startColumn, int startLine, int endColumn, int endLine){ ScreenWindow* sw = m_terminalDisplay->screenWindow(); - //qDebug() << "Scroll to" << startLine; sw->scrollTo(startLine); sw->setTrackOutput(false); sw->notifyOutputChanged(); @@ -226,13 +224,10 @@ void QTermWidget::setColorScheme(const QString& origName) { const bool isFile = QFile::exists(origName); const QString& name = isFile ? QFileInfo(origName).baseName() : origName; - // avoid legacy (int) solution if (!availableColorSchemes().contains(name)) { if (isFile) { if (ColorSchemeManager::instance()->loadCustomColorScheme(origName)) cs = ColorSchemeManager::instance()->findColorScheme(name); - else - qWarning () << Q_FUNC_INFO << "cannot load color scheme from" << origName; } if (!cs) @@ -323,7 +318,6 @@ void QTermWidget::sendKeyEvent(QKeyEvent *e) { } void QTermWidget::resizeEvent(QResizeEvent*) { - //qDebug("global window resizing...with %d %d", this->size().width(), this->size().height()); m_terminalDisplay->resize(this->size()); } @@ -335,13 +329,9 @@ void QTermWidget::updateTerminalSize() { int minLines = -1; int minColumns = -1; - // minimum number of lines and columns that views require for - // their size to be taken into consideration ( to avoid problems - // with new view widgets which haven't yet been set to their correct size ) const int VIEW_LINES_THRESHOLD = 2; const int VIEW_COLUMNS_THRESHOLD = 2; - //select largest number of lines and columns that will fit in all visible views if ( m_terminalDisplay->isHidden() == false && m_terminalDisplay->lines() >= VIEW_LINES_THRESHOLD && m_terminalDisplay->columns() >= VIEW_COLUMNS_THRESHOLD ) { @@ -349,21 +339,12 @@ void QTermWidget::updateTerminalSize() { minColumns = (minColumns == -1) ? m_terminalDisplay->columns() : qMin( minColumns , m_terminalDisplay->columns() ); } - // backend emulation must have a _terminal of at least 1 column x 1 line in size if ( minLines > 0 && minColumns > 0 ) { m_emulation->setImageSize( minLines , minColumns ); } } void QTermWidget::monitorTimerDone() { - //FIXME: The idea here is that the notification popup will appear to tell the user than output from - //the terminal has stopped and the popup will disappear when the user activates the session. - // - //This breaks with the addition of multiple views of a session. The popup should disappear - //when any of the views of the session becomes active - - - //FIXME: Make message text for this notification and the activity notification more descriptive. if (m_monitorSilence) { emit silence(); emit stateChanged(NOTIFYSILENCE); @@ -383,7 +364,6 @@ void QTermWidget::activityStateSet(int state) { } if ( m_monitorActivity ) { - //FIXME: See comments in monitorTimerDone() if (!m_notifiedActivity) { m_notifiedActivity=true; emit activity(); diff --git a/AdaptixClient/Libs/Konsole/konsole.h b/AdaptixClient/Libs/Konsole/konsole.h index 79e759ee..c38e496f 100644 --- a/AdaptixClient/Libs/Konsole/konsole.h +++ b/AdaptixClient/Libs/Konsole/konsole.h @@ -49,7 +49,6 @@ public: void setTerminalBackgroundVideo(const QString& backgroundVideo); void setTerminalBackgroundMode(int mode); - /// Text codec, default is UTF-8 void setTextCodec(QStringEncoder codec); void setColorScheme(const QString & name); diff --git a/AdaptixClient/Libs/Konsole/util/ColorScheme.cpp b/AdaptixClient/Libs/Konsole/util/ColorScheme.cpp index 7f9beb9e..d0ab8b18 100644 --- a/AdaptixClient/Libs/Konsole/util/ColorScheme.cpp +++ b/AdaptixClient/Libs/Konsole/util/ColorScheme.cpp @@ -318,8 +318,6 @@ void ColorScheme::readColorEntry(QSettings *s, int index) { } } if (!ok) { - qWarning().nospace() << "Invalid color value " << colorStr << " for " - << colorName << ". Fallback to black."; r = g = b = 0; } entry.color = QColor(r, g, b); @@ -516,7 +514,6 @@ const ColorScheme *ColorSchemeManager::findColorScheme(const QString &name) { if (_colorSchemes.contains(name)) { return _colorSchemes[name]; } else { - // look for this color scheme QString path = findColorSchemePath(name); if (!path.isEmpty() && loadColorScheme(path)) { return findColorScheme(name); diff --git a/AdaptixClient/Libs/Konsole/util/KeyboardTranslator.cpp b/AdaptixClient/Libs/Konsole/util/KeyboardTranslator.cpp index afef5ea2..826744fe 100644 --- a/AdaptixClient/Libs/Konsole/util/KeyboardTranslator.cpp +++ b/AdaptixClient/Libs/Konsole/util/KeyboardTranslator.cpp @@ -732,7 +732,7 @@ KeyboardTranslator::Entry KeyboardTranslator::findEntry(int keyCode, Qt::Keyboar if (it.value().matches(keyCode, modifiers, state)) return *it; } - return Entry(); // entry not found + return Entry(); } void KeyboardTranslatorManager::addTranslator(KeyboardTranslator *translator) { @@ -745,7 +745,6 @@ void KeyboardTranslatorManager::addTranslator(KeyboardTranslator *translator) { bool KeyboardTranslatorManager::deleteTranslator(const QString &name) { Q_ASSERT(_translators.contains(name)); - // locate and delete QString path = findTranslatorPath(name); if (QFile::remove(path)) { _translators.remove(name); diff --git a/AdaptixClient/Libs/Konsole/util/utf8proc.c b/AdaptixClient/Libs/Konsole/util/utf8proc.c index ccf0c102..a24272e7 100644 --- a/AdaptixClient/Libs/Konsole/util/utf8proc.c +++ b/AdaptixClient/Libs/Konsole/util/utf8proc.c @@ -16967,9 +16967,6 @@ static const utf8proc_uint16_t utf8proc_combinations[] = { 74, 77, 1, 53694, 1, 53696, }; - -//////////////////////////////////////////////////////////////////////// - UTF8PROC_DLLEXPORT const utf8proc_int8_t utf8proc_utf8class[256] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -17049,18 +17046,17 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_iterate( *dst = uc; return 1; } - // Must be between 0xc2 and 0xf4 inclusive to be valid + if ((utf8proc_uint32_t)(uc - 0xc2) > (0xf4-0xc2)) return UTF8PROC_ERROR_INVALIDUTF8; - if (uc < 0xe0) { // 2-byte sequence - // Must have valid continuation character + if (uc < 0xe0) { + if (str >= end || !utf_cont(*str)) return UTF8PROC_ERROR_INVALIDUTF8; *dst = ((uc & 0x1f)<<6) | (*str & 0x3f); return 2; } - if (uc < 0xf0) { // 3-byte sequence + if (uc < 0xf0) { if ((str + 1 >= end) || !utf_cont(*str) || !utf_cont(str[1])) return UTF8PROC_ERROR_INVALIDUTF8; - // Check for surrogate chars if (uc == 0xed && *str > 0x9f) return UTF8PROC_ERROR_INVALIDUTF8; uc = ((uc & 0xf)<<12) | ((*str & 0x3f)<<6) | (str[1] & 0x3f); @@ -17069,11 +17065,8 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_iterate( *dst = uc; return 3; } - // 4-byte sequence - // Must have 3 valid continuation characters if ((str + 2 >= end) || !utf_cont(*str) || !utf_cont(str[1]) || !utf_cont(str[2])) return UTF8PROC_ERROR_INVALIDUTF8; - // Make sure in correct range (0x10000 - 0x10ffff) if (uc == 0xf0) { if (*str < 0x90) return UTF8PROC_ERROR_INVALIDUTF8; } else if (uc == 0xf4) { @@ -17097,8 +17090,6 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_encode_char(utf8proc_int32_t uc, ut dst[0] = (utf8proc_uint8_t)(0xC0 + (uc >> 6)); dst[1] = (utf8proc_uint8_t)(0x80 + (uc & 0x3F)); return 2; - // Note: we allow encoding 0xd800-0xdfff here, so as not to change - // the API, however, these are actually invalid in UTF-8 } else if (uc < 0x10000) { dst[0] = (utf8proc_uint8_t)(0xE0 + (uc >> 12)); dst[1] = (utf8proc_uint8_t)(0x80 + ((uc >> 6) & 0x3F)); @@ -17158,32 +17149,32 @@ UTF8PROC_DLLEXPORT const utf8proc_property_t *utf8proc_get_property(utf8proc_int static utf8proc_bool grapheme_break_simple(int lbc, int tbc) { return - (lbc == UTF8PROC_BOUNDCLASS_START) ? true : // GB1 - (lbc == UTF8PROC_BOUNDCLASS_CR && // GB3 - tbc == UTF8PROC_BOUNDCLASS_LF) ? false : // --- - (lbc >= UTF8PROC_BOUNDCLASS_CR && lbc <= UTF8PROC_BOUNDCLASS_CONTROL) ? true : // GB4 - (tbc >= UTF8PROC_BOUNDCLASS_CR && tbc <= UTF8PROC_BOUNDCLASS_CONTROL) ? true : // GB5 - (lbc == UTF8PROC_BOUNDCLASS_L && // GB6 - (tbc == UTF8PROC_BOUNDCLASS_L || // --- - tbc == UTF8PROC_BOUNDCLASS_V || // --- - tbc == UTF8PROC_BOUNDCLASS_LV || // --- - tbc == UTF8PROC_BOUNDCLASS_LVT)) ? false : // --- - ((lbc == UTF8PROC_BOUNDCLASS_LV || // GB7 - lbc == UTF8PROC_BOUNDCLASS_V) && // --- - (tbc == UTF8PROC_BOUNDCLASS_V || // --- - tbc == UTF8PROC_BOUNDCLASS_T)) ? false : // --- - ((lbc == UTF8PROC_BOUNDCLASS_LVT || // GB8 - lbc == UTF8PROC_BOUNDCLASS_T) && // --- - tbc == UTF8PROC_BOUNDCLASS_T) ? false : // --- - (tbc == UTF8PROC_BOUNDCLASS_EXTEND || // GB9 - tbc == UTF8PROC_BOUNDCLASS_ZWJ || // --- - tbc == UTF8PROC_BOUNDCLASS_SPACINGMARK || // GB9a - lbc == UTF8PROC_BOUNDCLASS_PREPEND) ? false : // GB9b - (lbc == UTF8PROC_BOUNDCLASS_E_ZWG && // GB11 (requires additional handling below) - tbc == UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC) ? false : // ---- - (lbc == UTF8PROC_BOUNDCLASS_REGIONAL_INDICATOR && // GB12/13 (requires additional handling below) - tbc == UTF8PROC_BOUNDCLASS_REGIONAL_INDICATOR) ? false : // ---- - true; // GB999 + (lbc == UTF8PROC_BOUNDCLASS_START) ? true : + (lbc == UTF8PROC_BOUNDCLASS_CR && + tbc == UTF8PROC_BOUNDCLASS_LF) ? false : + (lbc >= UTF8PROC_BOUNDCLASS_CR && lbc <= UTF8PROC_BOUNDCLASS_CONTROL) ? true : + (tbc >= UTF8PROC_BOUNDCLASS_CR && tbc <= UTF8PROC_BOUNDCLASS_CONTROL) ? true : + (lbc == UTF8PROC_BOUNDCLASS_L && + (tbc == UTF8PROC_BOUNDCLASS_L || + tbc == UTF8PROC_BOUNDCLASS_V || + tbc == UTF8PROC_BOUNDCLASS_LV || + tbc == UTF8PROC_BOUNDCLASS_LVT)) ? false : + ((lbc == UTF8PROC_BOUNDCLASS_LV || + lbc == UTF8PROC_BOUNDCLASS_V) && + (tbc == UTF8PROC_BOUNDCLASS_V || + tbc == UTF8PROC_BOUNDCLASS_T)) ? false : + ((lbc == UTF8PROC_BOUNDCLASS_LVT || + lbc == UTF8PROC_BOUNDCLASS_T) && + tbc == UTF8PROC_BOUNDCLASS_T) ? false : + (tbc == UTF8PROC_BOUNDCLASS_EXTEND || + tbc == UTF8PROC_BOUNDCLASS_ZWJ || + tbc == UTF8PROC_BOUNDCLASS_SPACINGMARK || + lbc == UTF8PROC_BOUNDCLASS_PREPEND) ? false : + (lbc == UTF8PROC_BOUNDCLASS_E_ZWG && + tbc == UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC) ? false : + (lbc == UTF8PROC_BOUNDCLASS_REGIONAL_INDICATOR && + tbc == UTF8PROC_BOUNDCLASS_REGIONAL_INDICATOR) ? false : + true; } static utf8proc_bool grapheme_break_extended(int lbc, int tbc, int licb, int ticb, utf8proc_int32_t *state) @@ -17195,13 +17186,13 @@ static utf8proc_bool grapheme_break_extended(int lbc, int tbc, int licb, int tic state_icb = licb == UTF8PROC_INDIC_CONJUNCT_BREAK_CONSONANT ? licb : UTF8PROC_INDIC_CONJUNCT_BREAK_NONE; } else { /* lbc and licb are already encoded in *state */ - state_bc = *state & 0xff; // 1st byte of state is bound class - state_icb = *state >> 8; // 2nd byte of state is indic conjunct break + state_bc = *state & 0xff; + state_icb = *state >> 8; } utf8proc_bool break_permitted = grapheme_break_simple(state_bc, tbc) && !(state_icb == UTF8PROC_INDIC_CONJUNCT_BREAK_LINKER - && ticb == UTF8PROC_INDIC_CONJUNCT_BREAK_CONSONANT); // GB9c + && ticb == UTF8PROC_INDIC_CONJUNCT_BREAK_CONSONANT); if (ticb == UTF8PROC_INDIC_CONJUNCT_BREAK_CONSONANT || state_icb == UTF8PROC_INDIC_CONJUNCT_BREAK_CONSONANT @@ -17213,12 +17204,12 @@ static utf8proc_bool grapheme_break_extended(int lbc, int tbc, int licb, int tic if (state_bc == tbc && tbc == UTF8PROC_BOUNDCLASS_REGIONAL_INDICATOR) state_bc = UTF8PROC_BOUNDCLASS_OTHER; - // Special support for GB11 (emoji extend* zwj / emoji) + else if (state_bc == UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC) { - if (tbc == UTF8PROC_BOUNDCLASS_EXTEND) // fold EXTEND codepoints into emoji + if (tbc == UTF8PROC_BOUNDCLASS_EXTEND) state_bc = UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC; else if (tbc == UTF8PROC_BOUNDCLASS_ZWJ) - state_bc = UTF8PROC_BOUNDCLASS_E_ZWG; // state to record emoji+zwg combo + state_bc = UTF8PROC_BOUNDCLASS_E_ZWG; else state_bc = tbc; } diff --git a/AdaptixClient/Resources/Resources.qrc b/AdaptixClient/Resources/Resources.qrc index 5ec1011f..523966fe 100755 --- a/AdaptixClient/Resources/Resources.qrc +++ b/AdaptixClient/Resources/Resources.qrc @@ -36,6 +36,7 @@ + icons/code_blocks_64dp.png icons/headphones_64dp.png icons/notes_64dp.png icons/format_list_bulleted_64dp.png @@ -61,8 +62,8 @@ icons/arrow_right_alt_64dp.png icons/storage_64dp.png - icons/archive_64dp.png - icons/unarchive_64dp.png + icons/file_open_64dp.png + icons/save_as_64dp.png icons/start_64dp.png icons/restart_64dp.png icons/stop_64dp.png diff --git a/AdaptixClient/Resources/icons/archive_64dp.png b/AdaptixClient/Resources/icons/archive_64dp.png deleted file mode 100644 index 78925457..00000000 Binary files a/AdaptixClient/Resources/icons/archive_64dp.png and /dev/null differ diff --git a/AdaptixClient/Resources/icons/code_blocks_64dp.png b/AdaptixClient/Resources/icons/code_blocks_64dp.png new file mode 100644 index 00000000..69783605 Binary files /dev/null and b/AdaptixClient/Resources/icons/code_blocks_64dp.png differ diff --git a/AdaptixClient/Resources/icons/file_open_64dp.png b/AdaptixClient/Resources/icons/file_open_64dp.png new file mode 100644 index 00000000..f78a8852 Binary files /dev/null and b/AdaptixClient/Resources/icons/file_open_64dp.png differ diff --git a/AdaptixClient/Resources/icons/save_as_64dp.png b/AdaptixClient/Resources/icons/save_as_64dp.png new file mode 100644 index 00000000..692f6f97 Binary files /dev/null and b/AdaptixClient/Resources/icons/save_as_64dp.png differ diff --git a/AdaptixClient/Resources/icons/unarchive_64dp.png b/AdaptixClient/Resources/icons/unarchive_64dp.png deleted file mode 100644 index f2c99693..00000000 Binary files a/AdaptixClient/Resources/icons/unarchive_64dp.png and /dev/null differ diff --git a/AdaptixClient/Resources/themes/dark.qss b/AdaptixClient/Resources/themes/dark.qss index a1b95766..73d43177 100644 --- a/AdaptixClient/Resources/themes/dark.qss +++ b/AdaptixClient/Resources/themes/dark.qss @@ -118,6 +118,7 @@ QTableWidget::item { QTableWidget::item:selected { background-color: #4A4A4A; color: #FFFFFF; + outline: none; } QTableWidget:disabled { @@ -334,6 +335,20 @@ QProgressBar:disabled { +QScrollArea { + background-color: #2A2A2A; + border: 1px solid #4A4A4A; + border-radius: 4px; +} + +QScrollArea:disabled { + background-color: #1A1A1A; + border: 1px solid #3A3A3A; + color: #4A4A4A; +} + + + QScrollBar:horizontal { border: none; background: #2A2A2A; @@ -737,7 +752,7 @@ QListWidget { } QListWidget::item { - padding: 4px; + padding: 0px; border-radius: 4px; color: #E0E0E0; } @@ -751,6 +766,7 @@ QListWidget::item:selected { background-color: #4A4A4A; color: #FFFFFF; border: 1px solid #5A5A5A; + margin: 1px; } QListWidget:focus { diff --git a/AdaptixClient/Resources/themes/dracula.qss b/AdaptixClient/Resources/themes/dracula.qss index e1a65a4a..41761a4a 100644 --- a/AdaptixClient/Resources/themes/dracula.qss +++ b/AdaptixClient/Resources/themes/dracula.qss @@ -294,6 +294,18 @@ QProgressBar:disabled { border: 1px solid #44475a; } +QScrollArea { + background-color: #44475a; + border: 1px solid #6272a4; + border-radius: 4px; +} + +QScrollArea:disabled { + background-color: #343746; + border: 1px solid #44475a; + color: #6272a4; +} + QScrollBar:horizontal { border: none; background: #44475a; @@ -662,7 +674,7 @@ QListWidget { } QListWidget::item { - padding: 4px; + padding: 2px; border-radius: 4px; color: #f8f8f2; } @@ -676,6 +688,7 @@ QListWidget::item:selected { background-color: #6272a4; color: #ffffff; border: 1px solid #bd93f9; + margin: 1px; } QListWidget:focus { diff --git a/AdaptixClient/Resources/themes/light_arc.qss b/AdaptixClient/Resources/themes/light_arc.qss index 44f7d827..e3b02bab 100644 --- a/AdaptixClient/Resources/themes/light_arc.qss +++ b/AdaptixClient/Resources/themes/light_arc.qss @@ -329,6 +329,18 @@ QProgressBar:disabled { +QScrollArea { + background-color: #FFFFFF; + border: 1px solid #D0D0D0; + border-radius: 4px; +} + +QScrollArea:disabled { + background-color: #E8E8E8; + border: 1px solid #E0E0E0; + color: #A0A0A0; +} + QScrollBar:horizontal { @@ -429,7 +441,6 @@ QGroupBox { QGroupBox::title { subcontrol-origin: margin; - subcontrol-position: top center; padding: 0 3px; background-color: #FFFFFF; color: #2E3440; @@ -440,6 +451,7 @@ QGroupBox::title { + QCheckBox { spacing: 5px; color: #2E3440; @@ -498,7 +510,7 @@ QListWidget { } QListWidget::item { - padding: 4px; + padding: 2px; /*border-radius: 4px;*/ background-color: #FFFFFF; color: #2E3440; @@ -512,7 +524,8 @@ QListWidget::item:hover { QListWidget::item:selected { background-color: #4A566A; color: #FFFFFF; - border: 1px solid #D0D0D0; + border: 1px solid #D0D0D0; + margin: 1px; } QListWidget:focus { diff --git a/AdaptixClient/Source/Agent/Agent.cpp b/AdaptixClient/Source/Agent/Agent.cpp index 1f48ee45..e848723b 100644 --- a/AdaptixClient/Source/Agent/Agent.cpp +++ b/AdaptixClient/Source/Agent/Agent.cpp @@ -19,7 +19,6 @@ Agent::Agent(QJsonObject jsonObjAgentData, AdaptixWidget* w) this->data.Id = jsonObjAgentData["a_id"].toString(); this->data.Name = jsonObjAgentData["a_name"].toString(); this->data.Listener = jsonObjAgentData["a_listener"].toString(); - this->data.Async = jsonObjAgentData["a_async"].toBool(); this->data.ExternalIP = jsonObjAgentData["a_external_ip"].toString(); this->data.InternalIP = jsonObjAgentData["a_internal_ip"].toString(); @@ -54,8 +53,9 @@ Agent::Agent(QJsonObject jsonObjAgentData, AdaptixWidget* w) for ( auto listenerData : this->adaptixWidget->Listeners) { if ( listenerData.ListenerName == this->data.Listener ) { - QString listenerType = listenerData.ListenerType.split("/")[0]; - if (listenerType == "internal") + QStringList parts = listenerData.ListenerFullName.split("/"); + this->listenerType = parts[2]; + if (parts[0] == "internal") this->connType = "internal"; else this->connType = "external"; @@ -123,26 +123,20 @@ Agent::Agent(QJsonObject jsonObjAgentData, AdaptixWidget* w) } auto regAgnet = this->adaptixWidget->GetRegAgent(data.Name, data.Listener, data.Os); - this->browsers = regAgnet.browsers; - this->Console = new ConsoleWidget(this, regAgnet.commander); - - if (this->browsers.FileBrowser) - this->FileBrowser = new BrowserFilesWidget(this); - - if (this->browsers.ProcessBrowser) - this->ProcessBrowser = new BrowserProcessWidget(this); - - if (this->browsers.RemoteTerminal) - this->Terminal = new TerminalWidget(this, adaptixWidget); + this->commander = regAgnet.commander; + this->Console = new ConsoleWidget(adaptixWidget, this, regAgnet.commander); + this->FileBrowser = new BrowserFilesWidget(this); + this->ProcessBrowser = new BrowserProcessWidget(this); + this->Terminal = new TerminalWidget(this, adaptixWidget); } Agent::~Agent() = default; void Agent::Update(QJsonObject jsonObjAgentData) { - int old_Sleep = this->data.Sleep; - int old_Jitter = this->data.Jitter; + int old_Sleep = this->data.Sleep; + int old_Jitter = this->data.Jitter; QString old_Color = this->data.Color; this->data.Sleep = jsonObjAgentData["a_sleep"].toDouble(); @@ -336,11 +330,9 @@ void Agent::UpdateImage() } else { if (data.Elevated) { - // this->item_Os->setIcon(QIcon(":/icons/unknown_red")); this->imageActive = QImage(":/graph/"+v+"/unknown_red"); } else { - // this->item_Os->setIcon(QIcon(":/icons/unknown_blue")); this->imageActive = QImage(":/graph/"+v+"/unknown_blue"); } this->imageInactive = QImage(":/graph/"+v+"/unknown_grey"); @@ -354,11 +346,11 @@ void Agent::UpdateImage() /// TASK -QString Agent::TasksStop(const QStringList &tasks) const +QString Agent::TasksCancel(const QStringList &tasks) const { QString message = QString(); bool ok = false; - bool result = HttpReqTaskStop( data.Id, tasks, *(adaptixWidget->GetProfile()), &message, &ok); + bool result = HttpReqTaskCancel( data.Id, tasks, *(adaptixWidget->GetProfile()), &message, &ok); if (!result) return "Response timeout"; @@ -394,10 +386,7 @@ void Agent::UnsetParent(const PivotData &pivotData) this->item_Last->setText( QString::fromUtf8("\u221E \u221E") ); } -void Agent::AddChild(const PivotData &pivotData) -{ - this->childsId.push_back(pivotData.ChildAgentId); -} +void Agent::AddChild(const PivotData &pivotData) { this->childsId.push_back(pivotData.ChildAgentId); } void Agent::RemoveChild(const PivotData &pivotData) { @@ -408,60 +397,3 @@ void Agent::RemoveChild(const PivotData &pivotData) } } } - -/// BROWSER - -QString Agent::BrowserDisks() const -{ - QString message = QString(); - bool ok = false; - bool result = HttpReqBrowserDisks( data.Id, *(adaptixWidget->GetProfile()), &message, &ok); - if (!result) - return "Response timeout"; - - return message; -} - -QString Agent::BrowserProcess() const -{ - QString message = QString(); - bool ok = false; - bool result = HttpReqBrowserProcess( data.Id, *(adaptixWidget->GetProfile()), &message, &ok); - if (!result) - return "Response timeout"; - - return message; -} - -QString Agent::BrowserList(const QString &path) const -{ - QString message = QString(); - bool ok = false; - bool result = HttpReqBrowserList( data.Id, path, *(adaptixWidget->GetProfile()), &message, &ok); - if (!result) - return "Response timeout"; - - return message; -} - -QString Agent::BrowserUpload(const QString &path, const QString &content) const -{ - QString message = QString(); - bool ok = false; - bool result = HttpReqBrowserUpload( data.Id, path, content, *(adaptixWidget->GetProfile()), &message, &ok); - if (!result) - return "Response timeout"; - - return message; -} - -QString Agent::BrowserDownload(const QString &path) const -{ - QString message = QString(); - bool ok = false; - bool result = HttpReqDownloadStart( data.Id, path, *(adaptixWidget->GetProfile()), &message, &ok); - if (!result) - return "Response timeout"; - - return message; -} diff --git a/AdaptixClient/Source/Agent/Commander.cpp b/AdaptixClient/Source/Agent/Commander.cpp index 23852508..43ecfc1c 100644 --- a/AdaptixClient/Source/Agent/Commander.cpp +++ b/AdaptixClient/Source/Agent/Commander.cpp @@ -1,4 +1,5 @@ #include +#include QString serializeParam(const QString &token) { @@ -30,35 +31,35 @@ QStringList unserializeParams(const QString &commandline) continue; } - // If we encounter a double quote + /* If we encounter a double quote */ if (c == '"') { inQuotes = !inQuotes; ++i; continue; } - // If we encounter a backslash, handle escape sequences + /* If we encounter a backslash, handle escape sequences */ if (c == '\\') { int numBS = 0; - // Count the number of consecutive backslashes + /*Count the number of consecutive backslashes*/ while (i < len && commandline[i] == '\\') { ++numBS; ++i; } - // Check if the next character is a double quote + /*Check if the next character is a double quote*/ if (i < len && commandline[i] == '"') { - // Append half the number of backslashes (integer division) + /*Append half the number of backslashes (integer division)*/ token.append(QString(numBS / 2, '\\')); if (numBS % 2 == 0) { - // Even number of backslashes: the quote is not escaped, so it toggles the quote state + /*Even number of backslashes: the quote is not escaped, so it toggles the quote state*/ inQuotes = !inQuotes; } else { - // Odd number of backslashes: the quote is escaped, add it to the token + /*Odd number of backslashes: the quote is escaped, add it to the token*/ token.append('"'); } ++i; } else { - // No double quote after backslashes: all backslashes are literal + /*No double quote after backslashes: all backslashes are literal*/ token.append(QString(numBS, '\\')); } continue; @@ -74,705 +75,274 @@ QStringList unserializeParams(const QString &commandline) return tokens; } -void BofPacker::Pack(const QString &type, const QJsonValue &jsonValue) + + +Commander::Commander() { - if (type == "CSTR") { - if (!jsonValue.isString()) - return; - - QByteArray valueData = jsonValue.toString().toUtf8(); - - if (valueData.size() == 0) { - QByteArray valueLengthData; - int strLength = 0; - valueLengthData.append(reinterpret_cast(&strLength), 4); - - data.append(valueLengthData); - } - else { - valueData.append('\0'); - - QByteArray valueLengthData; - int strLength = valueData.size(); - valueLengthData.append(reinterpret_cast(&strLength), 4); - - data.append(valueLengthData); - data.append(valueData); - } - } - else if (type == "WSTR") { - if (!jsonValue.isString()) - return; - - QString str = jsonValue.toString(); - - if (str.size() == 0) { - QByteArray valueLengthData; - int strLength = 0; - valueLengthData.append(reinterpret_cast(&strLength), 4); - - data.append(valueLengthData); - } - else { - const char16_t* utf16Data = reinterpret_cast(str.utf16()); - int utf16Length = str.size() + 1; - - QByteArray strData; - strData.append(reinterpret_cast(utf16Data), utf16Length * sizeof(char16_t)); - - QByteArray strLengthData; - int strLength = utf16Length * sizeof(char16_t); - strLengthData.append(reinterpret_cast(&strLength), 4); - - data.append(strLengthData); - data.append(strData); - } - } - else if (type == "INT") { - if (jsonValue.isString()) { - bool ok; - int num = jsonValue.toString().toInt(&ok); - if (!ok) - return; - - QByteArray numData; - numData.append(reinterpret_cast(&num), sizeof(num)); - data.append(numData); - } - else if (jsonValue.isDouble()) { - int num = jsonValue.toDouble(); - QByteArray numData; - numData.append(reinterpret_cast(&num), sizeof(num)); - data.append(numData); - } - else if (jsonValue.isBool()) { - int num = jsonValue.toBool(); - QByteArray numData; - numData.append(reinterpret_cast(&num), sizeof(num)); - data.append(numData); - } - } - else if (type == "SHORT") { - if (jsonValue.isString()) { - bool ok; - short num = jsonValue.toString().toShort(&ok); - if (!ok) - return; - - QByteArray numData; - numData.append(reinterpret_cast(&num), sizeof(num)); - data.append(numData); - } - else if (jsonValue.isDouble()) { - short num = jsonValue.toDouble(); - QByteArray numData; - numData.append(reinterpret_cast(&num), sizeof(num)); - data.append(numData); - } - else if (jsonValue.isBool()) { - short num = jsonValue.toBool(); - QByteArray numData; - numData.append(reinterpret_cast(&num), sizeof(num)); - data.append(numData); - } - } - else if (type == "BYTES") { - if (!jsonValue.isString()) - return; - - QByteArray bytes = QByteArray::fromBase64(jsonValue.toString().toUtf8()); - - QByteArray bytesLengthData; - int bytesLength = bytes.size(); - bytesLengthData.append(reinterpret_cast(&bytesLength), 4); - - data.append(bytesLengthData); - if (bytesLength > 0) - data.append(bytes); - } + regCommandsGroup = {}; + axCommandsGroup = {}; } -QString BofPacker::Build() const -{ - QByteArray strLengthData; - int strLength = data.size(); - strLengthData.append(reinterpret_cast(&strLength), sizeof(strLength)); - - strLengthData.append(data); - return strLengthData.toBase64(); -} - - - -Commander::Commander(){} - Commander::~Commander() = default; -bool Commander::AddRegCommands(const QByteArray &jsonData) +void Commander::AddRegCommands(const CommandsGroup &group) { regCommandsGroup = group; } + +void Commander::AddAxCommands(const CommandsGroup &group) { - QList commandsList; - - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(jsonData, &parseError); - - QJsonArray commandArray = document.array(); - for (QJsonValue value : commandArray) { - - QJsonObject jsonObject = value.toObject(); - Command cmd = this->ParseCommand(jsonObject); - commandsList.append(cmd); - } - - commands = commandsList; - return true; + axCommandsGroup.append(group); + emit commandsUpdated(); } -bool Commander::AddExtModule(const QString &filepath, const QString &extName, QList extCommands, QList extConstants) +void Commander::RemoveAxCommands(const QString &filepath) { - QList commandsList; - - for (QJsonObject jsonObject : extCommands) { - Command extCmd = this->ParseCommand(jsonObject); - commandsList.append(extCmd); - } - - QMap constantList; - for (QJsonObject jsonObject : extConstants) { - Constant constant = this->ParseConstant(jsonObject); - constantList[constant.Name] = constant; - } - - ExtModule extMod = {extName, filepath, commandsList, constantList}; - extModules[filepath] = extMod; - return true; -} - -void Commander::RemoveExtModule(const QString &filepath) -{ - extModules.remove(filepath); -} - - - -Constant Commander::ParseConstant(QJsonObject jsonObject) -{ - Constant constant; - constant.Name = jsonObject["name"].toString(); - - if (jsonObject.contains("map")) { - QJsonObject mapObject = jsonObject["map"].toObject(); - for ( auto it = mapObject.begin(); it != mapObject.end(); ++it ) - constant.Map[it.key()] = it.value().toString(); - } - return constant; -} - -Command Commander::ParseCommand(QJsonObject jsonObject) -{ - Command cmd; - cmd.name = jsonObject["command"].toString(); - cmd.message = jsonObject["message"].toString(); - cmd.description = jsonObject["description"].toString(); - cmd.example = jsonObject["example"].toString(); - cmd.exec = jsonObject["exec"].toString(); - - if (jsonObject.contains("subcommands")) { - QJsonArray subcommandArray = jsonObject["subcommands"].toArray(); - for (QJsonValue subCmdVal : subcommandArray) { - QJsonObject subCmdObj = subCmdVal.toObject(); - - Command subCmd; - subCmd.name = subCmdObj["name"].toString(); - subCmd.message = subCmdObj["message"].toString(); - subCmd.description = subCmdObj["description"].toString(); - subCmd.example = subCmdObj["example"].toString(); - subCmd.exec = subCmdObj["exec"].toString(); - - QJsonArray subArgsArray = subCmdObj["args"].toArray(); - for (QJsonValue subArgVal : subArgsArray) { - Argument subArg = ParseArgument(subArgVal.toString()); - if (subArg.valid) - subCmd.args.append(subArg); - } - cmd.subcommands.append(subCmd); - } - } else if (jsonObject.contains("args")) { - - QJsonArray argsArray = jsonObject["args"].toArray(); - for (QJsonValue argVal : argsArray) { - Argument arg = ParseArgument(argVal.toString()); - if (arg.valid) - cmd.args.append(arg); + for (int i = 0; i < axCommandsGroup.size(); ++i) { + if (axCommandsGroup[i].filepath == filepath) { + axCommandsGroup.removeAt(i); + i--; } } - return cmd; + emit commandsUpdated(); } -Argument Commander::ParseArgument(const QString &argString) +CommanderResult Commander::ProcessInput(QString agentId, QString cmdline) { - Argument arg = {0}; - QRegularExpression regex(R"((\w+)\s+([\[\<][^\s\]]+[\s\w-]*[\>\]])(\s*\([^\)]*\))?(?:\s+\{([\s\S]+)\})?)"); - QRegularExpressionMatch match = regex.match(argString); - - if ( !match.hasMatch()) { - error = "arguments not parsed"; - arg.valid = false; - return arg; - } - - arg.type = match.captured(1); - QString flagAndValue = match.captured(2).trimmed(); - QString defaultValue = match.captured(3).trimmed(); - arg.description = match.captured(4).trimmed(); - - if( !defaultValue.isEmpty() ) { - arg.defaultUsed = true; - arg.defaultValue = defaultValue.mid(1, defaultValue.size() - 2).trimmed(); - } - - if (flagAndValue.startsWith("<") && flagAndValue.endsWith(">")) { - arg.required = true; - } - else if (flagAndValue.startsWith("[") && flagAndValue.endsWith("]")) { - arg.required = false; - } - else { - error = "argument must be in <> or []"; - arg.valid = false; - return arg; - } - - int spaceIndex = flagAndValue.indexOf(' '); - if (spaceIndex != -1) { - arg.mark = flagAndValue.mid(1, spaceIndex - 1).trimmed(); - arg.name = flagAndValue.mid(spaceIndex + 1, flagAndValue.size() - spaceIndex - 2).trimmed(); - arg.flag = true; - } - else { - QString value = flagAndValue.mid(1, flagAndValue.size() - 2).trimmed(); - if( value.startsWith("-") || value.startsWith("/") ) { - arg.mark = value; - arg.flag = true; - } - else { - arg.name = value; - } - } - arg.valid = true; - return arg; -} - -CommanderResult Commander::ProcessInput(AgentData agentData, QString input) -{ - QStringList parts = unserializeParams(input); + QStringList parts = unserializeParams(cmdline); if (parts.isEmpty()) - return CommanderResult{true, "", false}; + return CommanderResult{false, true, "", {}, false, {}}; QString commandName = parts[0]; parts.removeAt(0); - if( commandName == "help") { + if( commandName == "help") return this->ProcessHelp(parts); - } - for (Command command : commands) { - if (command.name == commandName) { - return ProcessCommand(agentData, command, parts, {}); - } - } - - for ( auto extMod : extModules ) { - for (Command command : extMod.Commands) { + for ( auto script_group : axCommandsGroup ) { + for (Command command : script_group.commands) { if (command.name == commandName) { - return ProcessCommand(agentData, command, parts, extMod); + QJsonObject jsonObj; + jsonObj["command"] = command.name; + + if ( command.subcommands.isEmpty() ) { + + auto cmdResult = ProcessCommand(command, parts, jsonObj); + if ( !cmdResult.output && command.is_pre_hook) { + QString hook_result = ProcessPreHook(script_group.engine, command, agentId, cmdline, cmdResult.data, parts); + if (hook_result.isEmpty()) { + return CommanderResult{false, false, "", {}, true, {} }; + } else { + cmdResult.output = true; + cmdResult.error = true; + cmdResult.message = hook_result; + } + } + return cmdResult; + + } + else { + if ( parts.isEmpty() ) + return CommanderResult{true, true, "Subcommand must be set", {}, false, {}}; + + QString subCommandName = parts[0]; + parts.removeAt(0); + + for (Command subcommand : command.subcommands) { + if (subCommandName == subcommand.name) { + jsonObj["subcommand"] = subcommand.name; + + auto cmdResult = ProcessCommand(subcommand, parts, jsonObj); + if ( !cmdResult.output && subcommand.is_pre_hook) { + QString hook_result = ProcessPreHook(script_group.engine, subcommand, agentId, cmdline, cmdResult.data, parts); + if (hook_result.isEmpty()) { + return CommanderResult{false, false, "", {}, true, {} }; + } else { + cmdResult.output = true; + cmdResult.error = true; + cmdResult.message = hook_result; + } + } + return cmdResult; + } + } + return CommanderResult{true, true, "Subcommand not found", {}, false, {}}; + } } } } - return CommanderResult{true, "Command not found", true}; -} + for (Command command : regCommandsGroup.commands) { + if (command.name == commandName) { + QJsonObject jsonObj; + jsonObj["command"] = command.name; -CommanderResult Commander::ProcessCommand(AgentData agentData, Command command, QStringList args, ExtModule extMod) -{ - QString execStr = ""; - QList execArgs; + if ( command.subcommands.isEmpty() ) { - QJsonObject jsonObj; - jsonObj["command"] = command.name; - - if ( command.subcommands.size() == 0 ) { - - QMap parsedArgsMap; - - QString wideKey; - for (int i = 0; i < args.size(); ++i) { - QString arg = args[i]; - - bool isWideArgs = true; - - for (Argument commandArg : command.args) { - if (commandArg.flag) { - if (commandArg.type == "BOOL" && commandArg.mark == arg ) { - parsedArgsMap[commandArg.mark] = "true"; - wideKey = commandArg.mark; - isWideArgs = false; - break; - } - else if ( commandArg.mark == arg && args.size() > i+1 ) { - ++i; - parsedArgsMap[commandArg.name] = args[i]; - wideKey = commandArg.name; - isWideArgs = false; - break; + auto cmdResult = ProcessCommand(command, parts, jsonObj); + if ( !cmdResult.output && command.is_pre_hook) { + QString hook_result = ProcessPreHook(regCommandsGroup.engine, command, agentId, cmdline, cmdResult.data, parts); + if (hook_result.isEmpty()) { + return CommanderResult{false, false, "", {}, true, {} }; + } else { + cmdResult.output = true; + cmdResult.error = true; + cmdResult.message = hook_result; } } - else if (!parsedArgsMap.contains(commandArg.name)) { - parsedArgsMap[commandArg.name] = arg; + return cmdResult; + + } else { + if ( parts.isEmpty() ) + return CommanderResult{true, true, "Subcommand must be set", {}, false, {} }; + + QString subCommandName = parts[0]; + parts.removeAt(0); + + for (Command subcommand : command.subcommands) { + if (subCommandName == subcommand.name) { + jsonObj["subcommand"] = subcommand.name; + + auto cmdResult = ProcessCommand(subcommand, parts, jsonObj); + if ( !cmdResult.output && subcommand.is_pre_hook) { + QString hook_result = ProcessPreHook(regCommandsGroup.engine, subcommand, agentId, cmdline, cmdResult.data, parts); + if (hook_result.isEmpty()) { + return CommanderResult{false, false, "", {}, true, {} }; + } else { + cmdResult.output = true; + cmdResult.error = true; + cmdResult.message = hook_result; + } + + } + return cmdResult; + } + } + return CommanderResult{true, true, "Subcommand not found", {}, false, {} }; + } + } + } + + return CommanderResult{true, true, "Command not found", {}, false, {}}; +} + +QString Commander::ProcessPreHook(QJSEngine *engine, const Command &command, const QString &agentId, const QString &cmdline, const QJsonObject &jsonObj, QStringList args) +{ + if (!engine) + return "Ax Engine is not available"; + + QList jsArgs; + jsArgs << engine->toScriptValue(agentId); + jsArgs << engine->toScriptValue(cmdline); + jsArgs << engine->toScriptValue(jsonObj.toVariantMap()); + for (const QString& arg : args) { + jsArgs << engine->toScriptValue(arg); + } + + QJSValue result = command.pre_hook.call(jsArgs); + if (result.isError()) { + return "Error: " + result.property("message").toString(); + } + return ""; +} + +CommanderResult Commander::ProcessCommand(Command command, QStringList args, QJsonObject jsonObj) +{ + QMap parsedArgsMap; + QString wideKey; + + for (int i = 0; i < args.size(); ++i) { + QString arg = args[i]; + + bool isWideArgs = true; + + for (Argument commandArg : command.args) { + if (commandArg.flag) { + if (commandArg.type == "BOOL" && commandArg.mark == arg) { + parsedArgsMap[commandArg.mark] = "true"; + wideKey = commandArg.mark; + isWideArgs = false; + break; + } else if (commandArg.mark == arg && args.size() > i + 1) { + ++i; + parsedArgsMap[commandArg.name] = args[i]; wideKey = commandArg.name; isWideArgs = false; break; } - } - - if( isWideArgs ) { - QString wideStr; - for(int j = i; j < args.size(); ++j) { - wideStr += " " + args[j]; - } - parsedArgsMap[wideKey] += wideStr; + } else if (!parsedArgsMap.contains(commandArg.name)) { + parsedArgsMap[commandArg.name] = arg; + wideKey = commandArg.name; + isWideArgs = false; break; } } - for (Argument commandArg : command.args) { - if (parsedArgsMap.contains(commandArg.name) || parsedArgsMap.contains(commandArg.mark)) { - if (commandArg.type == "STRING") { - jsonObj[commandArg.name] = parsedArgsMap[commandArg.name]; - } else if (commandArg.type == "INT") { - jsonObj[commandArg.name] = parsedArgsMap[commandArg.name].toInt(); - } else if (commandArg.type == "BOOL") { - jsonObj[commandArg.mark] = parsedArgsMap[commandArg.mark] == "true"; - } else if (commandArg.type == "FILE") { - QString path = parsedArgsMap[commandArg.name]; - if (path.startsWith("~/")) - path = QDir::home().filePath(path.mid(2)); + if( isWideArgs ) { + QString wideStr; + for(int j = i; j < args.size(); ++j) { + wideStr += " " + args[j]; + } + parsedArgsMap[wideKey] += wideStr; + break; + } + } - QFile file(path); - if (file.open(QIODevice::ReadOnly)) { - QByteArray fileData = file.readAll(); - jsonObj[commandArg.name] = QString::fromLatin1(fileData.toBase64()); - file.close(); - } else { - return CommanderResult{true, "Failed to open file: " + path, true }; - } - } - } else if (commandArg.required) { - if (commandArg.defaultValue.isEmpty() && !commandArg.defaultUsed) { - return CommanderResult{true, "Missing required argument: " + commandArg.name, true }; + for (Argument commandArg : command.args) { + if (parsedArgsMap.contains(commandArg.name) || parsedArgsMap.contains(commandArg.mark)) { + if (commandArg.type == "STRING") { + jsonObj[commandArg.name] = parsedArgsMap[commandArg.name]; + } + else if (commandArg.type == "INT") { + jsonObj[commandArg.name] = parsedArgsMap[commandArg.name].toInt(); + } + else if (commandArg.type == "BOOL") { + jsonObj[commandArg.mark] = parsedArgsMap[commandArg.mark] == "true"; + } + else if (commandArg.type == "FILE") { + QString path = parsedArgsMap[commandArg.name]; + if (path.startsWith("~/")) + path = QDir::home().filePath(path.mid(2)); + + QFile file(path); + if (file.open(QIODevice::ReadOnly)) { + QByteArray fileData = file.readAll(); + jsonObj[commandArg.name] = QString::fromLatin1(fileData.toBase64()); + file.close(); } else { - if (commandArg.type == "STRING") { - jsonObj[commandArg.name] = commandArg.defaultValue; - } else if (commandArg.type == "INT") { - jsonObj[commandArg.name] = commandArg.defaultValue.toInt(); - } else if (commandArg.type == "BOOL") { - jsonObj[commandArg.mark] = commandArg.defaultValue == "true"; - } else if (commandArg.type == "FILE") { - QString path = commandArg.defaultValue; - if (path.startsWith("~/")) - path = QDir::home().filePath(path.mid(2)); - - QFile file(path); - if (file.open(QIODevice::ReadOnly)) { - QByteArray fileData = file.readAll(); - jsonObj[commandArg.name] = QString::fromLatin1(fileData.toBase64()); - file.close(); - } else { - return CommanderResult{true, "Failed to open file: " + path, true }; - } - } + return CommanderResult{true, true, "Failed to open file: " + path, {}, false, {}}; } } - } - - QString msg = command.message; - if( !msg.isEmpty() ) { - for ( QString k : parsedArgsMap.keys() ) { - QString param = "<" + k + ">"; - if( msg.contains(param) ) - msg = msg.replace(param, parsedArgsMap[k]); + } else if (commandArg.required) { + if ( (commandArg.defaultValue.isNull() || !commandArg.defaultValue.isValid()) && !commandArg.defaultUsed) { + return CommanderResult{true, true, "Missing required argument: " + commandArg.name, {}, false, {}}; } - jsonObj["message"] = msg; - } - - execStr = command.exec; - execArgs = command.args; - } - else { - if ( args.isEmpty() ) - return CommanderResult{true, "Subcommand must be set", true }; - - QString subCommandName = args[0]; - - for (Command subcommand : command.subcommands) { - if (subCommandName == subcommand.name) { - jsonObj["subcommand"] = subcommand.name; - - QMap parsedArgsMap; - - QString wideKey; - for (int i = 1; i < args.size(); ++i) { - QString arg = args[i]; - - bool isWideArgs = true; - - for (Argument commandArg : subcommand.args) { - if (commandArg.flag) { - if (commandArg.type == "BOOL" && commandArg.mark == arg ) { - parsedArgsMap[commandArg.mark] = "true"; - wideKey = commandArg.mark; - isWideArgs = false; - break; - } - else if ( commandArg.mark == arg && args.size() > i+1 ) { - ++i; - parsedArgsMap[commandArg.name] = args[i]; - wideKey = commandArg.name; - isWideArgs = false; - break; - } - } - else if (!parsedArgsMap.contains(commandArg.name)) { - parsedArgsMap[commandArg.name] = arg; - wideKey = commandArg.name; - isWideArgs = false; - break; - } - } - - if( isWideArgs ) { - QString wideStr; - for(int j = i; j < args.size(); ++j) { - wideStr += " " + args[j]; - } - parsedArgsMap[wideKey] += wideStr; - break; - } + else { + if (commandArg.type == "STRING" && commandArg.defaultValue.typeId() == QMetaType::QString) { + jsonObj[commandArg.name] = commandArg.defaultValue.toString(); + } else if (commandArg.type == "INT" && commandArg.defaultValue.typeId() == QMetaType::Int) { + jsonObj[commandArg.name] = commandArg.defaultValue.toInt(); + } else if (commandArg.type == "BOOL" && commandArg.defaultValue.typeId() == QMetaType::Bool) { + jsonObj[commandArg.mark] = commandArg.defaultValue.toBool(); } - - for (Argument subArg : subcommand.args) { - if (parsedArgsMap.contains(subArg.name) || parsedArgsMap.contains(subArg.mark)) { - if (subArg.type == "STRING") { - jsonObj[subArg.name] = parsedArgsMap[subArg.name]; - } else if (subArg.type == "INT") { - jsonObj[subArg.name] = parsedArgsMap[subArg.name].toInt(); - } else if (subArg.type == "BOOL") { - jsonObj[subArg.mark] = parsedArgsMap[subArg.mark] == "true"; - } else if (subArg.type == "FILE") { - QString path = parsedArgsMap[subArg.name]; - if (path.startsWith("~/")) - path = QDir::home().filePath(path.mid(2)); - - QFile file(path); - if (file.open(QIODevice::ReadOnly)) { - QByteArray fileData = file.readAll(); - jsonObj[subArg.name] = QString::fromLatin1(fileData.toBase64()); - file.close(); - } else { - return CommanderResult{true, "Failed to open file: " + path, true }; - } - } - } else if (subArg.required) { - if (subArg.defaultValue.isEmpty() && !subArg.defaultUsed) { - return CommanderResult{true, "Missing required argument for subcommand: " + subArg.name, true }; - } else { - if (subArg.type == "STRING") { - jsonObj[subArg.name] = subArg.defaultValue; - } else if (subArg.type == "INT") { - jsonObj[subArg.name] = subArg.defaultValue.toInt(); - } else if (subArg.type == "BOOL") { - jsonObj[subArg.mark] = subArg.defaultValue == "true"; - } else if (subArg.type == "FILE") { - QString path = subArg.defaultValue; - if (path.startsWith("~/")) - path = QDir::home().filePath(path.mid(2)); - - QFile file(path); - if (file.open(QIODevice::ReadOnly)) { - QByteArray fileData = file.readAll(); - jsonObj[subArg.name] = QString::fromLatin1(fileData.toBase64()); - file.close(); - } else { - return CommanderResult{true, "Failed to open file: " + path, true }; - } - } - } - } + else { + return CommanderResult{true, true, "Missing required argument: " + commandArg.name, {}, false, {}}; } - - QString msg = subcommand.message; - if( !msg.isEmpty() ) { - for ( QString k : parsedArgsMap.keys() ) { - QString param = "<" + k + ">"; - if( msg.contains(param) ) - msg = msg.replace(param, parsedArgsMap[k]); - } - jsonObj["message"] = msg; - } - - execStr = subcommand.exec; - execArgs = subcommand.args; - - break; } } } - if( !execStr.isEmpty() ) { - QString newInput = this->ProcessExecExtension( agentData, extMod, execStr, execArgs, jsonObj); - CommanderResult execCommandResult = this->ProcessInput(agentData, newInput); - if( !execCommandResult.error ) { - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(execCommandResult.message.toUtf8(), &parseError); - QJsonObject jsonObject = document.object(); - jsonObject["message"] = jsonObj["message"]; - QJsonDocument jsonDoc(jsonObject); - execCommandResult.message = jsonDoc.toJson(); + QString msg = command.message; + if( !msg.isEmpty() ) { + for ( QString k : parsedArgsMap.keys() ) { + QString param = "<" + k + ">"; + if( msg.contains(param) ) + msg = msg.replace(param, parsedArgsMap[k]); } - return execCommandResult; + jsonObj["message"] = msg; } - QJsonDocument jsonDoc(jsonObj); - return CommanderResult{false, jsonDoc.toJson(), false }; + return CommanderResult{false, false, "", jsonObj, false, {} }; } -QString Commander::ProcessExecExtension(const AgentData &agentData, ExtModule extMod, QString ExecString, QList args, QJsonObject jsonObj) -{ - /// $ARCH - - ExecString = ExecString.replace("$ARCH()", agentData.Arch, Qt::CaseSensitive); - - /// $EXT_DIR - - QFileInfo fi(extMod.FilePath); - QString dirPath = fi.absolutePath(); - ExecString = ExecString.replace("$EXT_DIR()", dirPath, Qt::CaseSensitive); - - /// $MAP() - - QRegularExpression mapRe(R"(\$MAP\(\s*(\w+)\s*,\s*(\w+)\s*\))"); - QRegularExpressionMatchIterator mapReIt = mapRe.globalMatch(ExecString); - while (mapReIt.hasNext()) { - QRegularExpressionMatch match = mapReIt.next(); - QString mapName = match.captured(1); - QString key = match.captured(2); - - QString value = ""; - if (extMod.Constants.contains(mapName)) { - if (extMod.Constants[mapName].Map.contains(key)) - value = extMod.Constants[mapName].Map[key]; - - } - - if (!value.isEmpty()) - ExecString = ExecString.replace(match.captured(0), value); - } - - /// $RAND - - QRegularExpression re(R"(\$RAND\(\s*(\d+)\s*,\s*(\w+)\s*\))"); - QRegularExpressionMatchIterator i = re.globalMatch(ExecString); - while (i.hasNext()) { - QRegularExpressionMatch match = i.next(); - int length = match.captured(1).toInt(); - QString setName = match.captured(2); - QString randomString = GenerateRandomString(length, setName); - if (!randomString.isEmpty()) - ExecString = ExecString.replace(match.captured(0), randomString); - } - - /// $HASH - - QRegularExpression hashRe(R"(\$HASH\(\s*(\w+)\s*,\s*(\d+)\s*,\s*([^)]+)\s*\))"); - QRegularExpressionMatchIterator hashIt = hashRe.globalMatch(ExecString); - while (hashIt.hasNext()) { - QRegularExpressionMatch match = hashIt.next(); - QString algorithm = match.captured(1); - int length = match.captured(2).toInt(); - QString inputString = match.captured(3).trimmed(); - //If arguments - QRegularExpression remainingArgsRegex(R"(\{\s*([^}]*)\s*\})"); - QRegularExpressionMatchIterator remainingIt = remainingArgsRegex.globalMatch(inputString); - while (remainingIt.hasNext()) { - QRegularExpressionMatch remainingMatch = remainingIt.next(); - QString paramName = remainingMatch.captured(1).trimmed(); - if( jsonObj.contains(paramName) && jsonObj[paramName].isString() ){ - QString paramValue = serializeParam(jsonObj[paramName].toString()); - inputString = inputString.replace(remainingMatch.captured(0), paramValue); - } - } - - QString hashString = GenerateHash(algorithm, length, inputString); - if (!hashString.isEmpty()) - ExecString = ExecString.replace(match.captured(0), hashString); - } - - /// BOF_PACK - - QRegularExpression packRegex(R"(\$PACK_BOF\s*\(([^)]*)\))"); - QRegularExpressionMatchIterator iter = packRegex.globalMatch(ExecString); - - while (iter.hasNext()) { - QRegularExpressionMatch match = iter.next(); - QString packContent = match.captured(1); /// $PACK(...) - - QRegularExpression paramRegex(R"((\s*([A-Z]+)\s+)?(?:\{\s*([^}]*)\s*\}|([^,\s][^,]*[^,\s])))"); - QRegularExpressionMatchIterator it = paramRegex.globalMatch(packContent); - - BofPacker packer; - while (it.hasNext()) { - QRegularExpressionMatch paramMatch = it.next(); - - QString type = paramMatch.captured(2); - if (type.isEmpty()) - type = "CSTR"; - - if (!paramMatch.captured(3).isEmpty()) { - QString value = paramMatch.captured(3); /// {param} - if( jsonObj.contains(value) ) - packer.Pack( type, jsonObj[value] ); - } - else if (!paramMatch.captured(4).isEmpty()) { - QString value = paramMatch.captured(4); /// param - packer.Pack( type, QJsonValue(value) ); - } - } - QString bofParam = packer.Build(); - ExecString = ExecString.replace(match.captured(0), bofParam); - } - - /// Arguments - - QRegularExpression remainingArgsRegex(R"(\{\s*([^}]*)\s*\})"); - QRegularExpressionMatchIterator remainingIt = remainingArgsRegex.globalMatch(ExecString); - - while (remainingIt.hasNext()) { - QRegularExpressionMatch remainingMatch = remainingIt.next(); - QString paramName = remainingMatch.captured(1).trimmed(); - if( jsonObj.contains(paramName) && jsonObj[paramName].isString() ){ - QString paramValue = serializeParam(jsonObj[paramName].toString()); - ExecString = ExecString.replace(remainingMatch.captured(0), paramValue); - } - } - - return ExecString; -} - - - -QString Commander::GetError() -{ - return error; -} +QString Commander::GetError() { return error; } CommanderResult Commander::ProcessHelp(QStringList commandParts) { @@ -784,7 +354,7 @@ CommanderResult Commander::ProcessHelp(QStringList commandParts) output << QString(" Command Description\n"); output << QString(" ------- -----------\n"); - for ( auto command : commands ) { + for (auto command : regCommandsGroup.commands) { QString commandName = command.name; if (!command.subcommands.isEmpty()) commandName += '*'; @@ -793,12 +363,12 @@ CommanderResult Commander::ProcessHelp(QStringList commandParts) output << " " + commandName + tab + " " + command.description + "\n"; } - for ( auto extMod : extModules.values() ){ + for ( auto script_group : axCommandsGroup ){ output << QString("\n"); - output << QString(" Extension - " + extMod.Name + "\n"); + output << QString(" Group - " + script_group.groupName + "\n"); output << QString(" =====================================\n"); - for ( auto command : extMod.Commands ) { + for ( auto command : script_group.commands ) { QString commandName = command.name; if ( command.subcommands.isEmpty() ) { QString tab = QString(TotalWidth - commandName.size(), ' '); @@ -814,24 +384,24 @@ CommanderResult Commander::ProcessHelp(QStringList commandParts) } } - return CommanderResult{true, result, false}; + return CommanderResult{false, true, result, {}, false, {}}; } else { Command foundCommand; QString commandName = commandParts[0]; - for (Command cmd : commands) { + for (Command cmd : regCommandsGroup.commands) { if (cmd.name == commandName) { foundCommand = cmd; break; } } - for( auto extMod : extModules.values()) { + for(auto script_group : axCommandsGroup) { if ( !foundCommand.name.isEmpty() ) break; - for (Command cmd : extMod.Commands) { + for (Command cmd : script_group.commands) { if (cmd.name == commandName) { foundCommand = cmd; break; @@ -840,7 +410,7 @@ CommanderResult Commander::ProcessHelp(QStringList commandParts) } if ( foundCommand.name.isEmpty() ) - return CommanderResult{true, "Unknown command: " + commandName, true}; + return CommanderResult{true, true, "Unknown command: " + commandName, {}, false, {}}; if (commandParts.size() == 1) { output << QString("\n"); @@ -881,7 +451,7 @@ CommanderResult Commander::ProcessHelp(QStringList commandParts) for (const auto &arg : foundCommand.args) { QString fullarg = (arg.required ? "<" : "[") + arg.mark + (arg.mark.isEmpty() || arg.name.isEmpty() ? "" : " ") + arg.name + (arg.required ? ">" : "]"); QString padding = QString(maxArgLength - fullarg.size(), ' '); - output << " " + fullarg + padding + " : " + arg.type + (arg.defaultUsed ? " (default: '" + arg.defaultValue + "'). " : ". ") + arg.description + "\n"; + output << " " + fullarg + padding + " : " + (arg.type + ".").leftJustified(9, ' ') + (arg.defaultUsed ? " (default: '" + arg.defaultValue.toString() + "'). " : " ") + arg.description + "\n"; } } } @@ -896,7 +466,7 @@ CommanderResult Commander::ProcessHelp(QStringList commandParts) } if ( foundSubCommand.name.isEmpty() ) - return CommanderResult{true, "Unknown subcommand: " + subCommandName, true}; + return CommanderResult{true, true, "Unknown subcommand: " + subCommandName, {}, false, {}}; output << " Command : " + foundCommand.name + " " + foundSubCommand.name +"\n"; if(!foundSubCommand.description.isEmpty()) @@ -921,14 +491,14 @@ CommanderResult Commander::ProcessHelp(QStringList commandParts) for (const auto &arg : foundSubCommand.args) { QString fullarg = (arg.required ? "<" : "[") + arg.mark + (arg.mark.isEmpty() || arg.name.isEmpty() ? "" : " ") + arg.name + (arg.required ? ">" : "]"); QString padding = QString(maxArgLength - fullarg.size(), ' '); - output << " " + fullarg + padding + " : " + arg.type + (arg.defaultUsed ? " (default: '" + arg.defaultValue + "'). " : ". ") + arg.description + "\n"; + output << " " + fullarg + padding + " : " + (arg.type + ".").leftJustified(9, ' ') + (arg.defaultUsed ? ".- (default: '" + arg.defaultValue.toString() + "'). " : " ") + arg.description + "\n"; } } } else { - return CommanderResult{true, "Error Help format: 'help [command [subcommand]]'", true}; + return CommanderResult{true, true, "Error Help format: 'help [command [subcommand]]'", {}, false, {}}; } - return CommanderResult{true, output.readAll(), false}; + return CommanderResult{false, true, output.readAll(), {}, false, {}}; } } @@ -937,7 +507,7 @@ QStringList Commander::GetCommands() QStringList commandList; QStringList helpCommandList; - for (Command cmd : commands) { + for (Command cmd : regCommandsGroup.commands) { helpCommandList << "help " + cmd.name; if (cmd.subcommands.isEmpty()) @@ -949,8 +519,8 @@ QStringList Commander::GetCommands() } } - for( auto extMod : extModules.values()) { - for (Command cmd : extMod.Commands) { + for( auto script_group : axCommandsGroup) { + for (Command cmd : script_group.commands) { helpCommandList << "help " + cmd.name; if (cmd.subcommands.isEmpty()) diff --git a/AdaptixClient/Source/Agent/Task.cpp b/AdaptixClient/Source/Agent/Task.cpp index 9d2dceb1..37576cc4 100644 --- a/AdaptixClient/Source/Agent/Task.cpp +++ b/AdaptixClient/Source/Agent/Task.cpp @@ -71,9 +71,9 @@ Task::~Task() = default; void Task::Update(QJsonObject jsonObjTaskData) { - this->data.Completed = jsonObjTaskData["a_completed"].toBool(); + this->data.Completed = jsonObjTaskData["a_completed"].toBool(); if (this->data.Completed) { - this->data.FinishTime = jsonObjTaskData["a_finish_time"].toDouble(); + this->data.FinishTime = jsonObjTaskData["a_finish_time"].toDouble(); QString finishTime = UnixTimestampGlobalToStringLocal(this->data.FinishTime); this->item_FinishTime->setText(finishTime); diff --git a/AdaptixClient/Source/Client/AxScript/AxCommandWrappers.cpp b/AdaptixClient/Source/Client/AxScript/AxCommandWrappers.cpp new file mode 100644 index 00000000..950e2db8 --- /dev/null +++ b/AdaptixClient/Source/Client/AxScript/AxCommandWrappers.cpp @@ -0,0 +1,214 @@ +#include + +AxCommandWrappers::AxCommandWrappers(const QString &name, const QString &description, const QString &example, const QString &message, QObject* parent) : QObject(parent) +{ + command = {}; + command.name = name; + command.description = description; + command.example = example; + command.message = message; +} + +Command AxCommandWrappers::getCommand() const { return this->command; } + +void AxCommandWrappers::addSubCommands(const QJSValue& value) +{ + if (value.isUndefined() || value.isNull()) + return; + + if (value.isArray()) { + const int length = value.property("length").toInt(); + for (int i = 0; i < length; ++i) { + QJSValue item = value.property(i); + + QObject* obj = item.toQObject(); + if (auto* commandWrapper = qobject_cast(obj)) { + command.subcommands.append(commandWrapper->getCommand()); + } + else { + emit scriptError("Item at index " + QString::number(i) + " is not an Command"); + } + } + } + else { + QObject* obj = value.toQObject(); + if (auto* commandWrapper = qobject_cast(obj)) { + command.subcommands.append(commandWrapper->getCommand()); + } + else { + emit scriptError("Item is not an Command"); + } + } + +} + +void AxCommandWrappers::addArgBool(const QString &flag, const QString &description) +{ + Argument arg = { "BOOL", "", false, true, flag, description, false, QVariant() }; + command.args.append(arg); +} + +void AxCommandWrappers::addArgBool(const QString &flag, const QString &description, const QJSValue &value) +{ + Argument arg = { "BOOL", "", true, true, flag, description, false, QVariant() }; + + if ( !value.isUndefined() && !value.isNull() ) { + arg.defaultUsed = true; + arg.defaultValue = value.toVariant(); + } + + command.args.append(arg); +} + +void AxCommandWrappers::addArgInt(const QString &name, const bool required, const QString &description) +{ + Argument arg = { "INT", name, required, false, "", description, false, QVariant() }; + command.args.append(arg); +} + +void AxCommandWrappers::addArgInt(const QString &name, const QString &description, const QJSValue &value) +{ + Argument arg = { "INT", name, true, false, "", description, false, QVariant() }; + + if ( !value.isUndefined() && !value.isNull() ) { + arg.defaultUsed = true; + arg.defaultValue = value.toVariant(); + } + + command.args.append(arg); +} + +void AxCommandWrappers::addArgFlagInt(const QString &flag, const QString &name, const bool required, const QString &description) +{ + Argument arg = { "INT", name, required, true, flag, description, false, QVariant() }; + command.args.append(arg); +} + +void AxCommandWrappers::addArgFlagInt(const QString &flag, const QString &name, const QString &description, const QJSValue &value) +{ + Argument arg = { "INT", name, true, true, flag, description, false, QVariant() }; + + if ( !value.isUndefined() && !value.isNull() ) { + arg.defaultUsed = true; + arg.defaultValue = value.toVariant(); + } + + command.args.append(arg); +} + +void AxCommandWrappers::addArgString(const QString &name, const bool required, const QString &description) +{ + Argument arg = { "STRING", name, required, false, "", description, false, QVariant() }; + command.args.append(arg); +} + +void AxCommandWrappers::addArgString(const QString &name, const QString &description, const QJSValue &value) +{ + Argument arg = { "STRING", name, true, false, "", description, false, QVariant() }; + + if ( !value.isUndefined() && !value.isNull() ) { + arg.defaultUsed = true; + arg.defaultValue = value.toVariant(); + } + + command.args.append(arg); +} + +void AxCommandWrappers::addArgFlagString(const QString &flag, const QString &name, const bool required, const QString &description) +{ + Argument arg = { "STRING", name, required, true, flag, description, false, QVariant() }; + command.args.append(arg); +} + +void AxCommandWrappers::addArgFlagString(const QString &flag, const QString &name, const QString &description, const QJSValue &value) +{ + Argument arg = { "STRING", name, true, true, flag, description, false, QVariant() }; + + if ( !value.isUndefined() && !value.isNull() ) { + arg.defaultUsed = true; + arg.defaultValue = value.toVariant(); + } + + command.args.append(arg); +} + +void AxCommandWrappers::addArgFile(const QString &name, const bool required, const QString &description) +{ + Argument arg = { "FILE", name, required, false, "", description, false, QVariant() }; + command.args.append(arg); +} + +void AxCommandWrappers::addArgFlagFile(const QString &flag, const QString &name, const bool required, const QString &description) +{ + Argument arg = { "FILE", name, required, true, flag, description, false, QVariant() }; + command.args.append(arg); +} + +void AxCommandWrappers::setPreHook(const QJSValue &handler) +{ + if (!handler.isCallable()) { + emit scriptError("handler is not function"); + return; + } + + command.is_pre_hook = true; + command.pre_hook = handler; +} + + +AxCommandGroupWrapper::AxCommandGroupWrapper(QJSEngine* engine, QObject* parent) : QObject(parent), parent(parent), engine(engine) {} + +void AxCommandGroupWrapper::SetParams(const QString &name, const QJSValue &array) +{ + this->name = name; + + if (array.isUndefined() || array.isNull() || !array.isArray()) { + emit scriptError("array is not the Command[]"); + return; + } + + const int length = array.property("length").toInt(); + for (int i = 0; i < length; ++i) { + QJSValue item = array.property(i); + + QObject* obj = item.toQObject(); + if (auto* commandWrapper = qobject_cast(obj)) { + this->commands.append(commandWrapper->getCommand()); + } else { + emit scriptError("Item at index " + QString::number(i) + " is not an Command"); + } + } +} + +QString AxCommandGroupWrapper::getName() const { return name; } + +QList AxCommandGroupWrapper::getCommands() const { return commands; } + +QJSEngine* AxCommandGroupWrapper::getEngine() const { return this->engine; } + +void AxCommandGroupWrapper::add(const QJSValue &value) { + if (value.isUndefined() || value.isNull()) + return; + + if (value.isArray()) { + const int length = value.property("length").toInt(); + for (int i = 0; i < length; ++i) { + QJSValue item = value.property(i); + + QObject* obj = item.toQObject(); + if (auto* commandWrapper = qobject_cast(obj)) { + this->commands.append(commandWrapper->getCommand()); + } else { + emit scriptError("Item at index " + QString::number(i) + " is not an Command"); + } + } + } + else { + QObject* obj = value.toQObject(); + if (auto* commandWrapper = qobject_cast(obj)) { + this->commands.append(commandWrapper->getCommand()); + } else { + emit scriptError("Item is not an Command"); + } + } +} diff --git a/AdaptixClient/Source/Client/AxScript/AxElementWrappers.cpp b/AdaptixClient/Source/Client/AxScript/AxElementWrappers.cpp new file mode 100644 index 00000000..04d8a212 --- /dev/null +++ b/AdaptixClient/Source/Client/AxScript/AxElementWrappers.cpp @@ -0,0 +1,1097 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +/// MENU + +AxActionWrapper::AxActionWrapper(const QString& text, const QJSValue& handler, QJSEngine* engine, QObject* parent) : AbstractAxMenuItem(parent), handler(handler), engine(engine) { pAction = new QAction(text, this); } + +QAction* AxActionWrapper::action() const { return this->pAction; } + +void AxActionWrapper::setContext(QVariantList context) +{ + disconnect(pAction, nullptr, this, nullptr); + connect(pAction, &QAction::triggered, this, [this, context]() { triggerWithContext(context); }); +} + +void AxActionWrapper::triggerWithContext(const QVariantList& arg) const +{ + if (!handler.isCallable()) + return; + + QJSValue jsContext = engine->toScriptValue(arg); + if (this->handler.isCallable()) + this->handler.call({ jsContext }); +} + + + +AxSeparatorWrapper::AxSeparatorWrapper(QObject* parent) : AbstractAxMenuItem(parent) +{ + pAction = new QAction(this); + pAction->setSeparator(true); +} + +QAction* AxSeparatorWrapper::action() const { return this->pAction; } + +void AxSeparatorWrapper::setContext(QVariantList context) {} + + + +AxMenuWrapper::AxMenuWrapper(const QString& title, QObject* parent) : AbstractAxMenuItem(parent) { pMenu = new QMenu(title); } + +QMenu* AxMenuWrapper::menu() const { return this->pMenu; } + +void AxMenuWrapper::setContext(const QVariantList context) +{ + for (auto item : items) + item->setContext(context); +} + +void AxMenuWrapper::addItem(AbstractAxMenuItem* axItem) +{ + items.append(axItem); + + if (auto* wrapper1 = dynamic_cast(axItem)) { + this->pMenu->addAction(wrapper1->action()); + } + else if (auto* wrapper2 = dynamic_cast(axItem)) { + this->pMenu->addAction(wrapper2->action()); + } + else if (auto* wrapper3 = dynamic_cast(axItem)) { + this->pMenu->addMenu(wrapper3->menu()); + } +} + + + +/// LAYOUT + +AxBoxLayoutWrapper::AxBoxLayoutWrapper(const QBoxLayout::Direction dir, QObject* parent) : QObject(parent) { boxLayout = new QBoxLayout(dir); } + +QBoxLayout* AxBoxLayoutWrapper::layout() const { return boxLayout; } + +void AxBoxLayoutWrapper::addWidget(QObject* wrapper) const +{ + if (auto* formElement = dynamic_cast(wrapper)) + boxLayout->addWidget(formElement->widget()); + else if (auto* spacerElement = qobject_cast(wrapper)) + boxLayout->addItem(spacerElement->widget()); +} + +/// GRID LAYOUT + +AxGridLayoutWrapper::AxGridLayoutWrapper(QObject* parent) : QObject(parent) { gridLayout = new QGridLayout(); } + +QGridLayout* AxGridLayoutWrapper::layout() const { return gridLayout; } + +void AxGridLayoutWrapper::addWidget(QObject* wrapper, const int row, const int col, const int rowSpan, const int colSpan) const +{ + if (auto* formElement = dynamic_cast(wrapper)) + gridLayout->addWidget(formElement->widget(), row, col, rowSpan, colSpan); + else if (auto* spacerElement = qobject_cast(wrapper)) + gridLayout->addItem(spacerElement->widget(), row, col, rowSpan, colSpan); +} + + +/// LINE + +AxLineWrapper::AxLineWrapper(const QFrame::Shape dir, QObject* parent) : QObject(parent) +{ + line = new QFrame(); + line->setFrameShape(dir); + if (dir == QFrame::VLine) + line->setMinimumHeight(25); + else + line->setMinimumWidth(25); +} + +QFrame* AxLineWrapper::widget() const { return line; } + + +/// SPACER + +AxSpacerWrapper::AxSpacerWrapper(const int w, const int h, const QSizePolicy::Policy hData, const QSizePolicy::Policy vData, QObject* parent) : QObject(parent) { spacer = new QSpacerItem(w, h, hData, vData); } + +QSpacerItem* AxSpacerWrapper::widget() const { return spacer; } + + +/// TEXTLINE + +AxTextLineWrapper::AxTextLineWrapper(QLineEdit* edit, QObject* parent) : QObject(parent), lineedit(edit) +{ + connect(lineedit, &QLineEdit::textChanged, this, &AxTextLineWrapper::textChanged); + connect(lineedit, &QLineEdit::textEdited, this, &AxTextLineWrapper::textEdited); + connect(lineedit, &QLineEdit::returnPressed, this, &AxTextLineWrapper::returnPressed); + connect(lineedit, &QLineEdit::editingFinished, this, &AxTextLineWrapper::editingFinished); +} + +QVariant AxTextLineWrapper::jsonMarshal() const { return lineedit->text(); } + +void AxTextLineWrapper::jsonUnmarshal(const QVariant& value) { lineedit->setText(value.toString()); } + +QLineEdit* AxTextLineWrapper::widget() const { return lineedit; } + +QString AxTextLineWrapper::text() const { return lineedit->text(); } + +void AxTextLineWrapper::setText(const QString& text) const { lineedit->setText(text); } + +void AxTextLineWrapper::setPlaceholder(const QString& text) const { lineedit->setPlaceholderText(text); } + +void AxTextLineWrapper::setReadOnly(const bool &readonly) const { lineedit->setReadOnly(readonly); } + +/// COMBO + +AxComboBoxWrapper::AxComboBoxWrapper(QComboBox* comboBox, QObject* parent) : QObject(parent), comboBox(comboBox) +{ + connect(comboBox, &QComboBox::currentTextChanged, this, &AxComboBoxWrapper::currentTextChanged); + connect(comboBox, &QComboBox::currentIndexChanged, this, &AxComboBoxWrapper::currentIndexChanged); +} + +QVariant AxComboBoxWrapper::jsonMarshal() const { return comboBox->currentText(); } + +void AxComboBoxWrapper::jsonUnmarshal(const QVariant& value) +{ + int index = comboBox->findText(value.toString()); + if (index != -1) + comboBox->setCurrentIndex(index); +} + +QComboBox * AxComboBoxWrapper::widget() const { return comboBox; } + +void AxComboBoxWrapper::addItem(const QString& text) const { comboBox->addItem(text); } + +void AxComboBoxWrapper::addItems(const QJSValue& array) const +{ + if (!array.isArray()) + return; + + QStringList items; + const int length = array.property("length").toInt(); + for (int i = 0; i < length; ++i) { + QJSValue val = array.property(i); + items << val.toString(); + } + + comboBox->addItems(items); +} + +void AxComboBoxWrapper::setItems(const QJSValue &array) const +{ + if (!array.isArray()) + return; + + QStringList items; + const int length = array.property("length").toInt(); + for (int i = 0; i < length; ++i) { + QJSValue val = array.property(i); + items << val.toString(); + } + + comboBox->clear(); + comboBox->addItems(items); +} + +void AxComboBoxWrapper::clear() const { comboBox->clear(); } + +QString AxComboBoxWrapper::currentText() const { return comboBox->currentText(); } + +int AxComboBoxWrapper::currentIndex() const { return comboBox->currentIndex(); } + +void AxComboBoxWrapper::setCurrentIndex(const int index) const { comboBox->setCurrentIndex(index); } + +/// SPIN + +AxSpinBoxWrapper::AxSpinBoxWrapper(QSpinBox* spin, QObject* parent) : QObject(parent), spin(spin) { + connect(spin, &QSpinBox::valueChanged, this, &AxSpinBoxWrapper::valueChanged); +} + +QVariant AxSpinBoxWrapper::jsonMarshal() const { return spin->value(); } + +void AxSpinBoxWrapper::jsonUnmarshal(const QVariant& value) { spin->setValue(value.toInt()); } + +QSpinBox* AxSpinBoxWrapper::widget() const { return spin; } + +int AxSpinBoxWrapper::value() const { return spin->value(); } + +void AxSpinBoxWrapper::setValue(const int value) const { spin->setValue(value); } + +void AxSpinBoxWrapper::setRange(const int min, const int max) const { spin->setRange(min, max); } + +/// DATE + +AxDateEditWrapper::AxDateEditWrapper(QDateEdit* edit, const QString &format, QObject* parent) : QObject(parent), dateedit(edit) +{ + edit->setCalendarPopup(true); + edit->setDisplayFormat(format); +} + +QVariant AxDateEditWrapper::jsonMarshal() const { return dateString(); } + +void AxDateEditWrapper::jsonUnmarshal(const QVariant& value) { setDateString(value.toString()); } + +QDateEdit* AxDateEditWrapper::widget() const { return dateedit; } + +QString AxDateEditWrapper::dateString() const { return dateedit->date().toString(Qt::ISODate); } + +void AxDateEditWrapper::setDateString(const QString& date) const { dateedit->setDate(QDate::fromString(date, Qt::ISODate)); } + +/// TIME + +AxTimeEditWrapper::AxTimeEditWrapper(QTimeEdit* edit, const QString &format, QObject* parent) : QObject(parent), timeedit(edit) +{ + timeedit->setDisplayFormat(format); +} + +QVariant AxTimeEditWrapper::jsonMarshal() const { return timeString(); } + +void AxTimeEditWrapper::jsonUnmarshal(const QVariant& value) { setTimeString(value.toString()); } + +QTimeEdit * AxTimeEditWrapper::widget() const { return timeedit; } + +QString AxTimeEditWrapper::timeString() const { return timeedit->time().toString("HH:mm:ss"); } + +void AxTimeEditWrapper::setTimeString(const QString& time) const { timeedit->setTime(QTime::fromString(time, "HH:mm:ss")); } + +/// TEXTMULTI + +AxTextMultiWrapper::AxTextMultiWrapper(QPlainTextEdit* edit, QObject* parent) : QObject(parent), textedit(edit) {} + +QVariant AxTextMultiWrapper::jsonMarshal() const { return text(); } + +void AxTextMultiWrapper::jsonUnmarshal(const QVariant& value) { setText(value.toString()); } + +QPlainTextEdit * AxTextMultiWrapper::widget() const { return textedit; } + +QString AxTextMultiWrapper::text() const { return textedit->toPlainText(); } + +void AxTextMultiWrapper::setText(const QString& text) const { textedit->setPlainText(text); } + +void AxTextMultiWrapper::appendText(const QString &text) const { textedit->appendPlainText(text); } + +void AxTextMultiWrapper::setPlaceholder(const QString& text) const { textedit->setPlaceholderText(text); } + +void AxTextMultiWrapper::setReadOnly(const bool &readonly) const { textedit->setReadOnly(readonly); } + +/// CHECK + +AxCheckBoxWrapper::AxCheckBoxWrapper(QCheckBox* box, QObject* parent) : QObject(parent), check(box) +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) + connect(check, &QCheckBox::checkStateChanged, this, &AxCheckBoxWrapper::stateChanged); +#else + connect(check, &QCheckBox::stateChanged, this, &AxCheckBoxWrapper::stateChanged); +#endif +} + +QVariant AxCheckBoxWrapper::jsonMarshal() const { return isChecked(); } + +void AxCheckBoxWrapper::jsonUnmarshal(const QVariant& value) { setChecked(value.toBool()); } + +QCheckBox * AxCheckBoxWrapper::widget() const { return check; } + +bool AxCheckBoxWrapper::isChecked() const { return check->isChecked(); } + +void AxCheckBoxWrapper::setChecked(const bool checked) const { check->setChecked(checked); } + +void AxSelectorFile::setPlaceholder(const QString& text) const { selector->input->setPlaceholderText(text); } + +/// LABEL + +AxLabelWrapper::AxLabelWrapper(QLabel* label, QObject* parent) : QObject(parent), label(label) {} + +QLabel* AxLabelWrapper::widget() const { return label; } + +void AxLabelWrapper::setText(const QString& text) const { label->setText(text); } + +QString AxLabelWrapper::text() const { return label->text(); } + +/// TAB + +AxTabWrapper::AxTabWrapper(QTabWidget* tabs, QObject* parent) : QObject(parent), tabs(tabs) {} + +QTabWidget* AxTabWrapper::widget() const { return tabs; } + +void AxTabWrapper::addTab(QObject* wrapper, const QString &title) const +{ + if (auto* formElement = dynamic_cast(wrapper)) + tabs->addTab(formElement->widget(), title); +} + +/// TABLE + +AxTableWidgetWrapper::AxTableWidgetWrapper(const QJSValue &headers, QTableWidget* tableWidget, QJSEngine* jsEngine, QObject* parent) : QObject(parent), table(tableWidget), engine(jsEngine) { + connect(table, &QTableWidget::cellChanged, this, &AxTableWidgetWrapper::cellChanged); + connect(table, &QTableWidget::cellClicked, this, &AxTableWidgetWrapper::cellClicked); + connect(table, &QTableWidget::cellDoubleClicked, this, &AxTableWidgetWrapper::cellDoubleClicked); + + table->setAlternatingRowColors( true ); + table->setAutoFillBackground( false ); + table->setShowGrid( false ); + table->setSortingEnabled( true ); + table->setWordWrap( true ); + table->setCornerButtonEnabled( true ); + table->setSelectionBehavior( QAbstractItemView::SelectRows ); + table->setFocusPolicy( Qt::NoFocus ); + table->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch ); + table->horizontalHeader()->setCascadingSectionResizes( true ); + table->horizontalHeader()->setHighlightSections( false ); + table->verticalHeader()->setVisible( false ); + + this->setColumns(headers); +} + +QTableWidget* AxTableWidgetWrapper::widget() const { return table; } + +QVariant AxTableWidgetWrapper::jsonMarshal() const +{ + QJsonArray rowsArray; + for (int row = 0; row < table->rowCount(); ++row) { + QJsonArray rowArray; + for (int col = 0; col < table->columnCount(); ++col) { + auto item = table->item(row, col); + rowArray.append(item ? item->text() : QString()); + } + rowsArray.append(rowArray); + } + return rowsArray; +} + +void AxTableWidgetWrapper::jsonUnmarshal(const QVariant& value) +{ + QJsonArray rowsArray = QJsonDocument::fromJson(value.toByteArray()).array(); + table->setRowCount(rowsArray.size()); + + for (int row = 0; row < rowsArray.size(); ++row) { + QJsonArray rowArray = rowsArray[row].toArray(); + if (table->columnCount() < rowArray.size()) + table->setColumnCount(rowArray.size()); + + for (int col = 0; col < rowArray.size(); ++col) { + QTableWidgetItem* item = table->item(row, col); + if (!item) { + item = new QTableWidgetItem(); + table->setItem(row, col, item); + } + item->setText(rowArray[col].toString()); + } + } +} + +void AxTableWidgetWrapper::addColumn(const QString &header) const +{ + int column = table->columnCount()+1; + table->setColumnCount(column); + table->setHorizontalHeaderItem(column-1, new QTableWidgetItem(header)); +} + +void AxTableWidgetWrapper::setColumns(const QJSValue &headers) const +{ + if (!headers.isArray()) + return; + + const int length = headers.property("length").toInt(); + + table->setColumnCount(length); + + for (int i = 0; i < length; ++i) { + QJSValue val = headers.property(i); + table->setHorizontalHeaderItem(i, new QTableWidgetItem(val.toString())); + } +} + +void AxTableWidgetWrapper::addItem(const QJSValue &items) const +{ + if (!items.isArray()) + return; + + if( table->rowCount() < 1 ) + table->setRowCount( 1 ); + else + table->setRowCount( table->rowCount() + 1 ); + + + bool isSortingEnabled = table->isSortingEnabled(); + table->setSortingEnabled( false ); + + const int length = items.property("length").toInt(); + for (int i = 0; i < table->columnCount(); i++ ) { + QString text = ""; + if (i < length) + text = items.property(i).toString(); + table->setItem( table->rowCount() - 1, i, new QTableWidgetItem(text) ); + } + table->setSortingEnabled( isSortingEnabled ); +} + +int AxTableWidgetWrapper::rowCount() const { return table->rowCount(); } + +int AxTableWidgetWrapper::columnCount() const { return table->columnCount(); } + +void AxTableWidgetWrapper::setRowCount(const int rows) { table->setRowCount(rows); } + +void AxTableWidgetWrapper::setColumnCount(const int cols) { table->setColumnCount(cols); } + +int AxTableWidgetWrapper::currentRow() const { return table->currentRow(); } + +int AxTableWidgetWrapper::currentColumn() const { return table->currentColumn(); } + +void AxTableWidgetWrapper::setSortingEnabled(const bool enable) { table->setSortingEnabled(enable); } + +void AxTableWidgetWrapper::resizeToContent(const int column) { table->horizontalHeader()->setSectionResizeMode(column, QHeaderView::ResizeToContents); } + +QString AxTableWidgetWrapper::text(const int row, const int column) const { return table->item(row, column)->text(); } + +void AxTableWidgetWrapper::setText(const int row, const int column, const QString &text) const { table->item(row, column)->setText(text); } + +void AxTableWidgetWrapper::setReadOnly(const bool read) +{ + for(int rowIndex = 0; rowIndex < table->rowCount(); rowIndex++) { + for(int columnIndex = 0; columnIndex < table->rowCount(); columnIndex++) { + auto item = table->item(rowIndex, columnIndex); + if (read) + item->setFlags(item->flags() & ~Qt::ItemIsEditable); + else + item->setFlags(item->flags() | Qt::ItemIsEditable); + } + } +} + +void AxTableWidgetWrapper::hideColumn(const int column) { table->hideColumn(column); } + +void AxTableWidgetWrapper::setHeadersVisible(const bool enable) { table->horizontalHeader()->setVisible(enable); } + +void AxTableWidgetWrapper::setColumnAlign(const int column, const QString &align) +{ + int iAlign= Qt::AlignLeft | Qt::AlignVCenter; + if (align == "center") + iAlign = Qt::AlignCenter; + else if (align == "right") + iAlign = Qt::AlignRight | Qt::AlignVCenter; + + for(int rowIndex = 0; rowIndex < table->rowCount(); rowIndex++) { + table->item(rowIndex, column)->setTextAlignment(static_cast(iAlign)); + } +} + +void AxTableWidgetWrapper::clear() +{ + QSignalBlocker blocker(table->selectionModel()); + for (int row = table->rowCount() - 1; row >= 0; row--) { + for (int col = 0; col < table->columnCount(); ++col) + table->takeItem(row, col); + table->removeRow(row); + } +} + +QJSValue AxTableWidgetWrapper::selectedRows() +{ + QSet rowSet; + for( int rowIndex = 0 ; rowIndex < table->rowCount() ; rowIndex++ ) { + if ( table->item(rowIndex, 0)->isSelected() ) + rowSet.insert(rowIndex); + } + + QJSValue jsArray = engine->newArray(rowSet.size()); + int i = 0; + for (int row : rowSet) { + jsArray.setProperty(i++, row); + } + return jsArray; +} + +/// LIST + +AxListWidgetWrapper::AxListWidgetWrapper(QListWidget* widget, QJSEngine* engine, QObject* parent) : QObject(parent), list(widget), engine(engine) +{ + list->setAlternatingRowColors(true); + list->setSelectionMode(QAbstractItemView::ExtendedSelection); + list->setEditTriggers(QAbstractItemView::DoubleClicked); + + list->setItemDelegate(new ListDelegate(list)); + + connect(list, &QListWidget::currentTextChanged, this, &AxListWidgetWrapper::currentTextChanged); + connect(list, &QListWidget::currentRowChanged, this, &AxListWidgetWrapper::currentRowChanged); + connect(list, &QListWidget::itemClicked, this, [this](const QListWidgetItem* item) { if (item) emit itemClickedText(item->text()); }); + connect(list, &QListWidget::itemDoubleClicked, this, [this](const QListWidgetItem* item) { if (item) emit itemDoubleClickedText(item->text()); }); +} + +QVariant AxListWidgetWrapper::jsonMarshal() const +{ + QVariantList listData; + for (int i = 0; i < list->count(); ++i) { + QListWidgetItem* item = list->item(i); + if (item) + listData << item->text(); + } + return listData; +} + +void AxListWidgetWrapper::jsonUnmarshal(const QVariant& value) +{ + list->clear(); + const QVariantList items = value.toList(); + for (const QVariant& v : items) { + list->addItem(v.toString()); + } +} + +QListWidget* AxListWidgetWrapper::widget() const { return list; } + +QJSValue AxListWidgetWrapper::items() +{ + QJSValue jsArray = engine->newArray(list->count()); + for (int i = 0; i < list->count(); ++i) { + QListWidgetItem* item = list->item(i); + if (item) + jsArray.setProperty(i, item->text()); + } + return jsArray; +} + +void AxListWidgetWrapper::addItem(const QString& text) +{ + QListWidgetItem* item = new QListWidgetItem(text); + if (readonly) + item->setFlags(item->flags() & ~Qt::ItemIsEditable); + else + item->setFlags(item->flags() | Qt::ItemIsEditable); + list->addItem(item); +} + +void AxListWidgetWrapper::addItems(const QJSValue &items) +{ + if (!items.isArray()) + return; + + const int length = items.property("length").toInt(); + for (int i = 0; i < length; i++ ) { + QString text = items.property(i).toString(); + + QListWidgetItem* item = new QListWidgetItem(text); + if (readonly) + item->setFlags(item->flags() & ~Qt::ItemIsEditable); + else + item->setFlags(item->flags() | Qt::ItemIsEditable); + list->addItem(item); + } +} + +void AxListWidgetWrapper::clear() { list->clear(); } + +void AxListWidgetWrapper::removeItem(const int index) { delete list->takeItem(index); } + +void AxListWidgetWrapper::setItemText(const int index, const QString& text) +{ + QListWidgetItem* item = list->item(index); + if (item) + item->setText(text); +} + +QString AxListWidgetWrapper::itemText(const int index) const +{ + QListWidgetItem* item = list->item(index); + return item ? item->text() : QString(); +} + +int AxListWidgetWrapper::count() const { return list->count(); } + +int AxListWidgetWrapper::currentRow() const { return list->currentRow(); } + +void AxListWidgetWrapper::setCurrentRow(const int row) { list->setCurrentRow(row); } + +QJSValue AxListWidgetWrapper::selectedRows() const +{ + QList items = list->selectedItems(); + QJSValue array = engine->newArray(items.size()); + for (int i = 0; i < items.size(); ++i) { + array.setProperty(i, list->row(items[i])); + } + return array; +} + +void AxListWidgetWrapper::setReadOnly(const bool readonly) +{ + this->readonly = readonly; + for (int i = 0; i < list->count(); ++i) { + QListWidgetItem* item = list->item(i); + if (this->readonly) + item->setFlags(item->flags() & ~Qt::ItemIsEditable); + else + item->setFlags(item->flags() | Qt::ItemIsEditable); + } +} + +/// BUTTON + +AxButtonWrapper::AxButtonWrapper(QPushButton* btn, QObject* parent) : QObject(parent), button(btn) { + connect(button, &QPushButton::clicked, this, &AxButtonWrapper::clicked); +} + +QPushButton* AxButtonWrapper::widget() const { return button; } + +/// GROUPBOX + +AxGroupBoxWrapper::AxGroupBoxWrapper(const bool checkable, QGroupBox* box, QObject *parent) : QObject(parent), groupBox(box) +{ + groupBox->setCheckable(checkable); + + groupBox->setLayout(new QHBoxLayout()); + groupBox->setStyleSheet("QGroupBox { border: 1px solid; margin-top: 14px; padding: 0px; } QGroupBox::title { subcontrol-position: top left; }"); + connect(groupBox, &QGroupBox::clicked, this, &AxGroupBoxWrapper::clicked); +} + +QVariant AxGroupBoxWrapper::jsonMarshal() const { return groupBox->isChecked(); } + +void AxGroupBoxWrapper::jsonUnmarshal(const QVariant &value) { groupBox->setChecked(value.toBool()); } + +QGroupBox* AxGroupBoxWrapper::widget() const { return groupBox; } + +void AxGroupBoxWrapper::setTitle(const QString &title) { groupBox->setTitle(title); } + +bool AxGroupBoxWrapper::isCheckable() const { return groupBox->isCheckable(); } + +void AxGroupBoxWrapper::setCheckable(const bool checkable) { groupBox->setCheckable(checkable); } + +bool AxGroupBoxWrapper::isChecked() const { return groupBox->isChecked(); } + +void AxGroupBoxWrapper::setChecked(const bool checked) { groupBox->setChecked(checked); } + +void AxGroupBoxWrapper::setPanel(QObject* panel) const +{ + if (auto* widget = dynamic_cast(panel)) { + delete groupBox->layout(); + QHBoxLayout* layout = new QHBoxLayout(); + layout->setContentsMargins(1,1,1,1); + layout->addWidget(widget->widget()); + groupBox->setLayout(layout); + } +} + +/// SCROLLAREA + +AxScrollAreaWrapper::AxScrollAreaWrapper(QScrollArea* area, QObject* parent) : QObject(parent), scrollArea(area) { scrollArea->setWidgetResizable(true); } + +QScrollArea* AxScrollAreaWrapper::widget() const { return scrollArea; } + +void AxScrollAreaWrapper::setPanel(QObject* panel) const +{ + if (auto* widget = dynamic_cast(panel)) + scrollArea->setWidget(widget->widget()); +} + +void AxScrollAreaWrapper::setWidgetResizable(const bool resizable) { scrollArea->setWidgetResizable(resizable); } + + +/// SPLITTER + +AxSplitterWrapper::AxSplitterWrapper(QSplitter* splitter, QObject *parent) : QObject(parent), splitter(splitter) +{ + splitter->setHandleWidth(3); + connect(splitter, &QSplitter::splitterMoved, this, &AxSplitterWrapper::splitterMoved); +} + +QSplitter* AxSplitterWrapper::widget() const { return splitter; } + +void AxSplitterWrapper::addPage(QObject *w) +{ + if (auto* widget = dynamic_cast(w)) + return splitter->addWidget(widget->widget()); +} + +void AxSplitterWrapper::setSizes(const QVariantList &sizes) +{ + QList list; + for (const QVariant& v : sizes) + list << v.toInt(); + splitter->setSizes(list); +} + +/// STACK + +AxStackedWidgetWrapper::AxStackedWidgetWrapper(QStackedWidget* widget, QObject *parent): QObject(parent), stack(widget) { + connect(stack, &QStackedWidget::currentChanged, this, &AxStackedWidgetWrapper::currentChanged); +} + +QStackedWidget* AxStackedWidgetWrapper::widget() const { return stack; } + +int AxStackedWidgetWrapper::addPage(QObject* page) +{ + if (auto* widget = dynamic_cast(page)) + return stack->addWidget(widget->widget()); + + return -1; +} + +int AxStackedWidgetWrapper::insertPage(const int index, QObject *page) +{ + if (auto* widget = dynamic_cast(page)) + return stack->insertWidget(index, widget->widget()); + + return -1; +} + +void AxStackedWidgetWrapper::removePage(const int index) +{ + if (auto* page = stack->widget(index)) + stack->removeWidget(page); +} + +void AxStackedWidgetWrapper::setCurrentIndex(const int index) { stack->setCurrentIndex(index); } + +int AxStackedWidgetWrapper::currentIndex() const { return stack->currentIndex(); } + +int AxStackedWidgetWrapper::count() const { return stack->count(); } + +/// PANEL + +AxPanelWrapper::AxPanelWrapper(QWidget* w, QObject* parent) : QObject(parent), panel(w) {} + +QWidget* AxPanelWrapper::widget() const { return panel; } + +void AxPanelWrapper::setLayout(QObject* layoutWrapper) const +{ + if (auto* grid = qobject_cast(layoutWrapper)) + panel->setLayout(grid->layout()); + else if (auto* box = qobject_cast(layoutWrapper)) + panel->setLayout(box->layout()); +} + +/// CONTAINER + +AxContainerWrapper::AxContainerWrapper(QJSEngine* jsEngine, QObject* parent) : QObject(parent), engine(jsEngine) {} + +void AxContainerWrapper::put(const QString& id, QObject* wrapper) { widgets[id] = wrapper; } + +QObject* AxContainerWrapper::get(const QString &id) { return widgets[id]; } + +bool AxContainerWrapper::contains(const QString &id) const { return widgets.contains(id); } + +void AxContainerWrapper::remove(const QString& id) +{ + if (widgets.contains(id)) { + widgets[id]->deleteLater(); /// ToDo: ??? + widgets.remove(id); + } +} + +QString AxContainerWrapper::toJson() +{ + QJsonObject json; + for (auto it = widgets.begin(); it != widgets.end(); ++it) { + auto* formElement = dynamic_cast(it.value()); + if (!formElement) + continue; + + QJsonValue value = QJsonValue::fromVariant(formElement->jsonMarshal()); + json.insert(it.key(), value); + } + + QJsonDocument doc(json); + return QString::fromUtf8(doc.toJson(QJsonDocument::Compact)); +} + +void AxContainerWrapper::fromJson(const QString& jsonString) +{ + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(jsonString.toUtf8(), &error); + + if (error.error != QJsonParseError::NoError || !doc.isObject()) + return; + + QJsonObject obj = doc.object(); + + for (auto it = widgets.begin(); it != widgets.end(); ++it) { + auto* formElement = dynamic_cast(it.value()); + if (!formElement) + continue; + + QString key = it.key(); + if (obj.contains(key)) + formElement->jsonUnmarshal(obj.value(key).toVariant()); + } +} + +QJSValue AxContainerWrapper::toProperty() +{ + QJSValue result = engine->newObject(); + + for (auto it = widgets.begin(); it != widgets.end(); ++it) { + auto* formElement = dynamic_cast(it.value()); + if (!formElement) + continue; + result.setProperty(it.key(), formElement->jsonMarshal().toString()); + } + return result; +} + +void AxContainerWrapper::fromProperty(const QJSValue &obj) +{ + if (!obj.isObject()) + return; + + for (auto it = widgets.begin(); it != widgets.end(); ++it) { + auto* formElement = dynamic_cast(it.value()); + if (!formElement) + continue; + + QString key = it.key(); + if (obj.hasProperty(key)) + formElement->jsonUnmarshal(obj.property(key).toString()); + } +} + +/// DIALOG + +AxDialogWrapper::AxDialogWrapper(const QString& title, QWidget* parent) : QObject(parent) +{ + dialog = new QDialog(parent); + dialog->setWindowTitle(title); + layout = new QVBoxLayout(dialog); + + buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + layout->addWidget(buttons); + dialog->setLayout(layout); + + connect(buttons, &QDialogButtonBox::accepted, dialog, &QDialog::accept); + connect(buttons, &QDialogButtonBox::rejected, dialog, &QDialog::reject); +} + +void AxDialogWrapper::setLayout(QObject* layoutWrapper) +{ + if (userLayout) { + layout->removeItem(userLayout); + delete userLayout; + userLayout = nullptr; + } + + if (auto* grid = qobject_cast(layoutWrapper)) + userLayout = grid->layout(); + else if (auto* box = qobject_cast(layoutWrapper)) + userLayout = box->layout(); + + if (userLayout) + layout->insertLayout(0, userLayout); +} + +void AxDialogWrapper::setSize(const int w, const int h ) const { dialog->resize(w, h); } + +bool AxDialogWrapper::exec() const { return dialog->exec() == QDialog::Accepted; } + +void AxDialogWrapper::close() const { dialog->close(); } + +void AxDialogWrapper::setButtonsText(const QString &ok_text, const QString &cancel_text) const +{ + QPushButton *okButton = buttons->button(QDialogButtonBox::Ok); + if (okButton) { + okButton->setText(ok_text); + } + QPushButton *cancelButton = buttons->button(QDialogButtonBox::Cancel); + if (cancelButton) { + cancelButton->setText(cancel_text); + } +} + +/// FILE SELECTOR + +AxSelectorFile::AxSelectorFile(FileSelector* selector, QObject* parent) : QObject(parent), selector(selector) {} + +FileSelector* AxSelectorFile::widget() const { return selector; } + +QVariant AxSelectorFile::jsonMarshal() const { return selector->content; } + +void AxSelectorFile::jsonUnmarshal(const QVariant& value) +{ + selector->content = value.toString(); + selector->input->setText("Selected..."); +} + +/// SELECTOR CREDENTIALS + +AxDialogCreds::AxDialogCreds(const QJSValue &headers, QVector vecCreds, QTableWidget *tableWidget, QPushButton *button, QWidget *parent) : tableWidget(tableWidget), chooseButton(button) +{ + tableWidget->setAlternatingRowColors( true ); + tableWidget->setAutoFillBackground( false ); + tableWidget->setShowGrid( false ); + tableWidget->setSortingEnabled( true ); + tableWidget->setWordWrap( true ); + tableWidget->setCornerButtonEnabled( true ); + tableWidget->setSelectionBehavior( QAbstractItemView::SelectRows ); + tableWidget->setFocusPolicy( Qt::NoFocus ); + tableWidget->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch ); + tableWidget->horizontalHeader()->setCascadingSectionResizes( true ); + tableWidget->horizontalHeader()->setHighlightSections( false ); + tableWidget->verticalHeader()->setVisible( false ); + + chooseButton->setText("Choose"); + + spacer_1 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Maximum); + spacer_2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Maximum); + + bottomLayout = new QHBoxLayout(); + bottomLayout->addItem(spacer_1); + bottomLayout->addWidget(chooseButton); + bottomLayout->addItem(spacer_2); + + searchWidget = new QWidget(this); + + searchLineEdit = new QLineEdit(searchWidget); + searchLineEdit->setPlaceholderText("filter"); + + hideButton = new ClickableLabel("X"); + hideButton->setCursor( Qt::PointingHandCursor ); + + searchLayout = new QHBoxLayout(searchWidget); + searchLayout->setContentsMargins(0, 0, 0, 0); + searchLayout->setSpacing(4); + searchLayout->addWidget(searchLineEdit); + searchLayout->addWidget(hideButton); + + mainLayout = new QVBoxLayout(); + mainLayout->addWidget(searchWidget); + mainLayout->addWidget(tableWidget); + mainLayout->addLayout(bottomLayout); + + setLayout(mainLayout); + + connect(searchLineEdit, &QLineEdit::textEdited, this, &AxDialogCreds::handleSearch); + connect(chooseButton, &QPushButton::clicked, this, &AxDialogCreds::onClicked); + connect(hideButton, &ClickableLabel::clicked, this, &AxDialogCreds::clearSearch); + + for (auto cred : vecCreds) { + QMap map; + map["id"] = cred.CredId; + map["username"] = cred.Username; + map["password"] = cred.Password; + map["realm"] = cred.Realm; + map["type"] = cred.Type; + map["tag"] = cred.Tag; + map["date"] = cred.Date; + map["storage"] = cred.Storage; + map["agent_id"] = cred.AgentId; + map["host"] = cred.Host; + credList.append(map); + allData[cred.CredId] = cred; + } + + int columns = 0; + tableWidget->setColumnCount(columns); + + const int length = headers.property("length").toInt(); + for (int i = 0; i < length; ++i) { + QString val = headers.property(i).toString(); + if (FIELD_MAP_CREDS.contains(val)) { + columns += 1; + tableWidget->setColumnCount(columns); + tableWidget->setHorizontalHeaderItem(columns - 1, new QTableWidgetItem(FIELD_MAP_CREDS[val])); + table_headers.append(val); + } + } + columns += 1; + tableWidget->setColumnCount(columns); + tableWidget->setHorizontalHeaderItem(columns - 1, new QTableWidgetItem("CredId")); + table_headers.append("id"); + tableWidget->hideColumn(columns - 1); + + if (columns > 2) { + for (int i = 0 ; i < columns-2; ++i) { + tableWidget->horizontalHeader()->setSectionResizeMode(i, QHeaderView::ResizeToContents); + } + } + + tableWidget->setRowCount(credList.size()); + for (int col = 0; col < table_headers.size(); ++col) { + for (int row = 0; row < credList.size(); row++) { + auto header = table_headers[col]; + auto item = new QTableWidgetItem(credList[row][header]); + item->setFlags(item->flags() & ~Qt::ItemIsEditable); + tableWidget->setItem(row, col, item); + } + } + + handleSearch(); +} + +QVector AxDialogCreds::data() { return selectedData; } + +void AxDialogCreds::onClicked() +{ + selectedData.clear(); + int columns = tableWidget->columnCount(); + for( int rowIndex = 0 ; rowIndex < tableWidget->rowCount() ; rowIndex++ ) { + if ( tableWidget->item(rowIndex, 0)->isSelected() ) { + QString id = tableWidget->item(rowIndex, columns-1)->text(); + selectedData.append(allData[id]); + } + } + this->accept(); +} + +void AxDialogCreds::handleSearch() +{ + QString filterText = searchLineEdit->text(); + + for (int row = 0; row < tableWidget->rowCount(); row++) { + bool match = false; + for (int col = 0; col < tableWidget->columnCount()-1; ++col) { + if (tableWidget->item(row, col) && tableWidget->item(row, col)->text().contains(filterText, Qt::CaseInsensitive)) { + match = true; + break; + } + } + tableWidget->setRowHidden(row, !match); + } +} + +void AxDialogCreds::clearSearch() +{ + searchLineEdit->clear(); + handleSearch(); +} + +AxSelectorCreds::AxSelectorCreds(const QJSValue &headers, QTableWidget* tableWidget, QPushButton* button, AxScriptEngine* jsEngine, QWidget* parent) : QObject(parent), scriptEngine(jsEngine) +{ + auto vecCreds = scriptEngine->manager()->GetCredentials(); + + dialog = new AxDialogCreds(headers, vecCreds, tableWidget, button); + dialog->setWindowTitle("Choose credentials"); +} + +void AxSelectorCreds::setSize(const int w, const int h ) const { dialog->resize(w, h); } + +QJSValue AxSelectorCreds::exec() const +{ + QVector vecCreds; + if (dialog->exec() == QDialog::Accepted) { + vecCreds = dialog->data(); + } + + QVariantList list; + for (auto cred : vecCreds) { + QVariantMap map; + map["id"] = cred.CredId; + map["username"] = cred.Username; + map["password"] = cred.Password; + map["realm"] = cred.Realm; + map["type"] = cred.Type; + map["tag"] = cred.Tag; + map["date"] = cred.Date; + map["storage"] = cred.Storage; + map["agent_id"] = cred.AgentId; + map["host"] = cred.Host; + list.append(map); + } + return this->scriptEngine->engine()->toScriptValue(list); +} + +void AxSelectorCreds::close() const { dialog->close(); } \ No newline at end of file diff --git a/AdaptixClient/Source/Client/AxScript/AxScriptEngine.cpp b/AdaptixClient/Source/Client/AxScript/AxScriptEngine.cpp new file mode 100644 index 00000000..aa61936c --- /dev/null +++ b/AdaptixClient/Source/Client/AxScript/AxScriptEngine.cpp @@ -0,0 +1,157 @@ +#include +#include +#include +#include +#include +#include + +AxScriptEngine::AxScriptEngine(AxScriptManager* script_manager, const QString &name, QObject *parent) : QObject(parent), scriptManager(script_manager) +{ + jsEngine = std::make_unique(); + jsEngine->installExtensions(QJSEngine::ConsoleExtension); + + bridgeApp = std::make_unique(this, this); + bridgeForm = std::make_unique(this, this); + bridgeEvent = std::make_unique(this, this); + bridgeMenu = std::make_unique(this, this); + + jsEngine->globalObject().setProperty("ax", jsEngine->newQObject(bridgeApp.get())); + jsEngine->globalObject().setProperty("form", jsEngine->newQObject(bridgeForm.get())); + jsEngine->globalObject().setProperty("event", jsEngine->newQObject(bridgeEvent.get())); + jsEngine->globalObject().setProperty("menu", jsEngine->newQObject(bridgeMenu.get())); + + connect(bridgeApp.get(), &BridgeApp::consoleError, script_manager, &AxScriptManager::consolePrintError); + connect(bridgeApp.get(), &BridgeApp::consoleMessage, script_manager, &AxScriptManager::consolePrintMessage); + connect(bridgeApp.get(), &BridgeApp::engineError, this, &AxScriptEngine::engineError); + connect(bridgeForm.get(), &BridgeForm::scriptError, this, &AxScriptEngine::engineError); + connect(bridgeEvent.get(), &BridgeEvent::scriptError, this, &AxScriptEngine::engineError); + + context.name = name; +} + +AxScriptEngine::~AxScriptEngine() +{ + for (auto action : context.actions) { + if (action) delete action; + } + context.actions.clear(); +/* + for (auto obj : context.objects){ + if (obj) delete obj; + } +*/ + context.objects.clear(); + + bridgeApp.reset(); + bridgeForm.reset(); + bridgeEvent.reset(); + bridgeMenu.reset(); + jsEngine.reset(); +} + +QJSEngine* AxScriptEngine::engine() const { return jsEngine.get(); } + +BridgeApp* AxScriptEngine::app() const { return bridgeApp.get(); } + +BridgeForm* AxScriptEngine::form() const { return bridgeForm.get(); } + +BridgeEvent* AxScriptEngine::event() const { return bridgeEvent.get(); } + +BridgeMenu* AxScriptEngine::menu() const { return bridgeMenu.get(); } + +AxScriptManager* AxScriptEngine::manager() const { return this->scriptManager; } + +void AxScriptEngine::registerObject(QObject *obj) { context.objects.append(obj); } + +void AxScriptEngine::registerAction(QAction *action) { context.actions.append(action); } + +///// + +void AxScriptEngine::registerEvent(const QString &type, const QJSValue &handler, const QSet &list_agents, const QSet &list_os, const QSet &list_listeners, const QString &id) +{ + QSet os; + if (list_os.contains("windows")) os.insert(1); + if (list_os.contains("linux")) os.insert(2); + if (list_os.contains("macos")) os.insert(3); + + AxEvent event = {handler, id, list_agents, list_listeners, os, jsEngine.get()}; + + if ( type == "FileBroserDisks") context.eventFileBroserDisks.append(event); + else if (type == "FileBroserList") context.eventFileBroserList.append(event); + else if (type == "FileBroserUpload") context.eventFileBroserUpload.append(event); + else if (type == "ProcessBrowserList") context.eventProcessBrowserList.append(event); +} + +QList AxScriptEngine::getEvents(const QString &type) +{ + if ( type == "FileBroserDisks") return context.eventFileBroserDisks; + else if (type == "FileBroserList") return context.eventFileBroserList; + else if (type == "FileBroserUpload") return context.eventFileBroserUpload; + else if (type == "ProcessBrowserList") return context.eventProcessBrowserList; + + return QList(); +} + +void AxScriptEngine::removeEvent(const QString &id) +{ + for (int i=0; i< context.eventFileBroserDisks.size(); i++) { + if (id == context.eventFileBroserDisks[i].event_id) { + context.eventFileBroserDisks.removeAt(i); + i--; + } + } +} + + +///// + +void AxScriptEngine::registerMenu(const QString &type, AbstractAxMenuItem *menu, const QSet &list_agents, const QSet &list_os, const QSet &list_listeners) +{ + QSet os; + if (list_os.contains("windows")) os.insert(1); + if (list_os.contains("linux")) os.insert(2); + if (list_os.contains("macos")) os.insert(3); + + AxMenuItem item = {menu, list_agents, list_listeners, os}; + + if ( type == "SessionMain") context.menuSessionMain.append(item); + else if (type == "SessionAgent") context.menuSessionAgent.append(item); + else if (type == "SessionBrowser") context.menuSessionBrowser.append(item); + else if (type == "SessionAccess") context.menuSessionAccess.append(item); + else if (type == "FileBrowser") context.menuFileBrowser.append(item); + else if (type == "ProcessBrowser") context.menuProcessBrowser.append(item); + else if (type == "DownloadRunning") context.menuDownloadRunning.append(item); + else if (type == "DownloadFinished") context.menuDownloadFinished.append(item); + else if (type == "Tasks") context.menuTasks.append(item); + else if (type == "TasksJob") context.menuTasksJob.append(item); +} + +QList AxScriptEngine::getMenuItems(const QString &type) +{ + if ( type == "SessionMain") return context.menuSessionMain; + else if (type == "SessionAgent") return context.menuSessionAgent; + else if (type == "SessionBrowser") return context.menuSessionBrowser; + else if (type == "SessionAccess") return context.menuSessionAccess; + else if (type == "FileBrowser") return context.menuFileBrowser; + else if (type == "ProcessBrowser") return context.menuProcessBrowser; + else if (type == "DownloadRunning") return context.menuDownloadRunning; + else if (type == "DownloadFinished") return context.menuDownloadFinished; + else if (type == "Tasks") return context.menuTasks; + else if (type == "TasksJob") return context.menuTasksJob; + + return QList(); +} + +void AxScriptEngine::engineError(const QString &message) { engine()->throwError(QJSValue::TypeError, message); } + +bool AxScriptEngine::execute(const QString &code) +{ + QJSValue result = jsEngine->evaluate(code, context.name); + context.scriptObject = result; + if (result.isError()) { + QString error = QStringLiteral("%1\n at line %2 in %3\n stack: %4\n").arg(result.toString()).arg(result.property("lineNumber").toInt()).arg(context.name).arg(result.property("stack").toString()); + scriptManager->consolePrintError(error); + return false; + } + return true; +} diff --git a/AdaptixClient/Source/Client/AxScript/AxScriptManager.cpp b/AdaptixClient/Source/Client/AxScript/AxScriptManager.cpp new file mode 100644 index 00000000..c651cc26 --- /dev/null +++ b/AdaptixClient/Source/Client/AxScript/AxScriptManager.cpp @@ -0,0 +1,522 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +AxScriptManager::AxScriptManager(AdaptixWidget* main_widget, QObject *parent): QObject(parent), adaptixWidget(main_widget) { + mainScript = new AxScriptEngine(this, "main", this); +} + +AxScriptManager::~AxScriptManager() = default; + +QJSEngine* AxScriptManager::MainScriptEngine() { return mainScript->engine(); } + +void AxScriptManager::Clear() +{ + ResetMain(); + + qDeleteAll(agents_scripts); + agents_scripts.clear(); + qDeleteAll(listeners_scripts); + listeners_scripts.clear(); + qDeleteAll(scripts); + scripts.clear(); +} + +void AxScriptManager::ResetMain() +{ + auto commanderList = adaptixWidget->GetCommandersAll(); + for (auto commander : commanderList) + commander->RemoveAxCommands(mainScript->context.name); + + if (mainScript) + delete mainScript; + + mainScript = new AxScriptEngine(this, "main", this); +} + +QJSEngine* AxScriptManager::GetEngine(const QString &name) +{ + if (agents_scripts.contains(name) && agents_scripts[name]) + return agents_scripts[name]->engine(); + + if (scripts.contains(name) && scripts[name]) + return scripts[name]->engine(); + + if (name == "main" && mainScript) + return mainScript->engine(); + + return nullptr; +} + +AdaptixWidget* AxScriptManager::GetAdaptix() const { return adaptixWidget; } + +QMap AxScriptManager::GetAgents() const { return adaptixWidget->AgentsMap; } + +QVector AxScriptManager::GetCredentials() const { return adaptixWidget->Credentials; } + +QStringList AxScriptManager::GetInterfaces() const { return adaptixWidget->addresses; } + +/// MAIN + +QStringList AxScriptManager::ListenerScriptList() { return listeners_scripts.keys(); } + +void AxScriptManager::ListenerScriptAdd(const QString &name, const QString &ax_script) +{ + if (listeners_scripts.contains(name)) + return; + + AxScriptEngine* script = new AxScriptEngine(this, name, this); + script->execute(ax_script); + + listeners_scripts[name] = script; +} + +QJSEngine* AxScriptManager::ListenerScriptEngine(const QString &name) +{ + if (!listeners_scripts.contains(name)) return nullptr; + return listeners_scripts[name]->engine(); +} + + + +QStringList AxScriptManager::AgentScriptList() { return agents_scripts.keys(); } + +void AxScriptManager::AgentScriptAdd(const QString &name, const QString &ax_script) +{ + if (agents_scripts.contains(name)) return; + + AxScriptEngine* script = new AxScriptEngine(this, name, this); + script->execute(ax_script); + + agents_scripts[name] = script; +} + +QJSEngine* AxScriptManager::AgentScriptEngine(const QString &name) +{ + if (!agents_scripts.contains(name)) return nullptr; + return agents_scripts[name]->engine(); +} + +QJSValue AxScriptManager::AgentScriptExecute(const QString &name, const QString &code) +{ + QJSValue result; + if (agents_scripts.contains(name)) { + QJSValue func = agents_scripts[name]->engine()->globalObject().property(code); + if (func.isCallable()) { + QJSValueList args; + args << QJSValue("BeaconHTTP"); + result = func.call(args); + } + } + return result; +} + + + +QStringList AxScriptManager::ScriptList() { return scripts.keys(); } + +bool AxScriptManager::ScriptAdd(ExtensionFile* ext) +{ + AxScriptEngine* script = new AxScriptEngine(this, ext->FilePath, this); + scripts[ext->FilePath] = script; + bool result = script->execute(ext->Code); + if (result) { + QJSValue metadata = script->engine()->globalObject().property("metadata"); + if (metadata.isObject()) { + ext->Name = metadata.property("name").toString(); + ext->Description = metadata.property("description").toString(); + ext->NoSave = metadata.property("nosave").toBool(); + } + } + else { + ext->Enabled = false; + ext->Message = QString("%1\n at line %2 in %3").arg(script->context.scriptObject.toString()).arg(script->context.scriptObject.property("lineNumber").toInt()).arg(ext->FilePath); + } + return result; +} + +void AxScriptManager::ScriptRemove(const ExtensionFile &ext) +{ + auto scriptEngine = scripts.take(ext.FilePath); + + auto commanderList = adaptixWidget->GetCommandersAll(); + for (auto commander : commanderList) + commander->RemoveAxCommands(ext.FilePath); + + delete scriptEngine; +} + + + +void AxScriptManager::GlobalScriptLoad(const QString &path) { emit adaptixWidget->LoadGlobalScriptSignal(path); } + +void AxScriptManager::GlobalScriptUnload(const QString &path) { emit adaptixWidget->UnloadGlobalScriptSignal(path); } + +void AxScriptManager::RegisterCommandsGroup(const CommandsGroup &group, const QStringList &listeners, const QStringList &agents, const QList &os) +{ + auto commanderList = adaptixWidget->GetCommanders(listeners, agents, os); + for (auto commander : commanderList) + commander->AddAxCommands(group); +} + +void AxScriptManager::RemoveEvent(const QString &event_id) +{ + QList list = this->agents_scripts.values() + this->scripts.values(); + list.append(this->mainScript); + + for (const auto script : list) + script->removeEvent(event_id); +} + +QList AxScriptManager::FilterMenuItems(const QStringList &agentIds, const QString &menuType) +{ + QSet agentTypes; + QSet listenerTypes; + QSet osTypes; + for (auto agent_id: agentIds) { + if (adaptixWidget->AgentsMap.contains(agent_id)) { + agentTypes.insert(adaptixWidget->AgentsMap[agent_id]->data.Name); + osTypes.insert(adaptixWidget->AgentsMap[agent_id]->data.Os); + listenerTypes.insert(adaptixWidget->AgentsMap[agent_id]->listenerType); + } + } + + QList list = this->agents_scripts.values() + this->scripts.values(); + list.append(this->mainScript); + + QList items; + for (const auto script : list) + items += script->getMenuItems(menuType); + + QList ret; + for (int i = 0; i < items.size(); ++i) { + AxMenuItem item = items[i]; + + if ( !item.agents.contains(agentTypes) ) + continue; + if (item.os.size() > 0 && !item.os.contains(osTypes)) + continue; + if (item.listenerts.size() > 0 && !item.listenerts.contains(listenerTypes)) + continue; + + ret.append(item); + } + return ret; +} + +QList AxScriptManager::FilterEvents(const QString &agentId, const QString &eventType) +{ + QList ret; + + if( !adaptixWidget->AgentsMap.contains(agentId) ) + return ret; + + QString agentType = adaptixWidget->AgentsMap[agentId]->data.Name; + QString listenerType = adaptixWidget->AgentsMap[agentId]->listenerType; + int osType = adaptixWidget->AgentsMap[agentId]->data.Os; + + QList list = this->agents_scripts.values() + this->scripts.values(); + list.append(this->mainScript); + + QList items; + for (const auto script : list) { + items += script->getEvents(eventType); + } + + for (int i = 0; i < items.size(); ++i) { + AxEvent event = items[i]; + + if ( !event.agents.contains(agentType) ) + continue; + if (event.os.size() > 0 && !event.os.contains(osType)) + continue; + if (event.listenerts.size() > 0 && !event.listenerts.contains(listenerType)) + continue; + + ret.append(event); + } + return ret; +} + +/// APP + +void AxScriptManager::AppAgentSetColor(const QStringList &agents, const QString &background, const QString &foreground, const bool reset) +{ + QString message = ""; + bool ok = false; + HttpReqAgentSetColor(agents, background, foreground, reset, *(adaptixWidget->GetProfile()), &message, &ok); +} + +void AxScriptManager::AppAgentSetImpersonate(const QString &id, const QString &impersonate, const bool elevated) +{ + QString message = ""; + bool ok = false; + HttpReqAgentSetImpersonate(id, impersonate, elevated, *(adaptixWidget->GetProfile()), &message, &ok); +} + +void AxScriptManager::AppAgentSetMark(const QStringList &agents, const QString &mark) +{ + QString message = ""; + bool ok = false; + HttpReqAgentSetMark(agents, mark, *(adaptixWidget->GetProfile()), &message, &ok); +} + +void AxScriptManager::AppAgentSetTag(const QStringList &agents, const QString &tag) +{ + QString message = ""; + bool ok = false; + HttpReqAgentSetTag(agents, tag, *(adaptixWidget->GetProfile()), &message, &ok); +} + +/// MENU + +int AxScriptManager::AddMenuSession(QMenu *menu, const QString &menuType, QStringList agentIds) +{ + QVariantList context; + for (auto agent_id: agentIds) { + if (adaptixWidget->AgentsMap.contains(agent_id)) { + context << agent_id; + } + } + int count = 0; + QList items = this->FilterMenuItems(agentIds, menuType); + for (int i = 0; i < items.size(); ++i) { + AxMenuItem item = items[i]; + + item.menu->setContext(context); + if (auto* item1 = dynamic_cast(item.menu)) + menu->addAction(item1->action()); + else if (auto* item2 = dynamic_cast(item.menu)) + menu->addAction(item2->action()); + else if (auto* item3 = dynamic_cast(item.menu)) + menu->addMenu(item3->menu()); + else + continue; + count++; + } + return count; +} + +int AxScriptManager::AddMenuFileBrowser(QMenu *menu, QVector files) +{ + if (files.empty()) return 0; + + QVariantList context; + for (auto file : files) { + if (adaptixWidget->AgentsMap.contains(file.agentId)) { + QVariantMap map; + map["agent_id"] = file.agentId; + map["path"] = file.path; + map["name"] = file.name; + map["type"] = file.type; + context << map; + } + } + + int count = 0; + QList items = this->FilterMenuItems(QStringList() << files[0].agentId, "FileBrowser"); + for (int i = 0; i < items.size(); ++i) { + AxMenuItem item = items[i]; + + item.menu->setContext(context); + if (auto* item1 = dynamic_cast(item.menu)) + menu->addAction(item1->action()); + else if (auto* item2 = dynamic_cast(item.menu)) + menu->addAction(item2->action()); + else if (auto* item3 = dynamic_cast(item.menu)) + menu->addMenu(item3->menu()); + else + continue; + count++; + } + return count; +} + +int AxScriptManager::AddMenuProcessBrowser(QMenu *menu, QVector processes) +{ + if (processes.empty()) return 0; + + QVariantList context; + for (auto proc : processes) { + if (adaptixWidget->AgentsMap.contains(proc.agentId)) { + QVariantMap map; + map["agent_id"] = proc.agentId; + map["pid"] = proc.pid; + map["ppid"] = proc.ppid; + map["arch"] = proc.arch; + map["session_id"] = proc.session_id; + map["context"] = proc.context; + map["process"] = proc.process; + context << map; + } + } + + int count = 0; + QList items = this->FilterMenuItems(QStringList() << processes[0].agentId, "ProcessBrowser"); + for (int i = 0; i < items.size(); ++i) { + AxMenuItem item = items[i]; + + item.menu->setContext(context); + if (auto* item1 = dynamic_cast(item.menu)) + menu->addAction(item1->action()); + else if (auto* item2 = dynamic_cast(item.menu)) + menu->addAction(item2->action()); + else if (auto* item3 = dynamic_cast(item.menu)) + menu->addMenu(item3->menu()); + else + continue; + count++; + } + return count; +} + +int AxScriptManager::AddMenuDownload(QMenu *menu, const QString &menuType, QVector files) +{ + if (files.empty()) return 0; + + QVariantList context; + for (auto file : files) { + if (adaptixWidget->AgentsMap.contains(file.agentId)) { + QVariantMap map; + map["agent_id"] = file.agentId; + map["file_id"] = file.fileId; + map["path"] = file.path; + map["state"] = file.state; + context << map; + } + } + + int count = 0; + QList items = this->FilterMenuItems(QStringList() << files[0].agentId, menuType); + for (int i = 0; i < items.size(); ++i) { + AxMenuItem item = items[i]; + + item.menu->setContext(context); + if (auto* item1 = dynamic_cast(item.menu)) + menu->addAction(item1->action()); + else if (auto* item2 = dynamic_cast(item.menu)) + menu->addAction(item2->action()); + else if (auto* item3 = dynamic_cast(item.menu)) + menu->addMenu(item3->menu()); + else + continue; + count++; + } + return count; +} + +int AxScriptManager::AddMenuTask(QMenu *menu, const QString &menuType, const QStringList &tasks) +{ + if (tasks.empty()) return 0; + + QSet agents; + + QVariantList context; + for (auto taskId : tasks) { + if (adaptixWidget->TasksMap.contains(taskId) && adaptixWidget->TasksMap[taskId]) { + TaskData taskData = adaptixWidget->TasksMap[taskId]->data; + QVariantMap map; + map["agent_id"] = taskData.AgentId; + map["task_id"] = taskData.TaskId; + map["state"] = taskData.Status; + if ( taskData.TaskType == 1 ) + map["type"] = "TASK"; + else if ( taskData.TaskType == 3 ) + map["type"] = "JOB"; + else if ( taskData.TaskType == 4 ) + map["type"] = "TUNNEL"; + else + map["type"] = "unknown"; + + context << map; + agents.insert(taskData.AgentId); + } + } + + int count = 0; + QList items = this->FilterMenuItems(QList(agents.begin(), agents.end()), menuType); + for (int i = 0; i < items.size(); ++i) { + AxMenuItem item = items[i]; + + item.menu->setContext(context); + if (auto* item1 = dynamic_cast(item.menu)) + menu->addAction(item1->action()); + else if (auto* item2 = dynamic_cast(item.menu)) + menu->addAction(item2->action()); + else if (auto* item3 = dynamic_cast(item.menu)) + menu->addMenu(item3->menu()); + else + continue; + count++; + } + return count; +} + + +/// EVENT + +void AxScriptManager::emitFileBrowserDisks(const QString &agentId) +{ + QList items = this->FilterEvents(agentId, "FileBroserDisks"); + for (int i = 0; i < items.size(); ++i) { + AxEvent event = items[i]; + if (event.jsEngine) { + QJSValue argId = event.jsEngine->toScriptValue(agentId); + event.handler.call(QJSValueList() << argId); + } + } +} + +void AxScriptManager::emitFileBrowserList(const QString &agentId, const QString &path) +{ + QList items = this->FilterEvents(agentId, "FileBroserList"); + for (int i = 0; i < items.size(); ++i) { + AxEvent event = items[i]; + if (event.jsEngine) { + QJSValue argId = event.jsEngine->toScriptValue(agentId); + QJSValue argPath = event.jsEngine->toScriptValue(path); + event.handler.call(QJSValueList() << argId << argPath); + } + } +} + +void AxScriptManager::emitFileBrowserUpload(const QString &agentId, const QString &path, const QString &localFilename) +{ + QList items = this->FilterEvents(agentId, "FileBroserUpload"); + for (int i = 0; i < items.size(); ++i) { + AxEvent event = items[i]; + if (event.jsEngine) { + QJSValue argId = event.jsEngine->toScriptValue(agentId); + QJSValue argPath = event.jsEngine->toScriptValue(path); + QJSValue argFile = event.jsEngine->toScriptValue(localFilename); + event.handler.call(QJSValueList() << argId << argPath << argFile); + } + } +} + +void AxScriptManager::emitProcessBrowserList(const QString &agentId) +{ + QList items = this->FilterEvents(agentId, "ProcessBrowserList"); + for (int i = 0; i < items.size(); ++i) { + AxEvent event = items[i]; + if (event.jsEngine) { + QJSValue argId = event.jsEngine->toScriptValue(agentId); + event.handler.call(QJSValueList() << argId); + } + } +} + +/// SLOTS + +void AxScriptManager::consolePrintMessage(const QString &message) { this->adaptixWidget->AxConsoleTab->PrintMessage(message); } + +void AxScriptManager::consolePrintError(const QString &message) { this->adaptixWidget->AxConsoleTab->PrintError(message); } diff --git a/AdaptixClient/Source/Client/AxScript/BridgeApp.cpp b/AdaptixClient/Source/Client/AxScript/BridgeApp.cpp new file mode 100644 index 00000000..516783a5 --- /dev/null +++ b/AdaptixClient/Source/Client/AxScript/BridgeApp.cpp @@ -0,0 +1,564 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +BridgeApp::BridgeApp(AxScriptEngine* scriptEngine, QObject* parent) : QObject(parent), scriptEngine(scriptEngine), widget(new QWidget()){} + +BridgeApp::~BridgeApp() { delete widget; } + +AxScriptEngine* BridgeApp::GetScriptEngine() const { return this->scriptEngine; } + + + +QJSValue BridgeApp::agents() const +{ + QVariantMap list; + auto mapAgents = scriptEngine->manager()->GetAgents(); + + for (auto agent : mapAgents) { + QVariantMap map; + map["id"] = agent->data.Id; + map["type"] = agent->data.Name; + map["listener"] = agent->data.Listener; + map["external_ip"] = agent->data.ExternalIP; + map["internal_ip"] = agent->data.InternalIP; + map["domain"] = agent->data.Domain; + map["computer"] = agent->data.Computer; + map["username"] = agent->data.Username; + map["impersonated"] = agent->data.Impersonated; + map["process"] = agent->data.Process; + map["arch"] = agent->data.Arch; + map["pid"] = agent->data.Pid.toInt(); + map["tid"] = agent->data.Tid.toInt(); + map["gmt"] = agent->data.GmtOffset; + map["elevated"] = agent->data.Elevated; + map["tags"] = agent->data.Tags; + map["async"] = agent->data.Async; + map["sleep"] = agent->data.Sleep; + map["os_full"] = agent->data.OsDesc; + + if (agent->data.Os == OS_WINDOWS) map["os"] = "windows"; + else if (agent->data.Os == OS_LINUX) map["os"] = "linux"; + else if (agent->data.Os == OS_MAC) map["os"] = "macos"; + else map["os"] = "unknown"; + + list[agent->data.Id] = map; + } + + return this->scriptEngine->engine()->toScriptValue(list); +} + +QJSValue BridgeApp::agent_info(const QString &id, const QString &property) const +{ + auto mapAgents = scriptEngine->manager()->GetAgents(); + if (!mapAgents.contains(id)) + return false; + + QJSValue ret; + auto info = mapAgents[id]->data; + + if (property == "id") + return QJSValue(info.Id); + if (property == "type") + return QJSValue(info.Name); + if (property == "listener") + return QJSValue(info.Listener); + if (property == "externalIP") + return QJSValue(info.ExternalIP); + if (property == "internalIP") + return QJSValue(info.InternalIP); + if (property == "domain") + return QJSValue(info.Domain); + if (property == "computer") + return QJSValue(info.Computer); + if (property == "username") + return QJSValue(info.Username); + if (property == "impersonated") + return QJSValue(info.Impersonated); + if (property == "process") + return QJSValue(info.Process); + if (property == "arch") + return QJSValue(info.Arch); + if (property == "pid") + return QJSValue(info.Pid.toInt()); + if (property == "tid") + return QJSValue(info.Tid.toInt()); + if (property == "gmt") + return QJSValue(info.GmtOffset); + if (property == "elevated") + return QJSValue(info.Elevated); + if (property == "tags") + return QJSValue(info.Tags); + if (property == "async") + return QJSValue(info.Async); + if (property == "sleep") + return QJSValue(info.Sleep); + if (property == "os_full") + return QJSValue(info.OsDesc); + if (property == "os") { + if (info.Os == OS_WINDOWS) return "windows"; + else if (info.Os == OS_LINUX) return "linux"; + else if (info.Os == OS_MAC) return "macos"; + else return "unknown"; + } + + return QJSValue::UndefinedValue; +} + +void BridgeApp::agent_set_color(const QJSValue &agents, const QString &background, const QString &foreground, const bool reset) +{ + if (agents.isUndefined() || agents.isNull() || !agents.isArray()) { + emit engineError("agent_set_color expected array of strings in agents parameter!"); + return; + } + + QStringList list_agents; + for (int i = 0; i < agents.property("length").toInt(); ++i) { + QJSValue val = agents.property(i); + list_agents << val.toString(); + } + + scriptEngine->manager()->AppAgentSetColor(list_agents, background, foreground, reset); +} + +void BridgeApp::agent_set_impersonate(const QString &id, const QString &impersonate, const bool elevated) { scriptEngine->manager()->AppAgentSetImpersonate(id, impersonate, elevated); } + +void BridgeApp::agent_set_mark(const QJSValue &agents, const QString &mark) +{ + if (agents.isUndefined() || agents.isNull() || !agents.isArray()) { + emit engineError("agent_set_color expected array of strings in agents parameter!"); + return; + } + + QStringList list_agents; + for (int i = 0; i < agents.property("length").toInt(); ++i) { + QJSValue val = agents.property(i); + list_agents << val.toString(); + } + + scriptEngine->manager()->AppAgentSetMark(list_agents, mark); +} + +void BridgeApp::agent_set_tag(const QJSValue &agents, const QString &tag) +{ + if (agents.isUndefined() || agents.isNull() || !agents.isArray()) { + emit engineError("agent_set_color expected array of strings in agents parameter!"); + return; + } + + QStringList list_agents; + for (int i = 0; i < agents.property("length").toInt(); ++i) { + QJSValue val = agents.property(i); + list_agents << val.toString(); + } + + scriptEngine->manager()->AppAgentSetTag(list_agents, tag); +} + +QString BridgeApp::arch(const QString &id) const +{ + auto mapAgents = scriptEngine->manager()->GetAgents(); + if (!mapAgents.contains(id)) + return "x86"; + + return mapAgents[id]->data.Arch; +} + +QString BridgeApp::bof_pack(const QString &types, const QJSValue &args) +{ + if (!args.isArray()) { + emit engineError("bof_pack expected array of arguments!"); + return ""; + } + + QStringList items = types.split(",", Qt::SkipEmptyParts); + int length = args.property("length").toInt(); + + if (items.size() != length) { + emit engineError("bof_pack expects the same number of types and arguments!"); + return ""; + } + + QByteArray data; + + for (int i = 0; i < length; ++i) { + QVariant value = args.property(i).toVariant(); + + if (items[i] == "cstr") { + if (!value.canConvert()) { + emit engineError(QString("bof_pack cannot convert argument at index %1 to string").arg(i)); + return ""; + } + + QByteArray valueData = value.toString().toUtf8(); + int strLength = valueData.size() + 1; + + QByteArray valueLengthData; + valueLengthData.append(reinterpret_cast(&strLength), 4); + data.append(valueLengthData); + + valueData.append('\0'); + data.append(valueData); + } + else if (items[i] == "wstr") { + if (!value.canConvert()) { + emit engineError(QString("bof_pack cannot convert argument at index %1 to string").arg(i)); + return ""; + } + + QString str = value.toString(); + const char16_t* utf16Data = reinterpret_cast(str.utf16()); + int utf16Length = str.size() + 1; + + QByteArray strData; + strData.append(reinterpret_cast(utf16Data), utf16Length * sizeof(char16_t)); + + QByteArray strLengthData; + int strLength = utf16Length * sizeof(char16_t); + strLengthData.append(reinterpret_cast(&strLength), 4); + + data.append(strLengthData); + data.append(strData); + } + else if (items[i] == "bytes") { + if (!value.canConvert()) { + emit engineError(QString("bof_pack cannot convert argument at index %1 to string").arg(i)); + return ""; + } + + QByteArray valueData = QByteArray::fromBase64(value.toString().toUtf8()); + int strLength = valueData.size(); + + QByteArray valueLengthData; + valueLengthData.append(reinterpret_cast(&strLength), 4); + data.append(valueLengthData); + data.append(valueData); + } + else if (items[i] == "int") { + if (!value.canConvert()) { + emit engineError(QString("bof_pack cannot convert argument at index %1 to int").arg(i)); + return ""; + } + + int num = value.toInt(); + QByteArray numData; + numData.append(reinterpret_cast(&num), sizeof(num)); + data.append(numData); + } + else if (items[i] == "short") { + if (!value.canConvert()) { + emit engineError(QString("bof_pack cannot convert argument at index %1 to short").arg(i)); + return ""; + } + + short num = static_cast(value.toInt()); + QByteArray numData; + numData.append(reinterpret_cast(&num), sizeof(num)); + data.append(numData); + } + else { + emit engineError(QString("bof_pack does not expect type '%1' (index %2)").arg(items[i]).arg(i)); + return ""; + } + } + + QByteArray strLengthData; + int strLength = data.size(); + strLengthData.append(reinterpret_cast(&strLength), sizeof(strLength)); + + strLengthData.append(data); + return strLengthData.toBase64(); +} + +void BridgeApp::copy_to_clipboard(const QString &text) { QApplication::clipboard()->setText(text); } + +void BridgeApp::console_message(const QString &id, const QString &message, const QString &type, const QString &text) +{ + auto mapAgents = scriptEngine->manager()->GetAgents(); + if (!mapAgents.contains(id)) + return; + + auto agent = mapAgents[id]; + if (!agent) + return; + + int msgType = CONSOLE_OUT; + if (type == "info") + msgType = CONSOLE_OUT_LOCAL_INFO; + else if (type == "success") + msgType = CONSOLE_OUT_LOCAL_SUCCESS; + else if (type == "error") + msgType = CONSOLE_OUT_LOCAL_ERROR; + + agent->Console->ConsoleOutputMessage(QDateTime::currentSecsSinceEpoch(), "", msgType, message, text, false); +} + +QJSValue BridgeApp::credentials() const +{ + QVariantMap list; + auto vecCreds = scriptEngine->manager()->GetCredentials(); + + for (auto cred : vecCreds) { + QVariantMap map; + map["id"] = cred.CredId; + map["username"] = cred.Username; + map["password"] = cred.Password; + map["realm"] = cred.Realm; + map["type"] = cred.Type; + map["tag"] = cred.Tag; + map["date"] = cred.Date; + map["storage"] = cred.Storage; + map["agent_id"] = cred.AgentId; + map["host"] = cred.Host; + + list[cred.CredId] = map; + } + + return this->scriptEngine->engine()->toScriptValue(list); +} + +void BridgeApp::credentials_add(const QString &username, const QString &password, const QString &realm, const QString &type, const QString &tag, const QString &storage, const QString &host) { scriptEngine->manager()->GetAdaptix()->CredentialsTab->CredentialsAdd(username, password, realm, type, tag, storage, host); } + +QObject* BridgeApp::create_command(const QString &name, const QString &description, const QString &example, const QString &message) +{ + auto* wrapper = new AxCommandWrappers(name, description, example, message, this); + connect(wrapper, &AxCommandWrappers::scriptError, this, &BridgeApp::engineError); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeApp::create_commands_group(const QString &name, const QJSValue &array) +{ + auto* wrapper = new AxCommandGroupWrapper(scriptEngine->engine(), this); + connect(wrapper, &AxCommandGroupWrapper::scriptError, this, &BridgeApp::engineError); + wrapper->SetParams(name, array); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +void BridgeApp::execute_alias(const QString &id, const QString &cmdline, const QString &command, const QString &message, const QJSValue &hook) const +{ + auto mapAgents = scriptEngine->manager()->GetAgents(); + if (!mapAgents.contains(id)) + return; + + auto agent = mapAgents[id]; + if (!agent) + return; + + auto cmdResult = agent->commander->ProcessInput(id, command); + if (!cmdResult.is_pre_hook) { + if (!message.isEmpty()) { + cmdResult.data["message"] = message; + } + + if (!hook.isUndefined() && !hook.isNull() && hook.isCallable()) + cmdResult.post_hook = {true, scriptEngine->context.name, hook}; + + agent->Console->ProcessCmdResult(cmdline, cmdResult, false); + } +} + +void BridgeApp::execute_browser(const QString &id, const QString &command) const +{ + auto mapAgents = scriptEngine->manager()->GetAgents(); + if (!mapAgents.contains(id)) + return; + + auto agent = mapAgents[id]; + if (!agent) + return; + + auto cmdResult = agent->commander->ProcessInput(id, command); + agent->Console->ProcessCmdResult(command, cmdResult, true); +} + +void BridgeApp::execute_command(const QString &id, const QString &command, const QJSValue &hook) const +{ + auto mapAgents = scriptEngine->manager()->GetAgents(); + if (!mapAgents.contains(id)) + return; + + auto agent = mapAgents[id]; + if (!agent) + return; + + auto cmdResult = agent->commander->ProcessInput(id, command); + if (!cmdResult.is_pre_hook) { + + if (!hook.isUndefined() && !hook.isNull() && hook.isCallable()) + cmdResult.post_hook = {true, scriptEngine->context.name, hook}; + + agent->Console->ProcessCmdResult(command, cmdResult, false); + } +} + +QString BridgeApp::file_basename(const QString &path) const +{ + int slash = qMax(path.lastIndexOf('/'), path.lastIndexOf('\\')); + return path.mid(slash + 1); +} + +bool BridgeApp::file_exists(const QString &path) const { return QFile::exists(path); } + +QString BridgeApp::file_read(QString path) const +{ + if (path.startsWith("~/")) + path = QDir::home().filePath(path.mid(2)); + + QFile file(path); + if (file.open(QIODevice::ReadOnly)) { + QByteArray fileData = file.readAll(); + file.close(); + return QString::fromLatin1(fileData.toBase64()); + } else { + return ""; + } +} + +QString BridgeApp::format_time(const QString &format, const int &time) const +{ + QDateTime epochDateTime = QDateTime::fromSecsSinceEpoch(time, QTimeZone("UTC")); + QDateTime localDateTime = epochDateTime.toTimeZone(QTimeZone::systemTimeZone()); + return localDateTime.toString(format); +} + +QJSValue BridgeApp::interfaces() const +{ + QVariantList list; + auto interfaces = scriptEngine->manager()->GetInterfaces(); + + for (auto addr : interfaces) + list.append(addr); + + return this->scriptEngine->engine()->toScriptValue(list); +} + +bool BridgeApp::is64(const QString &id) const +{ + auto mapAgents = scriptEngine->manager()->GetAgents(); + if (!mapAgents.contains(id)) + return false; + + return mapAgents[id]->data.Arch == "x64"; +} + +bool BridgeApp::isadmin(const QString &id) const +{ + auto mapAgents = scriptEngine->manager()->GetAgents(); + if (!mapAgents.contains(id)) + return false; + + return mapAgents[id]->data.Elevated; +} + +void BridgeApp::log(const QString &text) { emit consoleMessage(text); } + +void BridgeApp::log_error(const QString &text) { emit consoleError(text); } + +void BridgeApp::open_agent_console(const QString &id) { scriptEngine->manager()->GetAdaptix()->LoadConsoleUI(id); } + +void BridgeApp::open_access_tunnel(const QString &id, const bool socks4, const bool socks5, const bool lportfwd, const bool rportfwd) { scriptEngine->manager()->GetAdaptix()->ShowTunnelCreator(id, socks4, socks5, lportfwd, rportfwd); } + +void BridgeApp::open_browser_files(const QString &id) { scriptEngine->manager()->GetAdaptix()->LoadFileBrowserUI(id); } + +void BridgeApp::open_browser_process(const QString &id) { scriptEngine->manager()->GetAdaptix()->LoadProcessBrowserUI(id); } + +void BridgeApp::open_remote_terminal(const QString &id) { scriptEngine->manager()->GetAdaptix()->LoadTerminalUI(id); } + +QString BridgeApp::prompt_open_file(const QString &caption, const QString &filter) { return QFileDialog::getOpenFileName(nullptr, caption, QDir::homePath(), filter); } + +QString BridgeApp::prompt_open_dir(const QString &caption) { return QFileDialog::getExistingDirectory(nullptr, caption, QDir::homePath()); } + +QString BridgeApp::prompt_save_file(const QString &filename, const QString &caption, const QString &filter) { return QFileDialog::getSaveFileName(nullptr, caption, filename, filter); } + +void BridgeApp::register_commands_group(QObject *obj, const QJSValue &agents, const QJSValue &os, const QJSValue &listeners) +{ + QList list_os; + QStringList list_agents; + QStringList list_listeners; + + if (agents.isUndefined() || agents.isNull() || !agents.isArray()) { + emit engineError("register_commands_group expected array of strings in agents parameter!"); + return; + } + + if (os.isUndefined() && (os.isNull() || !os.isArray()) ) { + emit engineError("register_commands_group expected array of strings in os parameter!"); + return; + } + + if (listeners.isUndefined() && (listeners.isNull() || !listeners.isArray())) { + emit engineError("register_commands_group expected array of strings in listeners parameter!"); + return; + } + + for (int i = 0; i < os.property("length").toInt(); ++i) { + QJSValue val = os.property(i); + if (val.toString() == "windows") list_os.append(1); + else if (val.toString() == "linux") list_os.append(2); + else if (val.toString() == "macos") list_os.append(3); + } + + for (int i = 0; i < agents.property("length").toInt(); ++i) { + QJSValue val = agents.property(i); + list_agents << val.toString(); + } + + for (int i = 0; i < listeners.property("length").toInt(); ++i) { + QJSValue val = listeners.property(i); + list_listeners << val.toString(); + } + + auto wrapper = qobject_cast(obj); + if (!wrapper) { + emit engineError("register_commands_group no support object type!"); + return; + } + + CommandsGroup commandsGroup = {}; + commandsGroup.groupName = wrapper->getName(); + commandsGroup.commands = wrapper->getCommands(); + commandsGroup.engine = wrapper->getEngine(); + commandsGroup.filepath = scriptEngine->context.name; + + scriptEngine->manager()->RegisterCommandsGroup(commandsGroup, list_listeners, list_agents, list_os); +} + +void BridgeApp::script_import(const QString &path) +{ + QFile file(path); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + return scriptEngine->engine()->throwError("Could not open script: " + path); + } + QTextStream in(&file); + QString code = in.readAll(); + file.close(); + + scriptEngine->engine()->evaluate(code, path); +} + +void BridgeApp::script_load(const QString &path) { scriptEngine->manager()->GlobalScriptLoad(path); } + +void BridgeApp::script_unload(const QString &path) { scriptEngine->manager()->GlobalScriptUnload(path); } + +QString BridgeApp::script_dir() +{ +#ifdef Q_OS_WIN + return GetParentPathWindows(scriptEngine->context.name) + "\\"; +#else + return GetParentPathUnix(scriptEngine->context.name) + "/"; +#endif +} + +void BridgeApp::show_message(const QString &title, const QString &text) { QMessageBox::information(nullptr, title, text); } + +int BridgeApp::ticks() { return QDateTime::currentSecsSinceEpoch(); } + diff --git a/AdaptixClient/Source/Client/AxScript/BridgeEvent.cpp b/AdaptixClient/Source/Client/AxScript/BridgeEvent.cpp new file mode 100644 index 00000000..c2ac581e --- /dev/null +++ b/AdaptixClient/Source/Client/AxScript/BridgeEvent.cpp @@ -0,0 +1,65 @@ +#include +#include +#include + +BridgeEvent::BridgeEvent(AxScriptEngine* scriptEngine, QObject* parent) : QObject(parent), scriptEngine(scriptEngine) {} + +BridgeEvent::~BridgeEvent() {} + +void BridgeEvent::reg(const QString &event, const QString &type, const QJSValue &handler, const QJSValue &agents, const QJSValue &os, const QJSValue &listeners, const QString &event_id) +{ + if (!handler.isCallable()) { + emit scriptError( type + " -> handler in not Callable"); + return; + } + + QSet list_agents; + QSet list_os; + QSet list_listeners; + + if (agents.isUndefined() || agents.isNull() || !agents.isArray() || agents.property("length").toInt() == 0) { + emit scriptError(type + " -> agents in undefined"); + return; + } + + for (int i = 0; i < agents.property("length").toInt(); ++i) { + QJSValue val = agents.property(i); + list_agents.insert(val.toString()); + } + + if (!os.isUndefined() && !os.isNull() && os.isArray()) { + for (int i = 0; i < os.property("length").toInt(); ++i) { + QJSValue val = os.property(i); + list_os << val.toString(); + } + } + + if (!listeners.isUndefined() && !listeners.isNull() && listeners.isArray()) { + for (int i = 0; i < listeners.property("length").toInt(); ++i) { + QJSValue val = listeners.property(i); + list_listeners << val.toString(); + } + } + + this->scriptEngine->registerEvent(event, handler, list_agents, list_os, list_listeners, event_id); +} + + + +void BridgeEvent::on_filebrowser_disks(const QJSValue &handler, const QJSValue &agents, const QJSValue &os, const QJSValue &listeners, const QString &event_id) { + this->reg("FileBroserDisks", "on_filebrowser_disks", handler, agents, os, listeners, event_id); +} + +void BridgeEvent::on_filebrowser_list(const QJSValue &handler, const QJSValue &agents, const QJSValue &os, const QJSValue &listeners, const QString &event_id) { + this->reg("FileBroserList", "on_filebrowser_list", handler, agents, os, listeners, event_id); +} + +void BridgeEvent::on_filebrowser_upload(const QJSValue &handler, const QJSValue &agents, const QJSValue &os, const QJSValue &listeners, const QString &event_id) { + this->reg("FileBroserUpload", "on_filebrowser_upload", handler, agents, os, listeners, event_id); +} + +void BridgeEvent::on_processbrowser_list(const QJSValue &handler, const QJSValue &agents, const QJSValue &os, const QJSValue &listeners, const QString &event_id) { + this->reg("ProcessBrowserList", "on_processbrowser_list", handler, agents, os, listeners, event_id); +} + +void BridgeEvent::remove(const QString &event_id) { this->scriptEngine->manager()->RemoveEvent(event_id); } diff --git a/AdaptixClient/Source/Client/AxScript/BridgeForm.cpp b/AdaptixClient/Source/Client/AxScript/BridgeForm.cpp new file mode 100644 index 00000000..ae7e6753 --- /dev/null +++ b/AdaptixClient/Source/Client/AxScript/BridgeForm.cpp @@ -0,0 +1,290 @@ +#include +#include +#include +#include +#include +#include + +BridgeForm::BridgeForm(AxScriptEngine* scriptEngine, QObject* parent) : QObject(parent), scriptEngine(scriptEngine), widget(new QWidget()) {} + +BridgeForm::~BridgeForm() { delete widget; } + +void BridgeForm::connect(QObject* sender, const QString& signalName, const QJSValue& handler) +{ + if (!sender || !handler.isCallable()) { + emit scriptError("connect -> Invalid sender or handler"); + return; + } + + const QMetaObject* meta = sender->metaObject(); + for (int i = 0; i < meta->methodCount(); ++i) { + QMetaMethod method = meta->method(i); + if (method.methodType() != QMetaMethod::Signal) + continue; + + QString name = QString::fromLatin1(method.methodSignature()); + if (!name.startsWith(signalName)) + continue; + + int paramCount = method.parameterCount(); + auto* proxy = new SignalProxy(scriptEngine->engine(), handler, sender); + + bool connected = false; + + if (paramCount == 0) { + connected = QObject::connect(sender, method, proxy, proxy->metaObject()->method(proxy->metaObject()->indexOfSlot("call()"))); + } + else if (paramCount == 1) { + QByteArray typeName = method.parameterTypes().value(0); + if (typeName == "QString") + connected = QObject::connect(sender, method, proxy, proxy->metaObject()->method(proxy->metaObject()->indexOfSlot("callWithArg(QString)"))); + else if (typeName == "int") + connected = QObject::connect(sender, method, proxy, proxy->metaObject()->method(proxy->metaObject()->indexOfSlot("callWithArg(int)"))); + else if (typeName == "bool") + connected = QObject::connect(sender, method, proxy, proxy->metaObject()->method(proxy->metaObject()->indexOfSlot("callWithArg(bool)"))); + } + else if (paramCount == 2) { + QByteArray typeName1 = method.parameterTypes().value(0); + QByteArray typeName2 = method.parameterTypes().value(1); + if (typeName1 == "int" && typeName2 == "int") + connected = QObject::connect(sender, method, proxy, proxy->metaObject()->method(proxy->metaObject()->indexOfSlot("callWithArgs(int,int)"))); + } + else { + emit scriptError("connect -> Signal " + signalName + " has too many parameters (not supported)"); + return; + } + + if (!connected) + emit scriptError("connect -> Failed to connect signal " + method.methodSignature()); + + return; + } + + emit scriptError("connect -> Signal " + signalName + " not found"); +} + +/// Elements + +QObject* BridgeForm::create_vlayout() +{ + auto* wrapper = new AxBoxLayoutWrapper(QBoxLayout::TopToBottom, this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_hlayout() +{ + auto* wrapper = new AxBoxLayoutWrapper(QBoxLayout::LeftToRight, this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_gridlayout() +{ + auto* wrapper = new AxGridLayoutWrapper(this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_vline() +{ + auto* wrapper = new AxLineWrapper(QFrame::VLine, this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_hline() +{ + auto* wrapper = new AxLineWrapper(QFrame::HLine, this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_vspacer() +{ + auto* wrapper = new AxSpacerWrapper(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding, this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_hspacer() +{ + auto* wrapper = new AxSpacerWrapper(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum, this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_label(const QString& text) +{ + auto* label = new QLabel(text, widget); + auto* wrapper = new AxLabelWrapper(label, this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_textline(const QString &text) +{ + auto* edit = new QLineEdit(text, widget); + auto* wrapper = new AxTextLineWrapper(edit, this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_combo() +{ + auto* combo = new QComboBox(widget); + auto* wrapper = new AxComboBoxWrapper(combo, this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_check(const QString& label) +{ + auto* check = new QCheckBox(label, widget); + auto* wrapper = new AxCheckBoxWrapper(check, this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_spin() +{ + auto* spin = new QSpinBox(widget); + auto* wrapper = new AxSpinBoxWrapper(spin, this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_dateline(const QString& format) +{ + auto* date_widget = new QDateEdit(widget); + auto* wrapper = new AxDateEditWrapper(date_widget, format, this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_timeline(const QString& format) +{ + auto* time_widget = new QTimeEdit(widget); + auto* wrapper = new AxTimeEditWrapper(time_widget, format, this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_button(const QString& text) +{ + auto* btn = new QPushButton(text, widget); + auto* wrapper = new AxButtonWrapper(btn, this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_textmulti(const QString& text) +{ + auto* textEdit = new QPlainTextEdit(text, widget); + auto* wrapper = new AxTextMultiWrapper(textEdit, this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_list() +{ + auto* list = new QListWidget(widget); + auto* wrapper = new AxListWidgetWrapper(list, scriptEngine->engine(), this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_table(const QJSValue &headers) +{ + auto* table = new QTableWidget(widget); + auto* wrapper = new AxTableWidgetWrapper(headers, table, scriptEngine->engine(), this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_selector_file() +{ + auto fileSelector = new FileSelector(widget); + auto* wrapper = new AxSelectorFile(fileSelector, this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_tabs() +{ + auto* tabWidget = new QTabWidget(widget); + auto* wrapper = new AxTabWrapper(tabWidget, this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_groupbox(const QString &title, const bool checkable) +{ + auto* box = new QGroupBox(title, widget); + auto* wrapper = new AxGroupBoxWrapper(checkable, box, this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_hsplitter() +{ + auto* splitter = new QSplitter(Qt::Horizontal, widget); + auto* wrapper = new AxSplitterWrapper(splitter, this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_vsplitter() +{ + auto* splitter = new QSplitter(Qt::Vertical, widget); + auto* wrapper = new AxSplitterWrapper(splitter, this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_scrollarea() +{ + auto* area = new QScrollArea(widget); + auto* wrapper = new AxScrollAreaWrapper(area, this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_panel() +{ + auto* panel = new QWidget(widget); + auto* wrapper = new AxPanelWrapper(panel, this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_stack() +{ + auto* stack = new QStackedWidget(widget); + auto* wrapper = new AxStackedWidgetWrapper(stack, this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_container() +{ + auto* wrapper = new AxContainerWrapper(scriptEngine->engine(), this); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_dialog(const QString& title) const +{ + auto* wrapper = new AxDialogWrapper(title, widget); + scriptEngine->registerObject(wrapper); + return wrapper; +} + +QObject* BridgeForm::create_selector_credentials(const QJSValue &headers) const +{ + auto* table = new QTableWidget(widget); + auto* button = new QPushButton(widget); + auto* wrapper = new AxSelectorCreds(headers, table, button, scriptEngine, widget); + scriptEngine->registerObject(wrapper); + return wrapper; +} diff --git a/AdaptixClient/Source/Client/AxScript/BridgeMenu.cpp b/AdaptixClient/Source/Client/AxScript/BridgeMenu.cpp new file mode 100644 index 00000000..e367e6ab --- /dev/null +++ b/AdaptixClient/Source/Client/AxScript/BridgeMenu.cpp @@ -0,0 +1,107 @@ +#include +#include +#include + +BridgeMenu::BridgeMenu(AxScriptEngine* scriptEngine, QObject* parent) : QObject(parent), scriptEngine(scriptEngine), widget(new QWidget()) {} + +BridgeMenu::~BridgeMenu() { delete widget; } + +void BridgeMenu::reg(const QString &type, AbstractAxMenuItem *item, const QJSValue &agents, const QJSValue &os, const QJSValue &listeners) +{ + QSet list_agents; + QSet list_os; + QSet list_listeners; + + if (agents.isUndefined() || agents.isNull() || !agents.isArray() || agents.property("length").toInt() == 0) + return; + + for (int i = 0; i < agents.property("length").toInt(); ++i) { + QJSValue val = agents.property(i); + list_agents.insert(val.toString()); + } + + if (!os.isUndefined() && !os.isNull() && os.isArray()) { + for (int i = 0; i < os.property("length").toInt(); ++i) { + QJSValue val = os.property(i); + list_os << val.toString(); + } + } + + if (!listeners.isUndefined() && !listeners.isNull() && listeners.isArray()) { + for (int i = 0; i < listeners.property("length").toInt(); ++i) { + QJSValue val = listeners.property(i); + list_listeners << val.toString(); + } + } + + this->scriptEngine->registerMenu(type, item, list_agents, list_os, list_listeners); +} + +QList BridgeMenu::items() const { return menuItems; } + +void BridgeMenu::clear() +{ + qDeleteAll(menuItems); + menuItems.clear(); +} + +AxActionWrapper* BridgeMenu::create_action(const QString& text, const QJSValue& handler) +{ + auto* action = new AxActionWrapper(text, handler, scriptEngine->engine(), this); + menuItems.append(action); + return action; +} + +AxSeparatorWrapper* BridgeMenu::create_separator() +{ + auto* sep = new AxSeparatorWrapper(this); + menuItems.append(sep); + return sep; +} + +AxMenuWrapper* BridgeMenu::create_menu(const QString& title) +{ + auto* menu = new AxMenuWrapper(title, this); + menuItems.append(menu); + return menu; +} + +void BridgeMenu::add_session_main(AbstractAxMenuItem *item, const QJSValue &agents, const QJSValue &os, const QJSValue &listeners) { + this->reg("SessionMain", item, agents, os, listeners); +} + +void BridgeMenu::add_session_agent(AbstractAxMenuItem *item, const QJSValue &agents, const QJSValue &os, const QJSValue &listeners) { + this->reg("SessionAgent", item, agents, os, listeners); +} + +void BridgeMenu::add_session_browser(AbstractAxMenuItem* item, const QJSValue &agents, const QJSValue &os, const QJSValue &listeners){ + this->reg("SessionBrowser", item, agents, os, listeners); +} + +void BridgeMenu::add_session_access(AbstractAxMenuItem* item, const QJSValue &agents, const QJSValue &os, const QJSValue &listeners){ + this->reg("SessionAccess", item, agents, os, listeners); +} + +void BridgeMenu::add_filebrowser(AbstractAxMenuItem *item, const QJSValue &agents, const QJSValue &os, const QJSValue &listeners){ + this->reg("FileBrowser", item, agents, os, listeners); +} + +void BridgeMenu::add_processbrowser(AbstractAxMenuItem *item, const QJSValue &agents, const QJSValue &os, const QJSValue &listeners) { + this->reg("ProcessBrowser", item, agents, os, listeners); +} + +void BridgeMenu::add_downloads_running(AbstractAxMenuItem *item, const QJSValue &agents, const QJSValue &os, const QJSValue &listeners){ + this->reg("DownloadRunning", item, agents, os, listeners); +} + +void BridgeMenu::add_downloads_finished(AbstractAxMenuItem *item, const QJSValue &agents, const QJSValue &os, const QJSValue &listeners){ + this->reg("DownloadFinished", item, agents, os, listeners); +} + +void BridgeMenu::add_tasks(AbstractAxMenuItem *item, const QJSValue &agents, const QJSValue &os, const QJSValue &listeners){ + this->reg("Tasks", item, agents, os, listeners); +} + +void BridgeMenu::add_tasks_job(AbstractAxMenuItem *item, const QJSValue &agents, const QJSValue &os, const QJSValue &listeners) { + this->reg("TasksJob", item, agents, os, listeners); +} \ No newline at end of file diff --git a/AdaptixClient/Source/Client/Extender.cpp b/AdaptixClient/Source/Client/Extender.cpp index 5c0bda54..88ba5999 100644 --- a/AdaptixClient/Source/Client/Extender.cpp +++ b/AdaptixClient/Source/Client/Extender.cpp @@ -8,7 +8,6 @@ Extender::Extender(MainAdaptix* m) { mainAdaptix = m; dialogExtender = new DialogExtender(this); - this->LoadFromDB(); } @@ -17,147 +16,67 @@ Extender::~Extender() = default; void Extender::LoadFromDB() { auto list = mainAdaptix->storage->ListExtensions(); - for(int i=0; i < list.size(); i++) - this->LoadFromFile( list[i].FilePath, list[i].Enabled ); + for(auto ext : list) { + + QFile file(ext.FilePath); + if (!file.open(QIODevice::ReadOnly)) { + ext.Enabled = false; + ext.Message = "Cannot open file."; + } + ext.Code = QTextStream(&file).readAll(); + file.close(); + + extenderFiles[ext.FilePath] = ext; + dialogExtender->AddExtenderItem(extenderFiles[ext.FilePath]); + } } -void Extender::LoadFromFile(QString path, bool enabled) +void Extender::LoadFromFile(const QString &path, const bool enabled) { - QString fileContent; - QJsonObject rootObj; - QJsonDocument jsonDocument; - QJsonArray extensionsArray; - QJsonArray agentsArray; - QVector exConstants; - QMap > exCommands; - - ExtensionFile extensionFile = {0}; + ExtensionFile extensionFile = {}; extensionFile.FilePath = path; + extensionFile.Enabled = enabled; QFile file(path); if (!file.open(QIODevice::ReadOnly)) { - extensionFile.Comment = "File not readed"; extensionFile.Enabled = false; - extensionFile.Valid = false; + extensionFile.Message = "Cannot open file."; goto END; } - fileContent = QString(file.readAll()); + extensionFile.Code = QTextStream(&file).readAll(); file.close(); - jsonDocument = QJsonDocument::fromJson(fileContent.toUtf8()); - if ( jsonDocument.isNull() || !jsonDocument.isObject()) { - extensionFile.Comment = "Invalid JSON document!"; - extensionFile.Enabled = false; - extensionFile.Valid = false; - goto END; - } - - rootObj = jsonDocument.object(); - if( !rootObj.contains("name") && rootObj["name"].isString() ) { - extensionFile.Comment = "JSON document must include a required 'name' parameter"; - extensionFile.Enabled = false; - extensionFile.Valid = false; - goto END; - } - - if( !rootObj.contains("extensions") && rootObj["extensions"].isArray() ) { - extensionFile.Comment = "JSON document must include a required 'extensions' parameter"; - extensionFile.Enabled = false; - extensionFile.Valid = false; - goto END; - } - - extensionFile.Name = rootObj.value("name").toString(); - extensionFile.Description = rootObj.value("description").toString(); - - extensionsArray = rootObj.value("extensions").toArray(); - for (QJsonValue extensionValue : extensionsArray) { - QJsonObject extJsonObject = extensionValue.toObject(); - - if( !extJsonObject.contains("type") && extJsonObject["type"].isString() ) { - extensionFile.Comment = "Extension must include a required 'type' parameter"; - extensionFile.Enabled = false; - extensionFile.Valid = false; - goto END; - } - - QString type = extJsonObject.value("type").toString(); - if(type == "command") { - QStringList agentsList; - - if( !extJsonObject.contains("agents") && extJsonObject["agents"].isArray() ) { - extensionFile.Comment = "Extension must include a required 'agents' parameter"; - extensionFile.Enabled = false; - extensionFile.Valid = false; - goto END; - } - - agentsArray = extJsonObject.value("agents").toArray(); - for (QJsonValue agentStr : agentsArray) { - agentsList.push_back(agentStr.toString()); - } - - bool result = true; - QString msg = ValidExtCommand(extJsonObject, &result); - if (!result) { - extensionFile.Comment = msg; - extensionFile.Enabled = false; - extensionFile.Valid = false; - goto END; - } - - for(QString key : agentsList) { - exCommands[key].push_back(extJsonObject); - } - - } else if(type == "constant") { - bool result = true; - QString msg = ValidExtConstant(extJsonObject, &result); - if (!result) { - extensionFile.Comment = msg; - extensionFile.Enabled = false; - extensionFile.Valid = false; - goto END; - } - - exConstants.push_back(extJsonObject); - - } else { - extensionFile.Comment = "Unknown extension type"; - extensionFile.Enabled = false; - extensionFile.Valid = false; - goto END; - } - } - - extensionFile.Comment = fileContent; - extensionFile.ExConstants = exConstants; - extensionFile.ExCommands = exCommands; - extensionFile.Enabled = enabled; - extensionFile.Valid = true; - END: this->SetExtension(extensionFile); } -void Extender::SetExtension(const ExtensionFile &extFile) +void Extender::SetExtension(ExtensionFile extFile) { if(extenderFiles.contains(extFile.FilePath)) { - if( extFile.Valid && extFile.Enabled ) { + if(extFile.Enabled) { mainAdaptix->mainUI->RemoveExtension(extFile); - mainAdaptix->mainUI->AddNewExtension(extFile); + bool success = mainAdaptix->mainUI->AddNewExtension(&extFile); + if (!success) { + mainAdaptix->mainUI->RemoveExtension(extFile); + } } dialogExtender->UpdateExtenderItem(extFile); mainAdaptix->storage->UpdateExtension(extFile); } else { - extenderFiles[extFile.FilePath] = extFile; - if( extFile.Valid && extFile.Enabled ) - mainAdaptix->mainUI->AddNewExtension(extFile); + if( extFile.Enabled ) { + bool success = mainAdaptix->mainUI->AddNewExtension(&extFile); + if (!success) { + mainAdaptix->mainUI->RemoveExtension(extFile); + } + } - dialogExtender->AddExtenderItem(extFile); - if( !mainAdaptix->storage->ExistsExtension(extFile.FilePath)) - mainAdaptix->storage->AddExtension(extFile); + if (!extFile.NoSave) { + extenderFiles[extFile.FilePath] = extFile; + dialogExtender->AddExtenderItem(extFile); + if( !mainAdaptix->storage->ExistsExtension(extFile.FilePath)) + mainAdaptix->storage->AddExtension(extFile); + } } } @@ -166,9 +85,13 @@ void Extender::EnableExtension(const QString &path) if( !extenderFiles.contains(path) ) return; - if( extenderFiles[path].Valid && !extenderFiles[path].Enabled ) { + if( !extenderFiles[path].Enabled && extenderFiles[path].Message.isEmpty()) { extenderFiles[path].Enabled = true; - mainAdaptix->mainUI->AddNewExtension(extenderFiles[path]); + bool success = mainAdaptix->mainUI->AddNewExtension(&(extenderFiles[path])); + if (!success) { + mainAdaptix->mainUI->RemoveExtension(extenderFiles[path]); + } + dialogExtender->UpdateExtenderItem(extenderFiles[path]); mainAdaptix->storage->UpdateExtension(extenderFiles[path]); } @@ -179,7 +102,7 @@ void Extender::DisableExtension(const QString &path) if( !extenderFiles.contains(path) ) return; - if( extenderFiles[path].Valid && extenderFiles[path].Enabled ) { + if( extenderFiles[path].Enabled ) { extenderFiles[path].Enabled = false; mainAdaptix->mainUI->RemoveExtension(extenderFiles[path]); dialogExtender->UpdateExtenderItem(extenderFiles[path]); @@ -197,4 +120,22 @@ void Extender::RemoveExtension(const QString &path) mainAdaptix->storage->RemoveExtension(path); extenderFiles.remove(path); -} \ No newline at end of file +} + +void Extender::syncedOnReload(const QString &project) +{ + for (auto path : extenderFiles.keys()) { + if(extenderFiles[path].Enabled) { + bool success = mainAdaptix->mainUI->SyncExtension(project, &(extenderFiles[path])); + if (!success) { + mainAdaptix->mainUI->RemoveExtension(extenderFiles[path]); + } + } + dialogExtender->UpdateExtenderItem(extenderFiles[path]); + mainAdaptix->storage->UpdateExtension(extenderFiles[path]); + } +} + +void Extender::loadGlobalScript(const QString &path) { this->LoadFromFile(path, true); } + +void Extender::unloadGlobalScript(const QString &path) { this->RemoveExtension(path); } diff --git a/AdaptixClient/Source/Client/ProcessSyncPacket.cpp b/AdaptixClient/Source/Client/ProcessSyncPacket.cpp index 8e441f45..e37da0f9 100644 --- a/AdaptixClient/Source/Client/ProcessSyncPacket.cpp +++ b/AdaptixClient/Source/Client/ProcessSyncPacket.cpp @@ -13,9 +13,11 @@ #include #include #include +#include #include #include + bool AdaptixWidget::isValidSyncPacket(QJsonObject jsonObj) { if ( !jsonObj.contains("type") || !jsonObj["type"].isDouble() ) @@ -24,7 +26,8 @@ bool AdaptixWidget::isValidSyncPacket(QJsonObject jsonObj) int spType = jsonObj["type"].toDouble(); if( spType == TYPE_SYNC_START ) { - if ( !jsonObj.contains("count") || !jsonObj["count"].isDouble() ) return false; + if ( !jsonObj.contains("count") || !jsonObj["count"].isDouble() ) return false; + if ( !jsonObj.contains("interfaces") || !jsonObj["interfaces"].isArray() ) return false; return true; } if( spType == TYPE_SYNC_FINISH ) { @@ -40,7 +43,7 @@ bool AdaptixWidget::isValidSyncPacket(QJsonObject jsonObj) if( spType == TYPE_LISTENER_REG ) { if ( !jsonObj.contains("fn") || !jsonObj["fn"].isString() ) return false; - if ( !jsonObj.contains("ui") || !jsonObj["ui"].isString() ) return false; + if ( !jsonObj.contains("ax") || !jsonObj["ax"].isString() ) return false; return true; } if( spType == TYPE_LISTENER_START || spType == TYPE_LISTENER_EDIT ) { @@ -59,10 +62,9 @@ bool AdaptixWidget::isValidSyncPacket(QJsonObject jsonObj) } if( spType == TYPE_AGENT_REG ) { - if ( !jsonObj.contains("agent") || !jsonObj["agent"].isString() ) return false; - if ( !jsonObj.contains("watermark") || !jsonObj["watermark"].isString() ) return false; - if ( !jsonObj.contains("listeners_json") || !jsonObj["listeners_json"].isString() ) return false; - if ( !jsonObj.contains("handlers_json") || !jsonObj["handlers_json"].isString() ) return false; + if ( !jsonObj.contains("agent") || !jsonObj["agent"].isString() ) return false; + if ( !jsonObj.contains("ax") || !jsonObj["ax"].isString() ) return false; + if ( !jsonObj.contains("listeners") || !jsonObj["listeners"].isArray() ) return false; return true; } if( spType == TYPE_AGENT_NEW ) { @@ -142,10 +144,21 @@ bool AdaptixWidget::isValidSyncPacket(QJsonObject jsonObj) if (!jsonObj.contains("a_task_id") || !jsonObj["a_task_id"].isArray()) return false; return true; } - if( spType == TYPE_AGENT_TASK_REMOVE ) { + if ( spType == TYPE_AGENT_TASK_REMOVE ) { if (!jsonObj.contains("a_task_id") || !jsonObj["a_task_id"].isString()) return false; return true; } + if ( spType == TYPE_AGENT_TASK_HOOK ) { + if (!jsonObj.contains("a_id") || !jsonObj["a_id"].isString()) return false; + if (!jsonObj.contains("a_task_id") || !jsonObj["a_task_id"].isString()) return false; + if (!jsonObj.contains("a_hook_id") || !jsonObj["a_hook_id"].isString()) return false; + if (!jsonObj.contains("a_job_index") || !jsonObj["a_job_index"].isDouble()) return false; + if (!jsonObj.contains("a_msg_type") || !jsonObj["a_msg_type"].isDouble()) return false; + if (!jsonObj.contains("a_message") || !jsonObj["a_message"].isString()) return false; + if (!jsonObj.contains("a_text") || !jsonObj["a_text"].isString()) return false; + if (!jsonObj.contains("a_completed") || !jsonObj["a_completed"].isBool()) return false; + return true; + } if( spType == TYPE_AGENT_CONSOLE_OUT) { if (!jsonObj.contains("time") || !jsonObj["time"].isDouble()) return false; @@ -245,6 +258,35 @@ bool AdaptixWidget::isValidSyncPacket(QJsonObject jsonObj) return true; } + if ( spType == TYPE_CREDS_CREATE ) { + if (!jsonObj.contains("c_creds_id") || !jsonObj["c_creds_id"].isString()) return false; + if (!jsonObj.contains("c_username") || !jsonObj["c_username"].isString()) return false; + if (!jsonObj.contains("c_password") || !jsonObj["c_password"].isString()) return false; + if (!jsonObj.contains("c_realm") || !jsonObj["c_realm"].isString()) return false; + if (!jsonObj.contains("c_type") || !jsonObj["c_type"].isString()) return false; + if (!jsonObj.contains("c_tag") || !jsonObj["c_tag"].isString()) return false; + if (!jsonObj.contains("c_date") || !jsonObj["c_date"].isDouble()) return false; + if (!jsonObj.contains("c_storage") || !jsonObj["c_storage"].isString()) return false; + if (!jsonObj.contains("c_agent_id") || !jsonObj["c_agent_id"].isString()) return false; + if (!jsonObj.contains("c_host") || !jsonObj["c_host"].isString()) return false; + return true; + } + if ( spType == TYPE_CREDS_EDIT ) { + if (!jsonObj.contains("c_creds_id") || !jsonObj["c_creds_id"].isString()) return false; + if (!jsonObj.contains("c_username") || !jsonObj["c_username"].isString()) return false; + if (!jsonObj.contains("c_password") || !jsonObj["c_password"].isString()) return false; + if (!jsonObj.contains("c_realm") || !jsonObj["c_realm"].isString()) return false; + if (!jsonObj.contains("c_type") || !jsonObj["c_type"].isString()) return false; + if (!jsonObj.contains("c_tag") || !jsonObj["c_tag"].isString()) return false; + if (!jsonObj.contains("c_storage") || !jsonObj["c_storage"].isString()) return false; + if (!jsonObj.contains("c_host") || !jsonObj["c_host"].isString()) return false; + return true; + } + if ( spType == TYPE_CREDS_DELETE ) { + if (!jsonObj.contains("c_creds_id") || !jsonObj["c_creds_id"].isString()) return false; + return true; + } + if( spType == TYPE_BROWSER_DISKS ) { if (!jsonObj.contains("b_agent_id") || !jsonObj["b_agent_id"].isString()) return false; if (!jsonObj.contains("b_time") || !jsonObj["b_time"].isDouble()) return false; @@ -304,6 +346,13 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) if( spType == TYPE_SYNC_START ) { int count = jsonObj["count"].toDouble(); + QJsonArray interfaces = jsonObj["interfaces"].toArray(); + + for (QJsonValue addrValue : interfaces) { + QString addr = addrValue.toString(); + this->addresses.append(addr); + } + dialogSyncPacket->init(count); this->sync = true; this->setEnabled(false); @@ -325,13 +374,13 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) if( spType == TYPE_LISTENER_START ) { ListenerData newListener = {}; - newListener.ListenerName = jsonObj["l_name"].toString(); - newListener.ListenerType = jsonObj["l_type"].toString(); - newListener.BindHost = jsonObj["l_bind_host"].toString(); - newListener.BindPort = jsonObj["l_bind_port"].toString(); - newListener.AgentAddresses = jsonObj["l_agent_addr"].toString(); - newListener.Status = jsonObj["l_status"].toString(); - newListener.Data = jsonObj["l_data"].toString(); + newListener.ListenerName = jsonObj["l_name"].toString(); + newListener.ListenerFullName = jsonObj["l_type"].toString(); + newListener.BindHost = jsonObj["l_bind_host"].toString(); + newListener.BindPort = jsonObj["l_bind_port"].toString(); + newListener.AgentAddresses = jsonObj["l_agent_addr"].toString(); + newListener.Status = jsonObj["l_status"].toString(); + newListener.Data = jsonObj["l_data"].toString(); ListenersTab->AddListenerItem(newListener); return; @@ -339,13 +388,13 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) if( spType == TYPE_LISTENER_EDIT ) { ListenerData newListener = {}; - newListener.ListenerName = jsonObj["l_name"].toString(); - newListener.ListenerType = jsonObj["l_type"].toString(); - newListener.BindHost = jsonObj["l_bind_host"].toString(); - newListener.BindPort = jsonObj["l_bind_port"].toString(); - newListener.AgentAddresses = jsonObj["l_agent_addr"].toString(); - newListener.Status = jsonObj["l_status"].toString(); - newListener.Data = jsonObj["l_data"].toString(); + newListener.ListenerName = jsonObj["l_name"].toString(); + newListener.ListenerFullName = jsonObj["l_type"].toString(); + newListener.BindHost = jsonObj["l_bind_host"].toString(); + newListener.BindPort = jsonObj["l_bind_port"].toString(); + newListener.AgentAddresses = jsonObj["l_agent_addr"].toString(); + newListener.Status = jsonObj["l_status"].toString(); + newListener.Data = jsonObj["l_data"].toString(); ListenersTab->EditListenerItem(newListener); return; @@ -433,6 +482,7 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) QString id = idValue.toString(); if (TasksMap.contains(id)) { Task* task = TasksMap[id]; + task->data.Status = "Running"; task->item_Result->setText("Running"); } } @@ -444,6 +494,10 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) TasksTab->RemoveTaskItem(TaskId); return; } + if ( spType == TYPE_AGENT_TASK_HOOK ) + { + this->PostHookProcess(jsonObj); + } @@ -569,6 +623,46 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) + if ( spType == TYPE_CREDS_CREATE ) + { + CredentialData newCredential = {}; + newCredential.CredId = jsonObj["c_creds_id"].toString(); + newCredential.Username = jsonObj["c_username"].toString(); + newCredential.Password = jsonObj["c_password"].toString(); + newCredential.Realm = jsonObj["c_realm"].toString(); + newCredential.Type = jsonObj["c_type"].toString(); + newCredential.Tag = jsonObj["c_tag"].toString(); + newCredential.Storage = jsonObj["c_storage"].toString(); + newCredential.AgentId = jsonObj["c_agent_id"].toString(); + newCredential.Host = jsonObj["c_host"].toString(); + newCredential.Date = UnixTimestampGlobalToStringLocal(static_cast(jsonObj["c_date"].toDouble())); + + CredentialsTab->AddCredentialsItem(newCredential); + return; + } + if ( spType == TYPE_CREDS_EDIT ) { + CredentialData newCredential = {}; + newCredential.CredId = jsonObj["c_creds_id"].toString(); + newCredential.Username = jsonObj["c_username"].toString(); + newCredential.Password = jsonObj["c_password"].toString(); + newCredential.Realm = jsonObj["c_realm"].toString(); + newCredential.Type = jsonObj["c_type"].toString(); + newCredential.Tag = jsonObj["c_tag"].toString(); + newCredential.Storage = jsonObj["c_storage"].toString(); + newCredential.Host = jsonObj["c_host"].toString(); + + CredentialsTab->EditCredentialsItem(newCredential); + return; + } + if ( spType == TYPE_CREDS_DELETE ) { + QString credId = jsonObj["c_creds_id"].toString(); + + CredentialsTab->RemoveCredentialsItem(credId); + return; + } + + + if( spType == TYPE_TUNNEL_CREATE ) { TunnelData newTunnel = {0}; @@ -616,7 +710,7 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) if (AgentsMap.contains(agentId) ) { auto agent = AgentsMap[agentId]; - if (agent && agent->browsers.FileBrowser && agent->FileBrowser) + if (agent && agent->FileBrowser) agent->FileBrowser->SetDisksWin(time, msgType, message, data); } return; @@ -632,7 +726,7 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) if (AgentsMap.contains(agentId) ) { auto agent = AgentsMap[agentId]; - if (agent && agent->browsers.FileBrowser && agent->FileBrowser) + if (agent && agent->FileBrowser) agent->FileBrowser->AddFiles(time, msgType, message, path, data); } return; @@ -647,7 +741,7 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) if (AgentsMap.contains(agentId)) { auto agent = AgentsMap[agentId]; - if (agent && agent->browsers.ProcessBrowser && agent->ProcessBrowser) { + if (agent && agent->ProcessBrowser) { agent->ProcessBrowser->SetStatus(time, msgType, message); agent->ProcessBrowser->SetProcess(msgType, data); } @@ -663,7 +757,7 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) if (AgentsMap.contains(agentId) ) { auto agent = AgentsMap[agentId]; - if (agent && agent->browsers.FileBrowser && agent->FileBrowser) + if (agent && agent->FileBrowser) agent->FileBrowser->SetStatus(time, msgType, message); } return; @@ -730,19 +824,22 @@ void AdaptixWidget::processSyncPacket(QJsonObject jsonObj) if( spType == TYPE_LISTENER_REG ) { QString fn = jsonObj["fn"].toString(); - QString ui = jsonObj["ui"].toString(); + QString ax = jsonObj["ax"].toString(); - this->RegisterListenerConfig(fn, ui); + this->RegisterListenerConfig(fn, ax); return; } if( spType == TYPE_AGENT_REG ) { - QString agentName = jsonObj["agent"].toString(); - QString agentwatermark = jsonObj["watermark"].toString(); - QString listenersJson = jsonObj["listeners_json"].toString(); - QString handlersJson = jsonObj["handlers_json"].toString(); + QString agentName = jsonObj["agent"].toString(); + QString ax_script = jsonObj["ax"].toString(); + QJsonArray listenersArray = jsonObj["listeners"].toArray(); - this->RegisterAgentConfig(agentName, agentwatermark, handlersJson, listenersJson); + QStringList listeners; + for (QJsonValue listener : listenersArray) + listeners.append(listener.toString()); + + this->RegisterAgentConfig(agentName, ax_script, listeners); return; } } diff --git a/AdaptixClient/Source/Client/Requestor.cpp b/AdaptixClient/Source/Client/Requestor.cpp index 9f92b768..3f8eeffa 100644 --- a/AdaptixClient/Source/Client/Requestor.cpp +++ b/AdaptixClient/Source/Client/Requestor.cpp @@ -101,7 +101,7 @@ bool HttpReqGetOTP(const QString &type, const QString &objectId, AuthProfile pro return false; } -/// LISTENER +///LISTENER bool HttpReqListenerStart(const QString &listenerName, const QString &configType, const QString &configData, AuthProfile profile, QString* message, bool* ok ) { @@ -155,7 +155,7 @@ bool HttpReqListenerStop(const QString &listenerName, const QString &listenerTyp return false; } -/// AGENT +///AGENT QJsonObject HttpReqTimeout( int timeout, const QString &sUrl, const QByteArray &jsonData, const QString &token ) { @@ -199,14 +199,13 @@ QJsonObject HttpReqTimeout( int timeout, const QString &sUrl, const QByteArray & return jsonObject; } -bool HttpReqAgentGenerate(const QString &listenerName, const QString &listenerType, const QString &agentName, const QString &os, const QString &configData, AuthProfile profile, QString* message, bool* ok ) +bool HttpReqAgentGenerate(const QString &listenerName, const QString &listenerType, const QString &agentName, const QString &configData, AuthProfile profile, QString* message, bool* ok ) { QJsonObject dataJson; - dataJson["listener_name"] = listenerName; - dataJson["listener_type"] = listenerType; - dataJson["agent"] = agentName; - dataJson["operating_system"] = os; - dataJson["config"] = configData; + dataJson["listener_name"] = listenerName; + dataJson["listener_type"] = listenerType; + dataJson["agent"] = agentName; + dataJson["config"] = configData; QByteArray jsonData = QJsonDocument(dataJson).toJson(); QString sUrl = profile.GetURL() + "/agent/generate"; @@ -219,15 +218,8 @@ bool HttpReqAgentGenerate(const QString &listenerName, const QString &listenerTy return false; } -bool HttpReqAgentCommand(const QString &agentName, const QString &agentId, const QString &cmdLine, const QString &data, AuthProfile profile, QString* message, bool* ok ) +bool HttpReqAgentCommand(const QByteArray &jsonData, AuthProfile profile, QString* message, bool* ok ) { - QJsonObject dataJson; - dataJson["name"] = agentName; - dataJson["id"] = agentId; - dataJson["cmdline"] = cmdLine; - dataJson["data"] = data; - QByteArray jsonData = QJsonDocument(dataJson).toJson(); - QString sUrl = profile.GetURL() + "/agent/command/execute"; QJsonObject jsonObject = HttpReq(sUrl, jsonData, profile.GetAccessToken()); if ( jsonObject.contains("message") && jsonObject.contains("ok") ) { @@ -238,26 +230,6 @@ bool HttpReqAgentCommand(const QString &agentName, const QString &agentId, const return false; } -bool HttpReqAgentExit( QStringList agentsId, AuthProfile profile, QString* message, bool* ok ) -{ - QJsonArray arrayId; - for (QString item : agentsId) - arrayId.append(item); - - QJsonObject dataJson; - dataJson["agent_id_array"] = arrayId; - QByteArray jsonData = QJsonDocument(dataJson).toJson(); - - QString sUrl = profile.GetURL() + "/agent/exit"; - QJsonObject jsonObject = HttpReq(sUrl, jsonData, profile.GetAccessToken()); - if ( jsonObject.contains("message") && jsonObject.contains("ok") ) { - *message = jsonObject["message"].toString(); - *ok = jsonObject["ok"].toBool(); - return true; - } - return false; -} - bool HttpReqConsoleRemove( QStringList agentsId, AuthProfile profile, QString* message, bool* ok ) { QJsonArray arrayId; @@ -309,7 +281,7 @@ bool HttpReqAgentSetTag( QStringList agentsId, const QString &tag, AuthProfile p dataJson["tag"] = tag; QByteArray jsonData = QJsonDocument(dataJson).toJson(); - QString sUrl = profile.GetURL() + "/agent/settag"; + QString sUrl = profile.GetURL() + "/agent/set/tag"; QJsonObject jsonObject = HttpReq(sUrl, jsonData, profile.GetAccessToken()); if ( jsonObject.contains("message") && jsonObject.contains("ok") ) { *message = jsonObject["message"].toString(); @@ -330,7 +302,7 @@ bool HttpReqAgentSetMark( QStringList agentsId, const QString &mark, AuthProfile dataJson["mark"] = mark; QByteArray jsonData = QJsonDocument(dataJson).toJson(); - QString sUrl = profile.GetURL() + "/agent/setmark"; + QString sUrl = profile.GetURL() + "/agent/set/mark"; QJsonObject jsonObject = HttpReq(sUrl, jsonData, profile.GetAccessToken()); if ( jsonObject.contains("message") && jsonObject.contains("ok") ) { *message = jsonObject["message"].toString(); @@ -340,7 +312,7 @@ bool HttpReqAgentSetMark( QStringList agentsId, const QString &mark, AuthProfile return false; } -bool HttpReqAgentSetColor( QStringList agentsId, const QString &background, const QString &foreground, bool reset, AuthProfile profile, QString* message, bool* ok ) +bool HttpReqAgentSetColor( QStringList agentsId, const QString &background, const QString &foreground, const bool reset, AuthProfile profile, QString* message, bool* ok ) { QJsonArray arrayId; for (QString item : agentsId) @@ -353,7 +325,7 @@ bool HttpReqAgentSetColor( QStringList agentsId, const QString &background, cons dataJson["reset"] = reset; QByteArray jsonData = QJsonDocument(dataJson).toJson(); - QString sUrl = profile.GetURL() + "/agent/setcolor"; + QString sUrl = profile.GetURL() + "/agent/set/color"; QJsonObject jsonObject = HttpReq(sUrl, jsonData, profile.GetAccessToken()); if ( jsonObject.contains("message") && jsonObject.contains("ok") ) { *message = jsonObject["message"].toString(); @@ -363,7 +335,27 @@ bool HttpReqAgentSetColor( QStringList agentsId, const QString &background, cons return false; } -bool HttpReqTaskStop(const QString &agentId, QStringList tasksId, AuthProfile profile, QString* message, bool* ok ) +bool HttpReqAgentSetImpersonate(const QString &agentId, const QString &impersonate, const bool elevated, AuthProfile profile, QString* message, bool* ok ) +{ + QJsonObject dataJson; + dataJson["agent_id"] = agentId; + dataJson["impersonate"] = impersonate; + dataJson["elevated"] = elevated; + QByteArray jsonData = QJsonDocument(dataJson).toJson(); + + QString sUrl = profile.GetURL() + "/agent/set/impersonate"; + QJsonObject jsonObject = HttpReq(sUrl, jsonData, profile.GetAccessToken()); + if ( jsonObject.contains("message") && jsonObject.contains("ok") ) { + *message = jsonObject["message"].toString(); + *ok = jsonObject["ok"].toBool(); + return true; + } + return false; +} + +///TASK + +bool HttpReqTaskCancel(const QString &agentId, QStringList tasksId, AuthProfile profile, QString* message, bool* ok ) { QJsonArray arrayId; for (QString item : tasksId) @@ -374,7 +366,7 @@ bool HttpReqTaskStop(const QString &agentId, QStringList tasksId, AuthProfile pr dataJson["tasks_array"] = arrayId; QByteArray jsonData = QJsonDocument(dataJson).toJson(); - QString sUrl = profile.GetURL() + "/agent/task/stop"; + QString sUrl = profile.GetURL() + "/agent/task/cancel"; QJsonObject jsonObject = HttpReq(sUrl, jsonData, profile.GetAccessToken()); if ( jsonObject.contains("message") && jsonObject.contains("ok") ) { *message = jsonObject["message"].toString(); @@ -405,16 +397,9 @@ bool HttpReqTasksDelete(const QString &agentId, QStringList tasksId, AuthProfile return false; } -/// DOWNLOADS - -bool HttpReqDownloadStart(const QString &agentId, const QString &path, AuthProfile profile, QString* message, bool* ok ) +bool HttpReqTasksHook(const QByteArray &jsonData, AuthProfile profile, QString* message, bool* ok) { - QJsonObject dataJson; - dataJson["agent_id"] = agentId; - dataJson["path"] = path; - QByteArray jsonData = QJsonDocument(dataJson).toJson(); - - QString sUrl = profile.GetURL() + "/download/start"; + QString sUrl = profile.GetURL() + "/agent/task/hook"; QJsonObject jsonObject = HttpReq(sUrl, jsonData, profile.GetAccessToken()); if ( jsonObject.contains("message") && jsonObject.contains("ok") ) { *message = jsonObject["message"].toString(); @@ -424,6 +409,8 @@ bool HttpReqDownloadStart(const QString &agentId, const QString &path, AuthProfi return false; } +/// DOWNLOADS + bool HttpReqDownloadAction(const QString &action, const QString &fileId, AuthProfile profile, QString* message, bool* ok ) { QJsonObject dataJson; @@ -440,75 +427,6 @@ bool HttpReqDownloadAction(const QString &action, const QString &fileId, AuthPro return false; } -/// BROWSER - -bool HttpReqBrowserDisks(const QString &agentId, AuthProfile profile, QString* message, bool* ok ) -{ - QJsonObject dataJson; - dataJson["agent_id"] = agentId; - QByteArray jsonData = QJsonDocument(dataJson).toJson(); - - QString sUrl = profile.GetURL() + "/browser/disks"; - QJsonObject jsonObject = HttpReq(sUrl, jsonData, profile.GetAccessToken()); - if ( jsonObject.contains("message") && jsonObject.contains("ok") ) { - *message = jsonObject["message"].toString(); - *ok = jsonObject["ok"].toBool(); - return true; - } - return false; -} - -bool HttpReqBrowserProcess(const QString &agentId, AuthProfile profile, QString* message, bool* ok ) -{ - QJsonObject dataJson; - dataJson["agent_id"] = agentId; - QByteArray jsonData = QJsonDocument(dataJson).toJson(); - - QString sUrl = profile.GetURL() + "/browser/process"; - QJsonObject jsonObject = HttpReq(sUrl, jsonData, profile.GetAccessToken()); - if ( jsonObject.contains("message") && jsonObject.contains("ok") ) { - *message = jsonObject["message"].toString(); - *ok = jsonObject["ok"].toBool(); - return true; - } - return false; -} - -bool HttpReqBrowserList(const QString &agentId, const QString &path, AuthProfile profile, QString* message, bool* ok ) -{ - QJsonObject dataJson; - dataJson["agent_id"] = agentId; - dataJson["path"] = path; - QByteArray jsonData = QJsonDocument(dataJson).toJson(); - - QString sUrl = profile.GetURL() + "/browser/files"; - QJsonObject jsonObject = HttpReq(sUrl, jsonData, profile.GetAccessToken()); - if ( jsonObject.contains("message") && jsonObject.contains("ok") ) { - *message = jsonObject["message"].toString(); - *ok = jsonObject["ok"].toBool(); - return true; - } - return false; -} - -bool HttpReqBrowserUpload(const QString &agentId, const QString &path, const QString &content, AuthProfile profile, QString* message, bool* ok ) -{ - QJsonObject dataJson; - dataJson["agent_id"] = agentId; - dataJson["remote_path"] = path; - dataJson["content"] = content; - QByteArray jsonData = QJsonDocument(dataJson).toJson(); - - QString sUrl = profile.GetURL() + "/browser/upload"; - QJsonObject jsonObject = HttpReq(sUrl, jsonData, profile.GetAccessToken()); - if ( jsonObject.contains("message") && jsonObject.contains("ok") ) { - *message = jsonObject["message"].toString(); - *ok = jsonObject["ok"].toBool(); - return true; - } - return false; -} - ///TUNNEL bool HttpReqTunnelStartServer(const QString &tunnelType, const QByteArray &jsonData, AuthProfile profile, QString* message, bool* ok) @@ -556,7 +474,7 @@ bool HttpReqTunnelSetInfo(const QString &tunnelId, const QString &info, AuthProf return false; } -/// SCREEN +///SCREEN bool HttpReqScreenSetNote( QStringList scrensId, const QString ¬e, AuthProfile profile, QString* message, bool* ok ) { @@ -597,4 +515,46 @@ bool HttpReqScreenRemove( QStringList scrensId, AuthProfile profile, QString* me return true; } return false; -} \ No newline at end of file +} + +///CREDS + +bool HttpReqCredentialsCreate(const QByteArray &jsonData, AuthProfile profile, QString *message, bool *ok) +{ + QString sUrl = profile.GetURL() + "/creds/add"; + QJsonObject jsonObject = HttpReq(sUrl, jsonData, profile.GetAccessToken()); + if ( jsonObject.contains("message") && jsonObject.contains("ok") ) { + *message = jsonObject["message"].toString(); + *ok = jsonObject["ok"].toBool(); + return true; + } + return false; +} + +bool HttpReqCredentialsEdit(const QByteArray &jsonData, AuthProfile profile, QString *message, bool *ok) +{ + QString sUrl = profile.GetURL() + "/creds/edit"; + QJsonObject jsonObject = HttpReq(sUrl, jsonData, profile.GetAccessToken()); + if ( jsonObject.contains("message") && jsonObject.contains("ok") ) { + *message = jsonObject["message"].toString(); + *ok = jsonObject["ok"].toBool(); + return true; + } + return false; +} + +bool HttpReqCredentialsRemove(const QString &credsId, AuthProfile profile, QString* message, bool* ok) +{ + QJsonObject dataJson; + dataJson["cred_id"] = credsId; + QByteArray jsonData = QJsonDocument(dataJson).toJson(); + + QString sUrl = profile.GetURL() + "/creds/remove"; + QJsonObject jsonObject = HttpReq(sUrl, jsonData, profile.GetAccessToken()); + if ( jsonObject.contains("message") && jsonObject.contains("ok") ) { + *message = jsonObject["message"].toString(); + *ok = jsonObject["ok"].toBool(); + return true; + } + return false; +} diff --git a/AdaptixClient/Source/Client/Storage.cpp b/AdaptixClient/Source/Client/Storage.cpp index d6726968..1e0187c7 100644 --- a/AdaptixClient/Source/Client/Storage.cpp +++ b/AdaptixClient/Source/Client/Storage.cpp @@ -224,7 +224,7 @@ QVector Storage::ListExtensions() query.prepare( "SELECT * FROM Extensions;" ); if ( query.exec() ) { while ( query.next() ) { - ExtensionFile ext; + ExtensionFile ext = {}; ext.FilePath = query.value("filepath").toString(); ext.Enabled = query.value("enabled").toBool(); list.push_back(ext); diff --git a/AdaptixClient/Source/Client/TunnelEndpoint.cpp b/AdaptixClient/Source/Client/TunnelEndpoint.cpp index 9e57ed78..c7ba1cf3 100644 --- a/AdaptixClient/Source/Client/TunnelEndpoint.cpp +++ b/AdaptixClient/Source/Client/TunnelEndpoint.cpp @@ -63,18 +63,20 @@ void TunnelEndpoint::SetTunnelId(const QString &tunnelId) void TunnelEndpoint::StopChannel(const QString& channelId) { - // auto it = tunnelChannels.find(channelId); - // if (it == tunnelChannels.end()) - // return; - // - // ChannelHandle handle = it.value(); - // tunnelChannels.erase(it); - // - // handle.worker->stop(); - // dQMetaObject::invokeMethod(handle.worker, "stop", Qt::QueuedConnection); - // - // handle.thread->quit(); - // handle.thread->wait(1000); + /* + auto it = tunnelChannels.find(channelId); + if (it == tunnelChannels.end()) + return; + + ChannelHandle handle = it.value(); + tunnelChannels.erase(it); + + handle.worker->stop(); + dQMetaObject::invokeMethod(handle.worker, "stop", Qt::QueuedConnection); + + handle.thread->quit(); + handle.thread->wait(1000); + */ } void TunnelEndpoint::Stop() @@ -82,22 +84,24 @@ void TunnelEndpoint::Stop() if (tcpServer->isListening()) tcpServer->close(); - // for (auto id : tunnelChannels.keys()) { - // auto handle = tunnelChannels.value(id); - // tunnelChannels.remove(id); - // - // if (handle.worker && handle.thread) { - // // handle.worker->stop(); - // QMetaObject::invokeMethod( handle.worker, "stop", Qt::BlockingQueuedConnection ); - // handle.thread->quit(); - // handle.thread->wait(); - // } - // - // if (handle.worker) - // handle.worker->deleteLater(); - // if (handle.thread) - // handle.thread->deleteLater(); - // } + /* + for (auto id : tunnelChannels.keys()) { + auto handle = tunnelChannels.value(id); + tunnelChannels.remove(id); + + if (handle.worker && handle.thread) { + // handle.worker->stop(); + QMetaObject::invokeMethod( handle.worker, "stop", Qt::BlockingQueuedConnection ); + handle.thread->quit(); + handle.thread->wait(); + } + + if (handle.worker) + handle.worker->deleteLater(); + if (handle.thread) + handle.thread->deleteLater(); + } + */ } void TunnelEndpoint::onStartLpfChannel() @@ -126,7 +130,6 @@ void TunnelEndpoint::onStartLpfChannel() connect(thread, &QThread::finished, thread, &QThread::deleteLater); connect(worker, &TunnelWorker::finished, this, [this, channelId]() {StopChannel(channelId);}); - // tunnelChannels.insert(channelId, { thread, worker, channelId }); thread->start(); } } @@ -145,7 +148,7 @@ void TunnelEndpoint::onStartSocks4Channel() } QByteArray header = clientSock->read(8); const uchar* d = reinterpret_cast(header.constData()); - if (d[0] != 0x04 || d[1] != 0x01) { // VER=4, CMD=1 (CONNECT) + if (d[0] != 0x04 || d[1] != 0x01) { /// VER=4, CMD=1 (CONNECT) clientSock->disconnectFromHost(); return; } @@ -220,7 +223,7 @@ void TunnelEndpoint::onStartSocks5Channel() QString dstAddress; quint16 dstPort; - if (addrType == 0x01) { // IPv4 + if (addrType == 0x01) { /// IPv4 if (request.size() < 10) { clientSock->disconnectFromHost(); continue; @@ -229,7 +232,7 @@ void TunnelEndpoint::onStartSocks5Channel() dstAddress = ip.toString(); dstPort = (static_cast(request[8]) << 8) | static_cast(request[9]); - } else if (addrType == 0x03) { // DNS + } else if (addrType == 0x03) { /// DNS uchar domainLen = static_cast(request[4]); if (request.size() < 5 + domainLen + 2) { clientSock->disconnectFromHost(); @@ -358,7 +361,7 @@ void TunnelEndpoint::onStartSocks5AuthChannel() QString dstAddress; quint16 dstPort; - if (addrType == 0x01) { // IPv4 + if (addrType == 0x01) { /// IPv4 if (request.size() < 10) { clientSock->disconnectFromHost(); continue; @@ -368,7 +371,7 @@ void TunnelEndpoint::onStartSocks5AuthChannel() dstAddress = ip.toString(); dstPort = (static_cast(request[8]) << 8) | static_cast(request[9]); - } else if (addrType == 0x03) { // DNS + } else if (addrType == 0x03) { /// DNS uchar domainLen = static_cast(request[4]); if (request.size() < 5 + domainLen + 2) { clientSock->disconnectFromHost(); diff --git a/AdaptixClient/Source/Client/WidgetBuilder.cpp b/AdaptixClient/Source/Client/WidgetBuilder.cpp deleted file mode 100644 index 20c37d91..00000000 --- a/AdaptixClient/Source/Client/WidgetBuilder.cpp +++ /dev/null @@ -1,549 +0,0 @@ -#include -#include - -WidgetBuilder::WidgetBuilder(const QByteArray& jsonData) -{ - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(jsonData, &parseError); - - if (parseError.error == QJsonParseError::NoError && document.isObject()) - qJsonObject = document.object(); - else - error = QString("JSON parse error: %1").arg(parseError.errorString()); -} - -WidgetBuilder::~WidgetBuilder() = default; - -QString WidgetBuilder::GetError() -{ - return error; -} - -QLayout* WidgetBuilder::BuildLayout(QString layoutType, QJsonObject rootObj, bool editable) -{ - QLayout* layout = nullptr; - - if (layoutType.isEmpty()) - layoutType = rootObj["layout"].toString(); - - if (layoutType == "vlayout") { - layout = new QVBoxLayout; - } else if (layoutType == "hlayout") { - layout = new QHBoxLayout; - } else if (layoutType == "glayout") { - layout = new QGridLayout; - } - else { - error = "Required base layout"; - return nullptr; - } - - lpVector.append(layout); - - QJsonArray elementsArray = rootObj["elements"].toArray(); - - for (const QJsonValue& elementValue : elementsArray) { - QJsonObject elementObj = elementValue.toObject(); - QString type = elementObj["type"].toString(); - QString id = elementObj["id"].toString(); - bool editMode = !elementObj.contains("editable") || elementObj["editable"].toBool(); - - QJsonArray positionArray = elementObj["position"].toArray(); - int row = positionArray.size() > 0 ? positionArray[0].toInt() : 0; - int col = positionArray.size() > 1 ? positionArray[1].toInt() : 0; - int rowSpan = positionArray.size() > 2 ? positionArray[2].toInt() : 1; - int colSpan = positionArray.size() > 3 ? positionArray[3].toInt() : 1; - - if (type == "label") { - auto label = new QLabel(widget); - - label->setText( elementObj["text"].toString() ); - - if (auto gridLayout = qobject_cast(layout)) { - gridLayout->addWidget(label, row, col, rowSpan, colSpan); - } else if (auto boxLayout = qobject_cast(layout)) { - boxLayout->addWidget(label); - } - } - else if (type == "vline" || type == "hline") { - auto line = new QFrame(widget); - - if (type == "vline"){ - line->setFrameShape(QFrame::VLine); - line->setMinimumHeight(25); - } - else { - line->setFrameShape(QFrame::HLine); - line->setMinimumWidth(25); - } - - if (auto gridLayout = qobject_cast(layout)) { - gridLayout->addWidget(line, row, col, rowSpan, colSpan); - } else if (auto boxLayout = qobject_cast(layout)) { - boxLayout->addWidget(line); - } - } - else if (type == "vspacer" || type == "hspacer") { - auto spacer = new QSpacerItem(40, 20); - if (type == "vspacer") - spacer->changeSize(40, 20, QSizePolicy::Minimum, QSizePolicy::Expanding); - else - spacer->changeSize(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); - - if (auto gridLayout = qobject_cast(layout)) { - gridLayout->addItem(spacer, row, col, rowSpan, colSpan); - } else if (auto boxLayout = qobject_cast(layout)) { - boxLayout->addItem(spacer); - } - } - else if (type == "input") { - auto lineEdit = new QLineEdit(widget); - - lineEdit->setPlaceholderText(elementObj["placeholder"].toString()); - lineEdit->setText(elementObj["text"].toString()); - - if (auto gridLayout = qobject_cast(layout)) { - gridLayout->addWidget(lineEdit, row, col, rowSpan, colSpan); - } else if (auto boxLayout = qobject_cast(layout)) { - boxLayout->addWidget(lineEdit); - } - - if (!id.isEmpty()) { - widgetMap[id] = lineEdit; - } - } - else if (type == "date_input") { - auto dateEdit = new QDateEdit(widget); - - dateEdit->setCalendarPopup(true); - dateEdit->setDateTime(QDateTime::currentDateTime()); - dateEdit->setDisplayFormat(elementObj["format"].toString()); - - if (auto gridLayout = qobject_cast(layout)) { - gridLayout->addWidget(dateEdit, row, col, rowSpan, colSpan); - } else if (auto boxLayout = qobject_cast(layout)) { - boxLayout->addWidget(dateEdit); - } - - if (!id.isEmpty()) { - widgetMap[id] = dateEdit; - } - } - else if (type == "time_input") { - auto timeEdit = new QTimeEdit(widget); - - timeEdit->setDateTime(QDateTime::currentDateTime()); - timeEdit->setDisplayFormat(elementObj["format"].toString()); - - if (auto gridLayout = qobject_cast(layout)) { - gridLayout->addWidget(timeEdit, row, col, rowSpan, colSpan); - } else if (auto boxLayout = qobject_cast(layout)) { - boxLayout->addWidget(timeEdit); - } - - if (!id.isEmpty()) { - widgetMap[id] = timeEdit; - } - } - else if (type == "file_selector") { - auto selector = new FileSelector(widget); - - selector->input->setPlaceholderText(elementObj["placeholder"].toString()); - - if (auto gridLayout = qobject_cast(layout)) { - gridLayout->addWidget(selector, row, col, rowSpan, colSpan); - } else if (auto boxLayout = qobject_cast(layout)) { - boxLayout->addWidget(selector); - } - - if (!id.isEmpty()) { - widgetMap[id] = selector; - } - } - else if (type == "combo") { - auto comboBox = new QComboBox(widget); - - comboBox->setCurrentText( elementObj["text"].toString() ); - - QJsonArray itemsArray = elementObj["items"].toArray(); - for (const QJsonValue& itemValue : itemsArray) { - comboBox->addItem(itemValue.toString()); - } - - if (auto gridLayout = qobject_cast(layout)) { - gridLayout->addWidget(comboBox, row, col, rowSpan, colSpan); - } else if (auto boxLayout = qobject_cast(layout)) { - boxLayout->addWidget(comboBox); - } - - if (!id.isEmpty()) { - widgetMap[id] = comboBox; - } - } - else if (type == "spinbox") { - auto spinBox = new QSpinBox(widget); - - if( elementObj.contains("min") && elementObj["min"].toDouble() ) - spinBox->setMinimum( elementObj["min"].toDouble() ); - - if( elementObj.contains("max") && elementObj["max"].toDouble() ) - spinBox->setMaximum( elementObj["max"].toDouble() ); - - if( elementObj.contains("value") && elementObj["value"].toDouble() ) - spinBox->setValue( elementObj["value"].toDouble() ); - - if (auto gridLayout = qobject_cast(layout)) { - gridLayout->addWidget(spinBox, row, col, rowSpan, colSpan); - } else if (auto boxLayout = qobject_cast(layout)) { - boxLayout->addWidget(spinBox); - } - - if (!id.isEmpty()) { - widgetMap[id] = spinBox; - } - } - else if (type == "textedit") { - auto textEdit = new QPlainTextEdit(widget); - textEdit->setLineWrapMode(QPlainTextEdit::NoWrap); - - textEdit->setPlaceholderText(elementObj["placeholder"].toString()); - textEdit->setPlainText( elementObj["text"].toString() ); - - if (auto gridLayout = qobject_cast(layout)) { - gridLayout->addWidget(textEdit, row, col, rowSpan, colSpan); - } else if (auto boxLayout = qobject_cast(layout)) { - boxLayout->addWidget(textEdit); - } - - if (!id.isEmpty()) { - widgetMap[id] = textEdit; - } - } - else if (type == "checkbox") { - auto checkBox = new QCheckBox(widget); - - checkBox->setText( elementObj["text"].toString() ); - checkBox->setChecked( elementObj["checked"].toBool(false) ); - - if (auto gridLayout = qobject_cast(layout)) { - gridLayout->addWidget(checkBox, row, col, rowSpan, colSpan); - } else if (auto boxLayout = qobject_cast(layout)) { - boxLayout->addWidget(checkBox); - } - - if (!id.isEmpty()) { - widgetMap[id] = checkBox; - } - } - if (type == "table") { - int rowCount = elementObj["rowCount"].toInt(0); - int columnCount = elementObj["columnCount"].toInt(0); - - auto tableWidget = new QTableWidget(rowCount, columnCount, widget); - tableWidget->setAutoFillBackground( false ); - tableWidget->setShowGrid( false ); - tableWidget->setSortingEnabled( true ); - tableWidget->setWordWrap( true ); - tableWidget->setCornerButtonEnabled( false ); - tableWidget->setSelectionBehavior( QAbstractItemView::SelectRows ); - tableWidget->setSelectionMode( QAbstractItemView::SingleSelection ); - tableWidget->setFocusPolicy( Qt::NoFocus ); - tableWidget->setAlternatingRowColors( true ); - tableWidget->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch ); - tableWidget->horizontalHeader()->setCascadingSectionResizes( true ); - tableWidget->horizontalHeader()->setHighlightSections( false ); - tableWidget->verticalHeader()->setVisible( false ); - - QJsonArray headersArray = elementObj["headers"].toArray(); - for (int i = 0; i < headersArray.size(); ++i) { - tableWidget->setHorizontalHeaderItem(i, new QTableWidgetItem(headersArray[i].toString())); - } - - QJsonArray dataArray = elementObj["data"].toArray(); - for (int i = 0; i < dataArray.size(); ++i) { - QJsonArray rowArray = dataArray[i].toArray(); - for (int j = 0; j < rowArray.size(); ++j) { - QTableWidgetItem* item = new QTableWidgetItem(rowArray[j].toString()); - tableWidget->setItem(i, j, item); - } - } - - if (auto gridLayout = qobject_cast(layout)) { - gridLayout->addWidget(tableWidget, row, col, rowSpan, colSpan); - } else if (auto boxLayout = qobject_cast(layout)) { - boxLayout->addWidget(tableWidget); - } - - if (!id.isEmpty()) { - widgetMap[id] = tableWidget; - } - } - else if (type == "spin_table") { - int rowCount = elementObj["row_count"].toInt(0); - int columnCount = elementObj["column_count"].toInt(0); - - auto spinTable = new SpinTable(rowCount, columnCount, widget); - - QJsonArray headersArray = elementObj["headers"].toArray(); - for (int i = 0; i < headersArray.size(); ++i) { - spinTable->table->setHorizontalHeaderItem(i, new QTableWidgetItem(headersArray[i].toString())); - } - - QJsonArray dataArray = elementObj["data"].toArray(); - for (int i = 0; i < dataArray.size(); ++i) { - QJsonArray rowArray = dataArray[i].toArray(); - for (int j = 0; j < rowArray.size(); ++j) { - QTableWidgetItem* item = new QTableWidgetItem(rowArray[j].toString()); - spinTable->table->setItem(i, j, item); - } - } - - if (auto gridLayout = qobject_cast(layout)) { - gridLayout->addWidget(spinTable->table, row, col, rowSpan, colSpan); - } - else if (auto boxLayout = qobject_cast(layout)) { - boxLayout->addWidget(spinTable); - } - - if (!id.isEmpty()) { - widgetMap[id] = spinTable; - } - } - else if (type == "tab") { - auto tabWidget = new QTabWidget(widget); - - QJsonArray tabsArray = elementObj["tabs"].toArray(); - for (const QJsonValue& tabValue : tabsArray) { - QJsonObject tabObj = tabValue.toObject(); - QString title = tabObj["title"].toString(); - - auto tabContent = new QWidget(); - QLayout* tabLayout = BuildLayout("", tabObj, editable); - tabContent->setLayout(tabLayout); - tabWidget->addTab(tabContent, title); - } - - if (auto gridLayout = qobject_cast(layout)) { - gridLayout->addWidget(tabWidget, row, col, rowSpan, colSpan); - } else if (auto boxLayout = qobject_cast(layout)) { - boxLayout->addWidget(tabWidget); - } - } - else if (type == "vlayout" || type == "hlayout" || type == "glayout") { - QLayout* nestedLayout = BuildLayout(type, elementObj, editable); - - if (auto gridLayout = qobject_cast(layout)) { - gridLayout->addLayout(nestedLayout, row, col, rowSpan, colSpan); - } else if (auto boxLayout = qobject_cast(layout)) { - boxLayout->addLayout(nestedLayout); - } - } - - if(editable && widgetMap[id]) - widgetMap[id]->setDisabled(!editMode); - } - - return layout; -} - -void WidgetBuilder::BuildWidget(bool editable) -{ - if (qJsonObject.isEmpty()) - return; - - widget = new QWidget; - auto layout = BuildLayout("", qJsonObject, editable); - widget->setLayout(layout); - valid = true; -} - -QWidget *WidgetBuilder::GetWidget() const -{ - return widget; -} - -QString WidgetBuilder::CollectData() -{ - QJsonObject collectedData; - QWidget* widget = nullptr; - for (auto it = widgetMap.begin(); it != widgetMap.end(); ++it) { - const QString& id = it.key(); - widget = it.value(); - - if (auto lineEdit = qobject_cast(widget)) { - collectedData[id] = lineEdit->text(); - } - - else if (auto timeEdit = qobject_cast(widget)) { - QTime time = timeEdit->time(); - collectedData[id] = time.toString(timeEdit->displayFormat()); - } - - else if (auto dateEdit = qobject_cast(widget)) { - QDate date = dateEdit->date(); - collectedData[id] = date.toString(dateEdit->displayFormat()); - } - - else if (auto fileSelector = qobject_cast(widget)) { - collectedData[id] = fileSelector->content; - } - - else if (auto comboBox = qobject_cast(widget)) { - collectedData[id] = comboBox->currentText(); - } - - else if (auto spinBox = qobject_cast(widget)) { - collectedData[id] = spinBox->value(); - } - - else if (auto textEdit = qobject_cast(widget)) { - collectedData[id] = textEdit->toPlainText(); - } - - else if (auto checkBox = qobject_cast(widget)) { - collectedData[id] = checkBox->isChecked(); - } - - else if (auto tableWidget = qobject_cast(widget)) { - QJsonArray tableData; - - for (int row = 0; row < tableWidget->rowCount(); ++row) { - QJsonArray rowData; - for (int col = 0; col < tableWidget->columnCount(); ++col) { - QTableWidgetItem* item = tableWidget->item(row, col); - if (item) { - rowData.append(item->text()); - } else { - rowData.append(""); - } - } - tableData.append(rowData); - } - - collectedData[id] = tableData; - } - - else if (auto spinTable = qobject_cast(widget)) { - QJsonArray tableData; - - for (int row = 0; row < spinTable->table->rowCount(); ++row) { - QJsonArray rowData; - for (int col = 0; col < spinTable->table->columnCount(); ++col) { - QTableWidgetItem *item = spinTable->table->item(row, col); - if (item) { - rowData.append(item->text()); - } else { - rowData.append(""); - } - tableData.append(rowData); - } - } - collectedData[id] = tableData; - } - } - - QJsonDocument jsonDocument(collectedData); - QByteArray doc = jsonDocument.toJson(QJsonDocument::Compact); - return QString::fromUtf8(doc); -} - -void WidgetBuilder::FillData(const QString &jsonString) -{ - QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonString.toUtf8()); - if (!jsonDoc.isObject()) { - return; - } - - QJsonObject data = jsonDoc.object(); - - for (auto it = data.begin(); it != data.end(); ++it) { - QString id = it.key(); - QJsonValue value = it.value(); - - if (!widgetMap.contains(id)) { - continue; - } - auto widget = widgetMap[id]; - - if (auto lineEdit = qobject_cast(widget)) { - lineEdit->setText(value.toString()); - } - - else if (auto timeEdit = qobject_cast(widget)) { - QTime time = QTime::fromString(value.toString(), timeEdit->displayFormat()); - if (time.isValid()) { - timeEdit->setTime(time); - } - } - - else if (auto dateEdit = qobject_cast(widget)) { - QDate date = QDate::fromString(value.toString(), dateEdit->displayFormat()); - if (date.isValid()) { - dateEdit->setDate(date); - } - } - - else if (auto comboBox = qobject_cast(widget)) { - int index = comboBox->findText(value.toString()); - if (index != -1) { - comboBox->setCurrentIndex(index); - } - } - - else if (auto spinBox = qobject_cast(widget)) { - spinBox->setValue(value.toDouble()); - } - - else if (auto plainTextEdit = qobject_cast(widget)) { - plainTextEdit->setPlainText(value.toString()); - } - - else if (auto fileSelector = qobject_cast(widget)) { - fileSelector->input->setText("selected..."); - fileSelector->button->setEnabled(false); - } - - else if (auto checkBox = qobject_cast(widget)) { - checkBox->setChecked(value.toBool()); - } - - else if (auto tableWidget = qobject_cast(widget)) { - QJsonArray tableData = value.toArray(); - - for (int row = 0; row < tableData.size(); ++row) { - QJsonArray rowArray = tableData[row].toArray(); - for (int col = 0; col < rowArray.size(); ++col) { - QString cellText = rowArray[col].toString(); - QTableWidgetItem* item = tableWidget->item(row, col); - if (!item) { - item = new QTableWidgetItem(); - tableWidget->setItem(row, col, item); - } - item->setText(cellText); - } - } - } - - else if (auto spinTable = qobject_cast(widget)) { - QJsonArray tableData = value.toArray(); - - for (int row = 0; row < tableData.size(); ++row) { - QJsonArray rowArray = tableData[row].toArray(); - for (int col = 0; col < rowArray.size(); ++col) { - QString cellText = rowArray[col].toString(); - QTableWidgetItem* item = spinTable->table->item(row, col); - if (!item) { - item = new QTableWidgetItem(); - spinTable->table->setItem(row, col, item); - } - item->setText(cellText); - } - } - } - } -} - -void WidgetBuilder::ClearWidget() const -{ - delete widget; -} diff --git a/AdaptixClient/Source/MainAdaptix.cpp b/AdaptixClient/Source/MainAdaptix.cpp index db62340d..78dfdac4 100644 --- a/AdaptixClient/Source/MainAdaptix.cpp +++ b/AdaptixClient/Source/MainAdaptix.cpp @@ -94,10 +94,7 @@ void MainAdaptix::Start() const QApplication::exec(); } -void MainAdaptix::Exit() -{ - QCoreApplication::quit(); -} +void MainAdaptix::Exit() { QCoreApplication::quit(); } void MainAdaptix::NewProject() const { diff --git a/AdaptixClient/Source/UI/Dialogs/DialogAgent.cpp b/AdaptixClient/Source/UI/Dialogs/DialogAgent.cpp index f4721d2f..f8c3d29d 100644 --- a/AdaptixClient/Source/UI/Dialogs/DialogAgent.cpp +++ b/AdaptixClient/Source/UI/Dialogs/DialogAgent.cpp @@ -1,7 +1,7 @@ #include #include -#include #include +#include DialogAgent::DialogAgent(const QString &listenerName, const QString &listenerType) { @@ -12,12 +12,11 @@ DialogAgent::DialogAgent(const QString &listenerName, const QString &listenerTyp this->listenerName = listenerName; this->listenerType = listenerType; - connect( buttonLoad, &QPushButton::clicked, this, &DialogAgent::onButtonLoad ); - connect( buttonSave, &QPushButton::clicked, this, &DialogAgent::onButtonSave ); - connect( agentCombobox, &QComboBox::currentTextChanged, this, &DialogAgent::changeConfig) ; - connect( osCombobox, &QComboBox::currentTextChanged, this, &DialogAgent::changeOs) ; - connect( generateButton, &QPushButton::clicked, this, &DialogAgent::onButtonGenerate ); - connect( closeButton, &QPushButton::clicked, this, &DialogAgent::onButtonClose ); + connect(buttonLoad, &QPushButton::clicked, this, &DialogAgent::onButtonLoad); + connect(buttonSave, &QPushButton::clicked, this, &DialogAgent::onButtonSave); + connect(agentCombobox, &QComboBox::currentTextChanged, this, &DialogAgent::changeConfig) ; + connect(generateButton, &QPushButton::clicked, this, &DialogAgent::onButtonGenerate); + connect(closeButton, &QPushButton::clicked, this, &DialogAgent::onButtonClose); } DialogAgent::~DialogAgent() = default; @@ -34,14 +33,11 @@ void DialogAgent::createUI() agentLabel = new QLabel("Agent: ", this); agentCombobox = new QComboBox(this); - osLabel = new QLabel("OS: ", this); - osCombobox = new QComboBox(this); - - buttonLoad = new QPushButton(QIcon(":/icons/unarchive"), "", this); + buttonLoad = new QPushButton(QIcon(":/icons/file_open"), "", this); buttonLoad->setIconSize( QSize( 25,25 )); buttonLoad->setToolTip("Load profile from file"); - buttonSave = new QPushButton(QIcon(":/icons/archive"), "", this); + buttonSave = new QPushButton(QIcon(":/icons/save_as"), "", this); buttonSave->setIconSize( QSize( 25,25 )); buttonSave->setToolTip("Save profile to file"); @@ -78,16 +74,14 @@ void DialogAgent::createUI() mainGridLayout = new QGridLayout( this ); mainGridLayout->addWidget( listenerLabel, 0, 0, 1, 1); mainGridLayout->addWidget( listenerInput, 0, 1, 1, 1); - mainGridLayout->addWidget( line_1, 0, 2, 3, 1); + mainGridLayout->addWidget( line_1, 0, 2, 2, 1); mainGridLayout->addWidget( buttonLoad, 0, 3, 1, 1); mainGridLayout->addWidget( agentLabel, 1, 0, 1, 1); mainGridLayout->addWidget( agentCombobox, 1, 1, 1, 1); mainGridLayout->addWidget( buttonSave, 1, 3, 1, 1); - mainGridLayout->addWidget( osLabel, 2, 0, 1, 1); - mainGridLayout->addWidget( osCombobox, 2, 1, 1, 1); - mainGridLayout->addItem( horizontalSpacer, 3, 0, 1, 4); - mainGridLayout->addWidget( agentConfigGroupbox, 4, 0, 1, 4); - mainGridLayout->addLayout( hLayoutBottom, 5, 0, 1, 4); + mainGridLayout->addItem( horizontalSpacer, 2, 0, 1, 4); + mainGridLayout->addWidget( agentConfigGroupbox, 3, 0, 1, 4); + mainGridLayout->addLayout( hLayoutBottom, 4, 0, 1, 4); this->setLayout(mainGridLayout); @@ -104,74 +98,40 @@ void DialogAgent::createUI() generateButton->setFixedHeight(buttonHeight); } -void DialogAgent::Start() -{ - this->exec(); -} +void DialogAgent::Start() { this->exec(); } -void DialogAgent::AddExAgents(const QVector ®Agents) +void DialogAgent::AddExAgents(const QStringList &agents, const QMap &widgets, const QMap &containers) { agentCombobox->clear(); - osCombobox->clear(); - agentsOs.clear(); - this->regAgents = regAgents; + this->agents = agents; + this->widgets = widgets; + this->containers = containers; - QSet agents; - for (auto regAgent : this->regAgents ) { - agents.insert(regAgent.agentName); + for (auto agent : agents) { + widgets[agent]->setParent(nullptr); + widgets[agent]->setParent(this); + containers[agent]->setParent(nullptr); + containers[agent]->setParent(this); - agentsOs[regAgent.agentName].insert(regAgent.operatingSystem); + configStackWidget->addWidget(widgets[agent]); - configStackWidget->addWidget(regAgent.builder->GetWidget()); + agentCombobox->addItem(agent); } + } - for (QString v : agents) - agentCombobox->addItem(v); -} - -void DialogAgent::SetProfile(const AuthProfile &profile) -{ - this->authProfile = profile; -} - -void DialogAgent::changeConfig(const QString &agentName) -{ - if (this->agentsOs.contains(agentName)) { - osCombobox->clear(); - for (auto os : this->agentsOs[agentName]) - osCombobox->addItem(os); - } -} - -void DialogAgent::changeOs(const QString &os) -{ - QString agentName = agentCombobox->currentText(); - for (auto regAgent : this->regAgents ) { - if (regAgent.agentName == agentName && regAgent.operatingSystem == os && regAgent.builder) { - auto w = regAgent.builder->GetWidget(); - configStackWidget->setCurrentWidget(w); - break; - } - } -} +void DialogAgent::SetProfile(const AuthProfile &profile) { this->authProfile = profile; } void DialogAgent::onButtonGenerate() { - QString agentName = agentCombobox->currentText(); - QString operatingSystem = osCombobox->currentText(); - QString configData = ""; - - for (auto regAgent : this->regAgents ) { - if (regAgent.agentName == agentName && regAgent.operatingSystem == operatingSystem && regAgent.builder) { - configData = regAgent.builder->CollectData(); - break; - } - } + QString agentName = agentCombobox->currentText(); + auto configData = QString(); + if (containers[agentName]) + configData = containers[agentName]->toJson(); QString message = QString(); bool ok = false; - bool result = HttpReqAgentGenerate(listenerName, listenerType, agentName, operatingSystem, configData, authProfile, &message, &ok); + bool result = HttpReqAgentGenerate(listenerName, listenerType, agentName, configData, authProfile, &message, &ok); if( !result ){ MessageError("Server is not responding"); return; @@ -244,10 +204,6 @@ void DialogAgent::onButtonLoad() MessageError("Required parameter 'agent' is missing"); return; } - if ( !jsonObject.contains("operating_system") || !jsonObject["operating_system"].isString() ) { - MessageError("Required parameter 'operating_system' is missing"); - return; - } if ( !jsonObject.contains("config") || !jsonObject["config"].isString() ) { MessageError("Required parameter 'config' is missing"); return; @@ -259,59 +215,32 @@ void DialogAgent::onButtonLoad() } QString agentType = jsonObject["agent"].toString(); - int typeIndex = agentCombobox->findText( agentType ); - if ( typeIndex == -1 || !this->agentsOs.contains(agentType)) { + if ( typeIndex == -1 ) { MessageError("No such agent exists"); return; } agentCombobox->setCurrentIndex(typeIndex); - this->changeConfig(agentType); - QString operatingSystem = jsonObject["operating_system"].toString(); - typeIndex = osCombobox->findText( operatingSystem ); - - QStringList items; - for (auto os : this->agentsOs[agentType]) - items.push_back(os); - - if(typeIndex == -1 || !items.contains(operatingSystem)) { - MessageError("No such agent exists"); - return; - } - agentCombobox->setCurrentIndex(typeIndex); - QString configData = jsonObject["config"].toString(); - for (auto regAgent : this->regAgents ) { - if (regAgent.agentName == agentType && regAgent.operatingSystem == operatingSystem && regAgent.builder) { - regAgent.builder->FillData(configData); - break; - } - } + containers[agentType]->fromJson(configData); } void DialogAgent::onButtonSave() { - QString agentName = agentCombobox->currentText(); - QString operatingSystem = osCombobox->currentText(); - - QString configData = ""; - for (auto regAgent : this->regAgents ) { - if (regAgent.agentName == agentName && regAgent.operatingSystem == operatingSystem && regAgent.builder) { - configData = regAgent.builder->CollectData(); - break; - } - } + QString configType = agentCombobox->currentText(); + auto configData = QString(); + if (containers[configType]) + configData = containers[configType]->toJson(); QJsonObject dataJson; - dataJson["listener_type"] = listenerType; - dataJson["agent"] = agentName; - dataJson["operating_system"] = operatingSystem; - dataJson["config"] = configData; + dataJson["listener_type"] = listenerType; + dataJson["agent"] = configType; + dataJson["config"] = configData; QByteArray fileContent = QJsonDocument(dataJson).toJson(); - QString tmpFilename = QString("%1_%2_config.json").arg(agentName).arg(operatingSystem) ; + QString tmpFilename = QString("%1_config.json").arg(configType); QString filePath = QFileDialog::getSaveFileName( nullptr, "Save File", tmpFilename, "JSON files (*.json)" ); if ( filePath.isEmpty()) return; @@ -333,9 +262,13 @@ void DialogAgent::onButtonSave() inputDialog.adjustSize(); inputDialog.move(QGuiApplication::primaryScreen()->geometry().center() - inputDialog.geometry().center()); inputDialog.exec(); + } -void DialogAgent::onButtonClose() +void DialogAgent::onButtonClose() { this->close(); } + +void DialogAgent::changeConfig(const QString &agentName) { - this->close(); -} + if (widgets.contains(agentName)) + configStackWidget->setCurrentWidget(widgets[agentName]); +} \ No newline at end of file diff --git a/AdaptixClient/Source/UI/Dialogs/DialogCredential.cpp b/AdaptixClient/Source/UI/Dialogs/DialogCredential.cpp new file mode 100644 index 00000000..8852b278 --- /dev/null +++ b/AdaptixClient/Source/UI/Dialogs/DialogCredential.cpp @@ -0,0 +1,129 @@ +#include + +DialogCredential::DialogCredential() +{ + this->createUI(); + + connect(createButton, &QPushButton::clicked, this, &DialogCredential::onButtonCreate); + connect(cancelButton, &QPushButton::clicked, this, &DialogCredential::onButtonCancel); +} + +DialogCredential::~DialogCredential() = default; + +void DialogCredential::createUI() +{ + this->resize(500, 300); + this->setWindowTitle( "Add credentials" ); + + usernameLabel = new QLabel("Username:", this); + usernameInput = new QLineEdit(this); + + passwordLabel = new QLabel("Password:", this); + passwordInput = new QLineEdit(this); + + realmLabel = new QLabel("Realm:", this); + realmInput = new QLineEdit(this); + + typeLabel = new QLabel("Type:", this); + typeCombo = new QComboBox(this); + typeCombo->setEditable(true); + typeCombo->addItems(QStringList() << "password" << "hash" << "rc4" << "aes128" << "aes256" << "token"); + typeCombo->setCurrentText(""); + + tagLabel = new QLabel("Tag:", this); + tagInput = new QLineEdit(this); + + storageLabel = new QLabel("Storage:", this); + storageCombo = new QComboBox(this); + storageCombo->setEditable(true); + storageCombo->addItems(QStringList() << "browser" << "dpapi" << "database" << "sam" << "lsass" << "ntds" << "manual"); + storageCombo->setCurrentText(""); + + hostLabel = new QLabel("Host:", this); + hostInput = new QLineEdit(this); + + spacer_1 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + spacer_2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + + createButton = new QPushButton("Save", this); + createButton->setProperty("ButtonStyle", "dialog"); + cancelButton = new QPushButton("Cancel", this); + cancelButton->setProperty("ButtonStyle", "dialog"); + + hLayoutBottom = new QHBoxLayout(); + hLayoutBottom->addItem(spacer_1); + hLayoutBottom->addWidget(createButton); + hLayoutBottom->addWidget(cancelButton); + hLayoutBottom->addItem(spacer_2); + + mainGridLayout = new QGridLayout(this); + mainGridLayout->setContentsMargins(4, 4, 4, 4 ); + mainGridLayout->addWidget(usernameLabel, 0, 0, 1, 1); + mainGridLayout->addWidget(usernameInput, 0, 1, 1, 1); + mainGridLayout->addWidget(passwordLabel, 1, 0, 1, 1); + mainGridLayout->addWidget(passwordInput, 1, 1, 1, 1); + mainGridLayout->addWidget(realmLabel, 2, 0, 1, 1); + mainGridLayout->addWidget(realmInput, 2, 1, 1, 1); + mainGridLayout->addWidget(typeLabel, 3, 0, 1, 1); + mainGridLayout->addWidget(typeCombo, 3, 1, 1, 1); + mainGridLayout->addWidget(tagLabel, 4, 0, 1, 1); + mainGridLayout->addWidget(tagInput, 4, 1, 1, 1); + mainGridLayout->addWidget(storageLabel, 5, 0, 1, 1); + mainGridLayout->addWidget(storageCombo, 5, 1, 1, 1); + mainGridLayout->addWidget(hostLabel, 6, 0, 1, 1); + mainGridLayout->addWidget(hostInput, 6, 1, 1, 1); + mainGridLayout->addLayout(hLayoutBottom, 7, 0, 1, 2); + + int buttonWidth = createButton->width(); + createButton->setFixedWidth(buttonWidth); + cancelButton->setFixedWidth(buttonWidth); + + int buttonHeight = createButton->height(); + createButton->setFixedHeight(buttonHeight); + cancelButton->setFixedHeight(buttonHeight); +} + +void DialogCredential::StartDialog() +{ + this->valid = false; + this->message = ""; + this->exec(); +} + +void DialogCredential::SetEditmode(const CredentialData &credentialData) +{ + this->setWindowTitle( "Edit credentials" ); + this->credsId = credentialData.CredId; + + this->usernameInput->setText(credentialData.Username); + this->passwordInput->setText(credentialData.Password); + this->realmInput->setText(credentialData.Realm); + this->typeCombo->setCurrentText(credentialData.Type); + this->tagInput->setText(credentialData.Tag); + this->storageCombo->setCurrentText(credentialData.Storage); + this->hostInput->setText(credentialData.Host); +} + +bool DialogCredential::IsValid() const { return this->valid; } + +QString DialogCredential::GetMessage() const { return this->message; } + +CredentialData DialogCredential::GetCredData() const { return this->data; } + +void DialogCredential::onButtonCreate() +{ + data = {}; + data.CredId = this->credsId; + data.Username = usernameInput->text(); + data.Password = passwordInput->text(); + data.Realm = realmInput->text(); + data.Type = typeCombo->currentText(); + data.Tag = tagInput->text(); + data.Storage = storageCombo->currentText(); + data.Host = hostInput->text(); + + this->valid = true; + this->close(); +} + +void DialogCredential::onButtonCancel() { this->close(); } \ No newline at end of file diff --git a/AdaptixClient/Source/UI/Dialogs/DialogDownloader.cpp b/AdaptixClient/Source/UI/Dialogs/DialogDownloader.cpp index f9a1e245..21446a39 100644 --- a/AdaptixClient/Source/UI/Dialogs/DialogDownloader.cpp +++ b/AdaptixClient/Source/UI/Dialogs/DialogDownloader.cpp @@ -20,7 +20,7 @@ DialogDownloader::DialogDownloader(const QString &url, const QString &otp, const statusLabel = new QLabel("Starting...", this); speedLabel = new QLabel("Speed: 0 KB/s", this); labelPath = new QLabel("File saved to:", this); - lineeditPash = new QLineEdit(savedPath, this); + lineeditPath = new QLineEdit(savedPath, this); cancelButton = new QPushButton("Cancel", this); QVBoxLayout *layout = new QVBoxLayout(this); @@ -28,12 +28,13 @@ DialogDownloader::DialogDownloader(const QString &url, const QString &otp, const layout->addWidget(progressBar); layout->addWidget(speedLabel); layout->addWidget(labelPath); - layout->addWidget(lineeditPash); + layout->addWidget(lineeditPath); layout->addWidget(cancelButton); setLayout(layout); labelPath->setVisible(false); - lineeditPash->setVisible(false); + lineeditPath->setVisible(false); + lineeditPath->setReadOnly(false); workerThread = new QThread(this); worker = new DownloaderWorker(url, otp, savedPath); @@ -59,7 +60,8 @@ DialogDownloader::DialogDownloader(const QString &url, const QString &otp, const speedLabel->setVisible(false); labelPath->setVisible(true); - lineeditPash->setVisible(true); + lineeditPath->setVisible(true); + lineeditPath->selectAll(); } cancelButton->setText("Close"); disconnect(cancelButton, nullptr, nullptr, nullptr); diff --git a/AdaptixClient/Source/UI/Dialogs/DialogExtender.cpp b/AdaptixClient/Source/UI/Dialogs/DialogExtender.cpp index eaaa5b50..43b5b45f 100644 --- a/AdaptixClient/Source/UI/Dialogs/DialogExtender.cpp +++ b/AdaptixClient/Source/UI/Dialogs/DialogExtender.cpp @@ -8,48 +8,54 @@ DialogExtender::DialogExtender(Extender* e) this->createUI(); - connect(buttonClose, &QPushButton::clicked, this, &DialogExtender::close); connect(tableWidget, &QTableWidget::customContextMenuRequested, this, &DialogExtender::handleMenu); connect(tableWidget, &QTableWidget::cellClicked, this, &DialogExtender::onRowSelect); + connect(buttonClose, &QPushButton::clicked, this, &DialogExtender::close); } DialogExtender::~DialogExtender() = default; void DialogExtender::createUI() { - this->setWindowTitle("Extender"); - this->resize(1200, 600); + this->setWindowTitle("AxScript manager"); + this->resize(1200, 700); - tableWidget = new QTableWidget(this ); - tableWidget->setColumnCount(5); + tableWidget = new QTableWidget(this); + tableWidget->setColumnCount(4); tableWidget->setContextMenuPolicy(Qt::CustomContextMenu ); - tableWidget->setAutoFillBackground(false ); - tableWidget->setShowGrid(false ); - tableWidget->setSortingEnabled(true ); - tableWidget->setWordWrap(true ); - tableWidget->setCornerButtonEnabled(false ); + tableWidget->setAutoFillBackground(false); + tableWidget->setShowGrid(false); + tableWidget->setSortingEnabled(true); + tableWidget->setWordWrap(true); + tableWidget->setCornerButtonEnabled(false); tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows ); - tableWidget->setSelectionMode(QAbstractItemView::SingleSelection ); tableWidget->setFocusPolicy(Qt::NoFocus ); - tableWidget->setAlternatingRowColors(true ); - tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch ); - tableWidget->horizontalHeader()->setCascadingSectionResizes(true ); - tableWidget->horizontalHeader()->setHighlightSections(false ); - tableWidget->verticalHeader()->setVisible(false ); + tableWidget->setAlternatingRowColors(true); + tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + tableWidget->horizontalHeader()->setCascadingSectionResizes(true); + tableWidget->horizontalHeader()->setHighlightSections(false); + tableWidget->verticalHeader()->setVisible(false); tableWidget->setHorizontalHeaderItem(0, new QTableWidgetItem("Name" ) ); tableWidget->setHorizontalHeaderItem(1, new QTableWidgetItem("Path" ) ); - tableWidget->setHorizontalHeaderItem(2, new QTableWidgetItem("Description" ) ); - tableWidget->setHorizontalHeaderItem(3, new QTableWidgetItem("Status" ) ); - tableWidget->setHorizontalHeaderItem(4, new QTableWidgetItem("Comment" ) ); + tableWidget->setHorizontalHeaderItem(2, new QTableWidgetItem("Status" ) ); + tableWidget->setHorizontalHeaderItem(3, new QTableWidgetItem("Description" ) ); - tableWidget->hideColumn(4); + tableWidget->hideColumn(3); textComment = new QTextEdit(this); - textComment->setReadOnly( true ); + textComment->setReadOnly(true); - auto hSpacer1 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); - auto hSpacer2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + splitter = new QSplitter(Qt::Vertical, this); + splitter->setContentsMargins(0, 0, 0, 0); + splitter->setHandleWidth(3); + splitter->setVisible(true); + splitter->addWidget(tableWidget); + splitter->addWidget(textComment); + splitter->setSizes(QList({500, 140})); + + spacer1 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + spacer2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); buttonClose = new QPushButton("Close", this); buttonClose->setProperty("ButtonStyle", "dialog"); @@ -57,29 +63,25 @@ void DialogExtender::createUI() layout = new QGridLayout(this); layout->setContentsMargins( 4, 4, 4, 4); - layout->addWidget(tableWidget, 0, 0, 1, 3); - layout->addWidget(textComment, 1, 0, 1, 3); - layout->addItem( hSpacer1, 2, 0, 1, 1); - layout->addWidget( buttonClose, 2, 1, 1, 1); - layout->addItem( hSpacer2, 2, 2, 1, 1); + layout->addWidget(splitter, 0, 0, 1, 3); + layout->addItem( spacer1, 1, 0, 1, 1); + layout->addWidget(buttonClose, 1, 1, 1, 1); + layout->addItem( spacer2, 1, 2, 1, 1); this->setLayout(layout); } void DialogExtender::AddExtenderItem(const ExtensionFile &extenderItem) const { - auto item_Name = new QTableWidgetItem( extenderItem.Name ); - auto item_Path = new QTableWidgetItem( extenderItem.FilePath ); - auto item_Description = new QTableWidgetItem( extenderItem.Description ); - auto item_Comment = new QTableWidgetItem( extenderItem.Comment ); - auto item_Status = new QTableWidgetItem( "" ); + auto item_Name = new QTableWidgetItem(extenderItem.Name); + auto item_Path = new QTableWidgetItem(extenderItem.FilePath); + auto item_Desc = new QTableWidgetItem(extenderItem.Description); + auto item_Status = new QTableWidgetItem(""); item_Name->setFlags( item_Name->flags() ^ Qt::ItemIsEditable ); item_Path->setFlags( item_Path->flags() ^ Qt::ItemIsEditable ); - item_Description->setFlags( item_Description->flags() ^ Qt::ItemIsEditable ); - item_Status->setFlags( item_Status->flags() ^ Qt::ItemIsEditable ); item_Status->setTextAlignment( Qt::AlignCenter ); if ( extenderItem.Enabled ) { @@ -87,8 +89,14 @@ void DialogExtender::AddExtenderItem(const ExtensionFile &extenderItem) const item_Status->setForeground(QColor(COLOR_NeonGreen)); } else { - item_Status->setText("Disable"); - item_Status->setForeground(QColor(COLOR_ChiliPepper)); + if (extenderItem.Message.isEmpty()) { + item_Status->setText("Disable"); + item_Status->setForeground(QColor(COLOR_BrightOrange)); + } + else { + item_Status->setText("Failed"); + item_Status->setForeground(QColor(COLOR_ChiliPepper)); + } } if( tableWidget->rowCount() < 1 ) @@ -100,14 +108,12 @@ void DialogExtender::AddExtenderItem(const ExtensionFile &extenderItem) const tableWidget->setSortingEnabled( false ); tableWidget->setItem( tableWidget->rowCount() - 1, 0, item_Name ); tableWidget->setItem( tableWidget->rowCount() - 1, 1, item_Path ); - tableWidget->setItem( tableWidget->rowCount() - 1, 2, item_Description ); - tableWidget->setItem( tableWidget->rowCount() - 1, 3, item_Status ); - tableWidget->setItem( tableWidget->rowCount() - 1, 4, item_Comment ); + tableWidget->setItem( tableWidget->rowCount() - 1, 2, item_Status ); + tableWidget->setItem( tableWidget->rowCount() - 1, 3, item_Desc ); tableWidget->setSortingEnabled( isSortingEnabled ); tableWidget->horizontalHeader()->setSectionResizeMode( 0, QHeaderView::ResizeToContents ); - tableWidget->horizontalHeader()->setSectionResizeMode( 1, QHeaderView::ResizeToContents ); - tableWidget->horizontalHeader()->setSectionResizeMode( 3, QHeaderView::ResizeToContents ); + tableWidget->horizontalHeader()->setSectionResizeMode( 2, QHeaderView::ResizeToContents ); } void DialogExtender::UpdateExtenderItem(const ExtensionFile &extenderItem) const @@ -116,18 +122,22 @@ void DialogExtender::UpdateExtenderItem(const ExtensionFile &extenderItem) const QTableWidgetItem *item = tableWidget->item(row, 1); if ( item && item->text() == extenderItem.FilePath ) { tableWidget->item(row, 0)->setText(extenderItem.Name); - tableWidget->item(row, 2)->setText(extenderItem.Description); - tableWidget->item(row, 4)->setText(extenderItem.Comment); + tableWidget->item(row, 3)->setText(extenderItem.Description); if ( extenderItem.Enabled ) { - tableWidget->item(row, 3)->setText("Enable"); - tableWidget->item(row, 3)->setForeground(QColor(COLOR_NeonGreen)); + tableWidget->item(row, 2)->setText("Enable"); + tableWidget->item(row, 2)->setForeground(QColor(COLOR_NeonGreen)); } else { - tableWidget->item(row, 3)->setText("Disable"); - tableWidget->item(row, 3)->setForeground(QColor(COLOR_ChiliPepper)); + if (extenderItem.Message.isEmpty()) { + tableWidget->item(row, 2)->setText("Disable"); + tableWidget->item(row, 2)->setForeground(QColor(COLOR_BrightOrange)); + } + else { + tableWidget->item(row, 2)->setText("Failed"); + tableWidget->item(row, 2)->setForeground(QColor(COLOR_ChiliPepper)); + } } - break; } } @@ -151,20 +161,20 @@ void DialogExtender::handleMenu(const QPoint &pos ) const QMenu menu = QMenu(); menu.addAction("Load new", this, &DialogExtender::onActionLoad ); - menu.addAction("Reload", this, &DialogExtender::onActionReload ); + menu.addAction("Reload", this, &DialogExtender::onActionReload ); menu.addSeparator(); - menu.addAction("Enable", this, &DialogExtender::onActionEnable ); + menu.addAction("Enable", this, &DialogExtender::onActionEnable ); menu.addAction("Disable", this, &DialogExtender::onActionDisable ); menu.addSeparator(); menu.addAction("Remove", this, &DialogExtender::onActionRemove ); - QPoint globalPos = tableWidget->mapToGlobal(pos ); - menu.exec(globalPos ); + QPoint globalPos = tableWidget->mapToGlobal(pos); + menu.exec(globalPos); } void DialogExtender::onActionLoad() const { - QString filePath = QFileDialog::getOpenFileName( nullptr, "Select file", QDir::homePath(), "*.json"); + QString filePath = QFileDialog::getOpenFileName(nullptr, "Load Script", "", "AxScript Files (*.axs)"); if ( filePath.isEmpty()) return; @@ -176,6 +186,7 @@ void DialogExtender::onActionReload() const for( int rowIndex = 0 ; rowIndex < tableWidget->rowCount() ; rowIndex++ ) { if ( tableWidget->item(rowIndex, 1)->isSelected() ) { auto filePath = tableWidget->item( rowIndex, 1 )->text(); + extender->RemoveExtension(filePath); extender->LoadFromFile(filePath, true); } } @@ -217,7 +228,4 @@ void DialogExtender::onActionRemove() const textComment->clear(); } -void DialogExtender::onRowSelect(int row, int column) const -{ - textComment->setText(tableWidget->item(row,4)->text()); -} +void DialogExtender::onRowSelect(const int row, int column) const { textComment->setText(tableWidget->item(row,3)->text()); } diff --git a/AdaptixClient/Source/UI/Dialogs/DialogListener.cpp b/AdaptixClient/Source/UI/Dialogs/DialogListener.cpp index b44db096..fb5a47ee 100644 --- a/AdaptixClient/Source/UI/Dialogs/DialogListener.cpp +++ b/AdaptixClient/Source/UI/Dialogs/DialogListener.cpp @@ -1,8 +1,8 @@ #include #include -#include +#include -DialogListener::DialogListener() +DialogListener::DialogListener(QWidget *parent) : QDialog(parent) { this->createUI(); @@ -30,11 +30,11 @@ void DialogListener::createUI() listenerTypeCombobox = new QComboBox(this); - buttonLoad = new QPushButton(QIcon(":/icons/unarchive"), "", this); + buttonLoad = new QPushButton(QIcon(":/icons/file_open"), "", this); buttonLoad->setIconSize( QSize( 25,25 )); buttonLoad->setToolTip("Load profile from file"); - buttonSave = new QPushButton(QIcon(":/icons/archive"), "", this); + buttonSave = new QPushButton(QIcon(":/icons/save_as"), "", this); buttonSave->setIconSize( QSize( 25,25 )); buttonSave->setToolTip("Save profile to file"); @@ -98,27 +98,28 @@ void DialogListener::createUI() buttonCancel->setFixedHeight(buttonHeight); } -void DialogListener::Start() -{ - this->exec(); -} +void DialogListener::Start() { this->exec(); } -void DialogListener::AddExListeners(const QMap &listeners) +void DialogListener::AddExListeners(const QStringList &listeners, const QMap &widgets, const QMap &containers) { - listenersUI = listeners; + this->listeners = listeners; + this->widgets = widgets; + this->containers = containers; - for (auto w : listenersUI.values()) { - configStackWidget->addWidget( w->GetWidget() ); + for (auto listener : listeners) { + widgets[listener]->setParent(nullptr); + widgets[listener]->setParent(this); + containers[listener]->setParent(nullptr); + containers[listener]->setParent(this); + + configStackWidget->addWidget(widgets[listener]); } listenerTypeCombobox->clear(); - listenerTypeCombobox->addItems( listenersUI.keys() ); + listenerTypeCombobox->addItems(listeners); } -void DialogListener::SetProfile(const AuthProfile &profile) -{ - this->authProfile = profile; -} +void DialogListener::SetProfile(const AuthProfile &profile) { this->authProfile = profile; } void DialogListener::SetEditMode(const QString &name) { @@ -132,10 +133,8 @@ void DialogListener::SetEditMode(const QString &name) void DialogListener::changeConfig(const QString &fn) { - if (listenersUI[fn]) { - auto w = listenersUI[fn]->GetWidget(); - configStackWidget->setCurrentWidget(w); - } + if (widgets.contains(fn)) + configStackWidget->setCurrentWidget(widgets[fn]); } void DialogListener::onButtonCreate() @@ -143,8 +142,8 @@ void DialogListener::onButtonCreate() auto configName= inputListenerName->text(); auto configType= listenerTypeCombobox->currentText(); auto configData = QString(); - if (listenersUI[configType]) - configData = listenersUI[configType]->CollectData(); + if (containers[configType]) + configData = containers[configType]->toJson(); QString message = QString(); bool result, ok = false; @@ -200,14 +199,14 @@ void DialogListener::onButtonLoad() QString configType = jsonObject["type"].toString(); int typeIndex = listenerTypeCombobox->findText( configType ); - if(typeIndex == -1 || !listenersUI.contains(configType)) { + if(typeIndex == -1 || !containers.contains(configType)) { MessageError("No such listener exists"); return; } QString configData = jsonObject["config"].toString(); listenerTypeCombobox->setCurrentIndex(typeIndex); - listenersUI[configType]->FillData(configData); + containers[configType]->fromJson(configData); } void DialogListener::onButtonSave() @@ -215,8 +214,8 @@ void DialogListener::onButtonSave() auto configName= inputListenerName->text(); auto configType= listenerTypeCombobox->currentText(); auto configData = QString(); - if (listenersUI[configType]) - configData = listenersUI[configType]->CollectData(); + if (containers[configType]) + configData = containers[configType]->toJson(); QJsonObject dataJson; dataJson["name"] = configName; @@ -248,7 +247,4 @@ void DialogListener::onButtonSave() inputDialog.exec(); } -void DialogListener::onButtonCancel() -{ - this->close(); -} \ No newline at end of file +void DialogListener::onButtonCancel() { this->close(); } \ No newline at end of file diff --git a/AdaptixClient/Source/UI/Dialogs/DialogSettings.cpp b/AdaptixClient/Source/UI/Dialogs/DialogSettings.cpp index 48f7dc50..5dacce9f 100644 --- a/AdaptixClient/Source/UI/Dialogs/DialogSettings.cpp +++ b/AdaptixClient/Source/UI/Dialogs/DialogSettings.cpp @@ -78,7 +78,6 @@ void DialogSettings::createUI() this->setWindowTitle("Adaptix Settings"); this->resize(600, 300); - /////////////// Main setting mainSettingWidget = new QWidget(this); mainSettingLayout = new QGridLayout(mainSettingWidget); @@ -145,7 +144,6 @@ void DialogSettings::createUI() mainSettingWidget->setLayout(mainSettingLayout); - /////////////// Sessions Table sessionsWidget = new QWidget(this); sessionsLayout = new QGridLayout(sessionsWidget); @@ -211,11 +209,6 @@ void DialogSettings::createUI() sessionsWidget->setLayout(sessionsLayout); - /////////////// Sessions Graph - - - - /////////////// Tasks Table tasksWidget = new QWidget(this); tasksLayout = new QGridLayout(tasksWidget); @@ -247,13 +240,11 @@ void DialogSettings::createUI() tasksLayout->addWidget(tasksGroup, 0, 0, 1, 1); tasksWidget->setLayout(tasksLayout); - ////////////// listSettings = new QListWidget(this); listSettings->setFixedWidth(150); listSettings->addItem("Main settings"); listSettings->addItem("Sessions table"); - // listSettings->addItem("Sessions graph"); listSettings->addItem("Tasks table"); listSettings->setCurrentRow(0); diff --git a/AdaptixClient/Source/UI/Dialogs/DialogTunnel.cpp b/AdaptixClient/Source/UI/Dialogs/DialogTunnel.cpp index a6424565..1dfa6888 100644 --- a/AdaptixClient/Source/UI/Dialogs/DialogTunnel.cpp +++ b/AdaptixClient/Source/UI/Dialogs/DialogTunnel.cpp @@ -1,9 +1,18 @@ #include #include -DialogTunnel::DialogTunnel() +DialogTunnel::DialogTunnel(const QString &agentId, const bool s4, const bool s5, const bool lpf, const bool rpf) { this->createUI(); + + tunnelTypeCombo->clear(); + this->AgentId = agentId; + + if (s4) tunnelTypeCombo->addItem("Socks4"); + if (s5) tunnelTypeCombo->addItem("Socks5"); + if (lpf) tunnelTypeCombo->addItem("Local port forwarding"); + if (rpf) tunnelTypeCombo->addItem("Reverse port forwarding"); + connect(tunnelTypeCombo, &QComboBox::currentTextChanged, this, &DialogTunnel::changeType); connect(buttonCreate, &QPushButton::clicked, this, &DialogTunnel::onButtonCreate); connect(buttonCancel, &QPushButton::clicked, this, &DialogTunnel::onButtonCancel); @@ -81,7 +90,6 @@ void DialogTunnel::createUI() buttonCreate->setFixedHeight(buttonHeight); buttonCancel->setFixedHeight(buttonHeight); - /// Socks5 socks5Widget = new QWidget(this); socks5LocalAddrLabel = new QLabel("Listen:", socks5Widget); socks5LocalAddrInput = new QLineEdit("0.0.0.0", socks5Widget); @@ -108,7 +116,7 @@ void DialogTunnel::createUI() socks5GridLayout->addWidget(socks5AuthPassInput, 3, 1, 1, 2); tunnelStackWidget->addWidget(socks5Widget); - /// Socks4 + socks4Widget = new QWidget(this); socks4LocalAddrLabel = new QLabel("Listen:", socks4Widget); socks4LocalAddrInput = new QLineEdit("0.0.0.0", socks4Widget); @@ -123,7 +131,7 @@ void DialogTunnel::createUI() socks4GridLayout->addWidget(socks4LocalPortSpin, 0, 2, 1, 1); tunnelStackWidget->addWidget(socks4Widget); - /// LPF + lpfWidget = new QWidget(this); lpfLocalAddrLabel = new QLabel("Listen:", lpfWidget); lpfLocalAddrInput = new QLineEdit("0.0.0.0", lpfWidget); @@ -147,7 +155,6 @@ void DialogTunnel::createUI() lpfGridLayout->addWidget(lpfTargetPortSpin, 1, 2, 1, 1); tunnelStackWidget->addWidget(lpfWidget); - // RPF rpfWidget = new QWidget(this); rpfPortLabel = new QLabel("Port:", rpfWidget); rpfPortSpin = new QSpinBox(rpfWidget); @@ -177,41 +184,15 @@ void DialogTunnel::StartDialog() this->exec(); } -bool DialogTunnel::IsValid() const -{ - return this->valid; -} +bool DialogTunnel::IsValid() const { return this->valid; } -QString DialogTunnel::GetMessage() const -{ - return this->message; -} +QString DialogTunnel::GetMessage() const { return this->message; } -QString DialogTunnel::GetTunnelType() const -{ - return this->tunnelType; -} +QString DialogTunnel::GetTunnelType() const { return this->tunnelType; } -QString DialogTunnel::GetEndpoint() const -{ - return this->tunnelEndpointCombo->currentText(); -} +QString DialogTunnel::GetEndpoint() const { return this->tunnelEndpointCombo->currentText(); } -QByteArray DialogTunnel::GetTunnelData() const -{ - return this->jsonData; -} - -void DialogTunnel::SetSettings(const QString &agentId, const bool s5, const bool s4, const bool lpf, const bool rpf) -{ - tunnelTypeCombo->clear(); - this->AgentId = agentId; - - if (s5) tunnelTypeCombo->addItem("Socks5"); - if (s4) tunnelTypeCombo->addItem("Socks4"); - if (lpf) tunnelTypeCombo->addItem("Local port forwarding"); - if (rpf) tunnelTypeCombo->addItem("Reverse port forwarding"); -} +QByteArray DialogTunnel::GetTunnelData() const { return this->jsonData; } void DialogTunnel::changeType(const QString &type) const { @@ -364,7 +345,4 @@ void DialogTunnel::onButtonCreate() this->close(); } -void DialogTunnel::onButtonCancel() -{ - this->close(); -} \ No newline at end of file +void DialogTunnel::onButtonCancel() { this->close(); } \ No newline at end of file diff --git a/AdaptixClient/Source/UI/Graph/GraphScene.cpp b/AdaptixClient/Source/UI/Graph/GraphScene.cpp index 4f81f7b3..e4ce7b3c 100644 --- a/AdaptixClient/Source/UI/Graph/GraphScene.cpp +++ b/AdaptixClient/Source/UI/Graph/GraphScene.cpp @@ -3,9 +3,11 @@ #include #include #include -#include +#include #include #include +#include + GraphScene::GraphScene(const int gridSize, QWidget* m, QObject* parent) : QGraphicsScene(parent) { @@ -32,216 +34,112 @@ void GraphScene::mouseMoveEvent( QGraphicsSceneMouseEvent* event ) void GraphScene::contextMenuEvent( QGraphicsSceneContextMenuEvent *event ) { - auto graphics_items = selectedItems(); - if(graphics_items.empty()) - if( (graphics_items = items(event->scenePos())).empty() ) - return QGraphicsScene::contextMenuEvent( event ); - - bool menuRemoteTerminal = false; - bool menuProcessBrowser = false; - bool menuFileBrowser = false; - bool menuExit = false; - bool menuTunnels = false; - - bool tunnelS5 = false; - bool tunnelS4 = false; - bool tunnelLpf = false; - bool tunnelRpf = false; - - int selectedCount = 0; - - bool valid = false; - for ( const auto& _graphics_item : graphics_items ) { - const auto item = dynamic_cast( _graphics_item ); - if ( item && item->agent ) { - valid = true; - - menuRemoteTerminal = item->agent->browsers.RemoteTerminal; - menuFileBrowser = item->agent->browsers.FileBrowser; - menuProcessBrowser = item->agent->browsers.ProcessBrowser; - menuTunnels = item->agent->browsers.SessionsMenuTunnels; - menuExit = item->agent->browsers.SessionsMenuExit; - - selectedCount++; - } - } - if (!valid) - return; - - QMenu menu = QMenu(); - - auto agentSep1 = new QAction(); - agentSep1->setSeparator(true); - auto agentSep2 = new QAction(); - agentSep2->setSeparator(true); - - auto agentMenu = new QMenu("Agent", &menu); - agentMenu->addAction("Tasks"); - if (menuFileBrowser || menuProcessBrowser || menuTunnels || menuRemoteTerminal) { - agentMenu->addAction(agentSep1); - if (menuRemoteTerminal) - agentMenu->addAction("Remote Terminal"); - if (menuFileBrowser) - agentMenu->addAction("File Browser"); - if (menuProcessBrowser) - agentMenu->addAction("Process Browser"); - if (menuTunnels && selectedCount == 1) - agentMenu->addAction("Create Tunnel"); - } - if (menuExit) { - agentMenu->addAction(agentSep2); - agentMenu->addAction("Exit"); - } - - auto itemMenu = new QMenu("Item", &menu); - itemMenu->addAction("Mark as Active"); - itemMenu->addAction("Mark as Inactive"); - - menu.addAction("Console"); - menu.addSeparator(); - menu.addMenu(agentMenu); - menu.addMenu(itemMenu); - menu.addSeparator(); - menu.addAction("Remove from server"); - - const auto action = menu.exec( event->screenPos() ); - if ( !action ) - return; - auto adaptixWidget = qobject_cast( mainWidget ); if (!adaptixWidget) return; + auto graphics_items = selectedItems(); + if(graphics_items.empty()) { + if( (graphics_items = items(event->scenePos())).empty() ) + return QGraphicsScene::contextMenuEvent( event ); + } + + QStringList agentIds; + for ( const auto& _graphics_item : graphics_items ) { + const auto item = dynamic_cast( _graphics_item ); + if ( item && item->agent ) + agentIds.append(item->agent->data.Id); + } + if (agentIds.size() == 0) + return; + + + + + + auto agentMenu = QMenu("Agent"); + agentMenu.addAction("Execute command"); + agentMenu.addAction("Task manager"); + agentMenu.addSeparator(); + + int agentCount = adaptixWidget->ScriptManager->AddMenuSession(&agentMenu, "SessionAgent", agentIds); + if (agentCount > 0) + agentMenu.addSeparator(); + + agentMenu.addAction("Remove console data"); + agentMenu.addAction("Remove from server"); + + + + auto sessionMenu = QMenu("Session"); + sessionMenu.addAction("Mark as Active"); + sessionMenu.addAction("Mark as Inactive"); + + + + auto ctxMenu = QMenu(); + ctxMenu.addAction("Console"); + ctxMenu.addSeparator(); + ctxMenu.addMenu(&agentMenu); + + auto browserMenu = QMenu("Browsers"); + int browserCount = adaptixWidget->ScriptManager->AddMenuSession(&browserMenu, "SessionBrowser", agentIds); + if (browserCount > 0) + ctxMenu.addMenu(&browserMenu); + + auto accessMenu = QMenu("Access"); + int accessCount = adaptixWidget->ScriptManager->AddMenuSession(&accessMenu, "SessionAccess", agentIds); + if (accessCount > 0) + ctxMenu.addMenu(&accessMenu); + + adaptixWidget->ScriptManager->AddMenuSession(&ctxMenu, "SessionMain", agentIds); + + ctxMenu.addSeparator(); + ctxMenu.addMenu(&sessionMenu); + ctxMenu.addAction("Set tag"); + + const auto action = ctxMenu.exec( event->screenPos() ); + if ( !action ) + return; + if ( action->text() == "Console" ) { - for ( const auto& _graphics_item : graphics_items ) { - const auto item = dynamic_cast( _graphics_item ); - if ( item && item->agent) - adaptixWidget->LoadConsoleUI(item->agent->data.Id); + for (QString agentId : agentIds) { + adaptixWidget->LoadConsoleUI(agentId); } } - else if ( action->text() == "Tasks") { + else if ( action->text() == "Execute command") { + bool ok = false; + QString cmd = QInputDialog::getText(nullptr,"Execute Command", "Command", QLineEdit::Normal, "", &ok); + if (!ok) + return; + const auto item = dynamic_cast( graphics_items[0] ); if ( item && item->agent) { - adaptixWidget->TasksTab->SetAgentFilter(item->agent->data.Id); + item->agent->Console->SetInput(cmd); + item->agent->Console->processInput(); + } + } + else if ( action->text() == "Task manager") { + for (QString agentId : agentIds) { + adaptixWidget->TasksTab->SetAgentFilter(agentId); adaptixWidget->SetTasksUI(); } } - else if ( action->text() == "Remote Terminal" ) { - for ( const auto& _graphics_item : graphics_items ) { - const auto item = dynamic_cast( _graphics_item ); - if ( item && item->agent ) - adaptixWidget->LoadTerminalUI(item->agent->data.Id); - } - } - else if ( action->text() == "File Browser" ) { - for ( const auto& _graphics_item : graphics_items ) { - const auto item = dynamic_cast( _graphics_item ); - if ( item && item->agent ) - adaptixWidget->LoadFileBrowserUI(item->agent->data.Id); - } - } - else if ( action->text() == "Process Browser" ) { - for ( const auto& _graphics_item : graphics_items ) { - const auto item = dynamic_cast( _graphics_item ); - if ( item && item->agent ) - adaptixWidget->LoadProcessBrowserUI(item->agent->data.Id); - } - } - else if ( action->text() == "Create Tunnel" ) { - Agent* agent = nullptr; - for ( const auto& _graphics_item : graphics_items ) { - const auto item = dynamic_cast( _graphics_item ); - if ( item && item->agent ) { - agent = item->agent; - break; - } - } - - if (!agent) + else if ( action->text() == "Remove console data" ) { + QMessageBox::StandardButton reply = QMessageBox::question(nullptr, "Clear Confirmation", + "Are you sure you want to delete all agent console data and history from server (tasks will not be deleted from TaskManager)?\n\n" + "If you want to temporarily hide the contents of the agent console, do so through the agent console menu.", + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); + if (reply != QMessageBox::Yes) return; - - DialogTunnel dialogTunnel; - dialogTunnel.SetSettings(agent->data.Id, agent->browsers.Socks5, agent->browsers.Socks4, agent->browsers.Lportfwd, agent->browsers.Rportfwd); - - while (true) { - dialogTunnel.StartDialog(); - if (dialogTunnel.IsValid()) - break; - - QString msg = dialogTunnel.GetMessage(); - if (msg.isEmpty()) - return; - - MessageError(msg); - } - - QString tunnelType = dialogTunnel.GetTunnelType(); - QByteArray tunnelData = dialogTunnel.GetTunnelData(); + for (auto id : agentIds) + adaptixWidget->AgentsMap[id]->Console->Clear(); QString message = QString(); bool ok = false; - bool result = HttpReqTunnelStartServer(tunnelType, tunnelData, *(adaptixWidget->GetProfile()), &message, &ok); - if( !result ) { - MessageError("Server is not responding"); - return; - } - if ( !ok ) { - MessageError(message); - return; - } - - agent = nullptr; - } - else if ( action->text() == "Exit" ) { - QStringList listId; - for ( const auto& _graphics_item : graphics_items ) { - const auto item = dynamic_cast( _graphics_item ); - if ( item && item->agent && item->agent->browsers.SessionsMenuExit ) - listId.append(item->agent->data.Id); - } - if(listId.empty()) - return; - - QString message = QString(); - bool ok = false; - bool result = HttpReqAgentExit(listId, *(adaptixWidget->GetProfile()), &message, &ok); - if( !result ) { - MessageError("Response timeout"); - return; - } - } - else if ( action->text() == "Mark as Active" ) { - QStringList listId; - for ( const auto& _graphics_item : graphics_items ) { - const auto item = dynamic_cast( _graphics_item ); - if ( item && item->agent) - listId.append(item->agent->data.Id); - } - if(listId.empty()) - return; - - QString message = QString(); - bool ok = false; - bool result = HttpReqAgentSetMark(listId, "", *(adaptixWidget->GetProfile()), &message, &ok); - if( !result ) { - MessageError("Response timeout"); - return; - } - } - else if ( action->text() == "Mark as Inactive" ) { - QStringList listId; - for ( const auto& _graphics_item : graphics_items ) { - const auto item = dynamic_cast( _graphics_item ); - if ( item && item->agent) - listId.append(item->agent->data.Id); - } - if(listId.empty()) - return; - - QString message = QString(); - bool ok = false; - bool result = HttpReqAgentSetMark(listId, "Inactive", *(adaptixWidget->GetProfile()), &message, &ok); + bool result = HttpReqConsoleRemove(agentIds, *(adaptixWidget->GetProfile()), &message, &ok); if( !result ) { MessageError("Response timeout"); return; @@ -256,21 +154,44 @@ void GraphScene::contextMenuEvent( QGraphicsSceneContextMenuEvent *event ) if (reply != QMessageBox::Yes) return; - QStringList listId; - for ( const auto& _graphics_item : graphics_items ) { - const auto item = dynamic_cast( _graphics_item ); - if ( item && item->agent) - listId.append(item->agent->data.Id); - } - if(listId.empty()) - return; - QString message = QString(); bool ok = false; - bool result = HttpReqAgentRemove(listId, *(adaptixWidget->GetProfile()), &message, &ok); + bool result = HttpReqAgentRemove(agentIds, *(adaptixWidget->GetProfile()), &message, &ok); if( !result ) { MessageError("Response timeout"); return; } } + else if ( action->text() == "Mark as Active" ) { + QString message = QString(); + bool ok = false; + bool result = HttpReqAgentSetMark(agentIds, "", *(adaptixWidget->GetProfile()), &message, &ok); + if( !result ) { + MessageError("Response timeout"); + return; + } + } + else if ( action->text() == "Mark as Inactive" ) { + QString message = QString(); + bool ok = false; + bool result = HttpReqAgentSetMark(agentIds, "Inactive", *(adaptixWidget->GetProfile()), &message, &ok); + if( !result ) { + MessageError("Response timeout"); + return; + } + } + else if ( action->text() == "Set tag" ) { + QString tag = ""; + bool inputOk; + QString newTag = QInputDialog::getText(nullptr, "Set tags", "New tag", QLineEdit::Normal,tag, &inputOk); + if ( inputOk ) { + QString message = QString(); + bool ok = false; + bool result = HttpReqAgentSetTag(agentIds, newTag, *(adaptixWidget->GetProfile()), &message, &ok); + if( !result ) { + MessageError("Response timeout"); + return; + } + } + } } diff --git a/AdaptixClient/Source/UI/Graph/SessionsGraph.cpp b/AdaptixClient/Source/UI/Graph/SessionsGraph.cpp index 2131a425..a9415eb8 100644 --- a/AdaptixClient/Source/UI/Graph/SessionsGraph.cpp +++ b/AdaptixClient/Source/UI/Graph/SessionsGraph.cpp @@ -80,12 +80,12 @@ void SessionsGraph::RemoveAgent(Agent* agent, bool drawTree) agent->graphItem->parentLink = nullptr; } - ///CHILD + /// CHILD auto childs = agent->graphItem->childItems; if ( !childs.empty() ) { for (int i = 0; i < childs.size(); i++) { - // childs[i]->agent->graphItem->parentLink = nullptr; - // childs[i]->agent->graphItem->parentItem = nullptr; + /// childs[i]->agent->graphItem->parentLink = nullptr; + /// childs[i]->agent->graphItem->parentItem = nullptr; this->LinkToRoot(childs[i]->agent->graphItem); } } @@ -104,7 +104,7 @@ void SessionsGraph::RemoveAgent(Agent* agent, bool drawTree) } agent->graphItem->childLinks.clear(); - // NODE + /// NODE for ( int i = 0; i < this->items.size(); i++ ) { if ( this->items[ i ] == agent->graphItem ) { this->items.erase( this->items.begin() + i ); diff --git a/AdaptixClient/Source/UI/MainUI.cpp b/AdaptixClient/Source/UI/MainUI.cpp index 496652c4..78a733da 100644 --- a/AdaptixClient/Source/UI/MainUI.cpp +++ b/AdaptixClient/Source/UI/MainUI.cpp @@ -24,10 +24,14 @@ MainUI::MainUI() menuProject->addAction(newProjectAction); menuProject->addAction(closeProjectAction); - auto menuExtender = new QMenu("Extender", this); - auto extenderAction = new QAction("Open extender", this); - connect(extenderAction, &QAction::triggered, this, &MainUI::onExtender); - menuExtender->addAction(extenderAction); + auto axConsoleAction = new QAction("AxScript console ", this); + connect(axConsoleAction, &QAction::triggered, this, &MainUI::onAxScriptConsole); + auto scriptManagerAction = new QAction("Script manager", this); + connect(scriptManagerAction, &QAction::triggered, this, &MainUI::onScriptManager); + + auto menuExtender = new QMenu("AxScript", this); + menuExtender->addAction(axConsoleAction); + menuExtender->addAction(scriptManagerAction); auto menuSettings = new QMenu("Settings", this); auto settingsAction = new QAction("Open settings", this); @@ -51,10 +55,8 @@ MainUI::MainUI() MainUI::~MainUI() { - for (auto project : AdaptixProjects.keys()) { - delete AdaptixProjects[project]; - AdaptixProjects.remove(project); - } + qDeleteAll(AdaptixProjects); + AdaptixProjects.clear(); } void MainUI::closeEvent(QCloseEvent* event) @@ -66,27 +68,37 @@ void MainUI::closeEvent(QCloseEvent* event) void MainUI::AddNewProject(AuthProfile* profile, QThread* channelThread, WebSocketWorker* channelWsWorker) { auto adaptixWidget = new AdaptixWidget(profile, channelThread, channelWsWorker); - if (!adaptixWidget) - return; - - for (auto extFile : GlobalClient->extender->extenderFiles){ - if(extFile.Valid && extFile.Enabled) - adaptixWidget->AddExtension(extFile); - } + connect(adaptixWidget, &AdaptixWidget::SyncedOnReloadSignal, GlobalClient->extender, &Extender::syncedOnReload); + connect(adaptixWidget, &AdaptixWidget::LoadGlobalScriptSignal, GlobalClient->extender, &Extender::loadGlobalScript); + connect(adaptixWidget, &AdaptixWidget::UnloadGlobalScriptSignal, GlobalClient->extender, &Extender::unloadGlobalScript); QString tabName = " " + profile->GetProject() + " " ; int id = mainuiTabWidget->addTab( adaptixWidget, tabName); mainuiTabWidget->setCurrentIndex( id ); - AdaptixProjects[profile->GetProject()] = adaptixWidget; + AdaptixProjects.append(adaptixWidget); } -void MainUI::AddNewExtension(const ExtensionFile &extFile) +bool MainUI::AddNewExtension(ExtensionFile *extFile) +{ + bool result = true; + for (auto adaptixWidget : AdaptixProjects) { + if (adaptixWidget) { + result = adaptixWidget->AddExtension(extFile); + if (!result) + break; + } + } + return result; +} + +bool MainUI::SyncExtension(const QString &Project, ExtensionFile *extFile) { for (auto adaptixWidget : AdaptixProjects) { - if (adaptixWidget) - adaptixWidget->AddExtension(extFile); + if (adaptixWidget && adaptixWidget->GetProfile()->GetProject() == Project) + return adaptixWidget->AddExtension(extFile); } + return true; } void MainUI::RemoveExtension(const ExtensionFile &extFile) @@ -97,7 +109,8 @@ void MainUI::RemoveExtension(const ExtensionFile &extFile) } } -void MainUI::UpdateSessionsTableColumns() { +void MainUI::UpdateSessionsTableColumns() +{ for (auto adaptixWidget : AdaptixProjects) { if (adaptixWidget) adaptixWidget->SessionsTablePage->UpdateColumnsVisible(); @@ -115,7 +128,8 @@ void MainUI::UpdateGraphIcons() { } } -void MainUI::UpdateTasksTableColumns() { +void MainUI::UpdateTasksTableColumns() +{ for (auto adaptixWidget : AdaptixProjects) { if (adaptixWidget) adaptixWidget->TasksTab->UpdateColumnsVisible(); @@ -124,10 +138,7 @@ void MainUI::UpdateTasksTableColumns() { /// Actions -void MainUI::onNewProject() -{ - GlobalClient->NewProject(); -} +void MainUI::onNewProject() { GlobalClient->NewProject(); } void MainUI::onCloseProject() { @@ -136,20 +147,28 @@ void MainUI::onCloseProject() if (!adaptixWidget) return; - if (adaptixWidget) { - AdaptixProjects.remove(adaptixWidget->GetProfile()->GetProject()); - adaptixWidget->Close(); - delete adaptixWidget; + for (int i = 0; i < AdaptixProjects.size(); ++i) { + if (AdaptixProjects[i] == adaptixWidget) { + AdaptixProjects.remove(i); + break; + } } + + adaptixWidget->Close(); + delete adaptixWidget; + mainuiTabWidget->removeTab(currentIndex); } -void MainUI::onExtender() +void MainUI::onAxScriptConsole() { - GlobalClient->extender->dialogExtender->show(); + auto adaptixWidget = qobject_cast( mainuiTabWidget->currentWidget() ); + if (!adaptixWidget) + return; + + adaptixWidget->LoadAxConsoleUI(); } -void MainUI::onSettings() -{ - GlobalClient->settings->dialogSettings->show(); -} +void MainUI::onScriptManager() { GlobalClient->extender->dialogExtender->show(); } + +void MainUI::onSettings() { GlobalClient->settings->dialogSettings->show(); } diff --git a/AdaptixClient/Source/UI/Widgets/AdaptixWidget.cpp b/AdaptixClient/Source/UI/Widgets/AdaptixWidget.cpp index 07686acd..01bc4ecb 100644 --- a/AdaptixClient/Source/UI/Widgets/AdaptixWidget.cpp +++ b/AdaptixClient/Source/UI/Widgets/AdaptixWidget.cpp @@ -1,10 +1,11 @@ +#include #include #include -#include #include #include #include #include +#include #include #include #include @@ -13,21 +14,31 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include -#include +#include +#include AdaptixWidget::AdaptixWidget(AuthProfile* authProfile, QThread* channelThread, WebSocketWorker* channelWsWorker) { this->createUI(); - this->ChannelThread = channelThread; + this->ChannelThread = channelThread; this->ChannelWsWorker = channelWsWorker; + ScriptManager = new AxScriptManager(this, this); + connect(this, &AdaptixWidget::eventFileBrowserDisks, ScriptManager, &AxScriptManager::emitFileBrowserDisks); + connect(this, &AdaptixWidget::eventFileBrowserList, ScriptManager, &AxScriptManager::emitFileBrowserList); + connect(this, &AdaptixWidget::eventFileBrowserUpload, ScriptManager, &AxScriptManager::emitFileBrowserUpload); + connect(this, &AdaptixWidget::eventProcessBrowserList, ScriptManager, &AxScriptManager::emitProcessBrowserList); + + AxConsoleTab = new AxConsoleWidget(ScriptManager, this); LogsTab = new LogsWidget(); ListenersTab = new ListenersWidget(this); SessionsTablePage = new SessionsTableWidget(this); @@ -35,6 +46,7 @@ AdaptixWidget::AdaptixWidget(AuthProfile* authProfile, QThread* channelThread, W TunnelsTab = new TunnelsWidget(this); DownloadsTab = new DownloadsWidget(this); ScreenshotsTab = new ScreenshotsWidget(this); + CredentialsTab = new CredentialsWidget(this); TasksTab = new TasksWidget(this); mainStackedWidget->addWidget(SessionsTablePage); @@ -60,6 +72,7 @@ AdaptixWidget::AdaptixWidget(AuthProfile* authProfile, QThread* channelThread, W connect( tunnelButton, &QPushButton::clicked, this, &AdaptixWidget::LoadTunnelsUI); connect( downloadsButton, &QPushButton::clicked, this, &AdaptixWidget::LoadDownloadsUI); connect( screensButton, &QPushButton::clicked, this, &AdaptixWidget::LoadScreenshotsUI); + connect( credsButton, &QPushButton::clicked, this, &AdaptixWidget::LoadCredentialsUI); connect( reconnectButton, &QPushButton::clicked, this, &AdaptixWidget::OnReconnect); connect( mainTabWidget->tabBar(), &QTabBar::tabCloseRequested, this, &AdaptixWidget::RemoveTab ); @@ -77,7 +90,6 @@ AdaptixWidget::AdaptixWidget(AuthProfile* authProfile, QThread* channelThread, W /// TODO: Enable menu button targetsButton->setVisible(false); - credsButton->setVisible(false); keysButton->setVisible(false); HttpReqSync( *profile ); @@ -214,215 +226,9 @@ void AdaptixWidget::createUI() this->setLayout(mainGridLayout); } -AuthProfile* AdaptixWidget::GetProfile() const -{ - return this->profile; -} - -void AdaptixWidget::RegisterListenerConfig(const QString &fn, const QString &ui) -{ - auto widgetBuilder = new WidgetBuilder(ui.toLocal8Bit() ); - if(widgetBuilder->GetError().isEmpty()) - RegisterListeners[fn] = widgetBuilder; -} - -void AdaptixWidget::RegisterAgentConfig(const QString &agentName, const QString &watermark, const QString &handlersJson, const QString &listenersJson) -{ - QJsonParseError parseError; - - QJsonDocument handlersDocument = QJsonDocument::fromJson(handlersJson.toLocal8Bit(), &parseError); - if (parseError.error != QJsonParseError::NoError && handlersDocument.isObject()) { - LogError("JSON parse error: %s", parseError.errorString().toStdString().c_str()); - return; - } - if(!handlersDocument.isArray()) { - LogError("Error Listener %s Json Format", agentName.toStdString().c_str()); - return; - } - QJsonArray handlersArray = handlersDocument.array(); - for (QJsonValue handlerValue : handlersArray) { - QJsonObject handlerObj = handlerValue.toObject(); - if (!handlerObj.contains("id") || !handlerObj["id"].isString()) continue; - if (!handlerObj.contains("commands") || !handlerObj["commands"].isArray()) continue; - if (!handlerObj.contains("browsers") || !handlerObj["browsers"].isObject()) continue; - - QString handlerId = handlerObj["id"].toString(); - QJsonArray commandsArray = handlerObj["commands"].toArray(); - QJsonObject browsersObject = handlerObj["browsers"].toObject(); - - QByteArray commandsData = QJsonDocument(commandsArray).toJson(); - auto commander = new Commander(); - bool result = true; - QString msg = ValidCommandsFile(commandsData, &result); - if (result) { - commander->AddRegCommands(commandsData); - } - Commanders[agentName][handlerId] = commander; - - BrowsersConfig browsersConfig = {}; - if (browsersObject.contains("remote_terminal") && browsersObject["remote_terminal"].isBool()) - browsersConfig.RemoteTerminal = browsersObject["remote_terminal"].toBool(); - if (browsersObject.contains("file_browser") && browsersObject["file_browser"].isBool()) - browsersConfig.FileBrowser = browsersObject["file_browser"].toBool(); - if (browsersObject.contains("file_browser_disks") && browsersObject["file_browser_disks"].isBool()) - browsersConfig.FileBrowserDisks = browsersObject["file_browser_disks"].toBool(); - if (browsersObject.contains("file_browser_download") && browsersObject["file_browser_download"].isBool()) - browsersConfig.FileBrowserDownload = browsersObject["file_browser_download"].toBool(); - if (browsersObject.contains("file_browser_upload") && browsersObject["file_browser_upload"].isBool()) - browsersConfig.FileBrowserUpload = browsersObject["file_browser_upload"].toBool(); - if (browsersObject.contains("process_browser") && browsersObject["process_browser"].isBool()) - browsersConfig.ProcessBrowser = browsersObject["process_browser"].toBool(); - if (browsersObject.contains("downloads_cancel") && browsersObject["downloads_cancel"].isBool()) - browsersConfig.DownloadsCancel = browsersObject["downloads_cancel"].toBool(); - if (browsersObject.contains("downloads_resume") && browsersObject["downloads_resume"].isBool()) - browsersConfig.DownloadsResume = browsersObject["downloads_resume"].toBool(); - if (browsersObject.contains("downloads_pause") && browsersObject["downloads_pause"].isBool()) - browsersConfig.DownloadsPause = browsersObject["downloads_pause"].toBool(); - if (browsersObject.contains("tasks_job_kill") && browsersObject["tasks_job_kill"].isBool()) - browsersConfig.TasksJobKill = browsersObject["tasks_job_kill"].toBool(); - if (browsersObject.contains("socks4") && browsersObject["socks4"].isBool()) - browsersConfig.Socks4 = browsersObject["socks4"].toBool(); - if (browsersObject.contains("socks5") && browsersObject["socks5"].isBool()) - browsersConfig.Socks5 = browsersObject["socks5"].toBool(); - if (browsersObject.contains("lportfwd") && browsersObject["lportfwd"].isBool()) - browsersConfig.Lportfwd = browsersObject["lportfwd"].toBool(); - if (browsersObject.contains("rportfwd") && browsersObject["rportfwd"].isBool()) - browsersConfig.Rportfwd = browsersObject["rportfwd"].toBool(); - if (browsersObject.contains("sessions_menu_tunnels") && browsersObject["sessions_menu_tunnels"].isBool()) - browsersConfig.SessionsMenuTunnels = browsersObject["sessions_menu_tunnels"].toBool(); - if (browsersObject.contains("sessions_menu_exit") && browsersObject["sessions_menu_exit"].isBool()) - browsersConfig.SessionsMenuExit = browsersObject["sessions_menu_exit"].toBool(); - - AgentBrowserConfigs[agentName][handlerId] = browsersConfig; - } - - QJsonDocument listenersDocument = QJsonDocument::fromJson(listenersJson.toLocal8Bit(), &parseError); - if (parseError.error != QJsonParseError::NoError && listenersDocument.isObject()) { - LogError("JSON parse error: %s", parseError.errorString().toStdString().c_str()); - return; - } - if(!listenersDocument.isArray()) { - LogError("Error Listener %s Json Format", agentName.toStdString().c_str()); - return; - } - QJsonArray listenersArray = listenersDocument.array(); - for (QJsonValue listenerValue : listenersArray) { - QJsonObject listenerObj = listenerValue.toObject(); - if (!listenerObj.contains("listener_name") || !listenerObj["listener_name"].isString()) continue; - if (!listenerObj.contains("configs") || !listenerObj["configs"].isArray()) continue; - - QString listenerName = listenerObj["listener_name"].toString(); - QJsonArray osConfigsArray = listenerObj["configs"].toArray(); - - for (QJsonValue osConfigValue : osConfigsArray) { - QJsonObject osConfigObj = osConfigValue.toObject(); - if (!osConfigObj.contains("operating_system") || !osConfigObj["operating_system"].isString()) continue; - if (!osConfigObj.contains("handler") || !osConfigObj["handler"].isString()) continue; - if (!osConfigObj.contains("generate_ui") || !osConfigObj["generate_ui"].isObject()) continue; - - QString operatingSystem = osConfigObj["operating_system"].toString(); - QString handler = osConfigObj["handler"].toString(); - QJsonObject uiObj = osConfigObj["generate_ui"].toObject(); - - QByteArray uiData = QJsonDocument(uiObj).toJson(); - auto widgetBuilder = new WidgetBuilder( uiData ); - if( ! widgetBuilder->GetError().isEmpty() ) { - delete widgetBuilder; - widgetBuilder = nullptr; - } - - RegAgentConfig config = {agentName, watermark, listenerName, operatingSystem, handler, widgetBuilder, Commanders[agentName][handler], AgentBrowserConfigs[agentName][handler], true}; - RegisterAgents.push_back(config); - } - } -} - -void AdaptixWidget::ClearAdaptix() -{ - LogsTab->Clear(); - DownloadsTab->Clear(); - ScreenshotsTab->Clear(); - TasksTab->Clear(); - ListenersTab->Clear(); - SessionsGraphPage->Clear(); - SessionsTablePage->Clear(); - TunnelsTab->Clear(); - - for (auto tunnelId : ClientTunnels.keys()) { - auto tunnel = ClientTunnels[tunnelId]; - ClientTunnels.remove(tunnelId); - tunnel->Stop(); - delete tunnel; - } - ClientTunnels.clear(); - - for (int i = 0; i < RegisterAgents.size(); i++) { - WidgetBuilder* builder = RegisterAgents[i].builder; - RegisterAgents.remove(i); - i--; - delete builder; - } - - for (auto commanderMap : Commanders ) { - for (auto k : commanderMap.keys()) { - Commander* commander = commanderMap[k]; - commanderMap.remove(k); - delete commander; - } - } - Commanders.clear(); - - AgentBrowserConfigs.clear(); - - for (auto listenerName : RegisterListeners.keys()){ - WidgetBuilder* builder = RegisterListeners[listenerName]; - RegisterListeners.remove(listenerName); - delete builder; - } -} - -void AdaptixWidget::Close() -{ - TickThread->quit(); - TickThread->wait(); - delete TickThread; - - ChannelThread->quit(); - ChannelThread->wait(); - delete ChannelThread; - - ChannelWsWorker->webSocket->close(); - - this->ClearAdaptix(); -} - - - -RegAgentConfig AdaptixWidget::GetRegAgent(const QString &agentName, const QString &listenerName, const int os) -{ - QString operatingSystem = "windows"; - if (os == OS_LINUX) - operatingSystem = "linux"; - else if (os == OS_MAC) - operatingSystem = "mac"; - - QString listener = ""; - for ( auto listenerData : this->Listeners) { - if ( listenerData.ListenerName == listenerName ) { - listener = listenerData.ListenerType.split("/")[2]; - break; - } - } - - for (auto regAgent : this->RegisterAgents) { - if (regAgent.agentName == agentName && regAgent.listenerName == listener && regAgent.operatingSystem == operatingSystem) - return regAgent; - } - - return {}; -} - +/// MAIN +AuthProfile* AdaptixWidget::GetProfile() const { return this->profile; } void AdaptixWidget::AddTab(QWidget *tab, const QString &title, const QString &icon) const { @@ -451,82 +257,451 @@ void AdaptixWidget::RemoveTab(int index) const mainTabWidget->setMovable(false); } -void AdaptixWidget::AddExtension(ExtensionFile ext) +bool AdaptixWidget::AddExtension(ExtensionFile* ext) { - if( Extensions.contains(ext.FilePath) ) - return; - - Extensions[ext.FilePath] = ext; - - if( !synchronized ) - return; - - for (QString agentName : ext.ExCommands.keys()) { - if (Commanders.contains(agentName)) { - for (auto commander : Commanders[agentName] ) { - if (commander) { - bool result = commander->AddExtModule(ext.FilePath, ext.Name, ext.ExCommands[agentName], ext.ExConstants); - if (result) { - for( auto agent : AgentsMap ){ - if( agent && agent->Console ) - agent->Console->UpgradeCompleter(); - } - } - } - } - } + if (ScriptManager->ScriptList().contains(ext->FilePath)) { + ext->Enabled = false; + ext->Message = "Script already loaded"; + return false; } + + if( !synchronized ) { + ext->Enabled = false; + ext->Message = "C2 not synchronized"; + return false; + } + + return ScriptManager->ScriptAdd(ext); } void AdaptixWidget::RemoveExtension(const ExtensionFile &ext) { - Extensions.remove(ext.FilePath); + if (!ScriptManager->ScriptList().contains(ext.FilePath)) + return; - for (QString agentName : ext.ExCommands.keys()) { - if (Commanders.contains(agentName)) { - for (auto commander : Commanders[agentName] ) { - if (commander) { - commander->RemoveExtModule(ext.FilePath); - for( auto agent : AgentsMap ){ - if( agent && agent->Console ) - agent->Console->UpgradeCompleter(); - } - } + return ScriptManager->ScriptRemove(ext); +} + +void AdaptixWidget::Close() +{ + TickThread->quit(); + TickThread->wait(); + delete TickThread; + + ChannelThread->quit(); + ChannelThread->wait(); + delete ChannelThread; + + ChannelWsWorker->webSocket->close(); + + this->ClearAdaptix(); +} + +void AdaptixWidget::ClearAdaptix() +{ + AxConsoleTab->OutputClear(); + LogsTab->Clear(); + DownloadsTab->Clear(); + ScreenshotsTab->Clear(); + TasksTab->Clear(); + ListenersTab->Clear(); + SessionsGraphPage->Clear(); + SessionsTablePage->Clear(); + TunnelsTab->Clear(); + CredentialsTab->Clear(); + + for (auto tunnelId : ClientTunnels.keys()) { + auto tunnel = ClientTunnels[tunnelId]; + ClientTunnels.remove(tunnelId); + tunnel->Stop(); + delete tunnel; + } + ClientTunnels.clear(); + + ScriptManager->Clear(); + + for (auto regAgent : RegisterAgents) + delete regAgent.commander; + + RegisterAgents.clear(); +} + +/// REGISTER + +void AdaptixWidget::RegisterListenerConfig(const QString &fn, const QString &ax_script) { ScriptManager->ListenerScriptAdd(fn, ax_script); } + +void AdaptixWidget::RegisterAgentConfig(const QString &agentName, const QString &ax_script, const QStringList &listeners) +{ + ScriptManager->AgentScriptAdd(agentName, ax_script); + + QJSEngine* engine = ScriptManager->AgentScriptEngine(agentName); + if (!engine) + return; + + QJSValue func = engine->globalObject().property("RegisterCommands"); + if (!func.isCallable()) { + ScriptManager->consolePrintError(agentName + " - function RegisterCommands is not registered"); + return; + } + + for (auto listener : listeners) { + + QJSValueList args; + args << QJSValue(listener); + QJSValue registerResult = func.call(args); + if (registerResult.isError()) { + QString error = QStringLiteral("%1\n at line %2 in %3\n stack: %4").arg(registerResult.toString()).arg(registerResult.property("lineNumber").toInt()).arg(agentName).arg(registerResult.property("stack").toString()); + ScriptManager->consolePrintError(error); + return; + } + if (!registerResult.isObject()) { + ScriptManager->consolePrintError(agentName + " - function RegisterCommands must return CommandsGroup objects"); + return; + } + + QJSValue commands_windows = registerResult.property("commands_windows"); + if ( !commands_windows.isUndefined() && commands_windows.isQObject()) { + QObject* objPanel = commands_windows.toQObject(); + auto* wrapper = dynamic_cast(objPanel); + if (wrapper) { + CommandsGroup commandsGroup = {}; + commandsGroup.groupName = wrapper->getName(); + commandsGroup.commands = wrapper->getCommands(); + commandsGroup.engine = wrapper->getEngine(); + commandsGroup.filepath = ""; + + Commander* commander = new Commander(); + commander->AddRegCommands(commandsGroup); + + RegAgentConfig config = {agentName, listener, OS_WINDOWS, commander, true}; + RegisterAgents.push_back(config); + } + else { + ScriptManager->consolePrintError(agentName + " - commands_windows must return CommandsGroup object"); + } + } + + QJSValue commands_linux = registerResult.property("commands_linux"); + if ( !commands_linux.isUndefined() && commands_linux.isQObject()) { + QObject* objPanel = commands_linux.toQObject(); + auto* wrapper = dynamic_cast(objPanel); + if (wrapper) { + CommandsGroup commandsGroup = {}; + commandsGroup.groupName = wrapper->getName(); + commandsGroup.commands = wrapper->getCommands(); + commandsGroup.engine = wrapper->getEngine(); + commandsGroup.filepath = ""; + + Commander* commander = new Commander(); + commander->AddRegCommands(commandsGroup); + + RegAgentConfig config = {agentName, listener, OS_LINUX, commander, true}; + RegisterAgents.push_back(config); + } + else { + ScriptManager->consolePrintError(agentName + " - commands_linux must return CommandsGroup object"); + } + } + + QJSValue commands_macos = registerResult.property("commands_macos"); + if ( !commands_macos.isUndefined() && commands_macos.isQObject()) { + QObject* objPanel = commands_macos.toQObject(); + auto* wrapper = dynamic_cast(objPanel); + if (wrapper) { + CommandsGroup commandsGroup = {}; + commandsGroup.groupName = wrapper->getName(); + commandsGroup.commands = wrapper->getCommands(); + commandsGroup.engine = wrapper->getEngine(); + commandsGroup.filepath = ""; + + Commander* commander = new Commander(); + commander->AddRegCommands(commandsGroup); + + RegAgentConfig config = {agentName, listener, OS_MAC, commander, true}; + RegisterAgents.push_back(config); + } + else { + ScriptManager->consolePrintError(agentName + " - commands_macos must return CommandsGroup object"); } } } } +QList AdaptixWidget::GetAgentNames(const QString &listenerType) const +{ + QSet names; + for (auto regAgent : this->RegisterAgents) { + if (regAgent.listenerType == listenerType) + names.insert(regAgent.name); + } + return names.values(); +} +RegAgentConfig AdaptixWidget::GetRegAgent(const QString &agentName, const QString &listenerName, const int os) +{ + if (os == OS_WINDOWS || os == OS_LINUX || os == OS_MAC) { + QString listener = ""; + for ( auto listenerData : this->Listeners) { + if ( listenerData.ListenerName == listenerName ) { + listener = listenerData.ListenerFullName.split("/")[2]; + break; + } + } + + for (auto regAgent : this->RegisterAgents) { + if (regAgent.name == agentName && regAgent.listenerType == listener && regAgent.os == os) + return regAgent; + } + } + + return {}; +} + +QList AdaptixWidget::GetCommanders(const QStringList &listeners, const QStringList &agents, const QList &os) const +{ + QList commanders; + for (auto regAgent : this->RegisterAgents) { + if ( !agents.contains(regAgent.name) ) continue; + + if ( !listeners.empty() && !listeners.contains(regAgent.listenerType)) continue; + + if ( !os.empty() && !os.contains(regAgent.os) ) continue; + + commanders.append(regAgent.commander); + } + return commanders; +} + +QList AdaptixWidget::GetCommandersAll() const +{ + QList commanders; + for (auto regAgent : this->RegisterAgents) + commanders.append(regAgent.commander); + return commanders; +} + +void AdaptixWidget::PostHookProcess(QJsonObject jsonHookObj) +{ + QString hookId = jsonHookObj["a_hook_id"].toString(); + bool completed = jsonHookObj["a_completed"].toBool(); + + if (PostHooksJS.contains(hookId)) { + PostHook post_hooks = PostHooksJS[hookId]; + if (completed) + PostHooksJS.remove(hookId); + + auto jsEngine = ScriptManager->GetEngine(post_hooks.engineName); + if (jsEngine && post_hooks.hook.isCallable()) { + + int jobIndex = jsonHookObj["a_job_index"].toDouble(); + + QJsonObject obj; + obj["agent"] = jsonHookObj["a_id"].toString();; + obj["message"] = jsonHookObj["a_message"].toString(); + obj["text"] = jsonHookObj["a_text"].toString(); + obj["completed"] = completed; + obj["index"] = jobIndex; + + int msgType = jsonHookObj["a_msg_type"].toDouble(); + if (msgType == CONSOLE_OUT_LOCAL_INFO || msgType == CONSOLE_OUT_INFO) + obj["type"] = "info"; + else if (msgType == CONSOLE_OUT_LOCAL_ERROR || msgType == CONSOLE_OUT_ERROR) + obj["type"] = "error"; + else if (msgType == CONSOLE_OUT_LOCAL_SUCCESS || msgType == CONSOLE_OUT_SUCCESS) + obj["type"] = "success"; + else + obj["type"] = ""; + + QJSValue result = post_hooks.hook.call(QJSValueList() << jsEngine->toScriptValue(obj)); + if (result.isObject()) { + QJsonObject modifiedObj = result.toVariant().toJsonObject(); + + if (modifiedObj.contains("message") && modifiedObj["message"].isString()) + jsonHookObj["a_message"] = modifiedObj["message"].toString(); + if (modifiedObj.contains("text") && modifiedObj["text"].isString()) + jsonHookObj["a_text"] = modifiedObj["text"].toString(); + if (modifiedObj.contains("type") && modifiedObj["type"].isString()) { + QString modifiedType = modifiedObj["type"].toString(); + if (modifiedType == "info") + jsonHookObj["a_msg_type"] = CONSOLE_OUT_INFO; + else if (modifiedType == "error") + jsonHookObj["a_msg_type"] = CONSOLE_OUT_ERROR; + else if (modifiedType == "success") + jsonHookObj["a_msg_type"] = CONSOLE_OUT_SUCCESS; + else + jsonHookObj["a_msg_type"] = CONSOLE_OUT; + } + } + } + } + + QByteArray jsonData = QJsonDocument(jsonHookObj).toJson(); + + QString message = ""; + bool ok = false; + bool result = HttpReqTasksHook(jsonData, *profile, &message, &ok); + if( !result ) { + MessageError("Server is not responding"); + return; + } + if (!ok) MessageError(message); +} + +/// SHOW PANELS + +void AdaptixWidget::LoadConsoleUI(const QString &AgentId) +{ + if( !AgentsMap.contains(AgentId) ) + return; + + auto agent = AgentsMap[AgentId]; + if (agent && agent->Console) { + auto text = QString("Console [%1]").arg( AgentId ); + this->AddTab(AgentsMap[AgentId]->Console, text); + AgentsMap[AgentId]->Console->InputFocus(); + } + +} + +void AdaptixWidget::LoadTasksOutput() const { this->AddTab(TasksTab->taskOutputConsole, "Task Output", ":/icons/job"); } + +void AdaptixWidget::LoadFileBrowserUI(const QString &AgentId) +{ + if( !AgentsMap.contains(AgentId) ) + return; + + auto agent = AgentsMap[AgentId]; + if (agent && agent->FileBrowser) { + auto text = QString("Files [%1]").arg( AgentId ); + this->AddTab(AgentsMap[AgentId]->FileBrowser, text); + } +} + +void AdaptixWidget::LoadProcessBrowserUI(const QString &AgentId) +{ + if( !AgentsMap.contains(AgentId) ) + return; + + auto agent = AgentsMap[AgentId]; + if (agent && agent->ProcessBrowser) { + auto text = QString("Processes [%1]").arg( AgentId ); + this->AddTab(AgentsMap[AgentId]->ProcessBrowser, text); + } +} + +void AdaptixWidget::LoadTerminalUI(const QString &AgentId) +{ + if( !AgentsMap.contains(AgentId) ) + return; + + auto agent = AgentsMap[AgentId]; + if (agent && agent->Terminal) { + auto text = QString("Terminal [%1]").arg( AgentId ); + this->AddTab(AgentsMap[AgentId]->Terminal, text); + } +} + +void AdaptixWidget::ShowTunnelCreator(const QString &AgentId, const bool socks4, const bool socks5, const bool lportfwd, const bool rportfwd) +{ + DialogTunnel* dialogTunnel = new DialogTunnel(AgentId, socks4, socks5, lportfwd, rportfwd); + + while (true) { + dialogTunnel->StartDialog(); + if (dialogTunnel->IsValid()) + break; + + QString msg = dialogTunnel->GetMessage(); + if (msg.isEmpty()) { + delete dialogTunnel; + return; + } + + MessageError(msg); + } + + QString tunnelType = dialogTunnel->GetTunnelType(); + QString endpoint = dialogTunnel->GetEndpoint(); + QByteArray tunnelData = dialogTunnel->GetTunnelData(); + + if ( endpoint == "Teamserver" ) { + QString message = ""; + bool ok = false; + bool result = HttpReqTunnelStartServer(tunnelType, tunnelData, *profile, &message, &ok); + if( !result ) { + MessageError("Server is not responding"); + delete dialogTunnel; + return; + } + if (!ok) MessageError(message); + } + else { + auto tunnelEndpoint = new TunnelEndpoint(); + bool started = tunnelEndpoint->StartTunnel(profile, tunnelType, tunnelData); + if (started) { + QString message = ""; + bool ok = false; + bool result = HttpReqTunnelStartServer(tunnelType, tunnelData, *profile, &message, &ok); + if( !result ) { + MessageError("Server is not responding"); + delete tunnelEndpoint; + delete dialogTunnel; + return; + } + + if ( !ok ) { + MessageError(message); + delete tunnelEndpoint; + delete dialogTunnel; + return; + } + QString tunnelId = message; + + tunnelEndpoint->SetTunnelId(tunnelId); + this->ClientTunnels[tunnelId] = tunnelEndpoint; + MessageSuccess("Tunnel " + tunnelId + " started"); + } + else { + delete tunnelEndpoint; + } + } + delete dialogTunnel; +} /// SLOTS +void AdaptixWidget::ChannelClose() const +{ + QIcon onReconnectButton = RecolorIcon(QIcon(":/icons/unlink"), COLOR_ChiliPepper); + reconnectButton->setIcon(onReconnectButton); + ChannelThread->quit(); +} + +void AdaptixWidget::DataHandler(const QByteArray &data) +{ + QJsonParseError parseError; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &parseError); + + if ( parseError.error != QJsonParseError::NoError || !jsonDoc.isObject() ) { + LogError("Error parsing JSON data: %s", parseError.errorString().toStdString().c_str()); + return; + } + + QJsonObject jsonObj = jsonDoc.object(); + if( !this->isValidSyncPacket(jsonObj) ) { + LogError("Invalid SyncPacket"); + return; + } + + this->processSyncPacket(jsonObj); +} + void AdaptixWidget::OnSynced() { synchronized = true; this->SessionsGraphPage->TreeDraw(); - for (auto ext : Extensions) { - - for (QString agentName : ext.ExCommands.keys()) { - - if (Commanders.contains(agentName)) { - for (auto commander : Commanders[agentName] ) { - - if (commander) { - bool result = commander->AddExtModule(ext.FilePath, ext.Name, ext.ExCommands[agentName], ext.ExConstants); - if (result) { - for( auto agent : AgentsMap ){ - if( agent && agent->Console ) - agent->Console->UpgradeCompleter(); - } - } - } - } - } - } - } + emit SyncedOnReloadSignal(profile->GetProject()); } void AdaptixWidget::SetSessionsTableUI() const @@ -555,38 +730,22 @@ void AdaptixWidget::SetTasksUI() const this->AddTab(TasksTab->taskOutputConsole, "Task Output", ":/icons/job"); } -void AdaptixWidget::LoadLogsUI() const +void AdaptixWidget::LoadAxConsoleUI() const { this->AddTab(AxConsoleTab, "AxScript Console", ":/icons/code_blocks"); } + +void AdaptixWidget::LoadLogsUI() const { this->AddTab(LogsTab, "Logs", ":/icons/logs"); } + +void AdaptixWidget::LoadListenersUI() const { this->AddTab(ListenersTab, "Listeners", ":/icons/listeners"); } + +void AdaptixWidget::LoadTunnelsUI() const { this->AddTab(TunnelsTab, "Tunnels", ":/icons/vpn"); } + +void AdaptixWidget::LoadDownloadsUI() const { this->AddTab(DownloadsTab, "Downloads", ":/icons/downloads"); } + +void AdaptixWidget::LoadScreenshotsUI() const { this->AddTab(ScreenshotsTab, "Screenshots", ":/icons/picture"); } + +void AdaptixWidget::LoadCredentialsUI() const { this->AddTab(CredentialsTab, "Credentials", ":/icons/key"); } + +void AdaptixWidget::OnReconnect() { - this->AddTab(LogsTab, "Logs", ":/icons/logs"); -} - -void AdaptixWidget::LoadListenersUI() const -{ - this->AddTab(ListenersTab, "Listeners", ":/icons/listeners"); -} - -void AdaptixWidget::LoadTunnelsUI() const -{ - this->AddTab(TunnelsTab, "Tunnels", ":/icons/vpn"); -} - -void AdaptixWidget::LoadDownloadsUI() const -{ - this->AddTab(DownloadsTab, "Downloads", ":/icons/downloads"); -} - -void AdaptixWidget::LoadScreenshotsUI() const -{ - this->AddTab(ScreenshotsTab, "Screenshots", ":/icons/picture"); -} - -void AdaptixWidget::LoadTasksOutput() const -{ - this->AddTab(TasksTab->taskOutputConsole, "Task Output", ":/icons/job"); -} - -void AdaptixWidget::OnReconnect() { - if (ChannelThread->isRunning()) { bool result = HttpReqJwtUpdate(profile); if (!result) { @@ -612,79 +771,3 @@ void AdaptixWidget::OnReconnect() { } } -void AdaptixWidget::LoadConsoleUI(const QString &AgentId) -{ - if( !AgentsMap.contains(AgentId) ) - return; - - auto agent = AgentsMap[AgentId]; - if (agent && agent->Console) { - auto text = QString("Console [%1]").arg( AgentId ); - this->AddTab(AgentsMap[AgentId]->Console, text); - AgentsMap[AgentId]->Console->InputFocus(); - } - -} - -void AdaptixWidget::LoadFileBrowserUI(const QString &AgentId) -{ - if( !AgentsMap.contains(AgentId) ) - return; - - auto agent = AgentsMap[AgentId]; - if (agent && agent->browsers.FileBrowser && agent->FileBrowser) { - auto text = QString("Files [%1]").arg( AgentId ); - this->AddTab(AgentsMap[AgentId]->FileBrowser, text); - } -} - -void AdaptixWidget::LoadProcessBrowserUI(const QString &AgentId) -{ - if( !AgentsMap.contains(AgentId) ) - return; - - auto agent = AgentsMap[AgentId]; - if (agent && agent->browsers.ProcessBrowser && agent->ProcessBrowser) { - auto text = QString("Processes [%1]").arg( AgentId ); - this->AddTab(AgentsMap[AgentId]->ProcessBrowser, text); - } -} - -void AdaptixWidget::LoadTerminalUI(const QString &AgentId) -{ - if( !AgentsMap.contains(AgentId) ) - return; - - auto agent = AgentsMap[AgentId]; - if (agent && agent->browsers.RemoteTerminal && agent->Terminal) { - auto text = QString("Terminal [%1]").arg( AgentId ); - this->AddTab(AgentsMap[AgentId]->Terminal, text); - } - -} - -void AdaptixWidget::ChannelClose() const -{ - QIcon onReconnectButton = RecolorIcon(QIcon(":/icons/unlink"), COLOR_ChiliPepper); - reconnectButton->setIcon(onReconnectButton); - ChannelThread->quit(); -} - -void AdaptixWidget::DataHandler(const QByteArray &data) -{ - QJsonParseError parseError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &parseError); - - if ( parseError.error != QJsonParseError::NoError || !jsonDoc.isObject() ) { - LogError("Error parsing JSON data: %s", parseError.errorString().toStdString().c_str()); - return; - } - - QJsonObject jsonObj = jsonDoc.object(); - if( !this->isValidSyncPacket(jsonObj) ) { - LogError("Invalid SyncPacket"); - return; - } - - this->processSyncPacket(jsonObj); -} diff --git a/AdaptixClient/Source/UI/Widgets/AxConsoleWidget.cpp b/AdaptixClient/Source/UI/Widgets/AxConsoleWidget.cpp new file mode 100644 index 00000000..e6532a37 --- /dev/null +++ b/AdaptixClient/Source/UI/Widgets/AxConsoleWidget.cpp @@ -0,0 +1,303 @@ +#include +#include +#include +#include +#include +#include +#include + +AxConsoleWidget::AxConsoleWidget(AxScriptManager* m, AdaptixWidget* w): adaptixWidget(w), scriptManager(m) +{ + this->createUI(); + + connect(InputLineEdit, &QLineEdit::returnPressed, this, &AxConsoleWidget::processInput, Qt::QueuedConnection ); + connect(searchLineEdit, &QLineEdit::returnPressed, this, &AxConsoleWidget::handleSearch); + connect(nextButton, &ClickableLabel::clicked, this, &AxConsoleWidget::handleSearch); + connect(prevButton, &ClickableLabel::clicked, this, &AxConsoleWidget::handleSearchBackward); + connect(hideButton, &ClickableLabel::clicked, this, &AxConsoleWidget::toggleSearchPanel); + connect(OutputTextEdit, &TextEditConsole::ctx_find, this, &AxConsoleWidget::toggleSearchPanel); + connect(OutputTextEdit, &TextEditConsole::ctx_history, this, &AxConsoleWidget::handleShowHistory); + connect(ResetButton, &QPushButton::clicked, this, &AxConsoleWidget::onResetScript); + + shortcutSearch = new QShortcut(QKeySequence("Ctrl+F"), OutputTextEdit); + shortcutSearch->setContext(Qt::WidgetShortcut); + connect(shortcutSearch, &QShortcut::activated, this, &AxConsoleWidget::toggleSearchPanel); + + shortcutSearch = new QShortcut(QKeySequence("Ctrl+L"), OutputTextEdit); + shortcutSearch->setContext(Qt::WidgetShortcut); + connect(shortcutSearch, &QShortcut::activated, OutputTextEdit, &QTextEdit::clear); + + shortcutSearch = new QShortcut(QKeySequence("Ctrl+A"), OutputTextEdit); + shortcutSearch->setContext(Qt::WidgetShortcut); + connect(shortcutSearch, &QShortcut::activated, OutputTextEdit, &QTextEdit::selectAll); + + shortcutSearch = new QShortcut(QKeySequence("Ctrl+H"), OutputTextEdit); + shortcutSearch->setContext(Qt::WidgetShortcut); + connect(shortcutSearch, &QShortcut::activated, this, &AxConsoleWidget::handleShowHistory); + + kphInputLineEdit = new KPH_ConsoleInput(InputLineEdit, OutputTextEdit, this); + InputLineEdit->installEventFilter(kphInputLineEdit); +} + +AxConsoleWidget::~AxConsoleWidget() {} + +void AxConsoleWidget::createUI() +{ + searchWidget = new QWidget(this); + + prevButton = new ClickableLabel("<"); + prevButton->setCursor( Qt::PointingHandCursor ); + + nextButton = new ClickableLabel(">"); + nextButton->setCursor( Qt::PointingHandCursor ); + + searchLabel = new QLabel("0 of 0"); + searchLineEdit = new QLineEdit(); + searchLineEdit->setPlaceholderText("Find"); + searchLineEdit->setMaximumWidth(300); + + hideButton = new ClickableLabel("X"); + hideButton->setCursor( Qt::PointingHandCursor ); + + spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + + searchLayout = new QHBoxLayout(searchWidget); + searchLayout->setContentsMargins(0, 3, 0, 0); + searchLayout->setSpacing(4); + searchLayout->addWidget(prevButton); + searchLayout->addWidget(nextButton); + searchLayout->addWidget(searchLabel); + searchLayout->addWidget(searchLineEdit); + searchLayout->addWidget(hideButton); + searchLayout->addSpacerItem(spacer); + + OutputTextEdit = new TextEditConsole(this, 30000, true, true); + OutputTextEdit->setReadOnly(true); + OutputTextEdit->setProperty( "TextEditStyle", "console" ); + OutputTextEdit->setFont( QFont( "Hack" )); + + CmdLabel = new QLabel( "ax >", this ); + CmdLabel->setProperty( "LabelStyle", "console" ); + + InputLineEdit = new QLineEdit(this); + InputLineEdit->setProperty( "LineEditStyle", "console" ); + InputLineEdit->setFont( QFont( "Hack" )); + + ResetButton = new QPushButton("Reset AxScript"); + + MainGridLayout = new QGridLayout(this ); + MainGridLayout->setVerticalSpacing(4 ); + MainGridLayout->setContentsMargins(0, 1, 0, 4 ); + MainGridLayout->addWidget( searchWidget, 0, 0, 1, 3 ); + MainGridLayout->addWidget( OutputTextEdit, 1, 0, 1, 3 ); + MainGridLayout->addWidget( CmdLabel, 2, 0, 1, 1 ); + MainGridLayout->addWidget( InputLineEdit, 2, 1, 1, 1 ); + MainGridLayout->addWidget( ResetButton, 2, 2, 1, 1 ); + + searchWidget->setVisible(false); +} + +void AxConsoleWidget::findAndHighlightAll(const QString &pattern) +{ + allSelections.clear(); + + QTextCursor cursor(OutputTextEdit->document()); + cursor.movePosition(QTextCursor::Start); + + QTextCharFormat baseFmt; + baseFmt.setBackground(Qt::blue); + baseFmt.setForeground(Qt::white); + + while (true) { + auto found = OutputTextEdit->document()->find(pattern, cursor); + if (found.isNull()) + break; + + QTextEdit::ExtraSelection sel; + sel.cursor = found; + sel.format = baseFmt; + allSelections.append(sel); + + cursor = found; + } + + OutputTextEdit->setExtraSelections(allSelections); +} + +void AxConsoleWidget::highlightCurrent() const +{ + if (allSelections.isEmpty()) { + searchLabel->setText("0 of 0"); + return; + } + + auto sels = allSelections; + + QTextCharFormat activeFmt; + activeFmt.setBackground(Qt::white); + activeFmt.setForeground(Qt::black); + + sels[currentIndex].format = activeFmt; + + OutputTextEdit->setExtraSelections(sels); + + OutputTextEdit->setTextCursor(sels[currentIndex].cursor); + + searchLabel->setText(QString("%1 of %2").arg(currentIndex + 1).arg(sels.size())); +} + +void AxConsoleWidget::OutputClear() const { OutputTextEdit->clear(); } + +void AxConsoleWidget::InputFocus() const { InputLineEdit->setFocus(); } + +void AxConsoleWidget::AddToHistory(const QString &command) { kphInputLineEdit->AddToHistory(command); } + +void AxConsoleWidget::PrintMessage(const QString &message) { OutputTextEdit->appendColor(message + "\n", QColor(COLOR_ConsoleWhite)); } + +void AxConsoleWidget::PrintError(const QString &message) { OutputTextEdit->appendColor(message + "\n", QColor(COLOR_ChiliPepper)); } + +void AxConsoleWidget::processInput() +{ + QString code = TrimmedEnds(InputLineEdit->text()); + if (code.isEmpty()) + return; + + InputLineEdit->clear(); + if (code.isEmpty()) + return; + + this->AddToHistory(code); + + OutputTextEdit->appendColorUnderline("ax script", QColor(COLOR_LightGray)); + OutputTextEdit->appendColor(" >>> ", QColor(COLOR_LightGray)); + OutputTextEdit->appendColorBold(code + "\n", QColor(COLOR_White)); + + QJSValue result = scriptManager->MainScriptEngine()->evaluate(code); + if (result.isError()) { + QString errorString = QString("%1\n").arg(result.toString()); + OutputTextEdit->appendColor(errorString, QColor(COLOR_ChiliPepper)); + } + else if (!result.isUndefined()) { + QString message = result.toString(); + if (!message.isEmpty()) + OutputTextEdit->appendColor(message + "\n", QColor(COLOR_ConsoleWhite)); + } +} + +void AxConsoleWidget::toggleSearchPanel() +{ + if (this->searchWidget->isVisible()) { + this->searchWidget->setVisible(false); + searchLineEdit->setText(""); + handleSearch(); + } + else { + this->searchWidget->setVisible(true); + searchLineEdit->setFocus(); + searchLineEdit->selectAll(); + } +} + +void AxConsoleWidget::handleSearch() +{ + const QString pattern = searchLineEdit->text(); + if ( pattern.isEmpty() && allSelections.size() ) { + allSelections.clear(); + currentIndex = -1; + searchLabel->setText("0 of 0"); + OutputTextEdit->setExtraSelections({}); + return; + } + + if (currentIndex < 0 || allSelections.isEmpty() || allSelections[0].cursor.selectedText().compare( pattern, Qt::CaseInsensitive) != 0 ) { + findAndHighlightAll(pattern); + currentIndex = 0; + } + else { + currentIndex = (currentIndex + 1) % allSelections.size(); + } + + highlightCurrent(); +} + +void AxConsoleWidget::handleSearchBackward() +{ + const QString pattern = searchLineEdit->text(); + if (pattern.isEmpty() && allSelections.size()) { + allSelections.clear(); + currentIndex = -1; + searchLabel->setText("0 of 0"); + OutputTextEdit->setExtraSelections({}); + return; + } + + if (currentIndex < 0 || allSelections.isEmpty() || allSelections[0].cursor.selectedText().compare( pattern, Qt::CaseInsensitive) != 0 ) { + findAndHighlightAll(pattern); + currentIndex = allSelections.size() - 1; + } + else { + currentIndex = (currentIndex - 1 + allSelections.size()) % allSelections.size(); + } + + highlightCurrent(); +} + +void AxConsoleWidget::handleShowHistory() +{ + if (!kphInputLineEdit) + return; + + QDialog *historyDialog = new QDialog(this); + historyDialog->setWindowTitle(tr("Command History")); + historyDialog->setAttribute(Qt::WA_DeleteOnClose); + + + QListWidget *historyList = new QListWidget(historyDialog); + historyList->setWordWrap(true); + historyList->setTextElideMode(Qt::ElideNone); + historyList->setAlternatingRowColors(true); + historyList->setItemDelegate(new QStyledItemDelegate(historyList)); + + QPushButton *closeButton = new QPushButton(tr("Close"), historyDialog); + + QVBoxLayout *layout = new QVBoxLayout(historyDialog); + layout->addWidget(historyList); + layout->addWidget(closeButton); + + const QStringList& history = kphInputLineEdit->getHistory(); + + for (const QString &command : history) { + QListWidgetItem *item = new QListWidgetItem(command); + item->setFlags(item->flags() & ~Qt::ItemIsEditable); + item->setToolTip(command); + int lines = (command.length() / 80) + 1; + item->setSizeHint(QSize(item->sizeHint().width(), lines * 20)); + historyList->addItem(item); + } + + if (history.isEmpty()) { + QListWidgetItem *item = new QListWidgetItem(tr("No command history available")); + item->setFlags(item->flags() & ~Qt::ItemIsEnabled); + historyList->addItem(item); + } + + connect(closeButton, &QPushButton::clicked, historyDialog, &QDialog::accept); + + connect(historyList, &QListWidget::itemDoubleClicked, this, [this, historyDialog](const QListWidgetItem *item) { + InputLineEdit->setText(item->text()); + historyDialog->accept(); + InputLineEdit->setFocus(); + }); + + historyDialog->resize(800, 500); + historyDialog->move(QCursor::pos() - QPoint(historyDialog->width()/2, historyDialog->height()/2)); + + historyDialog->setModal(true); + historyDialog->show(); +} + +void AxConsoleWidget::onResetScript() +{ + scriptManager->ResetMain(); + OutputTextEdit->clear(); +} diff --git a/AdaptixClient/Source/UI/Widgets/BrowserFilesWidget.cpp b/AdaptixClient/Source/UI/Widgets/BrowserFilesWidget.cpp index e5d79b86..47ac0b3a 100644 --- a/AdaptixClient/Source/UI/Widgets/BrowserFilesWidget.cpp +++ b/AdaptixClient/Source/UI/Widgets/BrowserFilesWidget.cpp @@ -1,7 +1,9 @@ #include #include +#include #include #include +#include void BrowserFileData::CreateBrowserFileData(const QString &path, const int os) { @@ -47,12 +49,12 @@ BrowserFilesWidget::BrowserFilesWidget(Agent* a) agent = a; this->createUI(); - connect(buttonDisks, &QPushButton::clicked, this, &BrowserFilesWidget::onDisks); - connect(buttonList, &QPushButton::clicked, this, &BrowserFilesWidget::onList); - connect(buttonParent, &QPushButton::clicked, this, &BrowserFilesWidget::onParent); - connect(buttonReload, &QPushButton::clicked, this, &BrowserFilesWidget::onReload); - connect(buttonUpload, &QPushButton::clicked, this, &BrowserFilesWidget::onUpload); - connect(inputPath, &QLineEdit::returnPressed, this, &BrowserFilesWidget::onList); + connect(buttonDisks, &QPushButton::clicked, this, &BrowserFilesWidget::onDisks); + connect(buttonList, &QPushButton::clicked, this, &BrowserFilesWidget::onList); + connect(buttonParent, &QPushButton::clicked, this, &BrowserFilesWidget::onParent); + connect(buttonReload, &QPushButton::clicked, this, &BrowserFilesWidget::onReload); + connect(buttonUpload, &QPushButton::clicked, this, &BrowserFilesWidget::onUpload); + connect(inputPath, &QLineEdit::returnPressed, this, &BrowserFilesWidget::onList); connect(tableWidget, &QTableWidget::doubleClicked, this, &BrowserFilesWidget::handleTableDoubleClicked); connect(treeBrowserWidget, &QTreeWidget::itemDoubleClicked, this, &BrowserFilesWidget::handleTreeDoubleClicked); connect(tableWidget, &QTableWidget::customContextMenuRequested, this, &BrowserFilesWidget::handleTableMenu ); @@ -86,15 +88,11 @@ void BrowserFilesWidget::createUI() buttonDisks->setIconSize( QSize( 24,24 )); buttonDisks->setFixedSize(37, 28); buttonDisks->setToolTip("Disks list"); - if (!agent->browsers.FileBrowserDisks) - buttonDisks->setEnabled(false); buttonUpload = new QPushButton(QIcon(":/icons/upload"), "", this); buttonUpload->setIconSize( QSize( 24,24 )); buttonUpload->setFixedSize(37, 28); buttonUpload->setToolTip("Upload File"); - if (!agent->browsers.FileBrowserUpload) - buttonUpload->setEnabled(false); line_2 = new QFrame(this); line_2->setFrameShape(QFrame::VLine); @@ -173,7 +171,7 @@ void BrowserFilesWidget::createUI() this->setLayout(mainGridLayout); } -void BrowserFilesWidget::SetDisksWin(qint64 time, int msgType, const QString &message, const QString &data) +void BrowserFilesWidget::SetDisksWin(const qint64 time, const int msgType, const QString &message, const QString &data) { QString sTime = UnixTimestampGlobalToStringLocal(time); QString status; @@ -205,7 +203,7 @@ void BrowserFilesWidget::SetDisksWin(qint64 time, int msgType, const QString &me inputPath->setText(currentPath); } -void BrowserFilesWidget::AddFiles(qint64 time, int msgType, const QString &message, const QString &path, const QString &data) +void BrowserFilesWidget::AddFiles(const qint64 time, const int msgType, const QString &message, const QString &path, const QString &data) { QString sTime = UnixTimestampGlobalToStringLocal(time); QString status; @@ -242,7 +240,7 @@ void BrowserFilesWidget::AddFiles(qint64 time, int msgType, const QString &messa inputPath->setText(currentPath); } -void BrowserFilesWidget::SetStatus( qint64 time, int msgType, const QString &message ) const +void BrowserFilesWidget::SetStatus(const qint64 time, const int msgType, const QString &message ) const { QString sTime = UnixTimestampGlobalToStringLocal(time); QString status; @@ -258,15 +256,9 @@ void BrowserFilesWidget::SetStatus( qint64 time, int msgType, const QString &mes /// PRIVATE -BrowserFileData* BrowserFilesWidget::getBrowserStore(const QString &path) -{ - return &browserStore[path]; -} +BrowserFileData* BrowserFilesWidget::getBrowserStore(const QString &path) { return &browserStore[path]; } -void BrowserFilesWidget::setBrowserStore(const QString &path, const BrowserFileData &fileData) -{ - browserStore[path] = fileData; -} +void BrowserFilesWidget::setBrowserStore(const QString &path, const BrowserFileData &fileData) { browserStore[path] = fileData; } BrowserFileData BrowserFilesWidget::createFileData(const QString &path) const { @@ -428,7 +420,6 @@ void BrowserFilesWidget::updateFileData(BrowserFileData* currenFileData, const Q browserStore.remove(data.Fullpath); oldFiles.remove(oldPath); } - } } @@ -503,33 +494,31 @@ void BrowserFilesWidget::cdBrowser(const QString &path) return; } - BrowserFileData fileData = * this->getBrowserStore(fPath); + BrowserFileData fileData = *this->getBrowserStore(fPath); if (fileData.Type == TYPE_FILE) return; if (fileData.Stored) { this->setStoredFileData(path, fileData); } else { - QString status = agent->BrowserList(path); - statusLabel->setText(status); + statusLabel->setText(""); + emit agent->adaptixWidget->eventFileBrowserList(agent->data.Id, path); } } - - /// SLOTS void BrowserFilesWidget::onDisks() const { - QString status = agent->BrowserDisks(); - statusLabel->setText(status); + statusLabel->setText(""); + emit agent->adaptixWidget->eventFileBrowserDisks(agent->data.Id); } void BrowserFilesWidget::onList() const { QString path = inputPath->text(); - QString status = agent->BrowserList(path); - statusLabel->setText(status); + statusLabel->setText(""); + emit agent->adaptixWidget->eventFileBrowserList(agent->data.Id, path); } void BrowserFilesWidget::onParent() @@ -566,12 +555,12 @@ void BrowserFilesWidget::onReload() const else path = "./"; - QString status = agent->BrowserList(path); - statusLabel->setText(status); + statusLabel->setText(""); + emit agent->adaptixWidget->eventFileBrowserList(agent->data.Id, path); } else { - QString status = agent->BrowserList(currentPath); - statusLabel->setText(status); + statusLabel->setText(""); + emit agent->adaptixWidget->eventFileBrowserList(agent->data.Id, currentPath); } } @@ -581,51 +570,18 @@ void BrowserFilesWidget::onUpload() const if ( path.isEmpty() ) return; - QString filePath = QFileDialog::getOpenFileName( nullptr, "Select file", QDir::homePath()); + QString remotePath = currentPath; + if (this->agent->data.Os == OS_WINDOWS) + remotePath += "\\"; + else + remotePath += "/"; + + QString filePath = QFileDialog::getOpenFileName(nullptr, "Select file", QDir::homePath()); if ( filePath.isEmpty()) return; - QFile file(filePath); - if (!file.open(QIODevice::ReadOnly)) - return; - - QByteArray fileContent = file.readAll(); - file.close(); - - QString base64Content = fileContent.toBase64(); - QString remotePath = currentPath; - if (this->agent->data.Os == OS_WINDOWS) - remotePath += "\\" + QFileInfo(filePath).fileName(); - else - remotePath += "/" + QFileInfo(filePath).fileName(); - - QString status = agent->BrowserUpload( remotePath, base64Content ); - statusLabel->setText(status); -} - -void BrowserFilesWidget::actionDownload() const -{ - // QString path = inputPath->text(); - // if ( path.isEmpty() ) - // return; - - if (currentPath.isEmpty()) - return; - - QList files; - for( int rowIndex = 0 ; rowIndex < tableWidget->rowCount() ; rowIndex++ ) { - if ( tableWidget->item(rowIndex, 0)->isSelected() ) { - auto filename = tableWidget->item( rowIndex, 0 )->text(); - - if (this->agent->data.Os == OS_WINDOWS) - files.append(currentPath + "\\" + filename); - else - files.append(currentPath + "/" + filename); - } - } - - for (auto file : files) - QString status = agent->BrowserDownload(file); + statusLabel->setText(""); + emit agent->adaptixWidget->eventFileBrowserUpload(agent->data.Id, remotePath, filePath); } void BrowserFilesWidget::handleTableDoubleClicked(const QModelIndex &index) @@ -656,13 +612,49 @@ void BrowserFilesWidget::handleTreeDoubleClicked(QTreeWidgetItem* item, int colu void BrowserFilesWidget::handleTableMenu(const QPoint &pos) { - if ( ! tableWidget->itemAt(pos) ) + if ( !tableWidget->itemAt(pos) || currentPath.isEmpty()) return; - if ( !agent->browsers.FileBrowserDownload ) + if ( !(agent && agent->adaptixWidget && agent->adaptixWidget->ScriptManager) ) return; + QString path = currentPath; + if (this->agent->data.Os == OS_WINDOWS) { + if (!path.endsWith("\\")) + path += "\\"; + } + else { + if (!path.endsWith("/")) + path += "/"; + } + + QVector items; + for( int rowIndex = 0 ; rowIndex < tableWidget->rowCount() ; rowIndex++ ) { + if ( tableWidget->item(rowIndex, 0)->isSelected() ) { + + auto filename = tableWidget->item( rowIndex, 0 )->text(); + auto fullname = path + filename; + if (this->agent->data.Os == OS_WINDOWS) + fullname = fullname.toLower(); + + if (!browserStore.contains(fullname)) + continue; + + DataMenuFileBrowser dataFile = {}; + dataFile.agentId = agent->data.Id; + dataFile.path = path; + + int filetype = this->getBrowserStore(fullname)->Type; + if (filetype == TYPE_FILE) + items.append(DataMenuFileBrowser{agent->data.Id, path, filename, "file"}); + else + items.append(DataMenuFileBrowser{agent->data.Id, path, filename, "dir"}); + } + } + auto ctxMenu = QMenu(); - ctxMenu.addAction( "Download", this, &BrowserFilesWidget::actionDownload); + + agent->adaptixWidget->ScriptManager->AddMenuFileBrowser(&ctxMenu, items); + ctxMenu.exec(tableWidget->horizontalHeader()->viewport()->mapToGlobal(pos)); } \ No newline at end of file diff --git a/AdaptixClient/Source/UI/Widgets/BrowserProcessWidget.cpp b/AdaptixClient/Source/UI/Widgets/BrowserProcessWidget.cpp index 5ba991cb..c096ebe3 100644 --- a/AdaptixClient/Source/UI/Widgets/BrowserProcessWidget.cpp +++ b/AdaptixClient/Source/UI/Widgets/BrowserProcessWidget.cpp @@ -1,15 +1,17 @@ #include #include #include +#include +#include BrowserProcessWidget::BrowserProcessWidget(Agent* a) { agent = a; this->createUI(); - connect(buttonReload, &QPushButton::clicked, this, &BrowserProcessWidget::onReload); - connect(inputFilter, &QLineEdit::textChanged, this, &BrowserProcessWidget::onFilter); - connect(tableWidget, &QTableWidget::customContextMenuRequested, this, &BrowserProcessWidget::handleTableMenu ); + connect(buttonReload, &QPushButton::clicked, this, &BrowserProcessWidget::onReload); + connect(inputFilter, &QLineEdit::textChanged, this, &BrowserProcessWidget::onFilter); + connect(tableWidget, &QTableWidget::customContextMenuRequested, this, &BrowserProcessWidget::handleTableMenu ); connect(tableWidget, &QTableWidget::clicked, this, &BrowserProcessWidget::onTableSelect ); connect(treeBrowserWidget, &QTreeWidget::clicked, this, &BrowserProcessWidget::onTreeSelect ); } @@ -42,7 +44,6 @@ void BrowserProcessWidget::createUI() tableWidget->setSelectionBehavior( QAbstractItemView::SelectRows ); tableWidget->setFocusPolicy( Qt::NoFocus ); tableWidget->setAlternatingRowColors( true ); - tableWidget->setSelectionMode( QAbstractItemView::SingleSelection ); tableWidget->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch ); tableWidget->horizontalHeader()->setCascadingSectionResizes( true ); tableWidget->horizontalHeader()->setHighlightSections( false ); @@ -314,7 +315,7 @@ void BrowserProcessWidget::setTreeProcessDataWin(QMapexpandAll(); } -void BrowserProcessWidget::addProcessToTreeWin(QTreeWidgetItem* parent, int parentPID, QMap processMap, QMap *nodeMap) +void BrowserProcessWidget::addProcessToTreeWin(QTreeWidgetItem* parent, const int parentPID, QMap processMap, QMap *nodeMap) { auto pids = processMap.keys(); for (int pid : pids) { @@ -366,7 +367,7 @@ void BrowserProcessWidget::setTreeProcessDataUnix(QMapexpandAll(); } -void BrowserProcessWidget::addProcessToTreeUnix(QTreeWidgetItem* parent, int parentPID, QMap processMap, QMap *nodeMap) +void BrowserProcessWidget::addProcessToTreeUnix(QTreeWidgetItem* parent, const int parentPID, QMap processMap, QMap *nodeMap) { auto pids = processMap.keys(); for (int pid : pids) { @@ -423,8 +424,8 @@ void BrowserProcessWidget::filterTableWidget(const QString &filterText) const void BrowserProcessWidget::onReload() const { - QString status = agent->BrowserProcess(); - statusLabel->setText(status); + statusLabel->setText(""); + emit agent->adaptixWidget->eventProcessBrowserList(agent->data.Id); } void BrowserProcessWidget::onFilter(const QString &text) const @@ -433,6 +434,47 @@ void BrowserProcessWidget::onFilter(const QString &text) const this->filterTableWidget(text); } +void BrowserProcessWidget::handleTableMenu(const QPoint &pos) +{ + if ( !tableWidget->itemAt(pos) ) + return; + + QVector items; + for( int rowIndex = 0 ; rowIndex < tableWidget->rowCount() ; rowIndex++ ) { + if ( tableWidget->item(rowIndex, 0)->isSelected() ) { + DataMenuProcessBrowser data; + data.agentId = this->agent->data.Id; + + if (this->agent->data.Os == OS_WINDOWS) { + data.pid = tableWidget->item(rowIndex, 0)->text(); + data.ppid = tableWidget->item(rowIndex, 1)->text(); + data.arch = tableWidget->item(rowIndex, 2)->text(); + data.session_id = tableWidget->item(rowIndex, 3)->text(); + data.context = tableWidget->item(rowIndex, 4)->text(); + data.process = tableWidget->item(rowIndex, 5)->text(); + } + else { + data.pid = tableWidget->item(rowIndex, 0)->text(); + data.ppid = tableWidget->item(rowIndex, 1)->text(); + data.session_id = tableWidget->item(rowIndex, 2)->text(); + data.context = tableWidget->item(rowIndex, 3)->text(); + data.process = tableWidget->item(rowIndex, 4)->text(); + } + items.append(data); + } + } + + auto ctxMenu = QMenu(); + + int count = agent->adaptixWidget->ScriptManager->AddMenuProcessBrowser(&ctxMenu, items); + if (count) { + ctxMenu.addSeparator(); + } + ctxMenu.addAction( "Copy PID", this, &BrowserProcessWidget::actionCopyPid); + + ctxMenu.exec(tableWidget->horizontalHeader()->viewport()->mapToGlobal(pos)); +} + void BrowserProcessWidget::actionCopyPid() const { int row = tableWidget->currentRow(); @@ -442,16 +484,6 @@ void BrowserProcessWidget::actionCopyPid() const } } -void BrowserProcessWidget::handleTableMenu(const QPoint &pos) -{ - if ( ! tableWidget->itemAt(pos) ) - return; - - auto ctxMenu = QMenu(); - ctxMenu.addAction( "Copy PID", this, &BrowserProcessWidget::actionCopyPid); - ctxMenu.exec(tableWidget->horizontalHeader()->viewport()->mapToGlobal(pos)); -} - void BrowserProcessWidget::onTableSelect() const { QString pid = tableWidget->item( tableWidget->currentRow(), 0 )->text(); diff --git a/AdaptixClient/Source/UI/Widgets/ConsoleWidget.cpp b/AdaptixClient/Source/UI/Widgets/ConsoleWidget.cpp index 389495b5..f49a3b02 100644 --- a/AdaptixClient/Source/UI/Widgets/ConsoleWidget.cpp +++ b/AdaptixClient/Source/UI/Widgets/ConsoleWidget.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -8,23 +7,27 @@ #include #include - -ConsoleWidget::ConsoleWidget( Agent* a, Commander* c) +ConsoleWidget::ConsoleWidget( AdaptixWidget* w, Agent* a, Commander* c) { - agent = a; - commander = c; + adaptixWidget = w; + agent = a; + commander = c; this->createUI(); - this->UpgradeCompleter(); + this->upgradeCompleter(); connect(CommandCompleter, QOverload::of(&QCompleter::activated), this, &ConsoleWidget::onCompletionSelected, Qt::DirectConnection); connect(InputLineEdit, &QLineEdit::returnPressed, this, &ConsoleWidget::processInput, Qt::QueuedConnection ); connect(searchLineEdit, &QLineEdit::returnPressed, this, &ConsoleWidget::handleSearch); connect(nextButton, &ClickableLabel::clicked, this, &ConsoleWidget::handleSearch); connect(prevButton, &ClickableLabel::clicked, this, &ConsoleWidget::handleSearchBackward); + connect(searchInput, &KPH_SearchInput::escPressed, this, &ConsoleWidget::toggleSearchPanel ); connect(hideButton, &ClickableLabel::clicked, this, &ConsoleWidget::toggleSearchPanel); connect(OutputTextEdit, &TextEditConsole::ctx_find, this, &ConsoleWidget::toggleSearchPanel); connect(OutputTextEdit, &TextEditConsole::ctx_history, this, &ConsoleWidget::handleShowHistory); + connect(commander, &Commander::commandsUpdated, this, &ConsoleWidget::upgradeCompleter); + + shortcutSearch = new QShortcut(QKeySequence("Ctrl+F"), OutputTextEdit); shortcutSearch->setContext(Qt::WidgetShortcut); @@ -64,6 +67,8 @@ void ConsoleWidget::createUI() searchLineEdit->setPlaceholderText("Find"); searchLineEdit->setMaximumWidth(300); + searchInput = new KPH_SearchInput(searchLineEdit, this); + hideButton = new ClickableLabel("X"); hideButton->setCursor( Qt::PointingHandCursor ); @@ -171,33 +176,21 @@ void ConsoleWidget::highlightCurrent() const searchLabel->setText(QString("%1 of %2").arg(currentIndex + 1).arg(sels.size())); } -/// -void ConsoleWidget::UpgradeCompleter() const + +void ConsoleWidget::upgradeCompleter() const { if (commander) completerModel->setStringList(commander->GetCommands()); } -void ConsoleWidget::InputFocus() const -{ - InputLineEdit->setFocus(); -} +void ConsoleWidget::InputFocus() const { InputLineEdit->setFocus(); } -void ConsoleWidget::AddToHistory(const QString &command) -{ - kphInputLineEdit->AddToHistory(command); -} +void ConsoleWidget::AddToHistory(const QString &command) { kphInputLineEdit->AddToHistory(command); } -void ConsoleWidget::SetInput(const QString &command) -{ - InputLineEdit->setText(command); -} +void ConsoleWidget::SetInput(const QString &command) { InputLineEdit->setText(command); } -void ConsoleWidget::Clear() -{ - OutputTextEdit->clear(); -} +void ConsoleWidget::Clear() { OutputTextEdit->clear(); } void ConsoleWidget::ConsoleOutputMessage(const qint64 timestamp, const QString &taskId, const int type, const QString &message, const QString &text, const bool completed ) const { @@ -217,10 +210,10 @@ void ConsoleWidget::ConsoleOutputMessage(const qint64 timestamp, const QString & else if (type == CONSOLE_OUT_ERROR || type == CONSOLE_OUT_LOCAL_ERROR) OutputTextEdit->appendColor("[-] ", QColor(COLOR_ChiliPepper)); else - OutputTextEdit->appendPlain("[!] "); + OutputTextEdit->appendPlain(" "); - QString printMessage = TrimmedEnds(message);// +"\n"; - if ( text.isEmpty() || type == CONSOLE_OUT_LOCAL_SUCCESS || type == CONSOLE_OUT_LOCAL_ERROR || type == CONSOLE_OUT_SUCCESS || type == CONSOLE_OUT_ERROR) + QString printMessage = TrimmedEnds(message); + if ( text.isEmpty() || type == CONSOLE_OUT_LOCAL_INFO || type == CONSOLE_OUT_LOCAL_SUCCESS || type == CONSOLE_OUT_LOCAL_ERROR || type == CONSOLE_OUT_SUCCESS || type == CONSOLE_OUT_ERROR) printMessage += "\n"; OutputTextEdit->appendPlain(printMessage); } @@ -262,8 +255,162 @@ void ConsoleWidget::ConsoleOutputPrompt(const qint64 timestamp, const QString &t } } +void ConsoleWidget::ProcessCmdResult(const QString &commandLine, const CommanderResult &cmdResult, const bool UI) +{ + if ( cmdResult.output ) { + if (UI) { + if (cmdResult.error) + MessageError(cmdResult.message); + } + else { + QString message = ""; + QString text = ""; + int type = 0; + + if (cmdResult.error) { + type = CONSOLE_OUT_LOCAL_ERROR; + message = cmdResult.message; + } + else { + type = CONSOLE_OUT_LOCAL; + text = cmdResult.message; + } + + this->ConsoleOutputPrompt(0, "", "", commandLine); + this->ConsoleOutputMessage(0, "", type, message, text, true); + } + return; + } + + QString hookId = ""; + if (cmdResult.post_hook.isSet) { + hookId = GenerateRandomString(8, "hex"); + while (adaptixWidget->PostHooksJS.contains(hookId)) + hookId = GenerateRandomString(8, "hex"); + + adaptixWidget->PostHooksJS[hookId] = cmdResult.post_hook; + } + + QJsonDocument jsonDoc(cmdResult.data); + QString commandData = jsonDoc.toJson(); + + QJsonObject dataJson; + dataJson["name"] = agent->data.Name; + dataJson["id"] = agent->data.Id; + dataJson["ui"] = UI; + dataJson["cmdline"] = commandLine; + dataJson["data"] = commandData; + dataJson["ax_hook_id"] = hookId; + QByteArray jsonData = QJsonDocument(dataJson).toJson(); + + /// 5 Mb + if (commandData.size() < 0x500000) { + QString message = QString(); + bool ok = false; + bool result = HttpReqAgentCommand(jsonData, *(agent->adaptixWidget->GetProfile()), &message, &ok); + if( !result ) { + if (cmdResult.post_hook.isSet && adaptixWidget->PostHooksJS.contains(hookId)) + adaptixWidget->PostHooksJS.remove(hookId); + MessageError("Response timeout"); + return; + } + if (!ok) { + if (cmdResult.post_hook.isSet && adaptixWidget->PostHooksJS.contains(hookId)) + adaptixWidget->PostHooksJS.remove(hookId); + this->ConsoleOutputPrompt(0, "", "", commandLine); + this->ConsoleOutputMessage(0, "", CONSOLE_OUT_LOCAL_ERROR, message, "", true); + } + } + else { + + /// 1. Get OTP + + QString message = QString(); + bool ok = false; + QString objId = GenerateRandomString(8, "hex"); + bool result = HttpReqGetOTP("tmp_upload", objId, *(agent->adaptixWidget->GetProfile()), &message, &ok); + if (!result) { + if (cmdResult.post_hook.isSet && adaptixWidget->PostHooksJS.contains(hookId)) + adaptixWidget->PostHooksJS.remove(hookId); + MessageError("Response timeout"); + return; + } + if (!ok) { + if (cmdResult.post_hook.isSet && adaptixWidget->PostHooksJS.contains(hookId)) + adaptixWidget->PostHooksJS.remove(hookId); + MessageError(message); + return; + } + QString otp = message; + + /// 2. Upload with OTP + + QString sUrl = agent->adaptixWidget->GetProfile()->GetURL() + "/otp/upload/temp"; + + auto* uploaderDialog = new DialogUploader(sUrl, otp, jsonData); + uploaderDialog->setAttribute(Qt::WA_DeleteOnClose); + + connect(uploaderDialog, &DialogUploader::finished, [&](const bool success) { + if (!success) { + if (cmdResult.post_hook.isSet && adaptixWidget->PostHooksJS.contains(hookId)) + adaptixWidget->PostHooksJS.remove(hookId); + return; + } + + /// 3. Send Command + + QJsonObject data2Json; + data2Json["object_id"] = objId; + QByteArray json2Data = QJsonDocument(data2Json).toJson(); + + sUrl = agent->adaptixWidget->GetProfile()->GetURL() + "/agent/command/file"; + QJsonObject jsonObject = HttpReq(sUrl, json2Data, agent->adaptixWidget->GetProfile()->GetAccessToken(), 10000); + if ( jsonObject.contains("message") && jsonObject.contains("ok") ) { + if (jsonObject["ok"].toBool() == false) { + if (cmdResult.post_hook.isSet && adaptixWidget->PostHooksJS.contains(hookId)) + adaptixWidget->PostHooksJS.remove(hookId); + MessageError( jsonObject["message"].toString()); + } + } + else { + if (cmdResult.post_hook.isSet && adaptixWidget->PostHooksJS.contains(hookId)) + adaptixWidget->PostHooksJS.remove(hookId); + MessageError("Response timeout"); + return; + } + }); + + uploaderDialog->exec(); + } +} + /// SLOTS +void ConsoleWidget::processInput() +{ + if (!commander) + return; + + QString commandLine = TrimmedEnds(InputLineEdit->text()); + + if ( this->userSelectedCompletion ) { + this->userSelectedCompletion = false; + return; + } + + InputLineEdit->clear(); + if (commandLine.isEmpty()) + return; + + this->AddToHistory(commandLine); + + auto cmdResult = commander->ProcessInput( agent->data.Id, commandLine ); + if (cmdResult.is_pre_hook) + return; + + this->ProcessCmdResult(commandLine, cmdResult, false); +} + void ConsoleWidget::toggleSearchPanel() { if (this->searchWidget->isVisible()) { @@ -376,117 +523,4 @@ void ConsoleWidget::handleShowHistory() historyDialog->show(); } -void ConsoleWidget::processInput() -{ - if (!commander) - return; - - QString commandLine = TrimmedEnds(InputLineEdit->text()); - - if ( this->userSelectedCompletion ) { - this->userSelectedCompletion = false; - return; - } - - InputLineEdit->clear(); - if (commandLine.isEmpty()) - return; - - this->AddToHistory(commandLine); - - auto cmdResult = commander->ProcessInput( agent->data, commandLine ); - if ( cmdResult.output ) { - QString message = ""; - QString text = ""; - int type = 0; - - if (cmdResult.error) { - type = CONSOLE_OUT_LOCAL_ERROR; - message = cmdResult.message; - } - else { - type = CONSOLE_OUT_LOCAL; - text = cmdResult.message; - } - - this->ConsoleOutputPrompt(0, "", "", commandLine); - this->ConsoleOutputMessage(0, "", type, message, text, true); - - return; - } - - /// 5 Mb - if (cmdResult.message.size() < 0x500000) { - QString message = QString(); - bool ok = false; - bool result = HttpReqAgentCommand(agent->data.Name, agent->data.Id, commandLine, cmdResult.message, *(agent->adaptixWidget->GetProfile()), &message, &ok); - if( !result ) { - MessageError("Response timeout"); - return; - } - if (!ok) { - this->ConsoleOutputPrompt(0, "", "", commandLine); - this->ConsoleOutputMessage(0, "", CONSOLE_OUT_LOCAL_ERROR, message, "", true); - } - } - else { - - /// 1. Get OTP - - QString message = QString(); - bool ok = false; - QString objId = GenerateRandomString(8, "hex"); - bool result = HttpReqGetOTP("tmp_upload", objId, *(agent->adaptixWidget->GetProfile()), &message, &ok); - if (!result) { - MessageError("Response timeout"); - return; - } - if (!ok) { - MessageError(message); - return; - } - QString otp = message; - - /// 2. Upload with OTP - - QJsonObject dataJson; - dataJson["name"] = agent->data.Name; - dataJson["id"] = agent->data.Id; - dataJson["cmdline"] = commandLine; - dataJson["data"] = cmdResult.message; - QByteArray jsonData = QJsonDocument(dataJson).toJson(); - - QString sUrl = agent->adaptixWidget->GetProfile()->GetURL() + "/otp/upload/temp"; - - auto* uploaderDialog = new DialogUploader(sUrl, otp, jsonData); - uploaderDialog->setAttribute(Qt::WA_DeleteOnClose); - - connect(uploaderDialog, &DialogUploader::finished, [&](const bool success) { - if (!success) - return; - - /// 3. Send Command - - QJsonObject data2Json; - data2Json["object_id"] = objId; - QByteArray json2Data = QJsonDocument(data2Json).toJson(); - - sUrl = agent->adaptixWidget->GetProfile()->GetURL() + "/agent/command/file"; - QJsonObject jsonObject = HttpReq(sUrl, json2Data, agent->adaptixWidget->GetProfile()->GetAccessToken(), 10000); - if ( jsonObject.contains("message") && jsonObject.contains("ok") ) { - if (jsonObject["ok"].toBool() == false) - MessageError( jsonObject["message"].toString()); - } - else { - MessageError("Response timeout"); - return; - } - }); - - uploaderDialog->exec(); - } -} - -void ConsoleWidget::onCompletionSelected(const QString &selectedText) { - userSelectedCompletion = true; -} +void ConsoleWidget::onCompletionSelected(const QString &selectedText) { userSelectedCompletion = true; } diff --git a/AdaptixClient/Source/UI/Widgets/CredentialsWidget.cpp b/AdaptixClient/Source/UI/Widgets/CredentialsWidget.cpp new file mode 100644 index 00000000..1924032d --- /dev/null +++ b/AdaptixClient/Source/UI/Widgets/CredentialsWidget.cpp @@ -0,0 +1,472 @@ +#include +#include +#include +#include +#include +#include + +CredentialsWidget::CredentialsWidget(AdaptixWidget* w) : adaptixWidget(w) +{ + this->createUI(); + + connect(tableWidget, &QTableWidget::customContextMenuRequested, this, &CredentialsWidget::handleCredentialsMenu); + connect(tableWidget, &QTableWidget::cellDoubleClicked, this, &CredentialsWidget::onEditCreds); + connect(tableWidget, &QTableWidget::itemSelectionChanged, this, [this](){tableWidget->setFocus();} ); + connect(hideButton, &ClickableLabel::clicked, this, &CredentialsWidget::toggleSearchPanel); + connect(inputFilter, &QLineEdit::textChanged, this, &CredentialsWidget::onFilterUpdate); + + shortcutSearch = new QShortcut(QKeySequence("Ctrl+F"), tableWidget); + shortcutSearch->setContext(Qt::WidgetShortcut); + connect(shortcutSearch, &QShortcut::activated, this, &CredentialsWidget::toggleSearchPanel); +} + +CredentialsWidget::~CredentialsWidget() = default; + +void CredentialsWidget::createUI() +{ + auto horizontalSpacer2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + + searchWidget = new QWidget(this); + searchWidget->setVisible(false); + + inputFilter = new QLineEdit(searchWidget); + inputFilter->setPlaceholderText("filter"); + inputFilter->setMaximumWidth(300); + + hideButton = new ClickableLabel("X"); + hideButton->setCursor( Qt::PointingHandCursor ); + + searchLayout = new QHBoxLayout(searchWidget); + searchLayout->setContentsMargins(0, 5, 0, 0); + searchLayout->setSpacing(4); + searchLayout->addWidget(inputFilter); + searchLayout->addWidget(hideButton); + searchLayout->addSpacerItem(horizontalSpacer2); + + tableWidget = new QTableWidget( this ); + tableWidget->setColumnCount( 10 ); + tableWidget->setContextMenuPolicy( Qt::CustomContextMenu ); + tableWidget->setAutoFillBackground( false ); + tableWidget->setShowGrid( false ); + tableWidget->setSortingEnabled( true ); + tableWidget->setWordWrap( true ); + tableWidget->setCornerButtonEnabled( false ); + tableWidget->setSelectionBehavior( QAbstractItemView::SelectRows ); + tableWidget->setFocusPolicy( Qt::NoFocus ); + tableWidget->setAlternatingRowColors( true ); + tableWidget->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch ); + tableWidget->horizontalHeader()->setCascadingSectionResizes( true ); + tableWidget->horizontalHeader()->setHighlightSections( false ); + tableWidget->verticalHeader()->setVisible( false ); + + tableWidget->setHorizontalHeaderItem(0, new QTableWidgetItem("CredId")); + tableWidget->setHorizontalHeaderItem(1, new QTableWidgetItem("Username")); + tableWidget->setHorizontalHeaderItem(2, new QTableWidgetItem("Password")); + tableWidget->setHorizontalHeaderItem(3, new QTableWidgetItem("Realm")); + tableWidget->setHorizontalHeaderItem(4, new QTableWidgetItem("Type")); + tableWidget->setHorizontalHeaderItem(5, new QTableWidgetItem("Tag")); + tableWidget->setHorizontalHeaderItem(6, new QTableWidgetItem("Date")); + tableWidget->setHorizontalHeaderItem(7, new QTableWidgetItem("Storage")); + tableWidget->setHorizontalHeaderItem(8, new QTableWidgetItem("Agent")); + tableWidget->setHorizontalHeaderItem(9, new QTableWidgetItem("Host")); + + tableWidget->hideColumn(0); + + mainGridLayout = new QGridLayout( this ); + mainGridLayout->setContentsMargins( 0, 0, 0, 0); + mainGridLayout->addWidget( searchWidget, 0, 0, 1, 1); + mainGridLayout->addWidget( tableWidget, 1, 0, 1, 1); +} + +bool CredentialsWidget::filterItem(const CredentialData &credentials) const +{ + if ( !this->searchWidget->isVisible() ) + return true; + + QString filter1 = this->inputFilter->text(); + if( !filter1.isEmpty() ) { + if ( credentials.Username.contains(filter1, Qt::CaseInsensitive) || + credentials.Password.contains(filter1, Qt::CaseInsensitive) || + credentials.Realm.contains(filter1, Qt::CaseInsensitive) || + credentials.Type.contains(filter1, Qt::CaseInsensitive) || + credentials.Tag.contains(filter1, Qt::CaseInsensitive) || + credentials.Storage.contains(filter1, Qt::CaseInsensitive) || + credentials.Host.contains(filter1, Qt::CaseInsensitive) || + credentials.AgentId.contains(filter1, Qt::CaseInsensitive) + ) + return true; + else + return false; + } + return true; +} + +void CredentialsWidget::addTableItem(const CredentialData &newCredentials) const +{ + auto item_CredId = new QTableWidgetItem( newCredentials.CredId ); + auto item_Username = new QTableWidgetItem( newCredentials.Username ); + auto item_Password = new QTableWidgetItem( newCredentials.Password ); + auto item_Realm = new QTableWidgetItem( newCredentials.Realm ); + auto item_Type = new QTableWidgetItem( newCredentials.Type ); + auto item_Tag = new QTableWidgetItem( newCredentials.Tag ); + auto item_Date = new QTableWidgetItem( newCredentials.Date ); + auto item_Storage = new QTableWidgetItem( newCredentials.Storage ); + auto item_Agent = new QTableWidgetItem( newCredentials.AgentId ); + auto item_Host = new QTableWidgetItem( newCredentials.Host ); + + item_Username->setFlags( item_Username->flags() ^ Qt::ItemIsEditable ); + item_Username->setTextAlignment( Qt::AlignLeft | Qt::AlignVCenter ); + + item_Password->setFlags( item_Password->flags() ^ Qt::ItemIsEditable ); + item_Password->setTextAlignment( Qt::AlignLeft | Qt::AlignVCenter ); + + item_Realm->setFlags( item_Realm->flags() ^ Qt::ItemIsEditable ); + item_Realm->setTextAlignment( Qt::AlignLeft | Qt::AlignVCenter ); + + item_Type->setFlags( item_Type->flags() ^ Qt::ItemIsEditable ); + item_Type->setTextAlignment( Qt::AlignCenter ); + + item_Tag->setFlags( item_Tag->flags() ^ Qt::ItemIsEditable ); + item_Tag->setTextAlignment( Qt::AlignLeft | Qt::AlignVCenter ); + + item_Date->setFlags( item_Date->flags() ^ Qt::ItemIsEditable ); + item_Date->setTextAlignment( Qt::AlignCenter ); + + item_Storage->setFlags( item_Storage->flags() ^ Qt::ItemIsEditable ); + item_Storage->setTextAlignment( Qt::AlignCenter ); + + item_Agent->setFlags( item_Agent->flags() ^ Qt::ItemIsEditable ); + item_Agent->setTextAlignment( Qt::AlignCenter ); + + item_Host->setFlags( item_Host->flags() ^ Qt::ItemIsEditable ); + item_Host->setTextAlignment( Qt::AlignLeft | Qt::AlignVCenter ); + + if( tableWidget->rowCount() < 1 ) + tableWidget->setRowCount( 1 ); + else + tableWidget->setRowCount( tableWidget->rowCount() + 1 ); + + bool isSortingEnabled = tableWidget->isSortingEnabled(); + tableWidget->setSortingEnabled( false ); + tableWidget->setItem( tableWidget->rowCount() - 1, 0, item_CredId ); + tableWidget->setItem( tableWidget->rowCount() - 1, 1, item_Username ); + tableWidget->setItem( tableWidget->rowCount() - 1, 2, item_Password ); + tableWidget->setItem( tableWidget->rowCount() - 1, 3, item_Realm ); + tableWidget->setItem( tableWidget->rowCount() - 1, 4, item_Type ); + tableWidget->setItem( tableWidget->rowCount() - 1, 5, item_Tag ); + tableWidget->setItem( tableWidget->rowCount() - 1, 6, item_Date ); + tableWidget->setItem( tableWidget->rowCount() - 1, 7, item_Storage ); + tableWidget->setItem( tableWidget->rowCount() - 1, 8, item_Agent ); + tableWidget->setItem( tableWidget->rowCount() - 1, 9, item_Host ); + tableWidget->setSortingEnabled( isSortingEnabled ); + + tableWidget->horizontalHeader()->setSectionResizeMode( 1, QHeaderView::ResizeToContents ); + tableWidget->horizontalHeader()->setSectionResizeMode( 2, QHeaderView::ResizeToContents ); + tableWidget->horizontalHeader()->setSectionResizeMode( 3, QHeaderView::ResizeToContents ); + tableWidget->horizontalHeader()->setSectionResizeMode( 6, QHeaderView::ResizeToContents ); + tableWidget->horizontalHeader()->setSectionResizeMode( 8, QHeaderView::ResizeToContents ); + tableWidget->horizontalHeader()->setSectionResizeMode( 9, QHeaderView::ResizeToContents ); + + tableWidget->verticalHeader()->setSectionResizeMode(tableWidget->rowCount() - 1, QHeaderView::ResizeToContents); +} + +/// Main + +void CredentialsWidget::Clear() const +{ + adaptixWidget->Credentials.clear(); + this->ClearTableContent(); + inputFilter->clear(); +} + +void CredentialsWidget::AddCredentialsItem(const CredentialData &newCredentials) const +{ + for( auto creds : adaptixWidget->Credentials ) { + if( creds.CredId == newCredentials.CredId ) + return; + } + + adaptixWidget->Credentials.push_back(newCredentials); + + if( !this->filterItem(newCredentials) ) + return; + + this->addTableItem(newCredentials); +} + +void CredentialsWidget::EditCredentialsItem(const CredentialData &newCredentials) const +{ + for ( int i = 0; i < adaptixWidget->Credentials.size(); i++ ) { + if( adaptixWidget->Credentials[i].CredId == newCredentials.CredId ) { + adaptixWidget->Credentials[i].Username = newCredentials.Username; + adaptixWidget->Credentials[i].Password = newCredentials.Password; + adaptixWidget->Credentials[i].Realm = newCredentials.Realm; + adaptixWidget->Credentials[i].Type = newCredentials.Type; + adaptixWidget->Credentials[i].Tag = newCredentials.Tag; + adaptixWidget->Credentials[i].Storage = newCredentials.Storage; + adaptixWidget->Credentials[i].Host = newCredentials.Host; + break; + } + } + + for (int row = 0; row < tableWidget->rowCount(); ++row) { + QTableWidgetItem *item = tableWidget->item(row, 0); + if ( item && item->text() == newCredentials.CredId ) { + tableWidget->item(row, 1)->setText(newCredentials.Username); + tableWidget->item(row, 2)->setText(newCredentials.Password); + tableWidget->item(row, 3)->setText(newCredentials.Realm); + tableWidget->item(row, 4)->setText(newCredentials.Type); + tableWidget->item(row, 5)->setText(newCredentials.Tag); + tableWidget->item(row, 7)->setText(newCredentials.Storage); + tableWidget->item(row, 9)->setText(newCredentials.Host); + } + } +} + +void CredentialsWidget::RemoveCredentialsItem(const QString &credId) const +{ + for ( int i = 0; i < adaptixWidget->Credentials.size(); i++ ) { + if( adaptixWidget->Credentials[i].CredId == credId ) { + adaptixWidget->Credentials.erase( adaptixWidget->Credentials.begin() + i ); + break; + } + } + + for (int row = 0; row < tableWidget->rowCount(); ++row) { + QTableWidgetItem *item = tableWidget->item(row, 0); + if ( item && item->text() == credId ) { + tableWidget->removeRow(row); + break; + } + } + + +} + +void CredentialsWidget::SetData() const +{ + this->ClearTableContent(); + + for (int i = 0; i < adaptixWidget->Credentials.size(); i++ ) { + if ( this->filterItem(adaptixWidget->Credentials[i]) ) + this->addTableItem(adaptixWidget->Credentials[i]); + } +} + +void CredentialsWidget::ClearTableContent() const +{ + for (int row = tableWidget->rowCount() - 1; row >= 0; row--) { + for (int col = 0; col < tableWidget->columnCount(); ++col) + tableWidget->takeItem(row, col); + + tableWidget->removeRow(row); + } +} + +/// Sender + +void CredentialsWidget::CredentialsAdd(const QString &username, const QString &password, const QString &realm, const QString &type, const QString &tag, const QString &storage, const QString &host) +{ + QJsonObject dataJson; + dataJson["username"] = username; + dataJson["password"] = password; + dataJson["realm"] = realm; + dataJson["type"] = type; + dataJson["tag"] = tag; + dataJson["storage"] = storage; + dataJson["host"] = host; + + QByteArray jsonData = QJsonDocument(dataJson).toJson(); + + QString message = ""; + bool ok = false; + bool result = HttpReqCredentialsCreate(jsonData, *(adaptixWidget->GetProfile()), &message, &ok); + if( !result ) { + MessageError("Server is not responding"); + return; + } + if (!ok) MessageError(message); +} + +/// Slots + +void CredentialsWidget::toggleSearchPanel() const +{ + if (this->searchWidget->isVisible()) + this->searchWidget->setVisible(false); + else + this->searchWidget->setVisible(true); + + this->SetData(); +} + +void CredentialsWidget::onFilterUpdate() const { this->SetData(); } + +void CredentialsWidget::handleCredentialsMenu(const QPoint &pos ) const +{ + auto ctxMenu = QMenu(); + + ctxMenu.addAction("Create", this, &CredentialsWidget::onCreateCreds ); + ctxMenu.addAction("Edit", this, &CredentialsWidget::onEditCreds ); + ctxMenu.addAction("Remove", this, &CredentialsWidget::onRemoveCreds ); + ctxMenu.addSeparator(); + ctxMenu.addAction("Export", this, &CredentialsWidget::onExportCreds ); + + QPoint globalPos = tableWidget->mapToGlobal(pos); + ctxMenu.exec(globalPos); +} + +void CredentialsWidget::onCreateCreds() +{ + DialogCredential* dialogCreds = new DialogCredential(); + while (true) { + dialogCreds->StartDialog(); + if (dialogCreds->IsValid()) + break; + + QString msg = dialogCreds->GetMessage(); + if (msg.isEmpty()) { + delete dialogCreds; + return; + } + + MessageError(msg); + } + + CredentialData credData = dialogCreds->GetCredData(); + + delete dialogCreds; + + this->CredentialsAdd(credData.Username, credData.Password, credData.Realm, credData.Type, credData.Tag, credData.Storage, credData.Host); +} + +void CredentialsWidget::onEditCreds() const +{ + if (tableWidget->selectionModel()->selectedRows().empty()) + return; + + auto credId = tableWidget->item( tableWidget->currentRow(), 0 )->text(); + + bool found = false; + CredentialData credentialData; + for (auto creds : adaptixWidget->Credentials) { + if (creds.CredId == credId) { + credentialData = creds; + found = true; + break; + } + } + + if (!found) + return; + + DialogCredential* dialogCreds = new DialogCredential(); + dialogCreds->SetEditmode(credentialData); + while (true) { + dialogCreds->StartDialog(); + if (dialogCreds->IsValid()) + break; + + QString msg = dialogCreds->GetMessage(); + if (msg.isEmpty()) { + delete dialogCreds; + return; + } + + MessageError(msg); + } + + CredentialData credData = dialogCreds->GetCredData(); + + QJsonObject dataJson; + dataJson["cred_id"] = credData.CredId; + dataJson["username"] = credData.Username; + dataJson["password"] = credData.Password; + dataJson["realm"] = credData.Realm; + dataJson["type"] = credData.Type; + dataJson["tag"] = credData.Tag; + dataJson["storage"] = credData.Storage; + dataJson["host"] = credData.Host; + QByteArray jsonData = QJsonDocument(dataJson).toJson(); + + delete dialogCreds; + + QString message = ""; + bool ok = false; + bool result = HttpReqCredentialsEdit(jsonData, *(adaptixWidget->GetProfile()), &message, &ok); + if( !result ) { + MessageError("Server is not responding"); + delete dialogCreds; + return; + } + if (!ok) MessageError(message); +} + +void CredentialsWidget::onRemoveCreds() const +{ + if (tableWidget->selectionModel()->selectedRows().empty()) + return; + + auto credId = tableWidget->item( tableWidget->currentRow(), 0 )->text(); + + QString message = QString(); + bool ok = false; + bool result = HttpReqCredentialsRemove(credId, *(adaptixWidget->GetProfile()), &message, &ok); + if( !result ){ + MessageError("Response timeout"); + return; + } + + if ( !ok ) MessageError(message); +} + +void CredentialsWidget::onExportCreds() const +{ + if (tableWidget->selectionModel()->selectedRows().empty()) + return; + + QInputDialog dialog; + dialog.setWindowTitle("Format for saving"); + dialog.setLabelText("Format:"); + dialog.setTextValue("%realm%\\%username%:%password%"); + QLineEdit *lineEdit = dialog.findChild(); + if (lineEdit) { + lineEdit->setMinimumWidth(400); + } + + bool inputOk = (dialog.exec() == QDialog::Accepted); + if (!inputOk) + return; + + QString format = dialog.textValue(); + + QString fileName = QFileDialog::getSaveFileName( nullptr, "Save credentials", "creds.txt", "Text Files (*.txt);;All Files (*)" ); + if ( fileName.isEmpty()) + return; + + QFile file(fileName); + if (!file.open(QIODevice::WriteOnly)) { + MessageError("Failed to open file for writing"); + return; + } + + QString content = ""; + for( int rowIndex = 0 ; rowIndex < tableWidget->rowCount() ; rowIndex++ ) { + if ( tableWidget->item(rowIndex, 1)->isSelected() ) { + + QString realm = tableWidget->item(rowIndex, 3)->text(); + QString username = tableWidget->item(rowIndex, 1)->text(); + QString password = tableWidget->item(rowIndex, 2)->text(); + + QString temp = format; + content += temp + .replace("%realm%", realm) + .replace("%username%", username) + .replace("%password%", password) + + "\n"; + } + } + + file.write(content.trimmed().toUtf8()); + file.close(); +} diff --git a/AdaptixClient/Source/UI/Widgets/DownloadsWidget.cpp b/AdaptixClient/Source/UI/Widgets/DownloadsWidget.cpp index f6484592..a00957e2 100644 --- a/AdaptixClient/Source/UI/Widgets/DownloadsWidget.cpp +++ b/AdaptixClient/Source/UI/Widgets/DownloadsWidget.cpp @@ -4,10 +4,12 @@ #include #include #include +#include -DownloadsWidget::DownloadsWidget(QWidget* w) + +DownloadsWidget::DownloadsWidget(AdaptixWidget* w) { - this->mainWidget = w; + this->adaptixWidget = w; this->createUI(); connect( tableWidget, &QTableWidget::customContextMenuRequested, this, &DownloadsWidget::handleDownloadsMenu ); @@ -24,6 +26,7 @@ void DownloadsWidget::createUI() tableWidget->setWordWrap( true ); tableWidget->setCornerButtonEnabled( true ); tableWidget->setSelectionBehavior( QAbstractItemView::SelectRows ); + tableWidget->setSelectionMode( QAbstractItemView::SingleSelection ); tableWidget->setFocusPolicy( Qt::NoFocus ); tableWidget->setAlternatingRowColors( true ); tableWidget->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch ); @@ -52,10 +55,6 @@ DownloadsWidget::~DownloadsWidget() = default; void DownloadsWidget::Clear() const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - adaptixWidget->Downloads.clear(); for (int index = tableWidget->rowCount(); index > 0; index-- ) tableWidget->removeRow(index -1 ); @@ -63,8 +62,7 @@ void DownloadsWidget::Clear() const void DownloadsWidget::AddDownloadItem(const DownloadData &newDownload ) { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget || adaptixWidget->Downloads.contains(newDownload.FileId)) + if ( adaptixWidget->Downloads.contains(newDownload.FileId) ) return; if( tableWidget->rowCount() < 1 ) @@ -144,18 +142,13 @@ void DownloadsWidget::AddDownloadItem(const DownloadData &newDownload ) tableWidget->horizontalHeader()->setSectionResizeMode( 8, QHeaderView::ResizeToContents ); tableWidget->horizontalHeader()->setSectionResizeMode( 9, QHeaderView::ResizeToContents ); - // tableWidget->setItemDelegate(new PaddingDelegate(tableWidget)); tableWidget->verticalHeader()->setSectionResizeMode(tableWidget->rowCount() - 1, QHeaderView::ResizeToContents); adaptixWidget->Downloads[newDownload.FileId] = newDownload; } -void DownloadsWidget::EditDownloadItem(const QString &fileId, int recvSize, int state) const +void DownloadsWidget::EditDownloadItem(const QString &fileId, const int recvSize, const int state) const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - adaptixWidget->Downloads[fileId].RecvSize = recvSize; adaptixWidget->Downloads[fileId].State = state; @@ -197,10 +190,6 @@ void DownloadsWidget::EditDownloadItem(const QString &fileId, int recvSize, int void DownloadsWidget::RemoveDownloadItem(const QString &fileId) const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - adaptixWidget->Downloads.remove(fileId); for ( int row = 0; row < tableWidget->rowCount(); row++ ) { @@ -218,55 +207,42 @@ void DownloadsWidget::handleDownloadsMenu(const QPoint &pos ) if ( !tableWidget->itemAt(pos) ) return; - bool menuDownloadResume = false; - bool menuDownloadPause = false; - bool menuDownloadCancel = false; + QVector files; - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - - for( int rowIndex = 0 ; rowIndex < tableWidget->rowCount() ; rowIndex++ ) { - if ( tableWidget->item(rowIndex, 2)->isSelected() ) { - QString agentId = tableWidget->item( rowIndex, 2 )->text(); - if (adaptixWidget->AgentsMap.contains(agentId) && adaptixWidget->AgentsMap[agentId]) { - auto agent = adaptixWidget->AgentsMap[agentId]; - if (agent) { - if (agent->browsers.DownloadsResume) menuDownloadResume = true; - if (agent->browsers.DownloadsPause) menuDownloadPause = true; - if (agent->browsers.DownloadsCancel) menuDownloadCancel = true; - } - } - } - } - - auto FileID = tableWidget->item( tableWidget->currentRow(), 0 )->text(); - auto Received = tableWidget->item( tableWidget->currentRow(), 8 )->text(); + DataMenuDownload data = {}; + data.agentId = tableWidget->item( tableWidget->currentRow(), 2 )->text(); + data.fileId = tableWidget->item( tableWidget->currentRow(), 0 )->text(); + data.path = tableWidget->item( tableWidget->currentRow(), 5 )->text(); auto ctxMenu = QMenu(); + auto Received = tableWidget->item( tableWidget->currentRow(), 8 )->text(); if(Received.compare("") == 0) { ctxMenu.addAction("Sync file to client", this, &DownloadsWidget::actionSync); - auto agentMenu = new QMenu("Sync as ...", &ctxMenu); - agentMenu->addAction("Curl command", this, &DownloadsWidget::actionSyncCurl); - agentMenu->addAction("Wget command", this, &DownloadsWidget::actionSyncWget); - ctxMenu.addMenu(agentMenu); - + auto syncMenu = new QMenu("Sync as ...", &ctxMenu); + syncMenu->addAction("Curl command", this, &DownloadsWidget::actionSyncCurl); + syncMenu->addAction("Wget command", this, &DownloadsWidget::actionSyncWget); + ctxMenu.addMenu(syncMenu); ctxMenu.addSeparator(); + + data.state = "finished"; + files.append(data); + int menuCount = adaptixWidget->ScriptManager->AddMenuDownload(&ctxMenu, "DownloadFinished", files); + if (menuCount > 0) + ctxMenu.addSeparator(); + ctxMenu.addAction("Delete file", this, &DownloadsWidget::actionDelete ); } else { if( tableWidget->cellWidget( tableWidget->currentRow(), 9)->isEnabled() ) { - if (menuDownloadPause) - ctxMenu.addAction("Pause", this, &DownloadsWidget::actionPause ); + data.state = "running"; + files.append(data); } else { - if (menuDownloadResume) - ctxMenu.addAction("Resume", this, &DownloadsWidget::actionResume ); + data.state = "stopped"; + files.append(data); } - - if (menuDownloadCancel) - ctxMenu.addAction("Cancel", this, &DownloadsWidget::actionCancel ); + adaptixWidget->ScriptManager->AddMenuDownload(&ctxMenu, "DownloadRunning", files); } ctxMenu.exec(tableWidget->horizontalHeader()->viewport()->mapToGlobal(pos) ); @@ -274,10 +250,6 @@ void DownloadsWidget::handleDownloadsMenu(const QPoint &pos ) void DownloadsWidget::actionSync() const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - if( tableWidget->item( tableWidget->currentRow(), 8 )->text() != "" ) return; @@ -314,10 +286,6 @@ void DownloadsWidget::actionSync() const void DownloadsWidget::actionSyncCurl() const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - if( tableWidget->item( tableWidget->currentRow(), 8 )->text() != "" ) return; @@ -358,10 +326,6 @@ void DownloadsWidget::actionSyncCurl() const void DownloadsWidget::actionSyncWget() const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - if( tableWidget->item( tableWidget->currentRow(), 8 )->text() != "" ) return; @@ -402,10 +366,6 @@ void DownloadsWidget::actionSyncWget() const void DownloadsWidget::actionDelete() const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - if( tableWidget->item( tableWidget->currentRow(), 8 )->text() == "" ) { QString fileId = tableWidget->item(tableWidget->currentRow(), 0)->text(); @@ -422,72 +382,3 @@ void DownloadsWidget::actionDelete() const } } } - -void DownloadsWidget::actionResume() const -{ - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - - if( tableWidget->item( tableWidget->currentRow(), 8 )->text() != "" ) { - - QString fileId = tableWidget->item(tableWidget->currentRow(), 0)->text(); - QString message = QString(); - bool ok = false; - bool result = HttpReqDownloadAction("resume", fileId, *(adaptixWidget->GetProfile()), &message, &ok); - if (!result) { - MessageError("Response timeout"); - return; - } - - if (!ok) { - MessageError(message); - } - } -} - -void DownloadsWidget::actionPause() const -{ - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - - if( tableWidget->item( tableWidget->currentRow(), 8 )->text() != "" ) { - - QString fileId = tableWidget->item(tableWidget->currentRow(), 0)->text(); - QString message = QString(); - bool ok = false; - bool result = HttpReqDownloadAction("pause", fileId, *(adaptixWidget->GetProfile()), &message, &ok); - if (!result) { - MessageError("Response timeout"); - return; - } - - if (!ok) { - MessageError(message); - } - } -} - -void DownloadsWidget::actionCancel() const -{ - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - - if( tableWidget->item( tableWidget->currentRow(), 8 )->text() != "" ) { - - QString fileId = tableWidget->item(tableWidget->currentRow(), 0)->text(); - QString message = QString(); - bool ok = false; - bool result = HttpReqDownloadAction("cancel", fileId, *(adaptixWidget->GetProfile()), &message, &ok); - if (!result) { - MessageError("Response timeout"); - return; - } - - if (!ok) { - MessageError(message); - } - } -} diff --git a/AdaptixClient/Source/UI/Widgets/ListenersWidget.cpp b/AdaptixClient/Source/UI/Widgets/ListenersWidget.cpp index 5a0b01fb..69ba2212 100644 --- a/AdaptixClient/Source/UI/Widgets/ListenersWidget.cpp +++ b/AdaptixClient/Source/UI/Widgets/ListenersWidget.cpp @@ -1,17 +1,17 @@ +#include #include #include #include #include #include -#include +#include +#include -ListenersWidget::ListenersWidget(QWidget* w) +ListenersWidget::ListenersWidget(AdaptixWidget* w) : adaptixWidget(w) { - this->mainWidget = w; - this->createUI(); - connect( tableWidget, &QTableWidget::customContextMenuRequested, this, &ListenersWidget::handleListenersMenu ); + connect(tableWidget, &QTableWidget::customContextMenuRequested, this, &ListenersWidget::handleListenersMenu); } ListenersWidget::~ListenersWidget() = default; @@ -49,10 +49,6 @@ void ListenersWidget::createUI() void ListenersWidget::Clear() const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - adaptixWidget->Listeners.clear(); for (int index = tableWidget->rowCount(); index > 0; index-- ) tableWidget->removeRow(index -1 ); @@ -60,17 +56,13 @@ void ListenersWidget::Clear() const void ListenersWidget::AddListenerItem(const ListenerData &newListener ) const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - for( auto listener : adaptixWidget->Listeners ) { if( listener.ListenerName == newListener.ListenerName ) return; } auto item_Name = new QTableWidgetItem( newListener.ListenerName ); - auto item_Type = new QTableWidgetItem( newListener.ListenerType ); + auto item_Type = new QTableWidgetItem( newListener.ListenerFullName ); auto item_BindHost = new QTableWidgetItem( newListener.BindHost ); auto item_BindPort = new QTableWidgetItem( newListener.BindPort ); auto item_AgentHost = new QTableWidgetItem( newListener.AgentAddresses ); @@ -117,7 +109,6 @@ void ListenersWidget::AddListenerItem(const ListenerData &newListener ) const tableWidget->horizontalHeader()->setSectionResizeMode( 3, QHeaderView::ResizeToContents ); tableWidget->horizontalHeader()->setSectionResizeMode( 5, QHeaderView::ResizeToContents ); - // tableWidget->setItemDelegate(new PaddingDelegate(tableWidget)); tableWidget->verticalHeader()->setSectionResizeMode(tableWidget->rowCount() - 1, QHeaderView::ResizeToContents); adaptixWidget->Listeners.push_back(newListener); @@ -125,10 +116,6 @@ void ListenersWidget::AddListenerItem(const ListenerData &newListener ) const void ListenersWidget::EditListenerItem(const ListenerData &newListener) const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - for ( int i = 0; i < adaptixWidget->Listeners.size(); i++ ) { if( adaptixWidget->Listeners[i].ListenerName == newListener.ListenerName ) { adaptixWidget->Listeners[i].BindHost = newListener.BindHost; @@ -159,10 +146,6 @@ void ListenersWidget::EditListenerItem(const ListenerData &newListener) const void ListenersWidget::RemoveListenerItem(const QString &listenerName) const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - for ( int i = 0; i < adaptixWidget->Listeners.size(); i++ ) { if( adaptixWidget->Listeners[i].ListenerName == listenerName ) { adaptixWidget->Listeners.erase( adaptixWidget->Listeners.begin() + i ); @@ -185,78 +168,173 @@ void ListenersWidget::handleListenersMenu(const QPoint &pos ) const { QMenu listenerMenu = QMenu(); - listenerMenu.addAction("Create", this, &ListenersWidget::createListener ); - listenerMenu.addAction("Edit", this, &ListenersWidget::editListener ); - listenerMenu.addAction("Remove", this, &ListenersWidget::removeListener ); + listenerMenu.addAction("Create", this, &ListenersWidget::onCreateListener ); + listenerMenu.addAction("Edit", this, &ListenersWidget::onEditListener ); + listenerMenu.addAction("Remove", this, &ListenersWidget::onRemoveListener ); listenerMenu.addSeparator(); - listenerMenu.addAction("Generate agent", this, &ListenersWidget::generateAgent ); + listenerMenu.addAction("Generate agent", this, &ListenersWidget::onGenerateAgent ); - QPoint globalPos = tableWidget->mapToGlobal( pos ); - listenerMenu.exec(globalPos ); + QPoint globalPos = tableWidget->mapToGlobal(pos); + listenerMenu.exec(globalPos); } -void ListenersWidget::createListener() const +void ListenersWidget::onCreateListener() const { - auto adaptixWidget = qobject_cast( mainWidget ); - if ( !adaptixWidget ) - return; + QStringList listeners; + QMap widgets; + QMap containers; - for( auto regLst : adaptixWidget->RegisterListeners ) { - regLst->BuildWidget(false); + auto listenersList = adaptixWidget->ScriptManager->ListenerScriptList(); + + for (auto listener : listenersList) { + auto engine = adaptixWidget->ScriptManager->ListenerScriptEngine(listener); + if (engine == nullptr) { + adaptixWidget->ScriptManager->consolePrintError(QString("Listener %1 is not registered").arg(listener)); + continue; + } + + QJSValue func = engine->globalObject().property("ListenerUI"); + if (!func.isCallable()) { + adaptixWidget->ScriptManager->consolePrintError(listener + " - function ListenerUI is not registered"); + continue; + } + + QJSValueList args; + args << QJSValue(true); + QJSValue result = func.call(args); + if (result.isError()) { + QString error = QStringLiteral("%1\n at line %2 in %3\n stack: %4").arg(result.toString()).arg(result.property("lineNumber").toInt()).arg(listener).arg(result.property("stack").toString()); + adaptixWidget->ScriptManager->consolePrintError(error); + continue; + } + + if (!result.isObject()) { + adaptixWidget->ScriptManager->consolePrintError(listener + " - function ListenerUI must return panel and container objects"); + continue; + } + + QJSValue ui_container = result.property("ui_container"); + QJSValue ui_panel = result.property("ui_panel"); + if ( ui_container.isUndefined() || !ui_container.isObject() || ui_panel.isUndefined() || !ui_panel.isQObject()) { + adaptixWidget->ScriptManager->consolePrintError(listener + " - function ListenerUI must return panel and container objects"); + + continue; + } + + QObject* objPanel = ui_panel.toQObject(); + auto* formElement = dynamic_cast(objPanel); + if (!formElement) { + adaptixWidget->ScriptManager->consolePrintError(listener + " - function ListenerUI must return panel and container objects"); + continue; + } + + QObject* objContainer = ui_container.toQObject(); + auto* container = dynamic_cast(objContainer); + if (!container) { + adaptixWidget->ScriptManager->consolePrintError(listener + " - function ListenerUI must return panel and container objects"); + continue; + } + + listeners.append(listener); + widgets[listener] = formElement->widget(); + containers[listener] = container; } - DialogListener dialogListener; - dialogListener.AddExListeners( adaptixWidget->RegisterListeners ); - dialogListener.SetProfile( *(adaptixWidget->GetProfile()) ); - dialogListener.Start(); - - for( auto regLst : adaptixWidget->RegisterListeners ) { - regLst->ClearWidget(); - } + DialogListener* dialogListener = new DialogListener(); + dialogListener->setAttribute(Qt::WA_DeleteOnClose); + dialogListener->SetProfile( *(adaptixWidget->GetProfile()) ); + dialogListener->AddExListeners(listeners, widgets, containers); + dialogListener->Start(); } -void ListenersWidget::editListener() const +void ListenersWidget::onEditListener() const { if (tableWidget->selectionModel()->selectedRows().empty()) return; auto listenerName = tableWidget->item( tableWidget->currentRow(), 0 )->text(); auto listenerType = tableWidget->item( tableWidget->currentRow(), 1 )->text(); - auto adaptixWidget = qobject_cast( mainWidget ); - if ( !adaptixWidget ) - return; QString listenerData = ""; for (auto listener : adaptixWidget->Listeners) { if(listener.ListenerName == listenerName) { listenerData = listener.Data; + break; } } - QMap tmpRegisterListenersUI; - tmpRegisterListenersUI[listenerType] = adaptixWidget->RegisterListeners[listenerType]; - tmpRegisterListenersUI[listenerType]->BuildWidget(true); - tmpRegisterListenersUI[listenerType]->FillData(listenerData); + QStringList listeners; + QMap widgets; + QMap containers; - DialogListener dialogListener; - dialogListener.SetEditMode(listenerName); - dialogListener.AddExListeners(tmpRegisterListenersUI ); - dialogListener.SetProfile( *(adaptixWidget->GetProfile()) ); - dialogListener.Start(); + auto engine = adaptixWidget->ScriptManager->ListenerScriptEngine(listenerType); + if (engine == nullptr) { + adaptixWidget->ScriptManager->consolePrintError(QString("Listener %1 is not registered").arg(listenerName)); + return;; + } - tmpRegisterListenersUI[listenerType]->ClearWidget(); + QJSValue func = engine->globalObject().property("ListenerUI"); + if (!func.isCallable()) { + adaptixWidget->ScriptManager->consolePrintError(listenerName + " - function ListenerUI is not registered"); + return; + } + + QJSValueList args; + args << QJSValue(false); + QJSValue result = func.call(args); + if (result.isError()) { + QString error = QStringLiteral("%1\n at line %2 in %3\n stack: %4").arg(result.toString()).arg(result.property("lineNumber").toInt()).arg(listenerName).arg(result.property("stack").toString()); + adaptixWidget->ScriptManager->consolePrintError(error); + return; + } + + if (!result.isObject()) { + adaptixWidget->ScriptManager->consolePrintError(listenerName + " - function ListenerUI must return panel and container objects"); + return; + } + + QJSValue ui_container = result.property("ui_container"); + QJSValue ui_panel = result.property("ui_panel"); + if ( ui_container.isUndefined() || !ui_container.isObject() || ui_panel.isUndefined() || !ui_panel.isQObject()) { + adaptixWidget->ScriptManager->consolePrintError(listenerName + " - function ListenerUI must return panel and container objects"); + return; + } + + QObject* objPanel = ui_panel.toQObject(); + auto* formElement = dynamic_cast(objPanel); + if (!formElement) { + adaptixWidget->ScriptManager->consolePrintError(listenerName + " - function ListenerUI must return panel and container objects"); + return; + } + + QObject* objContainer = ui_container.toQObject(); + auto* container = dynamic_cast(objContainer); + if (!container) { + adaptixWidget->ScriptManager->consolePrintError(listenerName + " - function ListenerUI must return panel and container objects"); + return; + } + + listeners.append(listenerType); + widgets[listenerType] = formElement->widget(); + containers[listenerType] = container; + + container->fromJson(listenerData); + + DialogListener* dialogListener = new DialogListener(); + dialogListener->setAttribute(Qt::WA_DeleteOnClose); + dialogListener->SetEditMode(listenerName); + dialogListener->SetProfile( *(adaptixWidget->GetProfile()) ); + dialogListener->AddExListeners(listeners, widgets, containers); + dialogListener->Start(); } -void ListenersWidget::removeListener() const +void ListenersWidget::onRemoveListener() const { if (tableWidget->selectionModel()->selectedRows().empty()) return; auto listenerName = tableWidget->item( tableWidget->currentRow(), 0 )->text(); auto listenerType = tableWidget->item( tableWidget->currentRow(), 1 )->text(); - auto adaptixWidget = qobject_cast( mainWidget ); - if ( !adaptixWidget ) - return; QString message = QString(); bool ok = false; @@ -271,38 +349,81 @@ void ListenersWidget::removeListener() const } } -void ListenersWidget::generateAgent() const +void ListenersWidget::onGenerateAgent() const { if (tableWidget->selectionModel()->selectedRows().empty()) return; auto listenerName = tableWidget->item( tableWidget->currentRow(), 0 )->text(); auto listenerType = tableWidget->item( tableWidget->currentRow(), 1 )->text(); - auto adaptixWidget = qobject_cast( mainWidget ); - if ( !adaptixWidget ) - return; QStringList parts = listenerType.split("/"); if (parts.size() != 3) { return; } QString targetListener = parts[2]; + QList agentNames = adaptixWidget->GetAgentNames(targetListener); - QVector tmpRegisterAgentsUI; - for( auto regAgent : adaptixWidget->RegisterAgents ) { - if (targetListener == regAgent.listenerName) { - tmpRegisterAgentsUI.push_back(regAgent); - if (tmpRegisterAgentsUI.last().builder) - tmpRegisterAgentsUI.last().builder->BuildWidget(false); + QStringList agents; + QMap widgets; + QMap containers; + + for (auto agent : agentNames) { + auto engine = adaptixWidget->ScriptManager->AgentScriptEngine(agent); + if (engine == nullptr) { + adaptixWidget->ScriptManager->consolePrintError(QString("Listener %1 is not registered").arg(agent)); + return;; } + + QJSValue func = engine->globalObject().property("GenerateUI"); + if (!func.isCallable()) { + adaptixWidget->ScriptManager->consolePrintError(listenerName + " - function GenerateUI is not registered"); + return; + } + + QJSValueList args; + args << QJSValue(targetListener); + QJSValue result = func.call(args); + if (result.isError()) { + QString error = QStringLiteral("%1\n at line %2 in %3\n stack: %4").arg(result.toString()).arg(result.property("lineNumber").toInt()).arg(listenerName).arg(result.property("stack").toString()); + adaptixWidget->ScriptManager->consolePrintError(error); + return; + } + + if (!result.isObject()) { + adaptixWidget->ScriptManager->consolePrintError(listenerName + " - function GenerateUI must return panel and container objects"); + return; + } + + QJSValue ui_container = result.property("ui_container"); + QJSValue ui_panel = result.property("ui_panel"); + if ( ui_container.isUndefined() || !ui_container.isObject() || ui_panel.isUndefined() || !ui_panel.isQObject()) { + adaptixWidget->ScriptManager->consolePrintError(listenerName + " - function GenerateUI must return panel and container objects"); + return; + } + + QObject* objPanel = ui_panel.toQObject(); + auto* formElement = dynamic_cast(objPanel); + if (!formElement) { + adaptixWidget->ScriptManager->consolePrintError(listenerName + " - function GenerateUI must return panel and container objects"); + return; + } + + QObject* objContainer = ui_container.toQObject(); + auto* container = dynamic_cast(objContainer); + if (!container) { + adaptixWidget->ScriptManager->consolePrintError(listenerName + " - function GenerateUI must return panel and container objects"); + return; + } + + agents.append(agent); + widgets[agent] = formElement->widget(); + containers[agent] = container; } - DialogAgent dialogAgent(listenerName, listenerType); - dialogAgent.AddExAgents(tmpRegisterAgentsUI); - dialogAgent.SetProfile( *(adaptixWidget->GetProfile()) ); - dialogAgent.Start(); - - for( auto regAgent: tmpRegisterAgentsUI ) { - regAgent.builder->ClearWidget(); - } + DialogAgent* dialogListener = new DialogAgent(listenerName, listenerType); + dialogListener->setAttribute(Qt::WA_DeleteOnClose); + dialogListener->SetProfile( *(adaptixWidget->GetProfile()) ); + dialogListener->AddExAgents(agents, widgets, containers); + dialogListener->Start(); } diff --git a/AdaptixClient/Source/UI/Widgets/LogsWidget.cpp b/AdaptixClient/Source/UI/Widgets/LogsWidget.cpp index ffe9f509..59f57572 100644 --- a/AdaptixClient/Source/UI/Widgets/LogsWidget.cpp +++ b/AdaptixClient/Source/UI/Widgets/LogsWidget.cpp @@ -28,11 +28,6 @@ LogsWidget::~LogsWidget() = default; void LogsWidget::createUI() { - /// Logs -// logsLabel = new QLabel(this); -// logsLabel->setText("Logs"); -// logsLabel->setAlignment(Qt::AlignCenter); - searchWidget = new QWidget(this); searchWidget->setVisible(false); @@ -69,7 +64,6 @@ void LogsWidget::createUI() logsGridLayout = new QGridLayout(this); logsGridLayout->setContentsMargins(1, 1, 1, 1); logsGridLayout->setVerticalSpacing(1); -// logsGridLayout->addWidget(logsLabel, 0, 0, 1, 1); logsGridLayout->addWidget( searchWidget, 0, 0, 1, 1); logsGridLayout->addWidget( logsConsoleTextEdit, 1, 0, 1, 1); @@ -94,12 +88,10 @@ void LogsWidget::createUI() todoWidget->setLayout(todoGridLayout); - /// Main + mainHSplitter = new QSplitter( Qt::Horizontal, this ); mainHSplitter->setHandleWidth(3); mainHSplitter->addWidget(logsWidget); -// mainHSplitter->addWidget(todoWidget); -// mainHSplitter->setSizes(QList({200, 40})); mainGridLayout = new QGridLayout( this ); mainGridLayout->setContentsMargins(0, 0, 0, 0); diff --git a/AdaptixClient/Source/UI/Widgets/ScreenshotsWidget.cpp b/AdaptixClient/Source/UI/Widgets/ScreenshotsWidget.cpp index b2691d28..59057b07 100644 --- a/AdaptixClient/Source/UI/Widgets/ScreenshotsWidget.cpp +++ b/AdaptixClient/Source/UI/Widgets/ScreenshotsWidget.cpp @@ -3,12 +3,7 @@ #include #include -ImageFrame::ImageFrame(QWidget* parent) - : QWidget(parent), - label(new QLabel), - scrollArea(new QScrollArea(this)), - ctrlPressed(false), - scaleFactor(1.0) +ImageFrame::ImageFrame(QWidget* parent) : QWidget(parent), label(new QLabel), scrollArea(new QScrollArea(this)), ctrlPressed(false), scaleFactor(1.0) { setFocusPolicy(Qt::StrongFocus); @@ -205,7 +200,6 @@ void ScreenshotsWidget::AddScreenshotItem(const ScreenData &newScreen) const tableWidget->horizontalHeader()->setSectionResizeMode( 2, QHeaderView::ResizeToContents ); tableWidget->horizontalHeader()->setSectionResizeMode( 3, QHeaderView::ResizeToContents ); - // tableWidget->setItemDelegate(new PaddingDelegate(tableWidget)); tableWidget->verticalHeader()->setSectionResizeMode(tableWidget->rowCount() - 1, QHeaderView::ResizeToContents); adaptixWidget->Screenshots[newScreen.ScreenId] = newScreen; diff --git a/AdaptixClient/Source/UI/Widgets/SessionsTableWidget.cpp b/AdaptixClient/Source/UI/Widgets/SessionsTableWidget.cpp index c6d9a54d..189ad1e1 100644 --- a/AdaptixClient/Source/UI/Widgets/SessionsTableWidget.cpp +++ b/AdaptixClient/Source/UI/Widgets/SessionsTableWidget.cpp @@ -8,15 +8,17 @@ #include #include #include +#include #include #include #include #include #include -SessionsTableWidget::SessionsTableWidget( QWidget* w ) +SessionsTableWidget::SessionsTableWidget( AdaptixWidget* w ) { - this->mainWidget = w; + this->adaptixWidget = w; + this->createUI(); connect( tableWidget, &QTableWidget::doubleClicked, this, &SessionsTableWidget::handleTableDoubleClicked ); @@ -37,6 +39,8 @@ SessionsTableWidget::SessionsTableWidget( QWidget* w ) shortcutSearch = new QShortcut(QKeySequence("Ctrl+F"), tableWidget); shortcutSearch->setContext(Qt::WidgetShortcut); connect(shortcutSearch, &QShortcut::activated, this, &SessionsTableWidget::toggleSearchPanel); + + } SessionsTableWidget::~SessionsTableWidget() = default; @@ -223,14 +227,10 @@ void SessionsTableWidget::addTableItem(const Agent* newAgent) const this->UpdateColumnsWidth(); } -/// PUBLIC + void SessionsTableWidget::AddAgentItem( Agent* newAgent ) const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - if ( adaptixWidget->AgentsMap.contains(newAgent->data.Id) ) return; @@ -245,8 +245,7 @@ void SessionsTableWidget::AddAgentItem( Agent* newAgent ) const void SessionsTableWidget::RemoveAgentItem(const QString &agentId) const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget || !adaptixWidget->AgentsMap.contains(agentId)) + if (!adaptixWidget->AgentsMap.contains(agentId)) return; Agent* agent = adaptixWidget->AgentsMap[agentId]; @@ -275,10 +274,6 @@ void SessionsTableWidget::SetData() const { this->ClearTableContent(); - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - for (int i = 0; i < adaptixWidget->AgentsVector.size(); i++ ) { QString agentId = adaptixWidget->AgentsVector[i]; Agent* agent = adaptixWidget->AgentsMap[agentId]; @@ -315,7 +310,6 @@ void SessionsTableWidget::UpdateColumnsWidth() const tableWidget->setColumnWidth(ColumnDomain, wUser); } - void SessionsTableWidget::ClearTableContent() const { for (int row = tableWidget->rowCount() - 1; row >= 0; row--) { @@ -328,10 +322,6 @@ void SessionsTableWidget::ClearTableContent() const void SessionsTableWidget::Clear() const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - adaptixWidget->AgentsVector.clear(); for (auto agentId : adaptixWidget->AgentsMap.keys()) { @@ -368,17 +358,10 @@ void SessionsTableWidget::handleTableDoubleClicked(const QModelIndex &index) con { QString AgentId = tableWidget->item(index.row(),0)->text(); - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - adaptixWidget->LoadConsoleUI(AgentId); } -void SessionsTableWidget::onFilterUpdate() const -{ - this->SetData(); -} +void SessionsTableWidget::onFilterUpdate() const { this->SetData(); } /// Menu @@ -387,70 +370,29 @@ void SessionsTableWidget::handleSessionsTableMenu(const QPoint &pos) if ( !tableWidget->itemAt(pos) ) return; - bool menuRemoteTerminal = false; - bool menuProcessBrowser = false; - bool menuFileBrowser = false; - bool menuExit = false; - bool menuTunnels = false; - - int selectedCount = 0; - - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - + QStringList agentIds; for( int rowIndex = 0 ; rowIndex < tableWidget->rowCount() ; rowIndex++ ) { if ( tableWidget->item(rowIndex, 0)->isSelected() ) { QString agentId = tableWidget->item( rowIndex, ColumnAgentID )->text(); - if (adaptixWidget->AgentsMap[agentId]) { - auto agent = adaptixWidget->AgentsMap[agentId]; - if (agent) { - menuRemoteTerminal = agent->browsers.RemoteTerminal; - menuFileBrowser = agent->browsers.FileBrowser; - menuProcessBrowser = agent->browsers.ProcessBrowser; - menuTunnels = agent->browsers.SessionsMenuTunnels; - menuExit = agent->browsers.SessionsMenuExit; - } - } - selectedCount++; + agentIds.append(agentId); } } - /// AGENT MENU + auto agentMenu = QMenu("Agent"); agentMenu.addAction("Execute command", this, &SessionsTableWidget::actionExecuteCommand); agentMenu.addAction("Task manager", this, &SessionsTableWidget::actionTasksBrowserOpen); agentMenu.addSeparator(); - if (menuExit) - agentMenu.addAction("Exit", this, &SessionsTableWidget::actionAgentExit); + + int agentCount = adaptixWidget->ScriptManager->AddMenuSession(&agentMenu, "SessionAgent", agentIds); + if (agentCount > 0) + agentMenu.addSeparator(); + agentMenu.addAction("Remove console data", this, &SessionsTableWidget::actionConsoleDelete); agentMenu.addAction("Remove from server", this, &SessionsTableWidget::actionAgentRemove); - /// BROWSER MENU - bool showBrowser = false; - auto browserMenu = QMenu("Browsers"); - if (menuFileBrowser || menuProcessBrowser || menuRemoteTerminal) { - showBrowser = true; - if (menuFileBrowser) - browserMenu.addAction("File Browser", this, &SessionsTableWidget::actionFileBrowserOpen); - if (menuProcessBrowser) - browserMenu.addAction("Process Browser", this, &SessionsTableWidget::actionProcessBrowserOpen); - if (menuRemoteTerminal) - browserMenu.addAction("Remote Terminal", this, &SessionsTableWidget::actionTerminalOpen); - } - - /// ACCESS MENU - - bool showAccess = false; - auto accessMenu = QMenu("Access"); - if (menuTunnels && selectedCount == 1) { - showAccess = true; - accessMenu.addAction("Create Tunnel", this, &SessionsTableWidget::actionCreateTunnel); - } - - /// SESSION MENU auto sessionMenu = QMenu("Session"); sessionMenu.addAction("Mark as Active", this, &SessionsTableWidget::actionMarkActive); @@ -462,16 +404,25 @@ void SessionsTableWidget::handleSessionsTableMenu(const QPoint &pos) sessionMenu.addSeparator(); sessionMenu.addAction( "Hide on client", this, &SessionsTableWidget::actionItemHide); - /// MAIN MENU + auto ctxMenu = QMenu(); ctxMenu.addAction("Console", this, &SessionsTableWidget::actionConsoleOpen); ctxMenu.addSeparator(); ctxMenu.addMenu(&agentMenu); - if (showBrowser) + + auto browserMenu = QMenu("Browsers"); + int browserCount = adaptixWidget->ScriptManager->AddMenuSession(&browserMenu, "SessionBrowser", agentIds); + if (browserCount > 0) ctxMenu.addMenu(&browserMenu); - if (showAccess) + + auto accessMenu = QMenu("Access"); + int accessCount = adaptixWidget->ScriptManager->AddMenuSession(&accessMenu, "SessionAccess", agentIds); + if (accessCount > 0) ctxMenu.addMenu(&accessMenu); + + adaptixWidget->ScriptManager->AddMenuSession(&ctxMenu, "SessionMain", agentIds); + ctxMenu.addSeparator(); ctxMenu.addMenu(&sessionMenu); ctxMenu.addAction("Set tag", this, &SessionsTableWidget::actionItemTag); @@ -482,10 +433,6 @@ void SessionsTableWidget::handleSessionsTableMenu(const QPoint &pos) void SessionsTableWidget::actionConsoleOpen() const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - for( int rowIndex = 0 ; rowIndex < tableWidget->rowCount() ; rowIndex++ ) { if ( tableWidget->item(rowIndex, 0)->isSelected() ) { auto agentId = tableWidget->item( rowIndex, ColumnAgentID )->text(); @@ -496,10 +443,6 @@ void SessionsTableWidget::actionConsoleOpen() const void SessionsTableWidget::actionExecuteCommand() { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - QStringList listId; for( int rowIndex = 0 ; rowIndex < tableWidget->rowCount() ; rowIndex++ ) { if ( tableWidget->item(rowIndex, 0)->isSelected() ) { @@ -524,170 +467,14 @@ void SessionsTableWidget::actionExecuteCommand() void SessionsTableWidget::actionTasksBrowserOpen() const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - QString agentId = tableWidget->item( tableWidget->currentRow(), ColumnAgentID )->text(); adaptixWidget->TasksTab->SetAgentFilter(agentId); adaptixWidget->SetTasksUI(); } -void SessionsTableWidget::actionTerminalOpen() const -{ - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - - for( int rowIndex = 0 ; rowIndex < tableWidget->rowCount() ; rowIndex++ ) { - if ( tableWidget->item(rowIndex, 0)->isSelected() ) { - auto agentId = tableWidget->item( rowIndex, ColumnAgentID )->text(); - adaptixWidget->LoadTerminalUI(agentId); - } - } -} - -void SessionsTableWidget::actionFileBrowserOpen() const -{ - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - - for( int rowIndex = 0 ; rowIndex < tableWidget->rowCount() ; rowIndex++ ) { - if ( tableWidget->item(rowIndex, 0)->isSelected() ) { - auto agentId = tableWidget->item( rowIndex, ColumnAgentID )->text(); - adaptixWidget->LoadFileBrowserUI(agentId); - } - } -} - -void SessionsTableWidget::actionProcessBrowserOpen() const -{ - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - - for( int rowIndex = 0 ; rowIndex < tableWidget->rowCount() ; rowIndex++ ) { - if ( tableWidget->item(rowIndex, 0)->isSelected() ) { - auto agentId = tableWidget->item( rowIndex, ColumnAgentID )->text(); - adaptixWidget->LoadProcessBrowserUI(agentId); - } - } -} - -void SessionsTableWidget::actionCreateTunnel() const -{ - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - - Agent* agent = nullptr; - - for( int rowIndex = 0 ; rowIndex < tableWidget->rowCount() ; rowIndex++ ) { - if ( tableWidget->item(rowIndex, 0)->isSelected() ) { - auto agentId = tableWidget->item( rowIndex, ColumnAgentID )->text(); - if (adaptixWidget->AgentsMap.contains(agentId) && adaptixWidget->AgentsMap[agentId]) { - agent = adaptixWidget->AgentsMap[agentId]; - break; - } - } - } - - if (!agent) - return; - - DialogTunnel dialogTunnel; - dialogTunnel.SetSettings(agent->data.Id, agent->browsers.Socks5, agent->browsers.Socks4, agent->browsers.Lportfwd, agent->browsers.Rportfwd); - - while (true) { - dialogTunnel.StartDialog(); - if (dialogTunnel.IsValid()) - break; - - QString msg = dialogTunnel.GetMessage(); - if (msg.isEmpty()) - return; - - MessageError(msg); - } - - QString tunnelType = dialogTunnel.GetTunnelType(); - QString endpoint = dialogTunnel.GetEndpoint(); - QByteArray tunnelData = dialogTunnel.GetTunnelData(); - - if ( endpoint == "Teamserver" ) { - QString message = ""; - bool ok = false; - bool result = HttpReqTunnelStartServer(tunnelType, tunnelData, *(adaptixWidget->GetProfile()), &message, &ok); - if( !result ) { - MessageError("Server is not responding"); - return; - } - if (!ok) MessageError(message); - } - else { - auto tunnelEndpoint = new TunnelEndpoint(); - bool started = tunnelEndpoint->StartTunnel(adaptixWidget->GetProfile(), tunnelType, tunnelData); - if (started) { - QString message = ""; - bool ok = false; - bool result = HttpReqTunnelStartServer(tunnelType, tunnelData, *(adaptixWidget->GetProfile()), &message, &ok); - if( !result ) { - MessageError("Server is not responding"); - delete tunnelEndpoint; - return; - } - - if ( !ok ) { - MessageError(message); - delete tunnelEndpoint; - return; - } - QString tunnelId = message; - - tunnelEndpoint->SetTunnelId(tunnelId); - adaptixWidget->ClientTunnels[tunnelId] = tunnelEndpoint; - MessageSuccess("Tunnel " + tunnelId + " started"); - } - else { - delete tunnelEndpoint; - } - } -} - -void SessionsTableWidget::actionAgentExit() const -{ - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - - QStringList listId; - for( int rowIndex = 0 ; rowIndex < tableWidget->rowCount() ; rowIndex++ ) { - if ( tableWidget->item(rowIndex, 0)->isSelected() ) { - auto agentId = tableWidget->item( rowIndex, ColumnAgentID )->text(); - listId.append(agentId); - } - } - - if(listId.empty()) - return; - - QString message = QString(); - bool ok = false; - bool result = HttpReqAgentExit(listId, *(adaptixWidget->GetProfile()), &message, &ok); - if( !result ) { - MessageError("Response timeout"); - return; - } -} - void SessionsTableWidget::actionMarkActive() const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - QStringList listId; for( int rowIndex = 0 ; rowIndex < tableWidget->rowCount() ; rowIndex++ ) { if ( tableWidget->item(rowIndex, 0)->isSelected() ) { @@ -710,10 +497,6 @@ void SessionsTableWidget::actionMarkActive() const void SessionsTableWidget::actionMarkInactive() const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - QStringList listId; for( int rowIndex = 0 ; rowIndex < tableWidget->rowCount() ; rowIndex++ ) { if ( tableWidget->item(rowIndex, 0)->isSelected() ) { @@ -736,10 +519,6 @@ void SessionsTableWidget::actionMarkInactive() const void SessionsTableWidget::actionItemColor() const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - QStringList listId; for( int rowIndex = 0 ; rowIndex < tableWidget->rowCount() ; rowIndex++ ) { if ( tableWidget->item(rowIndex, 0)->isSelected() ) { @@ -766,10 +545,6 @@ void SessionsTableWidget::actionItemColor() const void SessionsTableWidget::actionTextColor() const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - QStringList listId; for( int rowIndex = 0 ; rowIndex < tableWidget->rowCount() ; rowIndex++ ) { if ( tableWidget->item(rowIndex, 0)->isSelected() ) { @@ -796,10 +571,6 @@ void SessionsTableWidget::actionTextColor() const void SessionsTableWidget::actionColorReset() const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - QStringList listId; for( int rowIndex = 0 ; rowIndex < tableWidget->rowCount() ; rowIndex++ ) { if ( tableWidget->item(rowIndex, 0)->isSelected() ) { @@ -822,16 +593,11 @@ void SessionsTableWidget::actionColorReset() const void SessionsTableWidget::actionConsoleDelete() { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - QMessageBox::StandardButton reply = QMessageBox::question(this, "Clear Confirmation", "Are you sure you want to delete all agent console data and history from server (tasks will not be deleted from TaskManager)?\n\n" "If you want to temporarily hide the contents of the agent console, do so through the agent console menu.", QMessageBox::Yes | QMessageBox::No, QMessageBox::No); - if (reply != QMessageBox::Yes) return; @@ -860,10 +626,6 @@ void SessionsTableWidget::actionConsoleDelete() void SessionsTableWidget::actionAgentRemove() { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - QMessageBox::StandardButton reply = QMessageBox::question(this, "Delete Confirmation", "Are you sure you want to delete all information about the selected agents from the server?\n\n" "If you want to hide the record, simply choose: 'Item -> Hide on Client'.", @@ -895,10 +657,6 @@ void SessionsTableWidget::actionAgentRemove() void SessionsTableWidget::actionItemTag() const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - QStringList listId; for( int rowIndex = 0 ; rowIndex < tableWidget->rowCount() ; rowIndex++ ) { if ( tableWidget->item(rowIndex, 0)->isSelected() ) { @@ -930,10 +688,6 @@ void SessionsTableWidget::actionItemTag() const void SessionsTableWidget::actionItemHide() const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - for( int rowIndex = 0 ; rowIndex < tableWidget->rowCount() ; rowIndex++ ) { if ( tableWidget->item(rowIndex, 0)->isSelected() ) { @@ -950,10 +704,6 @@ void SessionsTableWidget::actionItemHide() const void SessionsTableWidget::actionItemsShowAll() const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - bool refact = false; for (auto agent : adaptixWidget->AgentsMap) { if (agent->show == false) { diff --git a/AdaptixClient/Source/UI/Widgets/TasksWidget.cpp b/AdaptixClient/Source/UI/Widgets/TasksWidget.cpp index 4b7b723a..a4dfecf1 100644 --- a/AdaptixClient/Source/UI/Widgets/TasksWidget.cpp +++ b/AdaptixClient/Source/UI/Widgets/TasksWidget.cpp @@ -4,12 +4,11 @@ #include #include #include +#include #include -TaskOutputWidget::TaskOutputWidget( ) -{ - this->createUI(); -} + +TaskOutputWidget::TaskOutputWidget() { this->createUI(); } TaskOutputWidget::~TaskOutputWidget() = default; @@ -47,9 +46,13 @@ void TaskOutputWidget::SetConten(const QString &message, const QString &text) co outputTextEdit->setText( TrimmedEnds(text) ); } -TasksWidget::TasksWidget( QWidget* w ) + + + + +TasksWidget::TasksWidget( AdaptixWidget* w ) { - this->mainWidget = w; + this->adaptixWidget = w; this->createUI(); taskOutputConsole = new TaskOutputWidget(); @@ -202,18 +205,13 @@ void TasksWidget::addTableItem(const Task* newTask) const tableWidget->horizontalHeader()->setSectionResizeMode( this->ColumnFinishTime, QHeaderView::ResizeToContents ); tableWidget->horizontalHeader()->setSectionResizeMode( this->ColumnResult, QHeaderView::ResizeToContents ); - // tableWidget->setItemDelegate(new PaddingDelegate(tableWidget)); tableWidget->verticalHeader()->setSectionResizeMode(tableWidget->rowCount() - 1, QHeaderView::ResizeToContents); } -/// PUBLIC + void TasksWidget::AddTaskItem(Task* newTask) const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - if ( adaptixWidget->TasksMap.contains(newTask->data.TaskId) ) return; @@ -231,8 +229,7 @@ void TasksWidget::AddTaskItem(Task* newTask) const void TasksWidget::RemoveTaskItem(const QString &taskId) const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget || !adaptixWidget->TasksMap.contains((taskId))) + if ( !adaptixWidget->TasksMap.contains((taskId))) return; Task* task = adaptixWidget->TasksMap[taskId]; @@ -266,10 +263,6 @@ void TasksWidget::RemoveTaskItem(const QString &taskId) const void TasksWidget::RemoveAgentTasksItem(const QString &agentId) const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - for (auto key : adaptixWidget->TasksMap.keys()) { Task* task = adaptixWidget->TasksMap[key]; if (task->data.AgentId == agentId) { @@ -311,10 +304,6 @@ void TasksWidget::SetData() const this->ClearTableContent(); - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - for (int i = 0; i < adaptixWidget->TasksVector.size(); i++ ) { QString taskId = adaptixWidget->TasksVector[i]; Task* task = adaptixWidget->TasksMap[taskId]; @@ -336,10 +325,6 @@ void TasksWidget::ClearTableContent() const void TasksWidget::Clear() const { - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - adaptixWidget->TasksVector.clear(); for (auto taskId : adaptixWidget->TasksMap.keys()) { @@ -380,23 +365,48 @@ void TasksWidget::toggleSearchPanel() void TasksWidget::handleTasksMenu( const QPoint &pos ) { - if ( ! tableWidget->itemAt(pos) ) + if ( !tableWidget->itemAt(pos) ) return; + bool cancel = false; + bool job_running = false; + bool remove = false; + + QStringList taskIds; + + for( int rowIndex = 0 ; rowIndex < tableWidget->rowCount() ; rowIndex++ ) { + if ( tableWidget->item(rowIndex, 0)->isSelected() ) { + QString result = tableWidget->item(rowIndex, this->ColumnResult)->text(); + QString type = tableWidget->item(rowIndex, this->ColumnTaskType)->text(); + if ( result == "Hosted" ) + cancel = true; + else if (result == "Running" && type == "JOB") + job_running = true; + else + remove = true; + + taskIds.append(tableWidget->item(rowIndex, this->ColumnTaskId)->text()); + } + } + auto ctxMenu = QMenu(); + ctxMenu.addAction("Copy taskID", this, &TasksWidget::actionCopyTaskId); + ctxMenu.addAction("Copy commandLine", this, &TasksWidget::actionCopyCmd); + ctxMenu.addSeparator(); + ctxMenu.addAction("Agent console", this, &TasksWidget::actionOpenConsole); + ctxMenu.addSeparator(); - auto ctxSep1 = new QAction(); - ctxSep1->setSeparator(true); - auto ctxSep2 = new QAction(); - ctxSep2->setSeparator(true); + int taskCount = adaptixWidget->ScriptManager->AddMenuTask(&ctxMenu, "Tasks", taskIds); + int jobCount = 0; + if (job_running) + jobCount = adaptixWidget->ScriptManager->AddMenuTask(&ctxMenu, "TasksJob", taskIds); + if (taskCount + jobCount > 0) + ctxMenu.addSeparator(); - ctxMenu.addAction( "Copy TaskID", this, &TasksWidget::actionCopyTaskId); - ctxMenu.addAction( "Copy CommandLine", this, &TasksWidget::actionCopyCmd); - ctxMenu.addAction(ctxSep1); - ctxMenu.addAction( "Agent Console", this, &TasksWidget::actionOpenConsole); - ctxMenu.addAction(ctxSep2); - ctxMenu.addAction( "Stop Task", this, &TasksWidget::actionStop); - ctxMenu.addAction( "Delete Task", this, &TasksWidget::actionDelete); + if (cancel) + ctxMenu.addAction("Cancel", this, &TasksWidget::actionCancel); + if (remove) + ctxMenu.addAction("Delete task", this, &TasksWidget::actionDelete); ctxMenu.exec(tableWidget->horizontalHeader()->viewport()->mapToGlobal(pos)); } @@ -408,9 +418,7 @@ void TasksWidget::onTableItemSelection(const QModelIndex ¤t, const QModelI return; QString taskId = tableWidget->item(row,this->ColumnTaskId)->text(); - - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget || !adaptixWidget->TasksMap.contains(taskId) ) + if (!adaptixWidget->TasksMap.contains(taskId) ) return; TaskData taskData = adaptixWidget->TasksMap[taskId]->data; @@ -418,10 +426,7 @@ void TasksWidget::onTableItemSelection(const QModelIndex ¤t, const QModelI adaptixWidget->LoadTasksOutput(); } -void TasksWidget::onAgentChange(QString agentId) const -{ - this->SetData(); -} +void TasksWidget::onAgentChange(QString agentId) const { this->SetData(); } void TasksWidget::actionCopyTaskId() const { @@ -444,22 +449,13 @@ void TasksWidget::actionCopyCmd() const void TasksWidget::actionOpenConsole() const { int row = tableWidget->currentRow(); - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - auto agentId = tableWidget->item( row, this->ColumnAgentId )->text(); adaptixWidget->LoadConsoleUI(agentId); } -void TasksWidget::actionStop() const +void TasksWidget::actionCancel() const { QMap agentTasks; - - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - for( int rowIndex = 0 ; rowIndex < tableWidget->rowCount() ; rowIndex++ ) { if ( tableWidget->item(rowIndex, 0)->isSelected() ) { auto agentId = tableWidget->item( rowIndex, this->ColumnAgentId )->text(); @@ -469,17 +465,12 @@ void TasksWidget::actionStop() const } for( QString agentId : agentTasks.keys()) - adaptixWidget->AgentsMap[agentId]->TasksStop(agentTasks[agentId]); + adaptixWidget->AgentsMap[agentId]->TasksCancel(agentTasks[agentId]); } void TasksWidget::actionDelete() const { QMap agentTasks; - - auto adaptixWidget = qobject_cast( mainWidget ); - if (!adaptixWidget) - return; - for( int rowIndex = 0 ; rowIndex < tableWidget->rowCount() ; rowIndex++ ) { if ( tableWidget->item(rowIndex, 0)->isSelected() ) { auto agentId = tableWidget->item( rowIndex, this->ColumnAgentId )->text(); diff --git a/AdaptixClient/Source/UI/Widgets/TerminalWidget.cpp b/AdaptixClient/Source/UI/Widgets/TerminalWidget.cpp index d219e7f9..aa3f2928 100644 --- a/AdaptixClient/Source/UI/Widgets/TerminalWidget.cpp +++ b/AdaptixClient/Source/UI/Widgets/TerminalWidget.cpp @@ -79,12 +79,6 @@ void TerminalWidget::createUI() startButton->setFixedSize(37, 28); startButton->setToolTip("Start terminal"); - // restartButton = new QPushButton( QIcon(":/icons/restart"), "", this ); - // restartButton->setIconSize( QSize( 24,24 )); - // restartButton->setFixedSize(37, 28); - // restartButton->setToolTip("Restart terminal"); - // restartButton->setEnabled(false); - stopButton = new QPushButton( QIcon(":/icons/stop"), "", this ); stopButton->setIconSize( QSize( 24,24 )); stopButton->setFixedSize(37, 28); @@ -117,7 +111,6 @@ void TerminalWidget::createUI() topHBoxLayout->addWidget(programComboBox); topHBoxLayout->addWidget(line_2); topHBoxLayout->addWidget(startButton); - // topHBoxLayout->addWidget(restartButton); topHBoxLayout->addWidget(stopButton); topHBoxLayout->addWidget(line_3); topHBoxLayout->addWidget(statusDescLabel); @@ -148,7 +141,6 @@ void TerminalWidget::setStatus(const QString &text) programInput->setEnabled(programComboBox->currentText() == "Custom program"); programComboBox->setEnabled(true); startButton->setEnabled(true); - // restartButton->setEnabled(false); stopButton->setEnabled(false); } } @@ -225,16 +217,16 @@ void TerminalWidget::handleTerminalMenu(const QPoint &pos) void TerminalWidget::SetKeys() { - // Ctrl+Shift+C: Copy + /* Ctrl+Shift+C: Copy */ QShortcut *copyShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_C), this->termWidget); connect(copyShortcut, &QShortcut::activated, this->termWidget, &QTermWidget::copyClipboard); - // Ctrl+Shift+V: Paste + /* Ctrl+Shift+V: Paste */ QShortcut *pasteShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_V), this->termWidget); connect(pasteShortcut, &QShortcut::activated, this->termWidget, &QTermWidget::pasteClipboard); - // Ctrl+Shift+F: Find + /* Ctrl+Shift+F: Find */ QShortcut *findShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_F), this->termWidget); connect(findShortcut, &QShortcut::activated, this->termWidget, &QTermWidget::toggleShowSearchBar); - // Ctrl+Shift+L: Clear + /* Ctrl+Shift+L: Clear */ QShortcut *clearShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_L), this->termWidget); connect(clearShortcut, &QShortcut::activated, this->termWidget, &QTermWidget::clear); } @@ -244,7 +236,6 @@ void TerminalWidget::onStart() programInput->setEnabled(false); programComboBox->setEnabled(false); startButton->setEnabled(false); - // restartButton->setEnabled(true); stopButton->setEnabled(true); this->setStatus("Waiting..."); @@ -293,15 +284,6 @@ void TerminalWidget::onRestart() void TerminalWidget::onStop() { - // if (terminalWorker && terminalThread) { - // QMetaObject::invokeMethod( terminalWorker, "stop", Qt::QueuedConnection ); - // - // terminalThread->quit(); - // terminalThread->wait(); - // - // terminalWorker = nullptr; - // terminalThread = nullptr; - // } if (!terminalWorker || !terminalThread) return; diff --git a/AdaptixClient/Source/Utils/Convert.cpp b/AdaptixClient/Source/Utils/Convert.cpp index 3e93a5b9..1955ba4a 100644 --- a/AdaptixClient/Source/Utils/Convert.cpp +++ b/AdaptixClient/Source/Utils/Convert.cpp @@ -15,7 +15,6 @@ bool IsValidURI(const QString &uri) return match.hasMatch(); } - QString ValidCommandsFile(const QByteArray &jsonData, bool* result) { QJsonParseError parseError; @@ -222,27 +221,32 @@ QString TextColorHtml(const QString &text, const QString &color) return R"()" + text.toHtmlEscaped() + R"()"; } -// QString TextUnderlineColorHtml(const QString &text, const QString &color) -// { -// if (text.isEmpty()) -// return ""; -// -// if (color.isEmpty()) -// return R"()" + text.toHtmlEscaped() + R"()"; -// -// return R"()" + text.toHtmlEscaped() + R"()"; -// } -// -// QString TextBoltColorHtml(const QString &text, const QString &color ) -// { -// if (text.isEmpty()) -// return ""; -// -// if (color.isEmpty()) -// return R"()" + text.toHtmlEscaped() + R"()"; -// -// return R"()" + text.toHtmlEscaped() + R"()"; -// } + +/* +QString TextUnderlineColorHtml(const QString &text, const QString &color) +{ + if (text.isEmpty()) + return ""; + + if (color.isEmpty()) + return R"()" + text.toHtmlEscaped() + R"()"; + + return R"()" + text.toHtmlEscaped() + R"()"; +} +*/ + +/* +QString TextBoltColorHtml(const QString &text, const QString &color ) +{ + if (text.isEmpty()) + return ""; + + if (color.isEmpty()) + return R"()" + text.toHtmlEscaped() + R"()"; + + return R"()" + text.toHtmlEscaped() + R"()"; +} +*/ QString FormatSecToStr(int seconds) { diff --git a/AdaptixClient/Source/Workers/DownloaderWorker.cpp b/AdaptixClient/Source/Workers/DownloaderWorker.cpp index 8720c474..6cdaf8ea 100644 --- a/AdaptixClient/Source/Workers/DownloaderWorker.cpp +++ b/AdaptixClient/Source/Workers/DownloaderWorker.cpp @@ -4,8 +4,8 @@ DownloaderWorker::DownloaderWorker(const QUrl &url, const QString &otp, const QString &savedPath) { this->savedPath = savedPath; - this->otp = otp; - this->url = QUrl(url); + this->otp = otp; + this->url = QUrl(url); this->cancelled = false; this->error = false; this->lastBytes = 0; @@ -17,10 +17,7 @@ DownloaderWorker::~DownloaderWorker() networkReply->deleteLater(); } -bool DownloaderWorker::IsError() -{ - return error; -} +bool DownloaderWorker::IsError() { return error; } void DownloaderWorker::start() { @@ -31,9 +28,9 @@ void DownloaderWorker::start() this->networkReply = this->networkManager->get(request); - connect(this->networkReply, &QNetworkReply::readyRead, this, &DownloaderWorker::onReadyRead); + connect(this->networkReply, &QNetworkReply::readyRead, this, &DownloaderWorker::onReadyRead); connect(this->networkReply, &QNetworkReply::downloadProgress, this, &DownloaderWorker::onProgress); - connect(this->networkReply, &QNetworkReply::finished, this, &DownloaderWorker::onFinished); + connect(this->networkReply, &QNetworkReply::finished, this, &DownloaderWorker::onFinished); connect(this->networkReply, QOverload::of(&QNetworkReply::errorOccurred), this, &DownloaderWorker::onError); this->savedFile.setFileName(this->savedPath); diff --git a/AdaptixClient/Source/Workers/TerminalWorker.cpp b/AdaptixClient/Source/Workers/TerminalWorker.cpp index 3571459c..271eb670 100644 --- a/AdaptixClient/Source/Workers/TerminalWorker.cpp +++ b/AdaptixClient/Source/Workers/TerminalWorker.cpp @@ -43,16 +43,6 @@ void TerminalWorker::start() void TerminalWorker::stop() { - // if (stopped.exchange(true)) - // return; - // - // if (websocket) { - // websocket->blockSignals(true); - // websocket->close(); - // } - // - // emit finished(); - if (stopped.exchange(true)) return; diff --git a/AdaptixClient/Source/Workers/TunnelWorker.cpp b/AdaptixClient/Source/Workers/TunnelWorker.cpp index 68de4b83..0b2d2d08 100644 --- a/AdaptixClient/Source/Workers/TunnelWorker.cpp +++ b/AdaptixClient/Source/Workers/TunnelWorker.cpp @@ -56,7 +56,6 @@ void TunnelWorker::stop() } if (websocket) { - // websocket->disconnect(this); websocket->blockSignals(true); websocket->close(); } diff --git a/AdaptixServer/core/connector/connector.go b/AdaptixServer/core/connector/connector.go index c271b717..0a7b40cf 100644 --- a/AdaptixServer/core/connector/connector.go +++ b/AdaptixServer/core/connector/connector.go @@ -32,17 +32,17 @@ type Teamserver interface { TsAgentCreate(agentCrc string, agentId string, beat []byte, listenerName string, ExternalIP string, Async bool) error TsAgentProcessData(agentId string, bodyData []byte) error TsAgentGetHostedTasksAll(agentId string, maxDataSize int) ([]byte, error) - TsAgentCommand(agentName string, agentId string, clientName string, cmdline string, args map[string]any) error - TsAgentGenerate(agentName string, config string, operatingSystem string, listenerWM string, listenerProfile []byte) ([]byte, string, error) + TsAgentCommand(agentName string, agentId string, clientName string, hookId string, cmdline string, ui bool, args map[string]any) error + TsAgentGenerate(agentName string, config string, listenerWM string, listenerProfile []byte) ([]byte, string, error) TsAgentUpdateData(newAgentData adaptix.AgentData) error - TsAgentImpersonate(agentId string, impersonated string, elevated bool) error TsAgentTerminate(agentId string, terminateTaskId string) error TsAgentRemove(agentId string) error TsAgentConsoleRemove(agentId string) error TsAgentSetTag(agentId string, tag string) error TsAgentSetMark(agentId string, makr string) error TsAgentSetColor(agentId string, background string, foreground string, reset bool) error + TsAgentSetImpersonate(agentId string, impersonated string, elevated bool) error TsAgentTickUpdate() TsAgentConsoleOutput(agentId string, messageType int, message string, clearText string, store bool) TsAgentConsoleOutputClient(agentId string, client string, messageType int, message string, clearText string) @@ -50,33 +50,26 @@ type Teamserver interface { TsTaskCreate(agentId string, cmdline string, client string, taskData adaptix.TaskData) TsTaskUpdate(agentId string, data adaptix.TaskData) TsTaskGetAvailableAll(agentId string, availableSize int) ([]adaptix.TaskData, error) - TsTaskStop(agentId string, taskId string) error + TsTaskCancel(agentId string, taskId string) error TsTaskDelete(agentId string, taskId string) error + TsTaskPostHook(hookData adaptix.TaskData, jobIndex int) error TsDownloadAdd(agentId string, fileId string, fileName string, fileSize int) error TsDownloadUpdate(fileId string, state int, data []byte) error TsDownloadClose(fileId string, reason int) error - // + TsDownloadSync(fileId string) (string, []byte, error) TsDownloadDelete(fileId string) error TsDownloadGetFilepath(fileId string) (string, error) TsUploadGetFilepath(fileId string) (string, error) TsUploadGetFileContent(fileId string) ([]byte, error) - // - TsDownloadTaskStart(agentId string, path string, username string) error - TsDownloadTaskCancel(fileId string, clientName string) error - TsDownloadTaskResume(fileId string, clientName string) error - TsDownloadTaskPause(fileId string, clientName string) error TsScreenshotDelete(screenId string) error TsScreenshotNote(screenId string, note string) error - TsAgentGuiDisks(agentId string, username string) error - TsAgentGuiProcess(agentId string, username string) error - TsAgentGuiFiles(agentId string, path string, username string) error - TsAgentGuiUpload(agentId string, path string, content []byte, username string) error - - TsAgentGuiExit(agentId string, username string) error + TsCredentilsAdd(username string, password string, realm string, credType string, tag string, storage string, agentId string, host string) error + TsCredentilsEdit(credId string, username string, password string, realm string, credType string, tag string, storage string, host string) error + TsCredentilsDelete(credId string) error TsClientGuiDisks(taskData adaptix.TaskData, jsonDrives string) TsClientGuiFiles(taskData adaptix.TaskData, path string, jsonFiles string) @@ -165,35 +158,32 @@ func NewTsConnector(ts Teamserver, tsProfile profile.TsProfile, tsResponse profi connector.Engine.POST(tsProfile.Endpoint+"/listener/create", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcListenerStart) connector.Engine.POST(tsProfile.Endpoint+"/listener/edit", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcListenerEdit) connector.Engine.POST(tsProfile.Endpoint+"/listener/stop", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcListenerStop) + connector.Engine.POST(tsProfile.Endpoint+"/agent/generate", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcAgentGenerate) + connector.Engine.POST(tsProfile.Endpoint+"/agent/remove", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcAgentRemove) connector.Engine.POST(tsProfile.Endpoint+"/agent/command/file", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcAgentCommandFile) connector.Engine.POST(tsProfile.Endpoint+"/agent/command/execute", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcAgentCommandExecute) connector.Engine.POST(tsProfile.Endpoint+"/agent/console/remove", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcAgentConsoleRemove) - connector.Engine.POST(tsProfile.Endpoint+"/agent/remove", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcAgentRemove) - connector.Engine.POST(tsProfile.Endpoint+"/agent/exit", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcAgentExit) - connector.Engine.POST(tsProfile.Endpoint+"/agent/settag", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcAgentSetTag) - connector.Engine.POST(tsProfile.Endpoint+"/agent/setmark", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcAgentSetMark) - connector.Engine.POST(tsProfile.Endpoint+"/agent/setcolor", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcAgentSetColor) + connector.Engine.POST(tsProfile.Endpoint+"/agent/set/tag", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcAgentSetTag) + connector.Engine.POST(tsProfile.Endpoint+"/agent/set/mark", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcAgentSetMark) + connector.Engine.POST(tsProfile.Endpoint+"/agent/set/color", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcAgentSetColor) + connector.Engine.POST(tsProfile.Endpoint+"/agent/set/impersonate", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcAgentSetImpersonate) - connector.Engine.POST(tsProfile.Endpoint+"/agent/task/stop", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcAgentTaskStop) + connector.Engine.POST(tsProfile.Endpoint+"/agent/task/cancel", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcAgentTaskCancel) connector.Engine.POST(tsProfile.Endpoint+"/agent/task/delete", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcAgentTaskDelete) + connector.Engine.POST(tsProfile.Endpoint+"/agent/task/hook", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcAgentTaskHook) connector.Engine.POST(tsProfile.Endpoint+"/download/sync", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcGuiDownloadSync) connector.Engine.POST(tsProfile.Endpoint+"/download/delete", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcGuiDownloadDelete) - connector.Engine.POST(tsProfile.Endpoint+"/download/start", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcGuiDownloadStart) - connector.Engine.POST(tsProfile.Endpoint+"/download/cancel", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcGuiDownloadCancel) - connector.Engine.POST(tsProfile.Endpoint+"/download/resume", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcGuiDownloadResume) - connector.Engine.POST(tsProfile.Endpoint+"/download/pause", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcGuiDownloadPause) - - connector.Engine.POST(tsProfile.Endpoint+"/browser/disks", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcGuiDisks) - connector.Engine.POST(tsProfile.Endpoint+"/browser/files", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcGuiFiles) - connector.Engine.POST(tsProfile.Endpoint+"/browser/upload", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcGuiUpload) - connector.Engine.POST(tsProfile.Endpoint+"/browser/process", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcGuiProcess) connector.Engine.POST(tsProfile.Endpoint+"/screen/setnote", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcScreenshotSetNote) connector.Engine.POST(tsProfile.Endpoint+"/screen/remove", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcScreenshotRemove) + connector.Engine.POST(tsProfile.Endpoint+"/creds/add", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcCredentialsAdd) + connector.Engine.POST(tsProfile.Endpoint+"/creds/edit", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcCredentialsEdit) + connector.Engine.POST(tsProfile.Endpoint+"/creds/remove", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcCredentialsRemove) + connector.Engine.POST(tsProfile.Endpoint+"/tunnel/start/socks5", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcTunnelStartSocks5) connector.Engine.POST(tsProfile.Endpoint+"/tunnel/start/socks4", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcTunnelStartSocks4) connector.Engine.POST(tsProfile.Endpoint+"/tunnel/start/lportfwd", token.ValidateAccessToken(), default404Middleware(tsResponse), connector.TcTunnelStartLpf) diff --git a/AdaptixServer/core/connector/tc_agents.go b/AdaptixServer/core/connector/tc_agents.go index 6d1f598b..491fdb93 100644 --- a/AdaptixServer/core/connector/tc_agents.go +++ b/AdaptixServer/core/connector/tc_agents.go @@ -1,10 +1,12 @@ package connector import ( + "AdaptixServer/core/utils/logs" "encoding/base64" "encoding/json" "errors" "fmt" + adaptix "github.com/Adaptix-Framework/axc2" "github.com/gin-gonic/gin" "net/http" ) @@ -13,7 +15,6 @@ type AgentConfig struct { ListenerName string `json:"listener_name"` ListenerType string `json:"listener_type"` AgentName string `json:"agent"` - Os string `json:"operating_system"` Config string `json:"config"` } @@ -39,7 +40,7 @@ func (tc *TsConnector) TcAgentGenerate(ctx *gin.Context) { return } - fileContent, fileName, err = tc.teamserver.TsAgentGenerate(agentConfig.AgentName, agentConfig.Config, agentConfig.Os, listenerWM, listenerProfile) + fileContent, fileName, err = tc.teamserver.TsAgentGenerate(agentConfig.AgentName, agentConfig.Config, listenerWM, listenerProfile) if err != nil { ctx.JSON(http.StatusOK, gin.H{"message": err.Error(), "ok": false}) return @@ -53,8 +54,10 @@ func (tc *TsConnector) TcAgentGenerate(ctx *gin.Context) { type CommandData struct { AgentName string `json:"name"` AgentId string `json:"id"` + UI bool `json:"ui"` CmdLine string `json:"cmdline"` Data string `json:"data"` + HookId string `json:"ax_hook_id"` } func (tc *TsConnector) TcAgentCommandExecute(ctx *gin.Context) { @@ -68,7 +71,7 @@ func (tc *TsConnector) TcAgentCommandExecute(ctx *gin.Context) { err = ctx.ShouldBindJSON(&commandData) if err != nil { - ctx.JSON(http.StatusOK, gin.H{"message": "invalid command data", "ok": false}) + ctx.JSON(http.StatusOK, gin.H{"message": "invalid JSON data", "ok": false}) return } @@ -86,10 +89,10 @@ func (tc *TsConnector) TcAgentCommandExecute(ctx *gin.Context) { err = json.Unmarshal([]byte(commandData.Data), &args) if err != nil { - fmt.Printf("Error parsing commands JSON: %s\n", err.Error()) + logs.Debug("", "Error parsing commands JSON: %s\n", err.Error()) } - err = tc.teamserver.TsAgentCommand(commandData.AgentName, commandData.AgentId, username, commandData.CmdLine, args) + err = tc.teamserver.TsAgentCommand(commandData.AgentName, commandData.AgentId, username, commandData.HookId, commandData.CmdLine, commandData.UI, args) if err != nil { ctx.JSON(http.StatusOK, gin.H{"message": err.Error(), "ok": false}) return @@ -114,7 +117,7 @@ func (tc *TsConnector) TcAgentCommandFile(ctx *gin.Context) { err = ctx.ShouldBindJSON(&commandData2) if err != nil { - ctx.JSON(http.StatusOK, gin.H{"message": "invalid command data", "ok": false}) + ctx.JSON(http.StatusOK, gin.H{"message": "invalid JSON data", "ok": false}) return } @@ -143,10 +146,10 @@ func (tc *TsConnector) TcAgentCommandFile(ctx *gin.Context) { err = json.Unmarshal([]byte(commandData.Data), &args) if err != nil { - fmt.Printf("Error parsing commands JSON: %s\n", err.Error()) + logs.Debug("", "Error parsing commands JSON: %s\n", err.Error()) } - err = tc.teamserver.TsAgentCommand(commandData.AgentName, commandData.AgentId, username, commandData.CmdLine, args) + err = tc.teamserver.TsAgentCommand(commandData.AgentName, commandData.AgentId, username, commandData.HookId, commandData.CmdLine, commandData.UI, args) if err != nil { ctx.JSON(http.StatusOK, gin.H{"message": err.Error(), "ok": false}) return @@ -155,57 +158,6 @@ func (tc *TsConnector) TcAgentCommandFile(ctx *gin.Context) { ctx.JSON(http.StatusOK, gin.H{"message": "", "ok": true}) } -type AgentExit struct { - AgentIdArray []string `json:"agent_id_array"` -} - -func (tc *TsConnector) TcAgentExit(ctx *gin.Context) { - var ( - agentExit AgentExit - err error - username string - ok bool - ) - - err = ctx.ShouldBindJSON(&agentExit) - if err != nil { - ctx.JSON(http.StatusOK, gin.H{"message": "invalid command data", "ok": false}) - return - } - - value, exists := ctx.Get("username") - if !exists { - ctx.JSON(http.StatusOK, gin.H{"message": "Server error: username not found in context", "ok": false}) - return - } - - username, ok = value.(string) - if !ok { - ctx.JSON(http.StatusOK, gin.H{"message": "Server error: invalid username type in context", "ok": false}) - return - } - - var errorsSlice []string - for _, agentId := range agentExit.AgentIdArray { - err = tc.teamserver.TsAgentGuiExit(agentId, username) - if err != nil { - errorsSlice = append(errorsSlice, err.Error()) - } - } - - if len(errorsSlice) > 0 { - message := "" - for i, errorMessage := range errorsSlice { - message += fmt.Sprintf("%d. %s\n", i+1, errorMessage) - } - - ctx.JSON(http.StatusOK, gin.H{"message": message, "ok": false}) - return - } - - ctx.JSON(http.StatusOK, gin.H{"message": "", "ok": true}) -} - type AgentRemove struct { AgentIdArray []string `json:"agent_id_array"` } @@ -218,7 +170,7 @@ func (tc *TsConnector) TcAgentConsoleRemove(ctx *gin.Context) { err = ctx.ShouldBindJSON(&agentRemove) if err != nil { - ctx.JSON(http.StatusOK, gin.H{"message": "invalid command data", "ok": false}) + ctx.JSON(http.StatusOK, gin.H{"message": "invalid JSON data", "ok": false}) return } @@ -251,7 +203,7 @@ func (tc *TsConnector) TcAgentRemove(ctx *gin.Context) { err = ctx.ShouldBindJSON(&agentRemove) if err != nil { - ctx.JSON(http.StatusOK, gin.H{"message": "invalid command data", "ok": false}) + ctx.JSON(http.StatusOK, gin.H{"message": "invalid JSON data", "ok": false}) return } @@ -276,6 +228,8 @@ func (tc *TsConnector) TcAgentRemove(ctx *gin.Context) { ctx.JSON(http.StatusOK, gin.H{"message": "", "ok": true}) } +/// Setters + type AgentTag struct { AgentIdArray []string `json:"agent_id_array"` Tag string `json:"tag"` @@ -289,7 +243,7 @@ func (tc *TsConnector) TcAgentSetTag(ctx *gin.Context) { err = ctx.ShouldBindJSON(&agentTag) if err != nil { - ctx.JSON(http.StatusOK, gin.H{"message": "invalid command data", "ok": false}) + ctx.JSON(http.StatusOK, gin.H{"message": "invalid JSON data", "ok": false}) return } @@ -327,7 +281,7 @@ func (tc *TsConnector) TcAgentSetMark(ctx *gin.Context) { err = ctx.ShouldBindJSON(&agentMark) if err != nil { - ctx.JSON(http.StatusOK, gin.H{"message": "invalid command data", "ok": false}) + ctx.JSON(http.StatusOK, gin.H{"message": "invalid JSON data", "ok": false}) return } @@ -367,7 +321,7 @@ func (tc *TsConnector) TcAgentSetColor(ctx *gin.Context) { err = ctx.ShouldBindJSON(&agentColor) if err != nil { - ctx.JSON(http.StatusOK, gin.H{"message": "invalid command data", "ok": false}) + ctx.JSON(http.StatusOK, gin.H{"message": "invalid JSON data", "ok": false}) return } @@ -392,12 +346,37 @@ func (tc *TsConnector) TcAgentSetColor(ctx *gin.Context) { ctx.JSON(http.StatusOK, gin.H{"message": "", "ok": true}) } +type AgentImpersonate struct { + AgentId string `json:"agent_id"` + Impersonate string `json:"impersonate"` + Elevated bool `json:"elevated"` +} + +func (tc *TsConnector) TcAgentSetImpersonate(ctx *gin.Context) { + var ( + agentImpersonate AgentImpersonate + err error + ) + + err = ctx.ShouldBindJSON(&agentImpersonate) + if err != nil { + ctx.JSON(http.StatusOK, gin.H{"message": "invalid JSON data", "ok": false}) + return + } + + _ = tc.teamserver.TsAgentSetImpersonate(agentImpersonate.AgentId, agentImpersonate.Impersonate, agentImpersonate.Elevated) + + ctx.JSON(http.StatusOK, gin.H{"message": "", "ok": true}) +} + +/// Tasks + type AgentTaskDelete struct { AgentId string `json:"agent_id"` TasksId []string `json:"tasks_array"` } -func (tc *TsConnector) TcAgentTaskStop(ctx *gin.Context) { +func (tc *TsConnector) TcAgentTaskCancel(ctx *gin.Context) { var ( agentTasks AgentTaskDelete err error @@ -405,13 +384,13 @@ func (tc *TsConnector) TcAgentTaskStop(ctx *gin.Context) { err = ctx.ShouldBindJSON(&agentTasks) if err != nil { - ctx.JSON(http.StatusOK, gin.H{"message": "invalid command data", "ok": false}) + ctx.JSON(http.StatusOK, gin.H{"message": "invalid JSON data", "ok": false}) return } var errorsSlice []string for _, taskId := range agentTasks.TasksId { - err = tc.teamserver.TsTaskStop(agentTasks.AgentId, taskId) + err = tc.teamserver.TsTaskCancel(agentTasks.AgentId, taskId) if err != nil { errorsSlice = append(errorsSlice, err.Error()) } @@ -438,7 +417,7 @@ func (tc *TsConnector) TcAgentTaskDelete(ctx *gin.Context) { err = ctx.ShouldBindJSON(&agentTasks) if err != nil { - ctx.JSON(http.StatusOK, gin.H{"message": "invalid command data", "ok": false}) + ctx.JSON(http.StatusOK, gin.H{"message": "invalid JSON data", "ok": false}) return } @@ -462,3 +441,60 @@ func (tc *TsConnector) TcAgentTaskDelete(ctx *gin.Context) { ctx.JSON(http.StatusOK, gin.H{"message": "", "ok": true}) } + +type AgentTaskHook struct { + AgentId string `json:"a_id"` + TaskId string `json:"a_task_id"` + HookId string `json:"a_hook_id"` + JobIndex int `json:"a_job_index"` + MessageType int `json:"a_msg_type"` + Message string `json:"a_message"` + Text string `json:"a_text"` + Completed bool `json:"a_completed"` +} + +func (tc *TsConnector) TcAgentTaskHook(ctx *gin.Context) { + var ( + username string + tasksHook AgentTaskHook + err error + ok bool + ) + + err = ctx.ShouldBindJSON(&tasksHook) + if err != nil { + ctx.JSON(http.StatusOK, gin.H{"message": "invalid JSON data", "ok": false}) + return + } + + value, exists := ctx.Get("username") + if !exists { + ctx.JSON(http.StatusOK, gin.H{"message": "Server error: username not found in context", "ok": false}) + return + } + + username, ok = value.(string) + if !ok { + ctx.JSON(http.StatusOK, gin.H{"message": "Server error: invalid username type in context", "ok": false}) + return + } + + hookData := adaptix.TaskData{ + AgentId: tasksHook.AgentId, + TaskId: tasksHook.TaskId, + HookId: tasksHook.HookId, + Client: username, + MessageType: tasksHook.MessageType, + Message: tasksHook.Message, + ClearText: tasksHook.Text, + Completed: tasksHook.Completed, + } + + err = tc.teamserver.TsTaskPostHook(hookData, tasksHook.JobIndex) + if err != nil { + ctx.JSON(http.StatusOK, gin.H{"message": err.Error(), "ok": false}) + return + } + + ctx.JSON(http.StatusOK, gin.H{"message": "", "ok": true}) +} diff --git a/AdaptixServer/core/connector/tc_browsers.go b/AdaptixServer/core/connector/tc_browsers.go deleted file mode 100644 index c8613906..00000000 --- a/AdaptixServer/core/connector/tc_browsers.go +++ /dev/null @@ -1,176 +0,0 @@ -package connector - -import ( - "errors" - "github.com/gin-gonic/gin" - "net/http" -) - -/// FileBrowser - -type DisksAction struct { - AgentId string `json:"agent_id"` -} - -func (tc *TsConnector) TcGuiDisks(ctx *gin.Context) { - var ( - disksAction DisksAction - username string - ok bool - err error - answer gin.H - ) - - err = ctx.ShouldBindJSON(&disksAction) - if err != nil { - _ = ctx.Error(errors.New("invalid action")) - return - } - - value, exists := ctx.Get("username") - if !exists { - ctx.JSON(http.StatusOK, gin.H{"message": "Server error: username not found in context", "ok": false}) - return - } - - username, ok = value.(string) - if !ok { - ctx.JSON(http.StatusOK, gin.H{"message": "Server error: invalid username type in context", "ok": false}) - return - } - - err = tc.teamserver.TsAgentGuiDisks(disksAction.AgentId, username) - if err != nil { - answer = gin.H{"message": err.Error(), "ok": false} - } else { - answer = gin.H{"message": "Wait...", "ok": true} - } - ctx.JSON(http.StatusOK, answer) -} - -type FilesAction struct { - AgentId string `json:"agent_id"` - Path string `json:"path"` -} - -func (tc *TsConnector) TcGuiFiles(ctx *gin.Context) { - var ( - filesAction FilesAction - username string - ok bool - err error - answer gin.H - ) - - err = ctx.ShouldBindJSON(&filesAction) - if err != nil { - _ = ctx.Error(errors.New("invalid action")) - return - } - - value, exists := ctx.Get("username") - if !exists { - ctx.JSON(http.StatusOK, gin.H{"message": "Server error: username not found in context", "ok": false}) - return - } - - username, ok = value.(string) - if !ok { - ctx.JSON(http.StatusOK, gin.H{"message": "Server error: invalid username type in context", "ok": false}) - return - } - - err = tc.teamserver.TsAgentGuiFiles(filesAction.AgentId, filesAction.Path, username) - if err != nil { - answer = gin.H{"message": err.Error(), "ok": false} - } else { - answer = gin.H{"message": "Wait...", "ok": true} - } - ctx.JSON(http.StatusOK, answer) -} - -type UploadAction struct { - AgentId string `json:"agent_id"` - RemotePath string `json:"remote_path"` - Content []byte `json:"content"` -} - -func (tc *TsConnector) TcGuiUpload(ctx *gin.Context) { - var ( - uploadAction UploadAction - username string - ok bool - err error - answer gin.H - ) - - err = ctx.ShouldBindJSON(&uploadAction) - if err != nil { - _ = ctx.Error(errors.New("invalid action")) - return - } - - value, exists := ctx.Get("username") - if !exists { - ctx.JSON(http.StatusOK, gin.H{"message": "Server error: username not found in context", "ok": false}) - return - } - - username, ok = value.(string) - if !ok { - ctx.JSON(http.StatusOK, gin.H{"message": "Server error: invalid username type in context", "ok": false}) - return - } - - err = tc.teamserver.TsAgentGuiUpload(uploadAction.AgentId, uploadAction.RemotePath, uploadAction.Content, username) - if err != nil { - answer = gin.H{"message": err.Error(), "ok": false} - } else { - answer = gin.H{"message": "Uploading...", "ok": true} - } - ctx.JSON(http.StatusOK, answer) -} - -/// ProcessBrowser - -type ProcessAction struct { - AgentId string `json:"agent_id"` -} - -func (tc *TsConnector) TcGuiProcess(ctx *gin.Context) { - var ( - processAction ProcessAction - username string - ok bool - err error - answer gin.H - ) - - err = ctx.ShouldBindJSON(&processAction) - if err != nil { - _ = ctx.Error(errors.New("invalid action")) - return - } - - value, exists := ctx.Get("username") - if !exists { - ctx.JSON(http.StatusOK, gin.H{"message": "Server error: username not found in context", "ok": false}) - return - } - - username, ok = value.(string) - if !ok { - ctx.JSON(http.StatusOK, gin.H{"message": "Server error: invalid username type in context", "ok": false}) - return - } - - err = tc.teamserver.TsAgentGuiProcess(processAction.AgentId, username) - if err != nil { - answer = gin.H{"message": err.Error(), "ok": false} - } else { - answer = gin.H{"message": "Wait...", "ok": true} - } - ctx.JSON(http.StatusOK, answer) -} - -/// Terminal diff --git a/AdaptixServer/core/connector/tc_creds.go b/AdaptixServer/core/connector/tc_creds.go new file mode 100644 index 00000000..f028d49c --- /dev/null +++ b/AdaptixServer/core/connector/tc_creds.go @@ -0,0 +1,82 @@ +package connector + +import ( + "github.com/gin-gonic/gin" + "net/http" +) + +type CredsAdd struct { + Username string `json:"username"` + Password string `json:"password"` + Realm string `json:"realm"` + Type string `json:"type"` + Tag string `json:"tag"` + Storage string `json:"storage"` + Host string `json:"host"` +} + +func (tc *TsConnector) TcCredentialsAdd(ctx *gin.Context) { + var credsAdd CredsAdd + err := ctx.ShouldBindJSON(&credsAdd) + if err != nil { + ctx.JSON(http.StatusOK, gin.H{"message": "invalid JSON data", "ok": false}) + return + } + + err = tc.teamserver.TsCredentilsAdd(credsAdd.Username, credsAdd.Password, credsAdd.Realm, credsAdd.Type, credsAdd.Tag, credsAdd.Storage, "", credsAdd.Host) + if err != nil { + ctx.JSON(http.StatusOK, gin.H{"message": err.Error(), "ok": false}) + return + } + + ctx.JSON(http.StatusOK, gin.H{"message": "", "ok": true}) +} + +type CredsEdit struct { + CredId string `json:"cred_id"` + Username string `json:"username"` + Password string `json:"password"` + Realm string `json:"realm"` + Type string `json:"type"` + Tag string `json:"tag"` + Storage string `json:"storage"` + Host string `json:"host"` +} + +func (tc *TsConnector) TcCredentialsEdit(ctx *gin.Context) { + var credsEdit CredsEdit + err := ctx.ShouldBindJSON(&credsEdit) + if err != nil { + ctx.JSON(http.StatusOK, gin.H{"message": "invalid JSON data", "ok": false}) + return + } + + err = tc.teamserver.TsCredentilsEdit(credsEdit.CredId, credsEdit.Username, credsEdit.Password, credsEdit.Realm, credsEdit.Type, credsEdit.Tag, credsEdit.Storage, credsEdit.Host) + if err != nil { + ctx.JSON(http.StatusOK, gin.H{"message": err.Error(), "ok": false}) + return + } + + ctx.JSON(http.StatusOK, gin.H{"message": "", "ok": true}) +} + +type CredsRemove struct { + CredId string `json:"cred_id"` +} + +func (tc *TsConnector) TcCredentialsRemove(ctx *gin.Context) { + var credsRemove CredsRemove + err := ctx.ShouldBindJSON(&credsRemove) + if err != nil { + ctx.JSON(http.StatusOK, gin.H{"message": "invalid JSON data", "ok": false}) + return + } + + err = tc.teamserver.TsCredentilsDelete(credsRemove.CredId) + if err != nil { + ctx.JSON(http.StatusOK, gin.H{"message": err.Error(), "ok": false}) + return + } + + ctx.JSON(http.StatusOK, gin.H{"message": "", "ok": true}) +} diff --git a/AdaptixServer/core/connector/tc_downloads.go b/AdaptixServer/core/connector/tc_downloads.go index c4214b9b..b5e87396 100644 --- a/AdaptixServer/core/connector/tc_downloads.go +++ b/AdaptixServer/core/connector/tc_downloads.go @@ -60,155 +60,3 @@ func (tc *TsConnector) TcGuiDownloadDelete(ctx *gin.Context) { ctx.JSON(http.StatusOK, answer) } - -type DownloadStartAction struct { - AgentId string `json:"agent_id"` - Path string `json:"path"` -} - -func (tc *TsConnector) TcGuiDownloadStart(ctx *gin.Context) { - var ( - downloadAction DownloadStartAction - username string - ok bool - err error - answer gin.H - ) - - err = ctx.ShouldBindJSON(&downloadAction) - if err != nil { - _ = ctx.Error(errors.New("invalid action")) - return - } - - value, exists := ctx.Get("username") - if !exists { - ctx.JSON(http.StatusOK, gin.H{"message": "Server error: username not found in context", "ok": false}) - return - } - - username, ok = value.(string) - if !ok { - ctx.JSON(http.StatusOK, gin.H{"message": "Server error: invalid username type in context", "ok": false}) - return - } - - err = tc.teamserver.TsDownloadTaskStart(downloadAction.AgentId, downloadAction.Path, username) - if err != nil { - answer = gin.H{"message": err.Error(), "ok": false} - } else { - answer = gin.H{"message": "Downloading...", "ok": true} - } - ctx.JSON(http.StatusOK, answer) -} - -func (tc *TsConnector) TcGuiDownloadCancel(ctx *gin.Context) { - var ( - downloadFid DownloadFileId - answer gin.H - username string - ok bool - err error - ) - - err = ctx.ShouldBindJSON(&downloadFid) - if err != nil { - _ = ctx.Error(errors.New("invalid action")) - return - } - - value, exists := ctx.Get("username") - if !exists { - ctx.JSON(http.StatusOK, gin.H{"message": "Server error: username not found in context", "ok": false}) - return - } - - username, ok = value.(string) - if !ok { - ctx.JSON(http.StatusOK, gin.H{"message": "Server error: invalid username type in context", "ok": false}) - return - } - - err = tc.teamserver.TsDownloadTaskCancel(downloadFid.File, username) - if err != nil { - answer = gin.H{"message": err.Error(), "ok": false} - } else { - answer = gin.H{"message": "file cancel", "ok": true} - } - - ctx.JSON(http.StatusOK, answer) -} - -func (tc *TsConnector) TcGuiDownloadResume(ctx *gin.Context) { - var ( - downloadFid DownloadFileId - answer gin.H - username string - ok bool - err error - ) - - err = ctx.ShouldBindJSON(&downloadFid) - if err != nil { - _ = ctx.Error(errors.New("invalid action")) - return - } - - value, exists := ctx.Get("username") - if !exists { - ctx.JSON(http.StatusOK, gin.H{"message": "Server error: username not found in context", "ok": false}) - return - } - - username, ok = value.(string) - if !ok { - ctx.JSON(http.StatusOK, gin.H{"message": "Server error: invalid username type in context", "ok": false}) - return - } - - err = tc.teamserver.TsDownloadTaskResume(downloadFid.File, username) - if err != nil { - answer = gin.H{"message": err.Error(), "ok": false} - } else { - answer = gin.H{"message": "file resume", "ok": true} - } - - ctx.JSON(http.StatusOK, answer) -} - -func (tc *TsConnector) TcGuiDownloadPause(ctx *gin.Context) { - var ( - downloadFid DownloadFileId - answer gin.H - username string - ok bool - err error - ) - - err = ctx.ShouldBindJSON(&downloadFid) - if err != nil { - _ = ctx.Error(errors.New("invalid action")) - return - } - - value, exists := ctx.Get("username") - if !exists { - ctx.JSON(http.StatusOK, gin.H{"message": "Server error: username not found in context", "ok": false}) - return - } - - username, ok = value.(string) - if !ok { - ctx.JSON(http.StatusOK, gin.H{"message": "Server error: invalid username type in context", "ok": false}) - return - } - - err = tc.teamserver.TsDownloadTaskPause(downloadFid.File, username) - if err != nil { - answer = gin.H{"message": err.Error(), "ok": false} - } else { - answer = gin.H{"message": "file pause", "ok": true} - } - - ctx.JSON(http.StatusOK, answer) -} diff --git a/AdaptixServer/core/connector/tc_listeners.go b/AdaptixServer/core/connector/tc_listeners.go index 1cf6252c..43342c30 100644 --- a/AdaptixServer/core/connector/tc_listeners.go +++ b/AdaptixServer/core/connector/tc_listeners.go @@ -26,7 +26,7 @@ func (tc *TsConnector) TcListenerStart(ctx *gin.Context) { } if isvalid.ValidListenerName(listener.ListenerName) == false { - ctx.JSON(http.StatusOK, gin.H{"message": "Invalid listener name", "ok": false}) + ctx.JSON(http.StatusOK, gin.H{"message": "Invalid JSON name", "ok": false}) return } diff --git a/AdaptixServer/core/connector/tc_screenshot.go b/AdaptixServer/core/connector/tc_screenshot.go index 29ee80b3..adc19983 100644 --- a/AdaptixServer/core/connector/tc_screenshot.go +++ b/AdaptixServer/core/connector/tc_screenshot.go @@ -14,7 +14,7 @@ func (tc *TsConnector) TcScreenshotRemove(ctx *gin.Context) { var screenRemove ScreenRemove err := ctx.ShouldBindJSON(&screenRemove) if err != nil { - ctx.JSON(http.StatusOK, gin.H{"message": "invalid command data", "ok": false}) + ctx.JSON(http.StatusOK, gin.H{"message": "invalid JSON data", "ok": false}) return } @@ -52,7 +52,7 @@ func (tc *TsConnector) TcScreenshotSetNote(ctx *gin.Context) { err = ctx.ShouldBindJSON(&screenNote) if err != nil { - ctx.JSON(http.StatusOK, gin.H{"message": "invalid command data", "ok": false}) + ctx.JSON(http.StatusOK, gin.H{"message": "invalid JSON data", "ok": false}) return } diff --git a/AdaptixServer/core/connector/tc_tunnels.go b/AdaptixServer/core/connector/tc_tunnels.go index a54037ee..408023c7 100644 --- a/AdaptixServer/core/connector/tc_tunnels.go +++ b/AdaptixServer/core/connector/tc_tunnels.go @@ -250,7 +250,6 @@ func (tc *TsConnector) TcTunnelStartRpf(ctx *gin.Context) { goto ERR } - //tunnelId _, err = tc.teamserver.TsTunnelClientStart(ta.AgentId, ta.Listen, 5, ta.Description, "", ta.Port, clientName, ta.Thost, ta.Tport, "", "") if err != nil { goto ERR diff --git a/AdaptixServer/core/database/database.go b/AdaptixServer/core/database/database.go index 272c310e..71b17370 100644 --- a/AdaptixServer/core/database/database.go +++ b/AdaptixServer/core/database/database.go @@ -139,6 +139,21 @@ func (dbms *DBMS) DatabaseInit() error { );` _, err = dbms.database.Exec(createTableQuery) + createTableQuery = `CREATE TABLE IF NOT EXISTS "Credentials" ( + "Id" INTEGER PRIMARY KEY AUTOINCREMENT, + "CredId" TEXT NOT NULL, + "Username" TEXT, + "Password" TEXT, + "Realm" TEXT, + "Type" TEXT, + "Tag" TEXT, + "Date" BIGINT, + "Storage" TEXT, + "AgentId" TEXT, + "Host" TEXT + );` + _, err = dbms.database.Exec(createTableQuery) + return err } diff --git a/AdaptixServer/core/database/db_agents.go b/AdaptixServer/core/database/db_agents.go index f0aeadc5..b46e2c8a 100644 --- a/AdaptixServer/core/database/db_agents.go +++ b/AdaptixServer/core/database/db_agents.go @@ -1,6 +1,7 @@ package database import ( + "AdaptixServer/core/utils/logs" "database/sql" "errors" "fmt" @@ -128,7 +129,7 @@ func (dbms *DBMS) DbAgentAll() []adaptix.AgentData { agents = append(agents, agentData) } } else { - fmt.Println(err.Error() + " --- Clear database file!") + logs.Debug("", err.Error()+" --- Clear database file!") } defer func(query *sql.Rows) { diff --git a/AdaptixServer/core/database/db_consoles.go b/AdaptixServer/core/database/db_consoles.go index 87ab4cfc..34236e9e 100644 --- a/AdaptixServer/core/database/db_consoles.go +++ b/AdaptixServer/core/database/db_consoles.go @@ -1,11 +1,11 @@ package database import ( + "AdaptixServer/core/utils/logs" "bytes" "database/sql" "encoding/json" "errors" - "fmt" ) func (dbms *DBMS) DbConsoleInsert(agentId string, packet interface{}) error { @@ -52,7 +52,7 @@ func (dbms *DBMS) DbConsoleAll(agentId string) [][]byte { consoles = append(consoles, message) } } else { - fmt.Println(err.Error() + " --- Clear database file!") + logs.Debug("", err.Error()+" --- Clear database file!") } defer func(query *sql.Rows) { _ = query.Close() diff --git a/AdaptixServer/core/database/db_creds.go b/AdaptixServer/core/database/db_creds.go new file mode 100644 index 00000000..b11cc96a --- /dev/null +++ b/AdaptixServer/core/database/db_creds.go @@ -0,0 +1,106 @@ +package database + +import ( + "AdaptixServer/core/utils/logs" + "database/sql" + "errors" + "fmt" + adaptix "github.com/Adaptix-Framework/axc2" +) + +func (dbms *DBMS) DbCredentialsExist(creedsId string) bool { + rows, err := dbms.database.Query("SELECT CredId FROM Credentials;") + if err != nil { + return false + } + defer func(rows *sql.Rows) { + _ = rows.Close() + }(rows) + + for rows.Next() { + rowCredsId := "" + _ = rows.Scan(&rowCredsId) + if creedsId == rowCredsId { + return true + } + } + return false +} + +func (dbms *DBMS) DbCredentialsAdd(credsData adaptix.CredsData) error { + ok := dbms.DatabaseExists() + if !ok { + return errors.New("database not exists") + } + + ok = dbms.DbCredentialsExist(credsData.CredId) + if ok { + return fmt.Errorf("creds %s alredy exists", credsData.CredId) + } + + insertQuery := `INSERT INTO Credentials (CredId, Username, Password, Realm, Type, Tag, Date, Storage, AgentId, Host) values(?,?,?,?,?,?,?,?,?,?);` + _, err := dbms.database.Exec(insertQuery, credsData.CredId, credsData.Username, credsData.Password, credsData.Realm, credsData.Type, + credsData.Tag, credsData.Date, credsData.Storage, credsData.AgentId, credsData.Host) + return err +} + +func (dbms *DBMS) DbCredentialsUpdate(credsData adaptix.CredsData) error { + + ok := dbms.DatabaseExists() + if !ok { + return errors.New("database not exists") + } + + ok = dbms.DbCredentialsExist(credsData.CredId) + if !ok { + return fmt.Errorf("creds %s not exists", credsData.CredId) + } + + updateQuery := `UPDATE Credentials SET Username = ?, Password = ?, Realm = ?, Type = ?, Tag = ?, Storage = ?, Host = ? WHERE CredId = ?;` + _, err := dbms.database.Exec(updateQuery, credsData.Username, credsData.Password, credsData.Realm, credsData.Type, + credsData.Tag, credsData.Storage, credsData.Host, credsData.CredId) + return err +} + +func (dbms *DBMS) DbCredentialsDelete(credId string) error { + ok := dbms.DatabaseExists() + if !ok { + return errors.New("database not exists") + } + + ok = dbms.DbCredentialsExist(credId) + if !ok { + return fmt.Errorf("creds %s not exists", credId) + } + + deleteQuery := `DELETE FROM Credentials WHERE CredId = ?;` + _, err := dbms.database.Exec(deleteQuery, credId) + return err +} + +func (dbms *DBMS) DbCredentialsAll() []*adaptix.CredsData { + var creds []*adaptix.CredsData + + ok := dbms.DatabaseExists() + if ok { + selectQuery := `SELECT CredId, Username, Password, Realm, Type, Tag, Date, Storage, AgentId, Host FROM Credentials;` + query, err := dbms.database.Query(selectQuery) + if err == nil { + for query.Next() { + credsData := &adaptix.CredsData{} + err = query.Scan(&credsData.CredId, &credsData.Username, &credsData.Password, &credsData.Realm, &credsData.Type, + &credsData.Tag, &credsData.Date, &credsData.Storage, &credsData.AgentId, &credsData.Host) + if err != nil { + continue + } + creds = append(creds, credsData) + } + } else { + logs.Debug("", err.Error()+" --- Clear database file!") + } + defer func(query *sql.Rows) { + _ = query.Close() + }(query) + } + return creds +} diff --git a/AdaptixServer/core/database/db_downloads.go b/AdaptixServer/core/database/db_downloads.go index 0c5f173d..e178e92f 100644 --- a/AdaptixServer/core/database/db_downloads.go +++ b/AdaptixServer/core/database/db_downloads.go @@ -1,6 +1,7 @@ package database import ( + "AdaptixServer/core/utils/logs" "database/sql" "errors" "fmt" @@ -82,7 +83,7 @@ func (dbms *DBMS) DbDownloadAll() []adaptix.DownloadData { downloads = append(downloads, downloadData) } } else { - fmt.Println(err.Error() + " --- Clear database file!") + logs.Debug("", err.Error()+" --- Clear database file!") } defer func(query *sql.Rows) { _ = query.Close() diff --git a/AdaptixServer/core/database/db_listeners.go b/AdaptixServer/core/database/db_listeners.go index 77167d4f..9a74c28e 100644 --- a/AdaptixServer/core/database/db_listeners.go +++ b/AdaptixServer/core/database/db_listeners.go @@ -1,6 +1,7 @@ package database import ( + "AdaptixServer/core/utils/logs" "database/sql" "errors" "fmt" @@ -103,7 +104,7 @@ func (dbms *DBMS) DbListenerAll() []ListenerRow { listeners = append(listeners, listenerRow) } } else { - fmt.Println(err.Error() + " --- Clear database file!") + logs.Debug("", err.Error()+" --- Clear database file!") } defer func(query *sql.Rows) { _ = query.Close() diff --git a/AdaptixServer/core/database/db_pivots.go b/AdaptixServer/core/database/db_pivots.go index 3cdf7fa3..c11f35b6 100644 --- a/AdaptixServer/core/database/db_pivots.go +++ b/AdaptixServer/core/database/db_pivots.go @@ -1,6 +1,7 @@ package database import ( + "AdaptixServer/core/utils/logs" "database/sql" "errors" "fmt" @@ -68,7 +69,6 @@ func (dbms *DBMS) DbPivotAll() []*adaptix.PivotData { selectQuery := `SELECT PivotId, PivotName, ParentAgentId, ChildAgentId FROM Pivots;` query, err := dbms.database.Query(selectQuery) if err == nil { - for query.Next() { pivotData := &adaptix.PivotData{} err = query.Scan(&pivotData.PivotId, &pivotData.PivotName, &pivotData.ParentAgentId, &pivotData.ChildAgentId) @@ -78,7 +78,7 @@ func (dbms *DBMS) DbPivotAll() []*adaptix.PivotData { pivots = append(pivots, pivotData) } } else { - fmt.Println(err.Error() + " --- Clear database file!") + logs.Debug("", err.Error()+" --- Clear database file!") } defer func(query *sql.Rows) { _ = query.Close() diff --git a/AdaptixServer/core/database/db_screenshots.go b/AdaptixServer/core/database/db_screenshots.go index 285857e5..86dc714b 100644 --- a/AdaptixServer/core/database/db_screenshots.go +++ b/AdaptixServer/core/database/db_screenshots.go @@ -1,6 +1,7 @@ package database import ( + "AdaptixServer/core/utils/logs" "database/sql" "errors" "fmt" @@ -92,7 +93,7 @@ func (dbms *DBMS) DbScreenshotAll() []adaptix.ScreenData { screens = append(screens, screenData) } } else { - fmt.Println(err.Error() + " --- Clear database file!") + logs.Debug("", err.Error()+" --- Clear database file!") } defer func(query *sql.Rows) { _ = query.Close() diff --git a/AdaptixServer/core/database/db_tasks.go b/AdaptixServer/core/database/db_tasks.go index 9745daab..b98313a1 100644 --- a/AdaptixServer/core/database/db_tasks.go +++ b/AdaptixServer/core/database/db_tasks.go @@ -1,6 +1,7 @@ package database import ( + "AdaptixServer/core/utils/logs" "database/sql" "errors" "fmt" @@ -95,7 +96,7 @@ func (dbms *DBMS) DbTasksAll(agentId string) []adaptix.TaskData { tasks = append(tasks, taskData) } } else { - fmt.Println(err.Error() + " --- Clear database file!") + logs.Debug("", err.Error()+" --- Clear database file!") } defer func(query *sql.Rows) { _ = query.Close() diff --git a/AdaptixServer/core/extender/ex_agent.go b/AdaptixServer/core/extender/ex_agent.go index af6d219d..38cf0365 100644 --- a/AdaptixServer/core/extender/ex_agent.go +++ b/AdaptixServer/core/extender/ex_agent.go @@ -5,12 +5,12 @@ import ( "github.com/Adaptix-Framework/axc2" ) -func (ex *AdaptixExtender) ExAgentGenerate(agentName string, config string, operatingSystem string, listenerWM string, listenerProfile []byte) ([]byte, string, error) { +func (ex *AdaptixExtender) ExAgentGenerate(agentName string, config string, listenerWM string, listenerProfile []byte) ([]byte, string, error) { module, ok := ex.agentModules[agentName] if !ok { return nil, "", errors.New("module not found") } - return module.F.AgentGenerate(config, operatingSystem, listenerWM, listenerProfile) + return module.AgentGenerate(config, listenerWM, listenerProfile) } func (ex *AdaptixExtender) ExAgentCreate(agentName string, beat []byte) (adaptix.AgentData, error) { @@ -18,15 +18,15 @@ func (ex *AdaptixExtender) ExAgentCreate(agentName string, beat []byte) (adaptix if !ok { return adaptix.AgentData{}, errors.New("module not found") } - return module.F.AgentCreate(beat) + return module.AgentCreate(beat) } -func (ex *AdaptixExtender) ExAgentCommand(client string, cmdline string, agentName string, agentData adaptix.AgentData, args map[string]any) error { +func (ex *AdaptixExtender) ExAgentCommand(agentName string, agentData adaptix.AgentData, args map[string]any) (adaptix.TaskData, adaptix.ConsoleMessageData, error) { module, ok := ex.agentModules[agentName] if !ok { - return errors.New("module not found") + return adaptix.TaskData{}, adaptix.ConsoleMessageData{}, errors.New("module not found") } - return module.F.AgentCommand(client, cmdline, agentData, args) + return module.AgentCommand(agentData, args) } func (ex *AdaptixExtender) ExAgentProcessData(agentData adaptix.AgentData, packedData []byte) ([]byte, error) { @@ -34,7 +34,7 @@ func (ex *AdaptixExtender) ExAgentProcessData(agentData adaptix.AgentData, packe if !ok { return nil, errors.New("module not found") } - return module.F.AgentProcessData(agentData, packedData) + return module.AgentProcessData(agentData, packedData) } func (ex *AdaptixExtender) ExAgentPackData(agentData adaptix.AgentData, tasks []adaptix.TaskData) ([]byte, error) { @@ -42,7 +42,7 @@ func (ex *AdaptixExtender) ExAgentPackData(agentData adaptix.AgentData, tasks [] if !ok { return nil, errors.New("module not found") } - return module.F.AgentPackData(agentData, tasks) + return module.AgentPackData(agentData, tasks) } func (ex *AdaptixExtender) ExAgentPivotPackData(agentName string, pivotId string, data []byte) (adaptix.TaskData, error) { @@ -50,212 +50,7 @@ func (ex *AdaptixExtender) ExAgentPivotPackData(agentName string, pivotId string if !ok { return adaptix.TaskData{}, errors.New("module not found") } - return module.F.AgentPivotPackData(pivotId, data) -} - -func (ex *AdaptixExtender) ExAgentBrowserDisks(agentData adaptix.AgentData) (adaptix.TaskData, error) { - module, ok := ex.agentModules[agentData.Name] - if !ok { - return adaptix.TaskData{}, errors.New("module not found") - } - - lName, err := ex.ts.TsListenerTypeByName(agentData.Listener) - if err != nil { - return adaptix.TaskData{}, err - } - supports, ok := module.Supports[lName] - if !ok { - return adaptix.TaskData{}, errors.New("function BrowserDisks is not supported") - } - supportConf, ok := supports[agentData.Os] - if !ok { - return adaptix.TaskData{}, errors.New("function BrowserDisks is not supported") - } - if !supportConf.FileBrowserDisks { - return adaptix.TaskData{}, errors.New("function BrowserDisks is not supported") - } - - return module.F.AgentBrowserDisks(agentData) -} - -func (ex *AdaptixExtender) ExAgentBrowserProcess(agentData adaptix.AgentData) (adaptix.TaskData, error) { - module, ok := ex.agentModules[agentData.Name] - if !ok { - return adaptix.TaskData{}, errors.New("module not found") - } - - lName, err := ex.ts.TsListenerTypeByName(agentData.Listener) - if err != nil { - return adaptix.TaskData{}, err - } - supports, ok := module.Supports[lName] - if !ok { - return adaptix.TaskData{}, errors.New("function ProcessBrowser is not supported") - } - supportConf, ok := supports[agentData.Os] - if !ok { - return adaptix.TaskData{}, errors.New("function ProcessBrowser is not supported") - } - if !supportConf.ProcessBrowser { - return adaptix.TaskData{}, errors.New("function ProcessBrowser is not supported") - } - - return module.F.AgentBrowserProcess(agentData) -} - -func (ex *AdaptixExtender) ExAgentBrowserFiles(agentData adaptix.AgentData, path string) (adaptix.TaskData, error) { - module, ok := ex.agentModules[agentData.Name] - if !ok { - return adaptix.TaskData{}, errors.New("module not found") - } - - lName, err := ex.ts.TsListenerTypeByName(agentData.Listener) - if err != nil { - return adaptix.TaskData{}, err - } - supports, ok := module.Supports[lName] - if !ok { - return adaptix.TaskData{}, errors.New("function FileBrowser is not supported") - } - supportConf, ok := supports[agentData.Os] - if !ok { - return adaptix.TaskData{}, errors.New("function FileBrowser is not supported") - } - if !supportConf.FileBrowser { - return adaptix.TaskData{}, errors.New("function FileBrowser is not supported") - } - - return module.F.AgentBrowserFiles(path, agentData) -} - -func (ex *AdaptixExtender) ExAgentBrowserUpload(agentData adaptix.AgentData, path string, content []byte) (adaptix.TaskData, error) { - module, ok := ex.agentModules[agentData.Name] - if !ok { - return adaptix.TaskData{}, errors.New("module not found") - } - - lName, err := ex.ts.TsListenerTypeByName(agentData.Listener) - if err != nil { - return adaptix.TaskData{}, err - } - supports, ok := module.Supports[lName] - if !ok { - return adaptix.TaskData{}, errors.New("function FileBrowserUpload is not supported") - } - supportConf, ok := supports[agentData.Os] - if !ok { - return adaptix.TaskData{}, errors.New("function FileBrowserUpload is not supported") - } - if !supportConf.FileBrowserUpload { - return adaptix.TaskData{}, errors.New("function FileBrowserUpload is not supported") - } - - return module.F.AgentBrowserUpload(path, content, agentData) -} - -/// Downloads - -func (ex *AdaptixExtender) ExAgentDownloadTaskStart(agentData adaptix.AgentData, path string) (adaptix.TaskData, error) { - module, ok := ex.agentModules[agentData.Name] - if !ok { - return adaptix.TaskData{}, errors.New("module not found") - } - - lName, err := ex.ts.TsListenerTypeByName(agentData.Listener) - if err != nil { - return adaptix.TaskData{}, err - } - supports, ok := module.Supports[lName] - if !ok { - return adaptix.TaskData{}, errors.New("function FileBrowserDownload is not supported") - } - supportConf, ok := supports[agentData.Os] - if !ok { - return adaptix.TaskData{}, errors.New("function FileBrowserDownload is not supported") - } - if !supportConf.FileBrowserDownload { - return adaptix.TaskData{}, errors.New("function FileBrowserDownload is not supported") - } - - return module.F.AgentTaskDownloadStart(path, agentData) -} - -func (ex *AdaptixExtender) ExAgentDownloadTaskCancel(agentData adaptix.AgentData, fileId string) (adaptix.TaskData, error) { - module, ok := ex.agentModules[agentData.Name] - if !ok { - return adaptix.TaskData{}, errors.New("module not found") - } - - lName, err := ex.ts.TsListenerTypeByName(agentData.Listener) - if err != nil { - return adaptix.TaskData{}, err - } - - supports, ok := module.Supports[lName] - if !ok { - return adaptix.TaskData{}, errors.New("function DownloadsMenuCancel is not supported") - } - supportConf, ok := supports[agentData.Os] - if !ok { - return adaptix.TaskData{}, errors.New("function DownloadsMenuCancel is not supported") - } - if !supportConf.DownloadsCancel { - return adaptix.TaskData{}, errors.New("function DownloadsMenuCancel is not supported") - } - - return module.F.AgentTaskDownloadCancel(fileId, agentData) -} - -func (ex *AdaptixExtender) ExAgentDownloadTaskResume(agentData adaptix.AgentData, fileId string) (adaptix.TaskData, error) { - module, ok := ex.agentModules[agentData.Name] - if !ok { - return adaptix.TaskData{}, errors.New("module not found") - } - - lName, err := ex.ts.TsListenerTypeByName(agentData.Listener) - if err != nil { - return adaptix.TaskData{}, err - } - - supports, ok := module.Supports[lName] - if !ok { - return adaptix.TaskData{}, errors.New("function DownloadsMenuResume is not supported") - } - supportConf, ok := supports[agentData.Os] - if !ok { - return adaptix.TaskData{}, errors.New("function DownloadsMenuResume is not supported") - } - if !supportConf.DownloadsResume { - return adaptix.TaskData{}, errors.New("function DownloadsMenuResume is not supported") - } - - return module.F.AgentTaskDownloadResume(fileId, agentData) -} - -func (ex *AdaptixExtender) ExAgentDownloadTaskPause(agentData adaptix.AgentData, fileId string) (adaptix.TaskData, error) { - module, ok := ex.agentModules[agentData.Name] - if !ok { - return adaptix.TaskData{}, errors.New("module not found") - } - - lName, err := ex.ts.TsListenerTypeByName(agentData.Listener) - if err != nil { - return adaptix.TaskData{}, err - } - - supports, ok := module.Supports[lName] - if !ok { - return adaptix.TaskData{}, errors.New("function DownloadsMenuPause is not supported") - } - supportConf, ok := supports[agentData.Os] - if !ok { - return adaptix.TaskData{}, errors.New("function DownloadsMenuPause is not supported") - } - if !supportConf.DownloadsPause { - return adaptix.TaskData{}, errors.New("function DownloadsMenuPause is not supported") - } - - return module.F.AgentTaskDownloadPause(fileId, agentData) + return module.AgentPivotPackData(pivotId, data) } /// Tunnels @@ -265,38 +60,7 @@ func (ex *AdaptixExtender) ExAgentTunnelCallbacks(agentData adaptix.AgentData, t if !ok { return nil, nil, nil, nil, nil, nil, errors.New("module not found") } - - lName, err := ex.ts.TsListenerTypeByName(agentData.Listener) - if err != nil { - return nil, nil, nil, nil, nil, nil, err - } - supports, ok := module.Supports[lName] - if !ok { - return nil, nil, nil, nil, nil, nil, errors.New("Tunnels are not supported") - } - supportConf, ok := supports[agentData.Os] - if !ok { - return nil, nil, nil, nil, nil, nil, errors.New("Tunnels are not supported") - } - - // TUNNEL_SOCKS4 - if tunnelType == 1 && !supportConf.Socks4 { - return nil, nil, nil, nil, nil, nil, errors.New("function Socks4 is not supported") - } - // TUNNEL_SOCKS5 or TUNNEL_SOCKS5_AUTH - if (tunnelType == 2 || tunnelType == 3) && !supportConf.Socks5 { - return nil, nil, nil, nil, nil, nil, errors.New("function Socks5 is not supported") - } - // TUNNEL_LPORTFWD - if tunnelType == 4 && !supportConf.Lportfwd { - return nil, nil, nil, nil, nil, nil, errors.New("function LocalPortForward is not supported") - } - // TUNNEL_RPORTFWD - if tunnelType == 5 && !supportConf.Rportfwd { - return nil, nil, nil, nil, nil, nil, errors.New("function ReversePortFwd is not supported") - } - - return module.F.AgentTunnelCallbacks() + return module.AgentTunnelCallbacks() } func (ex *AdaptixExtender) ExAgentTerminalCallbacks(agentData adaptix.AgentData) (func(int, string, int, int) (adaptix.TaskData, error), func(int, []byte) (adaptix.TaskData, error), func(int) (adaptix.TaskData, error), error) { @@ -305,75 +69,5 @@ func (ex *AdaptixExtender) ExAgentTerminalCallbacks(agentData adaptix.AgentData) if !ok { return nil, nil, nil, errors.New("module not found") } - - lName, err := ex.ts.TsListenerTypeByName(agentData.Listener) - if err != nil { - return nil, nil, nil, err - } - supports, ok := module.Supports[lName] - if !ok { - return nil, nil, nil, errors.New("RemoteTerminal are not supported") - } - supportConf, ok := supports[agentData.Os] - if !ok { - return nil, nil, nil, errors.New("RemoteTerminal are not supported") - } - - if !supportConf.RemoteTerminal { - return nil, nil, nil, errors.New("RemoteTerminal are not supported") - } - - return module.F.AgentTerminalCallbacks() -} - -//// - -func (ex *AdaptixExtender) ExAgentCtxExit(agentData adaptix.AgentData) (adaptix.TaskData, error) { - module, ok := ex.agentModules[agentData.Name] - if !ok { - return adaptix.TaskData{}, errors.New("module not found") - } - - lName, err := ex.ts.TsListenerTypeByName(agentData.Listener) - if err != nil { - return adaptix.TaskData{}, err - } - supports, ok := module.Supports[lName] - if !ok { - return adaptix.TaskData{}, errors.New("function SessionsMenuExit is not supported") - } - supportConf, ok := supports[agentData.Os] - if !ok { - return adaptix.TaskData{}, errors.New("function SessionsMenuExit is not supported") - } - if !supportConf.SessionsMenuExit { - return adaptix.TaskData{}, errors.New("function SessionsMenuExit is not supported") - } - - return module.F.AgentBrowserExit(agentData) -} - -func (ex *AdaptixExtender) ExAgentBrowserJobKill(agentData adaptix.AgentData, jobId string) (adaptix.TaskData, error) { - module, ok := ex.agentModules[agentData.Name] - if !ok { - return adaptix.TaskData{}, errors.New("module not found") - } - - lName, err := ex.ts.TsListenerTypeByName(agentData.Listener) - if err != nil { - return adaptix.TaskData{}, err - } - supports, ok := module.Supports[lName] - if !ok { - return adaptix.TaskData{}, errors.New("function TasksJobKill is not supported") - } - supportConf, ok := supports[agentData.Os] - if !ok { - return adaptix.TaskData{}, errors.New("function TasksJobKill is not supported") - } - if !supportConf.TasksJobKill { - return adaptix.TaskData{}, errors.New("function TasksJobKill is not supported") - } - - return module.F.AgentBrowserJobKill(jobId) + return module.AgentTerminalCallbacks() } diff --git a/AdaptixServer/core/extender/extender.go b/AdaptixServer/core/extender/extender.go index 37be44d2..852ba7c3 100644 --- a/AdaptixServer/core/extender/extender.go +++ b/AdaptixServer/core/extender/extender.go @@ -62,19 +62,6 @@ func (ex *AdaptixExtender) LoadPluginListener(config_path string, config_data [] return } - listenerUI, err := json.Marshal(configListener.UI) - if err != nil { - logs.Error("", "Error %s converting info to JSON: %s", config_path, err.Error()) - return - } - - listenerInfo := ListenerInfo{ - Name: configListener.ListenerName, - Type: configListener.ListenerType, - Protocol: configListener.Protocol, - UI: string(listenerUI), - } - plugin_path := filepath.Dir(config_path) + "/" + configListener.ExtenderFile plug, err := plugin.Open(plugin_path) if err != nil { @@ -100,6 +87,20 @@ func (ex *AdaptixExtender) LoadPluginListener(config_path string, config_data [] return } + ax_path := filepath.Dir(config_path) + "/" + configListener.AxFile + ax_content, err := os.ReadFile(ax_path) + if err != nil { + logs.Error("", "failed to read ax file %s: %s", ax_path, err.Error()) + return + } + + listenerInfo := ListenerInfo{ + Name: configListener.ListenerName, + Type: configListener.ListenerType, + Protocol: configListener.Protocol, + AX: string(ax_content), + } + err = ex.ts.TsListenerReg(listenerInfo) if err != nil { logs.Error("", "plugin %s does not registered: %s", plugin_path, err.Error()) @@ -118,72 +119,18 @@ func (ex *AdaptixExtender) LoadPluginAgent(config_path string, config_data []byt return } - browsersFunc := make(map[string]map[int]ExConfAgentSupportedBrowsers) - - for _, listener := range configAgent.Listeners { - for _, config := range listener.OsConfigs { - OStype := 0 - if config.Os == "windows" { - OStype = 1 - } else if config.Os == "linux" { - OStype = 2 - } else if config.Os == "mac" { - OStype = 3 - } else { - logs.Error("", "Error OS for listener: %s. Must be linux, windows, or mac.", listener.ListenerName) - return - } - - found := false - for _, handler := range configAgent.Handlers { - if config.Handler == handler.Id { - - var supportsFunc ExConfAgentSupportedBrowsers - hb, err := json.Marshal(handler.Browsers) - if err != nil { - logs.Error("", "Error %s converting info to JSON: %s", config_path, err.Error()) - return - } - err = json.Unmarshal(hb, &supportsFunc) - if err != nil { - logs.Error("", "Error config parse: %s", err.Error()) - return - } - - _, ok := browsersFunc[listener.ListenerName] - if !ok { - browsersFunc[listener.ListenerName] = make(map[int]ExConfAgentSupportedBrowsers) - } - browsersFunc[listener.ListenerName][OStype] = supportsFunc - - found = true - break - } - } - if !found { - logs.Error("", "Handler %s for listener %s not found.", config.Handler, listener.ListenerName) - return - } - } - } - - handlersJson, err := json.Marshal(configAgent.Handlers) + ax_path := filepath.Dir(config_path) + "/" + configAgent.AxFile + ax_content, err := os.ReadFile(ax_path) if err != nil { - logs.Error("", "Error %s converting info to JSON: %s", config_path, err.Error()) - return - } - - listenersJson, err := json.Marshal(configAgent.Listeners) - if err != nil { - logs.Error("", "Error %s converting info to JSON: %s", config_path, err.Error()) + logs.Error("", "failed to read ax file %s: %s", ax_path, err.Error()) return } agentInfo := AgentInfo{ - Name: configAgent.AgentName, - Watermark: configAgent.AgentWatermark, - ListenersJson: string(listenersJson), - HandlersJson: string(handlersJson), + Name: configAgent.AgentName, + Watermark: configAgent.AgentWatermark, + AX: string(ax_content), + Listeners: configAgent.Listeners, } plugin_path := filepath.Dir(config_path) + "/" + configAgent.ExtenderFile @@ -205,9 +152,9 @@ func (ex *AdaptixExtender) LoadPluginAgent(config_path string, config_data []byt return } - module, ok := pl_InitPlugin(ex.ts, filepath.Dir(plugin_path), agentInfo.Watermark).(ExtAgentFunc) + module, ok := pl_InitPlugin(ex.ts, filepath.Dir(plugin_path), agentInfo.Watermark).(ExtAgent) if !ok { - logs.Error("", "plugin %s does not implement the ExtAgentFunc interface", plugin_path) + logs.Error("", "plugin %s does not implement the ExtAgent interface", plugin_path) return } @@ -217,8 +164,5 @@ func (ex *AdaptixExtender) LoadPluginAgent(config_path string, config_data []byt return } - ex.agentModules[agentInfo.Name] = ExtAgent{ - F: module, - Supports: browsersFunc, - } + ex.agentModules[agentInfo.Name] = module } diff --git a/AdaptixServer/core/extender/utils.go b/AdaptixServer/core/extender/utils.go index 6fbb0a69..241d5fb8 100644 --- a/AdaptixServer/core/extender/utils.go +++ b/AdaptixServer/core/extender/utils.go @@ -7,57 +7,21 @@ import "github.com/Adaptix-Framework/axc2" type ExConfigListener struct { ExtenderType string `json:"extender_type"` ExtenderFile string `json:"extender_file"` + AxFile string `json:"ax_file"` ListenerName string `json:"listener_name"` ListenerType string `json:"listener_type"` Protocol string `json:"protocol"` - UI any `json:"ui"` } /// ExConfig Agent -type ExConfAgentOsConfigs struct { - Os string `json:"operating_system"` - Handler string `json:"handler"` - Ui any `json:"generate_ui"` -} - -type ExConfAgentListeners struct { - ListenerName string `json:"listener_name"` - OsConfigs []ExConfAgentOsConfigs `json:"configs"` -} - -type ExConfAgentSupportedBrowsers struct { - RemoteTerminal bool `json:"remote_terminal"` - FileBrowser bool `json:"file_browser"` - FileBrowserDisks bool `json:"file_browser_disks"` - FileBrowserDownload bool `json:"file_browser_download"` - FileBrowserUpload bool `json:"file_browser_upload"` - ProcessBrowser bool `json:"process_browser"` - DownloadsCancel bool `json:"downloads_cancel"` - DownloadsResume bool `json:"downloads_resume"` - DownloadsPause bool `json:"downloads_pause"` - TasksJobKill bool `json:"tasks_job_kill"` - SessionsMenuExit bool `json:"sessions_menu_exit"` - SessionsMenuTunnels bool `json:"sessions_menu_tunnels"` - Socks4 bool `json:"socks4"` - Socks5 bool `json:"socks5"` - Lportfwd bool `json:"lportfwd"` - Rportfwd bool `json:"rportfwd"` -} - -type ExConfAgentHandlers struct { - Id string `json:"id"` - Commands any `json:"commands"` - Browsers any `json:"browsers"` -} - type ExConfigAgent struct { - ExtenderType string `json:"extender_type"` - ExtenderFile string `json:"extender_file"` - AgentName string `json:"agent_name"` - AgentWatermark string `json:"agent_watermark"` - Listeners []ExConfAgentListeners `json:"listeners"` - Handlers []ExConfAgentHandlers `json:"handlers"` + ExtenderType string `json:"extender_type"` + ExtenderFile string `json:"extender_file"` + AxFile string `json:"ax_file"` + AgentName string `json:"agent_name"` + AgentWatermark string `json:"agent_watermark"` + Listeners []string `json:"listeners"` } /// Info @@ -66,14 +30,14 @@ type ListenerInfo struct { Name string Protocol string Type string - UI string + AX string } type AgentInfo struct { - Name string - Watermark string - ListenersJson string - HandlersJson string + Name string + Watermark string + AX string + Listeners []string } type Teamserver interface { @@ -91,34 +55,16 @@ type ExtListener interface { ListenerInteralHandler(name string, data []byte) (string, error) } -type ExtAgentFunc interface { - AgentGenerate(config string, operatingSystem string, listenerWM string, listenerProfile []byte) ([]byte, string, error) +type ExtAgent interface { + AgentGenerate(config string, listenerWM string, listenerProfile []byte) ([]byte, string, error) AgentCreate(beat []byte) (adaptix.AgentData, error) - AgentCommand(client string, cmdline string, agentData adaptix.AgentData, args map[string]any) error + AgentCommand(agentData adaptix.AgentData, args map[string]any) (adaptix.TaskData, adaptix.ConsoleMessageData, error) AgentProcessData(agentData adaptix.AgentData, packedData []byte) ([]byte, error) AgentPackData(agentData adaptix.AgentData, tasks []adaptix.TaskData) ([]byte, error) AgentPivotPackData(pivotId string, data []byte) (adaptix.TaskData, error) - AgentBrowserDisks(agentData adaptix.AgentData) (adaptix.TaskData, error) - AgentBrowserProcess(agentData adaptix.AgentData) (adaptix.TaskData, error) - AgentBrowserFiles(path string, agentData adaptix.AgentData) (adaptix.TaskData, error) - AgentBrowserUpload(path string, content []byte, agentData adaptix.AgentData) (adaptix.TaskData, error) - - AgentTaskDownloadStart(path string, agentData adaptix.AgentData) (adaptix.TaskData, error) - AgentTaskDownloadCancel(fileId string, agentData adaptix.AgentData) (adaptix.TaskData, error) - AgentTaskDownloadResume(fileId string, agentData adaptix.AgentData) (adaptix.TaskData, error) - AgentTaskDownloadPause(fileId string, agentData adaptix.AgentData) (adaptix.TaskData, error) - AgentTunnelCallbacks() (func(int, string, int) adaptix.TaskData, func(int, string, int) adaptix.TaskData, func(int, []byte) adaptix.TaskData, func(int, []byte) adaptix.TaskData, func(int) adaptix.TaskData, func(int, int) adaptix.TaskData, error) AgentTerminalCallbacks() (func(int, string, int, int) (adaptix.TaskData, error), func(int, []byte) (adaptix.TaskData, error), func(int) (adaptix.TaskData, error), error) - - AgentBrowserExit(agentData adaptix.AgentData) (adaptix.TaskData, error) - AgentBrowserJobKill(jobId string) (adaptix.TaskData, error) -} - -type ExtAgent struct { - Supports map[string]map[int]ExConfAgentSupportedBrowsers - F ExtAgentFunc } type AdaptixExtender struct { diff --git a/AdaptixServer/core/profile/utils.go b/AdaptixServer/core/profile/utils.go index ff8cb341..f046ae33 100644 --- a/AdaptixServer/core/profile/utils.go +++ b/AdaptixServer/core/profile/utils.go @@ -7,6 +7,7 @@ type AdaptixProfile struct { } type TsProfile struct { + Interface string `json:"interface"` Port int `json:"port"` Endpoint string `json:"endpoint"` Password string `json:"password"` diff --git a/AdaptixServer/core/server/server.go b/AdaptixServer/core/server/server.go index 0afdcb28..677642ef 100644 --- a/AdaptixServer/core/server/server.go +++ b/AdaptixServer/core/server/server.go @@ -8,6 +8,7 @@ import ( "AdaptixServer/core/utils/logs" "AdaptixServer/core/utils/safe" "encoding/json" + "net" "os" ) @@ -36,6 +37,7 @@ func NewTeamserver() *Teamserver { downloads: safe.NewMap(), tmp_uploads: safe.NewMap(), screenshots: safe.NewMap(), + credentials: safe.NewSlice(), tunnels: safe.NewMap(), terminals: safe.NewMap(), pivots: safe.NewSlice(), @@ -45,8 +47,9 @@ func NewTeamserver() *Teamserver { return ts } -func (ts *Teamserver) SetSettings(port int, endpoint string, password string, cert string, key string, extenders []string) { +func (ts *Teamserver) SetSettings(host string, port int, endpoint string, password string, cert string, key string, extenders []string) { ts.Profile.Server = &profile.TsProfile{ + Interface: host, Port: port, Endpoint: endpoint, Password: password, @@ -89,7 +92,6 @@ func (ts *Teamserver) RestoreData() { err error ) - /// DATABASE ok = ts.DBMS.DatabaseExists() if !ok { return @@ -109,6 +111,7 @@ func (ts *Teamserver) RestoreData() { TasksQueue: safe.NewSlice(), TunnelConnectTasks: safe.NewSlice(), RunningTasks: safe.NewMap(), + RunningJobs: safe.NewMap(), CompletedTasks: safe.NewMap(), PivotParent: nil, PivotChilds: safe.NewSlice(), @@ -204,6 +207,20 @@ func (ts *Teamserver) RestoreData() { } logs.Success(" ", "Restored %v screens", countScreenshots) + /// CREDENTIALS + countCredentials := 0 + restoreCredentials := ts.DBMS.DbCredentialsAll() + for _, restoreCredential := range restoreCredentials { + + ts.credentials.Put(restoreCredential) + + packet := CreateSpCredentialsAdd(*restoreCredential) + ts.TsSyncAllClients(packet) + + countCredentials++ + } + logs.Success(" ", "Restored %v credentials", countCredentials) + /// LISTENERS countListeners := 0 restoreListeners := ts.DBMS.DbListenerAll() @@ -224,6 +241,28 @@ func (ts *Teamserver) Start() { err error ) + interfaces, err := net.Interfaces() + if err == nil { + ts.Parameters.Interfaces = append(ts.Parameters.Interfaces, "0.0.0.0") + for _, i := range interfaces { + iAddrs, err := i.Addrs() + if err == nil { + for _, addr := range iAddrs { + ipNet, ok := addr.(*net.IPNet) + if ok { + if ipNet.IP.To4() != nil { + ts.Parameters.Interfaces = append(ts.Parameters.Interfaces, ipNet.IP.String()) + } + } + } + } + } + } + if len(ts.Parameters.Interfaces) == 0 { + ts.Parameters.Interfaces = append(ts.Parameters.Interfaces, "0.0.0.0") + ts.Parameters.Interfaces = append(ts.Parameters.Interfaces, "127.0.0.1") + } + ts.AdaptixServer, err = connector.NewTsConnector(ts, *ts.Profile.Server, *ts.Profile.ServerResponse) if err != nil { logs.Error("", "Failed to init HTTP handler: "+err.Error()) @@ -231,7 +270,7 @@ func (ts *Teamserver) Start() { } go ts.AdaptixServer.Start(&stopped) - logs.Success("", "Starting server -> https://%s:%v%s", "0.0.0.0", ts.Profile.Server.Port, ts.Profile.Server.Endpoint) + logs.Success("", "Starting server -> https://%s:%v%s", ts.Profile.Server.Interface, ts.Profile.Server.Port, ts.Profile.Server.Endpoint) ts.RestoreData() diff --git a/AdaptixServer/core/server/ts_agent.go b/AdaptixServer/core/server/ts_agent.go index f9a5aebb..c2be3e13 100644 --- a/AdaptixServer/core/server/ts_agent.go +++ b/AdaptixServer/core/server/ts_agent.go @@ -64,6 +64,7 @@ func (ts *Teamserver) TsAgentCreate(agentCrc string, agentId string, beat []byte TunnelConnectTasks: safe.NewSlice(), TunnelQueue: safe.NewSlice(), RunningTasks: safe.NewMap(), + RunningJobs: safe.NewMap(), CompletedTasks: safe.NewMap(), PivotParent: nil, PivotChilds: safe.NewSlice(), @@ -86,7 +87,7 @@ func (ts *Teamserver) TsAgentCreate(agentCrc string, agentId string, beat []byte return nil } -func (ts *Teamserver) TsAgentCommand(agentName string, agentId string, clientName string, cmdline string, args map[string]any) error { +func (ts *Teamserver) TsAgentCommand(agentName string, agentId string, clientName string, hookId string, cmdline string, ui bool, args map[string]any) error { if !ts.agent_configs.Contains(agentName) { return fmt.Errorf("agent %v not registered", agentName) } @@ -101,7 +102,21 @@ func (ts *Teamserver) TsAgentCommand(agentName string, agentId string, clientNam return fmt.Errorf("agent '%v' not active", agentId) } - return ts.Extender.ExAgentCommand(clientName, cmdline, agentName, agent.Data, args) + taskData, messageData, err := ts.Extender.ExAgentCommand(agentName, agent.Data, args) + if err != nil { + return err + } + taskData.HookId = hookId + if taskData.Type == TYPE_TASK && ui { + taskData.Type = TYPE_BROWSER + } + + ts.TsTaskCreate(agentId, cmdline, clientName, taskData) + + if (taskData.Type != TYPE_BROWSER) && (len(messageData.Message) > 0 || len(messageData.Text) > 0) { + ts.TsAgentConsoleOutput(agentId, messageData.Status, messageData.Message, messageData.Text, false) + } + return nil } func (ts *Teamserver) TsAgentProcessData(agentId string, bodyData []byte) error { @@ -271,29 +286,6 @@ func (ts *Teamserver) TsAgentUpdateData(newAgentData adaptix.AgentData) error { return nil } -func (ts *Teamserver) TsAgentImpersonate(agentId string, impersonated string, elevated bool) error { - value, ok := ts.agents.Get(agentId) - if !ok { - return errors.New("agent does not exist") - } - agent, _ := value.(*Agent) - - agent.Data.Impersonated = impersonated - if impersonated != "" && elevated { - agent.Data.Impersonated += " *" - } - - err := ts.DBMS.DbAgentUpdate(agent.Data) - if err != nil { - logs.Error("", err.Error()) - } - - packetNew := CreateSpAgentUpdate(agent.Data) - ts.TsSyncAllClients(packetNew) - - return nil -} - func (ts *Teamserver) TsAgentTerminate(agentId string, terminateTaskId string) error { value, ok := ts.agents.Get(agentId) if !ok { @@ -371,6 +363,10 @@ func (ts *Teamserver) TsAgentTerminate(agentId string, terminateTaskId string) e packet := CreateSpAgentTaskRemove(task) ts.TsSyncAllClients(packet) } + + if task.Type == TYPE_JOB { + agent.RunningJobs.Delete(task.TaskId) + } } /// Clear Pivots @@ -478,6 +474,8 @@ func (ts *Teamserver) TsAgentRemove(agentId string) error { return nil } +/// Setters + func (ts *Teamserver) TsAgentSetTag(agentId string, tag string) error { value, ok := ts.agents.Get(agentId) if !ok { @@ -563,6 +561,29 @@ func (ts *Teamserver) TsAgentSetColor(agentId string, background string, foregro return nil } +func (ts *Teamserver) TsAgentSetImpersonate(agentId string, impersonated string, elevated bool) error { + value, ok := ts.agents.Get(agentId) + if !ok { + return errors.New("agent does not exist") + } + agent, _ := value.(*Agent) + + agent.Data.Impersonated = impersonated + if impersonated != "" && elevated { + agent.Data.Impersonated += " *" + } + + err := ts.DBMS.DbAgentUpdate(agent.Data) + if err != nil { + logs.Error("", err.Error()) + } + + packetNew := CreateSpAgentUpdate(agent.Data) + ts.TsSyncAllClients(packetNew) + + return nil +} + /// Sync func (ts *Teamserver) TsAgentTickUpdate() { @@ -588,8 +609,8 @@ func (ts *Teamserver) TsAgentTickUpdate() { } } -func (ts *Teamserver) TsAgentGenerate(agentName string, config string, operatingSystem string, listenerWM string, listenerProfile []byte) ([]byte, string, error) { - return ts.Extender.ExAgentGenerate(agentName, config, operatingSystem, listenerWM, listenerProfile) +func (ts *Teamserver) TsAgentGenerate(agentName string, config string, listenerWM string, listenerProfile []byte) ([]byte, string, error) { + return ts.Extender.ExAgentGenerate(agentName, config, listenerWM, listenerProfile) } /// Console diff --git a/AdaptixServer/core/server/ts_browser.go b/AdaptixServer/core/server/ts_browser.go index d0c1248e..7aa84302 100644 --- a/AdaptixServer/core/server/ts_browser.go +++ b/AdaptixServer/core/server/ts_browser.go @@ -1,113 +1,10 @@ package server import ( - "fmt" "github.com/Adaptix-Framework/axc2" "strings" ) -/// AGENT - -func (ts *Teamserver) TsAgentGuiDisks(agentId string, clientName string) error { - value, ok := ts.agents.Get(agentId) - if !ok { - return fmt.Errorf("agent '%v' does not exist", agentId) - } - - agent, _ := value.(*Agent) - if agent.Active == false { - return fmt.Errorf("agent '%v' not active", agentId) - } - - taskData, err := ts.Extender.ExAgentBrowserDisks(agent.Data) - if err != nil { - return err - } - - ts.TsTaskCreate(agentId, "", clientName, taskData) - return nil -} - -func (ts *Teamserver) TsAgentGuiProcess(agentId string, clientName string) error { - value, ok := ts.agents.Get(agentId) - if !ok { - return fmt.Errorf("agent '%v' does not exist", agentId) - } - - agent, _ := value.(*Agent) - if agent.Active == false { - return fmt.Errorf("agent '%v' not active", agentId) - } - - taskData, err := ts.Extender.ExAgentBrowserProcess(agent.Data) - if err != nil { - return err - } - - ts.TsTaskCreate(agentId, "", clientName, taskData) - return nil -} - -func (ts *Teamserver) TsAgentGuiFiles(agentId string, path string, clientName string) error { - value, ok := ts.agents.Get(agentId) - if !ok { - return fmt.Errorf("agent '%v' does not exist", agentId) - } - - agent, _ := value.(*Agent) - if agent.Active == false { - return fmt.Errorf("agent '%v' not active", agentId) - } - - taskData, err := ts.Extender.ExAgentBrowserFiles(agent.Data, path) - if err != nil { - return err - } - - ts.TsTaskCreate(agentId, "", clientName, taskData) - return nil -} - -func (ts *Teamserver) TsAgentGuiUpload(agentId string, path string, content []byte, clientName string) error { - value, ok := ts.agents.Get(agentId) - if !ok { - return fmt.Errorf("agent '%v' does not exist", agentId) - } - - agent, _ := value.(*Agent) - if agent.Active == false { - return fmt.Errorf("agent '%v' not active", agentId) - } - - taskData, err := ts.Extender.ExAgentBrowserUpload(agent.Data, path, content) - if err != nil { - return err - } - - ts.TsTaskCreate(agentId, "", clientName, taskData) - return nil -} - -func (ts *Teamserver) TsAgentGuiExit(agentId string, clientName string) error { - value, ok := ts.agents.Get(agentId) - if !ok { - return fmt.Errorf("agent '%v' does not exist", agentId) - } - - agent, _ := value.(*Agent) - if agent.Active == false { - return fmt.Errorf("agent '%v' not active", agentId) - } - - taskData, err := ts.Extender.ExAgentCtxExit(agent.Data) - if err != nil { - return err - } - - ts.TsTaskCreate(agentId, "agent terminate", clientName, taskData) - return nil -} - /// SYNC func (ts *Teamserver) TsClientGuiDisks(taskData adaptix.TaskData, jsonDrives string) { diff --git a/AdaptixServer/core/server/ts_clients.go b/AdaptixServer/core/server/ts_clients.go index 9ab27b46..c1547324 100644 --- a/AdaptixServer/core/server/ts_clients.go +++ b/AdaptixServer/core/server/ts_clients.go @@ -58,7 +58,7 @@ func (ts *Teamserver) TsClientSync(username string) { socket := client.socket if !client.synced { - ts.TsSyncStored(socket) + ts.TsSyncStored(client) for { if client.tmp_store.Len() > 0 { diff --git a/AdaptixServer/core/server/ts_creds.go b/AdaptixServer/core/server/ts_creds.go new file mode 100644 index 00000000..12ba9261 --- /dev/null +++ b/AdaptixServer/core/server/ts_creds.go @@ -0,0 +1,97 @@ +package server + +import ( + "fmt" + adaptix "github.com/Adaptix-Framework/axc2" + "math/rand" + "time" +) + +func (ts *Teamserver) TsCredentilsAdd(username string, password string, realm string, credType string, tag string, storage string, agentId string, host string) error { + + for value := range ts.credentials.Iterator() { + cred := value.Item.(*adaptix.CredsData) + if cred.Username == username && cred.Realm == realm && cred.Password == password { + return nil + } + } + + credsData := &adaptix.CredsData{ + CredId: fmt.Sprintf("%08x", rand.Uint32()), + Username: username, + Password: password, + Realm: realm, + Type: credType, + Tag: tag, + Date: time.Now().Unix(), + Storage: storage, + AgentId: agentId, + Host: host, + } + + ts.credentials.Put(credsData) + + _ = ts.DBMS.DbCredentialsAdd(*credsData) + + packet := CreateSpCredentialsAdd(*credsData) + ts.TsSyncAllClients(packet) + + return nil +} + +func (ts *Teamserver) TsCredentilsEdit(credId string, username string, password string, realm string, credType string, tag string, storage string, host string) error { + + var cred *adaptix.CredsData + found := false + for value := range ts.credentials.Iterator() { + cred = value.Item.(*adaptix.CredsData) + if cred.CredId == credId { + + if cred.Username == username && cred.Realm == realm && cred.Password == password && cred.Type == credType && cred.Tag == tag && cred.Storage == storage && cred.Host == host { + return nil + } + + found = true + + cred.Username = username + cred.Password = password + cred.Realm = realm + cred.Type = credType + cred.Tag = tag + cred.Storage = storage + cred.Host = host + break + } + } + + if !found { + return fmt.Errorf("creds %s not exists", credId) + } + + _ = ts.DBMS.DbCredentialsUpdate(*cred) + + packet := CreateSpCredentialsUpdate(*cred) + ts.TsSyncAllClients(packet) + + return nil +} + +func (ts *Teamserver) TsCredentilsDelete(credId string) error { + + for i := uint(0); i < ts.credentials.Len(); i++ { + valuePivot, ok := ts.credentials.Get(i) + if ok { + if valuePivot.(*adaptix.CredsData).CredId == credId { + ts.credentials.Delete(i) + break + } + } + } + + _ = ts.DBMS.DbCredentialsDelete(credId) + + packet := CreateSpCredentialsDelete(credId) + ts.TsSyncAllClients(packet) + + return nil +} diff --git a/AdaptixServer/core/server/ts_downloads.go b/AdaptixServer/core/server/ts_downloads.go index 99a0f9f7..86c9279a 100644 --- a/AdaptixServer/core/server/ts_downloads.go +++ b/AdaptixServer/core/server/ts_downloads.go @@ -108,7 +108,7 @@ func (ts *Teamserver) TsDownloadClose(fileId string, reason int) error { err := downloadData.File.Close() if err != nil { - fmt.Println(fmt.Sprintf("Failed to finish download [%x] file: %v", downloadData.FileId, err)) + logs.Debug("", fmt.Sprintf("Failed to finish download [%x] file: %v", downloadData.FileId, err)) } if reason == DOWNLOAD_STATE_FINISHED { @@ -187,94 +187,6 @@ func (ts *Teamserver) TsDownloadDelete(fileId string) error { /// -func (ts *Teamserver) TsDownloadTaskStart(agentId string, path string, clientName string) error { - value, ok := ts.agents.Get(agentId) - if !ok { - return fmt.Errorf("agent '%v' does not exist", agentId) - } - - agent, _ := value.(*Agent) - if agent.Active == false { - return fmt.Errorf("agent '%v' not active", agentId) - } - - taskData, err := ts.Extender.ExAgentDownloadTaskStart(agent.Data, path) - if err != nil { - return err - } - - ts.TsTaskCreate(agentId, "", clientName, taskData) - return nil -} - -func (ts *Teamserver) TsDownloadTaskCancel(fileId string, clientName string) error { - value, ok := ts.downloads.Get(fileId) - if !ok { - return errors.New("File not found: " + fileId) - } - downloadData := value.(adaptix.DownloadData) - - value, ok = ts.agents.Get(downloadData.AgentId) - if !ok { - return errors.New("Agent not found: " + downloadData.AgentId) - } - agent := value.(*Agent) - - taskData, err := ts.Extender.ExAgentDownloadTaskCancel(agent.Data, fileId) - if err != nil { - return err - } - - ts.TsTaskCreate(agent.Data.Id, "", clientName, taskData) - return nil -} - -func (ts *Teamserver) TsDownloadTaskResume(fileId string, clientName string) error { - value, ok := ts.downloads.Get(fileId) - if !ok { - return errors.New("File not found: " + fileId) - } - downloadData := value.(adaptix.DownloadData) - - value, ok = ts.agents.Get(downloadData.AgentId) - if !ok { - return errors.New("Agent not found: " + downloadData.AgentId) - } - agent := value.(*Agent) - - taskData, err := ts.Extender.ExAgentDownloadTaskResume(agent.Data, fileId) - if err != nil { - return err - } - - ts.TsTaskCreate(agent.Data.Id, "", clientName, taskData) - return nil -} - -func (ts *Teamserver) TsDownloadTaskPause(fileId string, clientName string) error { - value, ok := ts.downloads.Get(fileId) - if !ok { - return errors.New("File not found: " + fileId) - } - downloadData := value.(adaptix.DownloadData) - - value, ok = ts.agents.Get(downloadData.AgentId) - if !ok { - return errors.New("Agent not found: " + downloadData.AgentId) - } - agent := value.(*Agent) - - taskData, err := ts.Extender.ExAgentDownloadTaskPause(agent.Data, fileId) - if err != nil { - return err - } - - ts.TsTaskCreate(agent.Data.Id, "", clientName, taskData) - return nil -} - -/// - func (ts *Teamserver) TsDownloadGetFilepath(fileId string) (string, error) { value, ok := ts.downloads.Get(fileId) if !ok { diff --git a/AdaptixServer/core/server/ts_event.go b/AdaptixServer/core/server/ts_event.go index d1218d28..63289034 100644 --- a/AdaptixServer/core/server/ts_event.go +++ b/AdaptixServer/core/server/ts_event.go @@ -15,10 +15,10 @@ func (ts *Teamserver) TsEventClient(connected bool, username string) { var packet SpEvent if connected { - message := fmt.Sprintf("Client '%v' connected to teamserver", username) + message := fmt.Sprintf("Operator '%v' connected to teamserver", username) packet = CreateSpEvent(EVENT_CLIENT_CONNECT, message) } else { - message := fmt.Sprintf("Client '%v' disconnected from teamserver", username) + message := fmt.Sprintf("Operator '%v' disconnected from teamserver", username) packet = CreateSpEvent(EVENT_CLIENT_DISCONNECT, message) } diff --git a/AdaptixServer/core/server/ts_sync.go b/AdaptixServer/core/server/ts_sync.go index a604a82f..0f5f8894 100644 --- a/AdaptixServer/core/server/ts_sync.go +++ b/AdaptixServer/core/server/ts_sync.go @@ -9,14 +9,14 @@ import ( "sort" ) -func (ts *Teamserver) TsSyncClient(username string, packet interface{}) { - var ( - buffer bytes.Buffer - err error - clientWS *websocket.Conn - ) +func (ts *Teamserver) TsClientConnected(username string) bool { + _, found := ts.clients.Get(username) + return found +} - err = json.NewEncoder(&buffer).Encode(packet) +func (ts *Teamserver) TsSyncClient(username string, packet interface{}) { + var buffer bytes.Buffer + err := json.NewEncoder(&buffer).Encode(packet) if err != nil { return } @@ -24,8 +24,9 @@ func (ts *Teamserver) TsSyncClient(username string, packet interface{}) { value, found := ts.clients.Get(username) if found { client := value.(*Client) - clientWS = client.socket - err = clientWS.WriteMessage(websocket.BinaryMessage, buffer.Bytes()) + client.lockSocket.Lock() + err = client.socket.WriteMessage(websocket.BinaryMessage, buffer.Bytes()) + client.lockSocket.Unlock() if err != nil { return } @@ -57,7 +58,7 @@ func (ts *Teamserver) TsSyncAllClients(packet interface{}) { }) } -func (ts *Teamserver) TsSyncStored(clientWS *websocket.Conn) { +func (ts *Teamserver) TsSyncStored(client *Client) { var ( buffer bytes.Buffer packets []interface{} @@ -71,21 +72,25 @@ func (ts *Teamserver) TsSyncStored(clientWS *websocket.Conn) { packets = append(packets, ts.TsPresyncTunnels()...) packets = append(packets, ts.TsPresyncEvents()...) packets = append(packets, ts.TsPresyncPivots()...) + packets = append(packets, ts.TsPresyncCredentials()...) - startPacket := CreateSpSyncStart(len(packets)) + client.lockSocket.Lock() + defer client.lockSocket.Unlock() + + startPacket := CreateSpSyncStart(len(packets), ts.Parameters.Interfaces) _ = json.NewEncoder(&buffer).Encode(startPacket) - _ = clientWS.WriteMessage(websocket.BinaryMessage, buffer.Bytes()) + _ = client.socket.WriteMessage(websocket.BinaryMessage, buffer.Bytes()) buffer.Reset() for _, p := range packets { var pBuffer bytes.Buffer _ = json.NewEncoder(&pBuffer).Encode(p) - _ = clientWS.WriteMessage(websocket.BinaryMessage, pBuffer.Bytes()) + _ = client.socket.WriteMessage(websocket.BinaryMessage, pBuffer.Bytes()) } finishPacket := CreateSpSyncFinish() _ = json.NewEncoder(&buffer).Encode(finishPacket) - _ = clientWS.WriteMessage(websocket.BinaryMessage, buffer.Bytes()) + _ = client.socket.WriteMessage(websocket.BinaryMessage, buffer.Bytes()) buffer.Reset() } @@ -95,14 +100,14 @@ func (ts *Teamserver) TsPresyncExtenders() []interface{} { var packets []interface{} ts.listener_configs.ForEach(func(key string, value interface{}) bool { listenerInfo := value.(extender.ListenerInfo) - p := CreateSpListenerReg(key, listenerInfo.UI) + p := CreateSpListenerReg(key, listenerInfo.AX) packets = append(packets, p) return true }) ts.agent_configs.ForEach(func(key string, value interface{}) bool { agentInfo := value.(extender.AgentInfo) - p := CreateSpAgentReg(agentInfo.Name, agentInfo.Watermark, agentInfo.ListenersJson, agentInfo.HandlersJson) + p := CreateSpAgentReg(agentInfo.Name, agentInfo.AX, agentInfo.Listeners) packets = append(packets, p) return true }) @@ -211,6 +216,16 @@ func (ts *Teamserver) TsPresyncScreenshots() []interface{} { return packets } +func (ts *Teamserver) TsPresyncCredentials() []interface{} { + var packets []interface{} + for value := range ts.credentials.Iterator() { + creds := value.Item.(*adaptix.CredsData) + p := CreateSpCredentialsAdd(*creds) + packets = append(packets, p) + } + return packets +} + func (ts *Teamserver) TsPresyncTunnels() []interface{} { var packets []interface{} ts.tunnels.ForEach(func(key string, value interface{}) bool { diff --git a/AdaptixServer/core/server/ts_syncpacket.go b/AdaptixServer/core/server/ts_syncpacket.go index bb5e4b5f..d027330b 100644 --- a/AdaptixServer/core/server/ts_syncpacket.go +++ b/AdaptixServer/core/server/ts_syncpacket.go @@ -39,6 +39,7 @@ const ( TYPE_AGENT_TASK_UPDATE = 0x4a TYPE_AGENT_TASK_SEND = 0x4b TYPE_AGENT_TASK_REMOVE = 0x4c + TYPE_AGENT_TASK_HOOK = 0x4d TYPE_DOWNLOAD_CREATE = 0x51 TYPE_DOWNLOAD_UPDATE = 0x52 @@ -63,6 +64,10 @@ const ( TYPE_PIVOT_CREATE = 0x71 TYPE_PIVOT_DELETE = 0x72 + + TYPE_CREDS_CREATE = 0x81 + TYPE_CREDS_EDIT = 0x82 + TYPE_CREDS_DELETE = 0x83 ) func CreateSpEvent(event int, message string) SpEvent { @@ -79,11 +84,12 @@ func CreateSpEvent(event int, message string) SpEvent { /// SYNC -func CreateSpSyncStart(count int) SyncPackerStart { +func CreateSpSyncStart(count int, addrs []string) SyncPackerStart { return SyncPackerStart{ SpType: TYPE_SYNC_START, - Count: count, + Count: count, + Addresses: addrs, } } @@ -95,12 +101,12 @@ func CreateSpSyncFinish() SyncPackerFinish { /// LISTENER -func CreateSpListenerReg(fn string, ui string) SyncPackerListenerReg { +func CreateSpListenerReg(fn string, ax string) SyncPackerListenerReg { return SyncPackerListenerReg{ SpType: TYPE_LISTENER_REG, ListenerFN: fn, - ListenerUI: ui, + ListenerAX: ax, } } @@ -134,14 +140,13 @@ func CreateSpListenerStop(name string) SyncPackerListenerStop { /// AGENT -func CreateSpAgentReg(agent string, watermark string, listenersJson string, handlersJson string) SyncPackerAgentReg { +func CreateSpAgentReg(agent string, ax string, listeners []string) SyncPackerAgentReg { return SyncPackerAgentReg{ SpType: TYPE_AGENT_REG, - Agent: agent, - Watermark: watermark, - ListenersJson: listenersJson, - HandlersJson: handlersJson, + Agent: agent, + AX: ax, + Listeners: listeners, } } @@ -261,6 +266,21 @@ func CreateSpAgentTaskRemove(taskData adaptix.TaskData) SyncPackerAgentTaskRemov } } +func CreateSpAgentTaskHook(taskData adaptix.TaskData, jobIndex int) SyncPackerAgentTaskHook { + return SyncPackerAgentTaskHook{ + SpType: TYPE_AGENT_TASK_HOOK, + + AgentId: taskData.AgentId, + TaskId: taskData.TaskId, + HookId: taskData.HookId, + JobIndex: jobIndex, + MessageType: taskData.MessageType, + Message: taskData.Message, + Text: taskData.ClearText, + Completed: taskData.Completed, + } +} + func CreateSpAgentConsoleOutput(agentId string, messageType int, message string, text string) SyncPackerAgentConsoleOutput { return SyncPackerAgentConsoleOutput{ SpCreateTime: time.Now().UTC().Unix(), @@ -392,6 +412,48 @@ func CreateSpScreenshotDelete(screenId string) SyncPackerScreenshotDelete { } } +/// SCREEN + +func CreateSpCredentialsAdd(credsData adaptix.CredsData) SyncPackerCredentialsAdd { + return SyncPackerCredentialsAdd{ + SpType: TYPE_CREDS_CREATE, + + CredId: credsData.CredId, + Username: credsData.Username, + Password: credsData.Password, + Realm: credsData.Realm, + Type: credsData.Type, + Tag: credsData.Tag, + Date: credsData.Date, + Storage: credsData.Storage, + AgentId: credsData.AgentId, + Host: credsData.Host, + } +} + +func CreateSpCredentialsUpdate(credsData adaptix.CredsData) SyncPackerCredentialsUpdate { + return SyncPackerCredentialsUpdate{ + SpType: TYPE_CREDS_EDIT, + + CredId: credsData.CredId, + Username: credsData.Username, + Password: credsData.Password, + Realm: credsData.Realm, + Type: credsData.Type, + Tag: credsData.Tag, + Storage: credsData.Storage, + Host: credsData.Host, + } +} + +func CreateSpCredentialsDelete(credsId string) SyncPackerCredentialsDelete { + return SyncPackerCredentialsDelete{ + SpType: TYPE_CREDS_DELETE, + + CredId: credsId, + } +} + /// BROWSER func CreateSpBrowserDisks(taskData adaptix.TaskData, data string) SyncPacketBrowserDisks { diff --git a/AdaptixServer/core/server/ts_tasks.go b/AdaptixServer/core/server/ts_tasks.go index e6ead298..b3f98948 100644 --- a/AdaptixServer/core/server/ts_tasks.go +++ b/AdaptixServer/core/server/ts_tasks.go @@ -3,6 +3,7 @@ package server import ( "AdaptixServer/core/utils/krypt" "AdaptixServer/core/utils/logs" + "AdaptixServer/core/utils/safe" "fmt" "github.com/Adaptix-Framework/axc2" "time" @@ -52,14 +53,14 @@ func (ts *Teamserver) TsTaskCreate(agentId string, cmdline string, client string case TYPE_TASK: if taskData.Sync { - packet := CreateSpAgentTaskSync(taskData) - ts.TsSyncAllClients(packet) + packet_task := CreateSpAgentTaskSync(taskData) + ts.TsSyncAllClients(packet_task) - packet2 := CreateSpAgentConsoleTaskSync(taskData) - ts.TsSyncAllClients(packet2) + packet_console := CreateSpAgentConsoleTaskSync(taskData) + ts.TsSyncAllClients(packet_console) - agent.OutConsole.Put(packet2) - _ = ts.DBMS.DbConsoleInsert(agentId, packet2) + agent.OutConsole.Put(packet_console) + _ = ts.DBMS.DbConsoleInsert(agentId, packet_console) } agent.TasksQueue.Put(taskData) @@ -68,14 +69,14 @@ func (ts *Teamserver) TsTaskCreate(agentId string, cmdline string, client string case TYPE_JOB: if taskData.Sync { - packet := CreateSpAgentTaskSync(taskData) - ts.TsSyncAllClients(packet) + packet_task := CreateSpAgentTaskSync(taskData) + ts.TsSyncAllClients(packet_task) - packet2 := CreateSpAgentConsoleTaskSync(taskData) - ts.TsSyncAllClients(packet2) + packet_console := CreateSpAgentConsoleTaskSync(taskData) + ts.TsSyncAllClients(packet_console) - agent.OutConsole.Put(packet2) - _ = ts.DBMS.DbConsoleInsert(agentId, packet2) + agent.OutConsole.Put(packet_console) + _ = ts.DBMS.DbConsoleInsert(agentId, packet_console) } agent.TasksQueue.Put(taskData) @@ -87,14 +88,14 @@ func (ts *Teamserver) TsTaskCreate(agentId string, cmdline string, client string agent.RunningTasks.Put(taskData.TaskId, taskData) } - packet := CreateSpAgentTaskSync(taskData) - ts.TsSyncAllClients(packet) + packet_task := CreateSpAgentTaskSync(taskData) + ts.TsSyncAllClients(packet_task) - packet2 := CreateSpAgentConsoleTaskSync(taskData) - ts.TsSyncAllClients(packet2) + packet_console := CreateSpAgentConsoleTaskSync(taskData) + ts.TsSyncAllClients(packet_console) - agent.OutConsole.Put(packet2) - _ = ts.DBMS.DbConsoleInsert(agentId, packet2) + agent.OutConsole.Put(packet_console) + _ = ts.DBMS.DbConsoleInsert(agentId, packet_console) if taskData.Completed { _ = ts.DBMS.DbTaskInsert(taskData) @@ -102,15 +103,14 @@ func (ts *Teamserver) TsTaskCreate(agentId string, cmdline string, client string } case TYPE_PROXY_DATA: - fmt.Println("----TYPE_PROXY_DATA----") - //agent.TunnelQueue.Put(taskData) + logs.Debug("", "----TYPE_PROXY_DATA----") default: break } } -func (ts *Teamserver) TsTaskUpdate(agentId string, taskData adaptix.TaskData) { +func (ts *Teamserver) TsTaskUpdate(agentId string, updateData adaptix.TaskData) { value, ok := ts.agents.Get(agentId) if !ok { logs.Error("", "TsTaskUpdate: agent %v not found", agentId) @@ -118,110 +118,297 @@ func (ts *Teamserver) TsTaskUpdate(agentId string, taskData adaptix.TaskData) { } agent, _ := value.(*Agent) - value, ok = agent.RunningTasks.GetDelete(taskData.TaskId) + value, ok = agent.RunningTasks.Get(updateData.TaskId) if !ok { return } task, _ := value.(adaptix.TaskData) task.Data = []byte("") - task.FinishDate = taskData.FinishDate - task.Completed = taskData.Completed if task.Type == TYPE_JOB { - if task.MessageType != CONSOLE_OUT_ERROR { - task.MessageType = taskData.MessageType - } + updateData.AgentId = agentId - var oldMessage string - if task.Message == "" { - oldMessage = taskData.Message - } else { - oldMessage = task.Message - } + if task.HookId != "" && task.Client != "" && ts.TsClientConnected(task.Client) { + updateData.HookId = task.HookId - oldText := task.ClearText + hookJob := &HookJob{ + Job: updateData, + Processed: false, + Sent: false, + } - task.Message = taskData.Message - task.ClearText = taskData.ClearText - - packet := CreateSpAgentTaskUpdate(task) - packet2 := CreateSpAgentConsoleTaskUpd(task) - - task.Message = oldMessage - task.ClearText = oldText + task.ClearText - - if task.Sync { - if task.Completed { - agent.CompletedTasks.Put(task.TaskId, task) + num := 0 + value2, ok := agent.RunningJobs.Get(task.TaskId) + if ok { + jobs := value2.(*safe.Slice) + jobs.Put(hookJob) + num = int(jobs.Len() - 1) } else { - agent.RunningTasks.Put(task.TaskId, task) + jobs := safe.NewSlice() + jobs.Put(hookJob) + agent.RunningJobs.Put(task.TaskId, jobs) } - if task.Completed { - _ = ts.DBMS.DbTaskInsert(task) + packet := CreateSpAgentTaskHook(updateData, num) + ts.TsSyncClient(task.Client, packet) + + } else { + if task.Sync { + + hookJob := &HookJob{ + Job: updateData, + Processed: true, + Sent: true, + } + + value2, ok := agent.RunningJobs.Get(task.TaskId) + + if updateData.Completed { + agent.RunningTasks.Delete(updateData.TaskId) + + if ok { + jobs := value2.(*safe.Slice) + jobs.Put(hookJob) + jobs_array := jobs.CutArray() + for _, job_value := range jobs_array { + jobData := job_value.(*HookJob) + if task.MessageType != CONSOLE_OUT_ERROR { + task.MessageType = jobData.Job.MessageType + } + if task.Message == "" { + task.Message = jobData.Job.Message + } + task.ClearText += jobData.Job.ClearText + } + + agent.RunningJobs.Delete(task.TaskId) + + } else { + task.MessageType = updateData.MessageType + task.Message = updateData.Message + task.ClearText = updateData.ClearText + } + + task.FinishDate = updateData.FinishDate + task.Completed = updateData.Completed + + agent.CompletedTasks.Put(task.TaskId, task) + _ = ts.DBMS.DbTaskInsert(task) + + } else { + if ok { + jobs := value2.(*safe.Slice) + jobs.Put(hookJob) + } else { + jobs := safe.NewSlice() + jobs.Put(hookJob) + agent.RunningJobs.Put(task.TaskId, jobs) + } + } + + packet_task_update := CreateSpAgentTaskUpdate(updateData) + packet_console_update := CreateSpAgentConsoleTaskUpd(updateData) + + ts.TsSyncAllClients(packet_task_update) + ts.TsSyncAllClients(packet_console_update) + + agent.OutConsole.Put(packet_console_update) + _ = ts.DBMS.DbConsoleInsert(agentId, packet_console_update) } - - ts.TsSyncAllClients(packet) - ts.TsSyncAllClients(packet2) - - agent.OutConsole.Put(packet2) - _ = ts.DBMS.DbConsoleInsert(agentId, packet2) } } else if task.Type == TYPE_TUNNEL { - var oldMessage string + agent.RunningTasks.Delete(updateData.TaskId) + + task.FinishDate = updateData.FinishDate + task.Completed = updateData.Completed + task.MessageType = updateData.MessageType + + var tmpTask = task + tmpTask.Message = updateData.Message + tmpTask.ClearText = updateData.ClearText + if task.Message == "" { - oldMessage = taskData.Message - } else { - oldMessage = task.Message + task.Message = updateData.Message } - oldText := task.ClearText - - task.MessageType = taskData.MessageType - task.Message = taskData.Message - task.ClearText = taskData.ClearText - - packet := CreateSpAgentTaskUpdate(task) - packet2 := CreateSpAgentConsoleTaskUpd(task) - - task.Message = oldMessage - task.ClearText = oldText + task.ClearText + task.ClearText += updateData.ClearText if task.Sync { if task.Completed { agent.CompletedTasks.Put(task.TaskId, task) + _ = ts.DBMS.DbTaskInsert(task) } else { agent.RunningTasks.Put(task.TaskId, task) } - if task.Completed { - _ = ts.DBMS.DbTaskInsert(task) - } + packet_task_update := CreateSpAgentTaskUpdate(tmpTask) + packet_console_update := CreateSpAgentConsoleTaskUpd(tmpTask) - ts.TsSyncAllClients(packet) - ts.TsSyncAllClients(packet2) + ts.TsSyncAllClients(packet_task_update) + ts.TsSyncAllClients(packet_console_update) - agent.OutConsole.Put(packet2) - _ = ts.DBMS.DbConsoleInsert(agentId, packet2) + agent.OutConsole.Put(packet_console_update) + _ = ts.DBMS.DbConsoleInsert(agentId, packet_console_update) } } else if task.Type == TYPE_TASK || task.Type == TYPE_BROWSER { - task.MessageType = taskData.MessageType - task.Message = taskData.Message - task.ClearText = taskData.ClearText + agent.RunningTasks.Delete(updateData.TaskId) + + task.FinishDate = updateData.FinishDate + task.Completed = updateData.Completed + task.MessageType = updateData.MessageType + task.Message = updateData.Message + task.ClearText = updateData.ClearText + + if task.HookId != "" && task.Client != "" && ts.TsClientConnected(task.Client) { + + agent.RunningTasks.Put(task.TaskId, task) + + packet := CreateSpAgentTaskHook(task, 0) + ts.TsSyncClient(task.Client, packet) + + } else { + if task.Sync { + if task.Completed { + agent.CompletedTasks.Put(task.TaskId, task) + _ = ts.DBMS.DbTaskInsert(task) + } else { + agent.RunningTasks.Put(task.TaskId, task) + } + + packet := CreateSpAgentTaskUpdate(task) + ts.TsSyncAllClients(packet) + + packet2 := CreateSpAgentConsoleTaskUpd(task) + ts.TsSyncAllClients(packet2) + + agent.OutConsole.Put(packet2) + _ = ts.DBMS.DbConsoleInsert(agentId, packet2) + } + } + } +} + +func (ts *Teamserver) TsTaskPostHook(hookData adaptix.TaskData, jobIndex int) error { + value, ok := ts.agents.Get(hookData.AgentId) + if !ok { + return fmt.Errorf("agent %v not found", hookData.AgentId) + } + agent, _ := value.(*Agent) + + value, ok = agent.RunningTasks.Get(hookData.TaskId) + if !ok { + return fmt.Errorf("task %v not found", hookData.TaskId) + } + task, _ := value.(adaptix.TaskData) + + if task.HookId == "" || task.HookId != hookData.HookId || task.Client != hookData.Client || !ts.TsClientConnected(task.Client) { + return fmt.Errorf("Operation not available") + } + + if task.Type == TYPE_JOB { + + if task.Sync { + + value2, ok := agent.RunningJobs.Get(task.TaskId) + if !ok { + return fmt.Errorf("job %v not found", task.TaskId) + } + jobs := value2.(*safe.Slice) + + jobValue, ok := jobs.Get(uint(jobIndex)) + if !ok { + return fmt.Errorf("job %v not found", task.TaskId) + } + jobData := jobValue.(*HookJob) + + jobData.Job.MessageType = hookData.MessageType + jobData.Job.Message = hookData.Message + jobData.Job.ClearText = hookData.ClearText + jobData.Processed = true + + completed := false + sent := false + + jobs.DirectLock() + defer jobs.DirectUnlock() + + slice := jobs.DirectSlice() + for i := 0; i < len(slice); i++ { + + hookJob := slice[i].(*HookJob) + if hookJob.Job.Completed { + completed = true + } + hookJob.mu.Lock() + if !hookJob.Sent { + if hookJob.Processed { + hookJob.Sent = true + + packet_task_update := CreateSpAgentTaskUpdate(hookJob.Job) + packet_console_update := CreateSpAgentConsoleTaskUpd(hookJob.Job) + + ts.TsSyncAllClients(packet_task_update) + ts.TsSyncAllClients(packet_console_update) + + agent.OutConsole.Put(packet_console_update) + _ = ts.DBMS.DbConsoleInsert(task.AgentId, packet_console_update) + sent = true + } else { + hookJob.mu.Unlock() + break + } + } + hookJob.mu.Unlock() + } + + if completed && sent { + agent.RunningTasks.Delete(task.TaskId) + + for i := 0; i < len(slice); i++ { + hookJob := slice[i].(*HookJob) + if task.MessageType != CONSOLE_OUT_ERROR { + task.MessageType = hookJob.Job.MessageType + } + if task.Message == "" { + task.Message = hookJob.Job.Message + } + task.ClearText += hookJob.Job.ClearText + + task.FinishDate = hookJob.Job.FinishDate + task.Completed = hookJob.Job.Completed + } + + agent.RunningJobs.Delete(task.TaskId) + + agent.CompletedTasks.Put(task.TaskId, task) + _ = ts.DBMS.DbTaskInsert(task) + } + } + + } else if task.Type == TYPE_TUNNEL { + + } else if task.Type == TYPE_TASK || task.Type == TYPE_BROWSER { + + _, ok = agent.RunningTasks.GetDelete(hookData.TaskId) + if !ok { + return fmt.Errorf("task %v not found", hookData.TaskId) + } + + task.MessageType = hookData.MessageType + task.Message = hookData.Message + task.ClearText = hookData.ClearText if task.Sync { if task.Completed { + task.HookId = "" agent.CompletedTasks.Put(task.TaskId, task) + _ = ts.DBMS.DbTaskInsert(task) } else { agent.RunningTasks.Put(task.TaskId, task) } - if task.Completed { - _ = ts.DBMS.DbTaskInsert(task) - } - packet := CreateSpAgentTaskUpdate(task) ts.TsSyncAllClients(packet) @@ -229,9 +416,39 @@ func (ts *Teamserver) TsTaskUpdate(agentId string, taskData adaptix.TaskData) { ts.TsSyncAllClients(packet2) agent.OutConsole.Put(packet2) - _ = ts.DBMS.DbConsoleInsert(agentId, packet2) + _ = ts.DBMS.DbConsoleInsert(task.AgentId, packet2) } } + return nil +} + +func (ts *Teamserver) TsTaskCancel(agentId string, taskId string) error { + value, ok := ts.agents.Get(agentId) + if !ok { + return fmt.Errorf("agent %v not found", agentId) + } + agent, _ := value.(*Agent) + + var task adaptix.TaskData + found := false + for i := uint(0); i < agent.TasksQueue.Len(); i++ { + if value, ok = agent.TasksQueue.Get(i); ok { + task = value.(adaptix.TaskData) + if task.TaskId == taskId { + agent.TasksQueue.Delete(i) + found = true + break + } + } + } + + if found { + packet := CreateSpAgentTaskRemove(task) + ts.TsSyncAllClients(packet) + return nil + } + + return nil } func (ts *Teamserver) TsTaskDelete(agentId string, taskId string) error { @@ -268,48 +485,6 @@ func (ts *Teamserver) TsTaskDelete(agentId string, taskId string) error { return nil } -func (ts *Teamserver) TsTaskStop(agentId string, taskId string) error { - value, ok := ts.agents.Get(agentId) - if !ok { - return fmt.Errorf("agent %v not found", agentId) - } - agent, _ := value.(*Agent) - - var task adaptix.TaskData - found := false - for i := uint(0); i < agent.TasksQueue.Len(); i++ { - if value, ok = agent.TasksQueue.Get(i); ok { - task = value.(adaptix.TaskData) - if task.TaskId == taskId { - agent.TasksQueue.Delete(i) - found = true - break - } - } - } - - if found { - packet := CreateSpAgentTaskRemove(task) - ts.TsSyncAllClients(packet) - return nil - } - - value, ok = agent.RunningTasks.Get(taskId) - if !ok { - return nil - } - if value.(adaptix.TaskData).Type != TYPE_JOB { - return fmt.Errorf("task %v in process", taskId) - } - - taskData, err := ts.Extender.ExAgentBrowserJobKill(agent.Data, taskId) - if err != nil { - return err - } - ts.TsTaskCreate(agent.Data.Id, "job kill "+taskId, "", taskData) - return nil -} - ///// Get Tasks func (ts *Teamserver) TsTaskGetAvailableAll(agentId string, availableSize int) ([]adaptix.TaskData, error) { @@ -446,7 +621,6 @@ func (ts *Teamserver) TsTaskGetAvailableTasks(agentId string, availableSize int) break } } - if len(sendTasks) > 0 { packet := CreateSpAgentTaskSend(sendTasks) ts.TsSyncAllClients(packet) diff --git a/AdaptixServer/core/server/ts_tunnels.go b/AdaptixServer/core/server/ts_tunnels.go index 6cd96800..b58e90c0 100644 --- a/AdaptixServer/core/server/ts_tunnels.go +++ b/AdaptixServer/core/server/ts_tunnels.go @@ -2,6 +2,7 @@ package server import ( "AdaptixServer/core/utils/krypt" + "AdaptixServer/core/utils/logs" "AdaptixServer/core/utils/proxy" "AdaptixServer/core/utils/safe" "context" @@ -658,9 +659,17 @@ func (ts *Teamserver) TsTunnelConnectionResume(AgentId string, channelId int, io if ok { if tunnel.Data.Client == "" { - relaySocketToTunnel(agent, tunnel, tunChannel, ioDirect) + if tunChannel.conn != nil { + relaySocketToTunnel(agent, tunnel, tunChannel, ioDirect) + } else { + logs.Debug("", "[ERROR] tunChannel.conn is nil in relaySocketToTunnel") + } } else { - relayWebsocketToTunnel(agent, tunnel, tunChannel, ioDirect) + if tunChannel.wsconn != nil { + relayWebsocketToTunnel(agent, tunnel, tunChannel, ioDirect) + } else { + logs.Debug("", "[ERROR] tunChannel.wsconn is nil in relayWebsocketToTunnel") + } } } } @@ -743,7 +752,7 @@ func handleTunChannelCreate(agent *Agent, tunnel *Tunnel, conn net.Conn) { case TUNNEL_SOCKS4: targetAddress, targetPort, err := proxy.CheckSocks4(conn) if err != nil { - //fmt.Println("Socks4 proxy error: ", err) + logs.Debug("", "[ERROR] Socks4 proxy error: ", err) return } taskData = tunnel.handlerConnectTCP(tunChannel.channelId, targetAddress, targetPort) @@ -751,7 +760,7 @@ func handleTunChannelCreate(agent *Agent, tunnel *Tunnel, conn net.Conn) { case TUNNEL_SOCKS5: targetAddress, targetPort, socksCommand, err := proxy.CheckSocks5(conn) if err != nil { - //fmt.Println("Socks5 proxy error: ", err) + logs.Debug("", "[ERROR] Socks5 proxy error: ", err) return } if socksCommand == 3 { @@ -764,7 +773,7 @@ func handleTunChannelCreate(agent *Agent, tunnel *Tunnel, conn net.Conn) { case TUNNEL_SOCKS5_AUTH: targetAddress, targetPort, socksCommand, err := proxy.CheckSocks5Auth(conn, tunnel.Data.AuthUser, tunnel.Data.AuthPass) if err != nil { - //fmt.Println("Socks5 proxy error: ", err) + logs.Debug("", "Socks5 proxy error: ", err) return } if socksCommand == 3 { @@ -896,11 +905,23 @@ func relaySocketToTunnel(agent *Agent, tunnel *Tunnel, tunChannel *TunnelChannel } go func() { + if tunChannel.pwSrv == nil || tunChannel.conn == nil { + logs.Debug("", "[ERROR relaySocketToTunnel] pwSrv or conn == nil — error copy (pwSrv <- conn)") + closeChannel() + return + } + io.Copy(tunChannel.pwSrv, tunChannel.conn) closeChannel() }() go func() { + if tunChannel.prTun == nil || tunChannel.conn == nil { + logs.Debug("", "[ERROR relaySocketToTunnel] prTun or conn == nil — error copy (conn <- prTun)") + closeChannel() + return + } + io.Copy(tunChannel.conn, tunChannel.prTun) closeChannel() }() diff --git a/AdaptixServer/core/server/utils.go b/AdaptixServer/core/server/utils.go index 5d34c39b..45735e1f 100644 --- a/AdaptixServer/core/server/utils.go +++ b/AdaptixServer/core/server/utils.go @@ -21,6 +21,7 @@ const ( CONSOLE_OUT_INFO = 5 CONSOLE_OUT_ERROR = 6 CONSOLE_OUT_SUCCESS = 7 + CONSOLE_OUT = 10 ) const ( @@ -49,11 +50,16 @@ type Client struct { tmp_store *safe.Slice } +type TsParameters struct { + Interfaces []string +} + type Teamserver struct { Profile *profile.AdaptixProfile DBMS *database.DBMS AdaptixServer *connector.TsConnector Extender *extender.AdaptixExtender + Parameters TsParameters listener_configs safe.Map // listenerFullName string : listenerInfo extender.ListenerInfo agent_configs safe.Map // agentName string : agentInfo extender.AgentInfo @@ -68,6 +74,7 @@ type Teamserver struct { downloads safe.Map // fileId string : downloadData DownloadData tmp_uploads safe.Map // fileId string : uploadData UploadData screenshots safe.Map // screeId string : screenData ScreenDataData + credentials *safe.Slice tunnels safe.Map // tunnelId string : tunnel Tunnel terminals safe.Map // terminalId string : terminal Terminal pivots *safe.Slice // : PivotData @@ -87,12 +94,20 @@ type Agent struct { TasksQueue *safe.Slice // taskData TaskData RunningTasks safe.Map // taskId string, taskData TaskData + RunningJobs safe.Map // taskId string, list []TaskData CompletedTasks safe.Map // taskId string, taskData TaskData PivotParent *adaptix.PivotData PivotChilds *safe.Slice } +type HookJob struct { + Sent bool + Processed bool + Job adaptix.TaskData + mu sync.Mutex +} + type TunnelChannel struct { channelId int protocol string @@ -150,7 +165,8 @@ type Terminal struct { type SyncPackerStart struct { SpType int `json:"type"` - Count int `json:"count"` + Count int `json:"count"` + Addresses []string `json:"interfaces"` } type SyncPackerFinish struct { @@ -171,7 +187,7 @@ type SyncPackerListenerReg struct { SpType int `json:"type"` ListenerFN string `json:"fn"` - ListenerUI string `json:"ui"` + ListenerAX string `json:"ax"` } type SyncPackerListenerStart struct { @@ -197,10 +213,9 @@ type SyncPackerListenerStop struct { type SyncPackerAgentReg struct { SpType int `json:"type"` - Agent string `json:"agent"` - Watermark string `json:"watermark"` - ListenersJson string `json:"listeners_json"` - HandlersJson string `json:"handlers_json"` + Agent string `json:"agent"` + AX string `json:"ax"` + Listeners []string `json:"listeners"` } type SyncPackerAgentNew struct { @@ -297,6 +312,19 @@ type SyncPackerAgentTaskUpdate struct { Completed bool `json:"a_completed"` } +type SyncPackerAgentTaskHook struct { + SpType int `json:"type"` + + AgentId string `json:"a_id"` + TaskId string `json:"a_task_id"` + HookId string `json:"a_hook_id"` + JobIndex int `json:"a_job_index"` + MessageType int `json:"a_msg_type"` + Message string `json:"a_message"` + Text string `json:"a_text"` + Completed bool `json:"a_completed"` +} + type SyncPackerAgentTaskSend struct { SpType int `json:"type"` @@ -412,6 +440,42 @@ type SyncPackerScreenshotDelete struct { ScreenId string `json:"s_screen_id"` } +/// CREDS + +type SyncPackerCredentialsAdd struct { + SpType int `json:"type"` + + CredId string `json:"c_creds_id"` + Username string `json:"c_username"` + Password string `json:"c_password"` + Realm string `json:"c_realm"` + Type string `json:"c_type"` + Tag string `json:"c_tag"` + Date int64 `json:"c_date"` + Storage string `json:"c_storage"` + AgentId string `json:"c_agent_id"` + Host string `json:"c_host"` +} + +type SyncPackerCredentialsUpdate struct { + SpType int `json:"type"` + + CredId string `json:"c_creds_id"` + Username string `json:"c_username"` + Password string `json:"c_password"` + Realm string `json:"c_realm"` + Type string `json:"c_type"` + Tag string `json:"c_tag"` + Storage string `json:"c_storage"` + Host string `json:"c_host"` +} + +type SyncPackerCredentialsDelete struct { + SpType int `json:"type"` + + CredId string `json:"c_creds_id"` +} + /// BROWSER type SyncPacketBrowserDisks struct { diff --git a/AdaptixServer/core/utils/safe/slice.go b/AdaptixServer/core/utils/safe/slice.go index d2642669..fd00cb7d 100644 --- a/AdaptixServer/core/utils/safe/slice.go +++ b/AdaptixServer/core/utils/safe/slice.go @@ -45,6 +45,17 @@ func (sl *Slice) Delete(index uint) { sl.items = append(sl.items[:index], sl.items[index+1:]...) } +func (sl *Slice) Replace(index uint, value interface{}) { + sl.mutex.Lock() + defer sl.mutex.Unlock() + + if index >= uint(len(sl.items)) { + return + } + + sl.items[index] = value +} + func (sl *Slice) DirectLock() { sl.mutex.RLock() } diff --git a/AdaptixServer/go.mod b/AdaptixServer/go.mod index d922db94..2e1ce52b 100644 --- a/AdaptixServer/go.mod +++ b/AdaptixServer/go.mod @@ -3,26 +3,26 @@ module AdaptixServer go 1.24.4 require ( - github.com/Adaptix-Framework/axc2 v0.5.0 + github.com/Adaptix-Framework/axc2 v0.7.0 github.com/gin-gonic/gin v1.10.1 - github.com/golang-jwt/jwt/v5 v5.2.2 + github.com/golang-jwt/jwt/v5 v5.2.3 github.com/gorilla/websocket v1.5.3 - github.com/mattn/go-sqlite3 v1.14.28 - golang.org/x/image v0.28.0 + github.com/mattn/go-sqlite3 v1.14.29 + golang.org/x/image v0.29.0 ) require ( - github.com/bytedance/sonic v1.13.3 // indirect - github.com/bytedance/sonic/loader v0.2.4 // indirect + github.com/bytedance/sonic v1.14.0 // indirect + github.com/bytedance/sonic/loader v0.3.0 // indirect github.com/cloudwego/base64x v0.1.5 // indirect github.com/gabriel-vasile/mimetype v1.4.9 // indirect github.com/gin-contrib/sse v1.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.26.0 // indirect + github.com/go-playground/validator/v10 v10.27.0 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.2.10 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -30,11 +30,11 @@ require ( github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.3.0 // indirect - golang.org/x/arch v0.18.0 // indirect - golang.org/x/crypto v0.39.0 // indirect - golang.org/x/net v0.41.0 // indirect - golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.26.0 // indirect + golang.org/x/arch v0.19.0 // indirect + golang.org/x/crypto v0.40.0 // indirect + golang.org/x/net v0.42.0 // indirect + golang.org/x/sys v0.34.0 // indirect + golang.org/x/text v0.27.0 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/AdaptixServer/go.sum b/AdaptixServer/go.sum index 9f1c8f8d..9a86a0f0 100644 --- a/AdaptixServer/go.sum +++ b/AdaptixServer/go.sum @@ -1,10 +1,10 @@ -github.com/Adaptix-Framework/axc2 v0.5.0 h1:x7gO3cUhpOTfrchkMKalcMxw/xzB3dDO1iZstTUsEsE= -github.com/Adaptix-Framework/axc2 v0.5.0/go.mod h1:jQ9Yca/qAs1xnMseCH4zGAeV4r0xps/xmXq5B6f9jog= -github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0= -github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= +github.com/Adaptix-Framework/axc2 v0.7.0 h1:KncnUnbcrODS9zlos/H+PiiXHQcrF+cmaZnG7zBld1A= +github.com/Adaptix-Framework/axc2 v0.7.0/go.mod h1:jQ9Yca/qAs1xnMseCH4zGAeV4r0xps/xmXq5B6f9jog= +github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ= +github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= -github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= +github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= @@ -20,27 +20,27 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= -github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= +github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= +github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= -github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0= +github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= -github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= -github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mattn/go-sqlite3 v1.14.29 h1:1O6nRLJKvsi1H2Sj0Hzdfojwt8GiGKm+LOfLaBFaouQ= +github.com/mattn/go-sqlite3 v1.14.29/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -61,19 +61,19 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= -golang.org/x/arch v0.18.0 h1:WN9poc33zL4AzGxqf8VtpKUnGvMi8O9lhNyBMF/85qc= -golang.org/x/arch v0.18.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= -golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= -golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= -golang.org/x/image v0.28.0 h1:gdem5JW1OLS4FbkWgLO+7ZeFzYtL3xClb97GaUzYMFE= -golang.org/x/image v0.28.0/go.mod h1:GUJYXtnGKEUgggyzh+Vxt+AviiCcyiwpsl8iQ8MvwGY= -golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= -golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/arch v0.19.0 h1:LmbDQUodHThXE+htjrnmVD73M//D9GTH6wFZjyDkjyU= +golang.org/x/arch v0.19.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= +golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= +golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/image v0.29.0 h1:HcdsyR4Gsuys/Axh0rDEmlBmB68rW1U9BUdB3UVHsas= +golang.org/x/image v0.29.0/go.mod h1:RVJROnf3SLK8d26OW91j4FrIHGbsJ8QnbEocVTOWQDA= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= -golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/AdaptixServer/main.go b/AdaptixServer/main.go index 1c64a008..a1610730 100644 --- a/AdaptixServer/main.go +++ b/AdaptixServer/main.go @@ -10,13 +10,14 @@ import ( "strings" ) -const VERSION = "0.6" +const VERSION = "0.7" func main() { fmt.Printf("\n[===== Adaptix Framework v%v =====]\n\n", VERSION) var ( err error + host = flag.String("i", "0.0.0.0", "Teamserver listen interface") port = flag.Int("p", 0, "Teamserver handler port") endpoint = flag.String("e", "", "Teamserver URI endpoint") password = flag.String("pw", "", "Teamserver password") @@ -33,7 +34,7 @@ func main() { flag.PrintDefaults() fmt.Printf("\nEither provide options individually or use a JSON config file with -config flag.\n\n") fmt.Printf("Example:\n") - fmt.Printf(" AdaptixServer -p port -pw password -e endpoint -sc SslCert -sk SslKey [-ex ext1,ext2,...] [-debug]\n") + fmt.Printf(" AdaptixServer -i 0.0.0.0 -p port -pw password -e endpoint -sc SslCert -sk SslKey [-ex ext1,ext2,...] [-debug]\n") fmt.Printf(" AdaptixServer -profile profile.json [-debug]\n") } flag.Parse() @@ -55,7 +56,7 @@ func main() { } } else if *port > 1 && *port < 65535 && *endpoint != "" && *password != "" { extenders := strings.Split(*extenderPath, ",") - ts.SetSettings(*port, *endpoint, *password, *certPath, *keyPath, extenders) + ts.SetSettings(*host, *port, *endpoint, *password, *certPath, *keyPath, extenders) } else { flag.Usage() os.Exit(0) @@ -70,5 +71,6 @@ func main() { token.InitJWT(ts.Profile.Server.ATokenLive, ts.Profile.Server.RTokenLive) ts.Extender.LoadPlugins(ts.Profile.Server.Extenders) + ts.Start() } diff --git a/AdaptixServer/profile.json b/AdaptixServer/profile.json index 9c5b889f..fb792f17 100644 --- a/AdaptixServer/profile.json +++ b/AdaptixServer/profile.json @@ -1,5 +1,6 @@ { "Teamserver": { + "interface": "0.0.0.0", "port": 4321, "endpoint": "/endpoint", "password": "pass", @@ -22,7 +23,7 @@ "headers": { "Content-Type": "text/html; charset=UTF-8", "Server": "AdaptixC2", - "Adaptix Version": "v0.6" + "Adaptix Version": "v0.7" }, "page": "404page.html" }, diff --git a/Dockerfile b/Dockerfile index 2af4651c..29f95150 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.23.7-bookworm AS builder +FROM golang:1.24.4-bookworm AS builder RUN apt-get update && apt-get upgrade -y && apt-get install -y mingw-w64 make libssl-dev qt6-base-dev qt6-websockets-dev sudo libcap2-bin build-essential checkinstall zlib1g-dev libssl-dev # client requires cmake version 3.28+ diff --git a/Extenders/agent_beacon/ax_config.axs b/Extenders/agent_beacon/ax_config.axs new file mode 100644 index 00000000..b2cf369e --- /dev/null +++ b/Extenders/agent_beacon/ax_config.axs @@ -0,0 +1,366 @@ +/// Beacon agent + +let exit_thread_action = menu.create_action("Terminate thread", function(value) { value.forEach(v => ax.execute_command(v, "terminate thread")) }); +let exit_process_action = menu.create_action("Terminate process", function(value) { value.forEach(v => ax.execute_command(v, "terminate process")) }); +let exit_menu = menu.create_menu("Exit"); +exit_menu.addItem(exit_thread_action) +exit_menu.addItem(exit_process_action) +menu.add_session_agent(exit_menu, ["beacon"]) + + +let file_browser_action = menu.create_action("File Browser", function(value) { value.forEach(v => ax.open_browser_files(v)) }); +let process_browser_action = menu.create_action("Process Browser", function(value) { value.forEach(v => ax.open_browser_process(v)) }); +menu.add_session_browser(file_browser_action, ["beacon"]) +menu.add_session_browser(process_browser_action, ["beacon"]) + + +let tunnel_access_action = menu.create_action("Create Tunnel", function(value) { ax.open_access_tunnel(value[0], true, true, true, true) }); +menu.add_session_access(tunnel_access_action, ["beacon"]) + + +let execute_action = menu.create_action("Execute", function(files_list) { + file = files_list[0]; + if(file.type != "file"){ return; } + + let label_bin = form.create_label("Binary:"); + let text_bin = form.create_textline(file.path + file.name); + text_bin.setEnabled(false); + let label_args = form.create_label("Arguments:"); + let text_args = form.create_textline(); + let output_check = form.create_check("Show command output"); + output_check.setChecked(true); + + let layout = form.create_gridlayout(); + layout.addWidget(label_bin, 0, 0, 1, 1); + layout.addWidget(text_bin, 0, 1, 1, 1); + layout.addWidget(label_args, 1, 0, 1, 1); + layout.addWidget(text_args, 1, 1, 1, 1); + layout.addWidget(output_check, 2, 1, 1, 1); + + let dialog = form.create_dialog("Execute binary"); + dialog.setSize(600, 100); + dialog.setLayout(layout); + if ( dialog.exec() == true ) + { + let command = "ps run "; + if( output_check.isChecked()) { command += "-o "; } + command += text_bin.text() + " " + text_args.text(); + + ax.execute_command(file.agent_id, command); + } +}); +let download_action = menu.create_action("Download", function(files_list) { + files_list.forEach((file) => { + if(file.type == "file") { + ax.execute_command(file.agent_id, "download " + file.path + file.name); + } + }); +}); +let remove_action = menu.create_action("Remove", function(files_list) { + files_list.forEach(file => ax.execute_command(file.agent_id, "rm " + file.path + file.name)) +}); +menu.add_filebrowser(execute_action, ["beacon"]) +menu.add_filebrowser(download_action, ["beacon"]) +menu.add_filebrowser(remove_action, ["beacon"]) + + + + +let download_stop_action = menu.create_action("Pause", function(files_list) { files_list.forEach( file => ax.execute_command(file.agent_id, "exfil stop " + file.file_id) ) }); +let download_start_action = menu.create_action("Resume", function(files_list) { files_list.forEach( file => ax.execute_command(file.agent_id, "exfil start " + file.file_id) ) }); +let download_separator1 = menu.create_separator() +let download_cancel_action = menu.create_action("Cancel", function(files_list) { files_list.forEach( file => ax.execute_command(file.agent_id, "exfil cancel " + file.file_id) ) }); +menu.add_downloads_running(download_stop_action, ["beacon"]) +menu.add_downloads_running(download_start_action, ["beacon"]) +menu.add_downloads_running(download_separator1, ["beacon"]) +menu.add_downloads_running(download_cancel_action, ["beacon"]) + + +let job_stop_action = menu.create_action("Stop job", function(tasks_list) { + tasks_list.forEach((task) => { + if(task.type == "JOB" && task.state == "Running") { + ax.execute_command(task.agent_id, "jobs kill " + task.task_id); + } + }); +}); +menu.add_tasks_job(job_stop_action, ["beacon"]) + + +var event_disks_action = function(id) { + ax.execute_browser(id, "disks"); +} +event.on_filebrowser_disks(event_disks_action, ["beacon"]); + +var event_files_action = function(id, path) { + ax.execute_browser(id, "ls " + path); +} +event.on_filebrowser_list(event_files_action, ["beacon"]); + +var event_upload_action = function(id, path, filepath) { + let filename = ax.file_basename(filepath); + ax.execute_browser(id, "upload " + filepath + " " + path + filename); +} +event.on_filebrowser_upload(event_upload_action, ["beacon"]); + +var event_process_action = function(id) { + ax.execute_browser(id, "ps list"); +} +event.on_processbrowser_list(event_process_action, ["beacon"]); + + +function RegisterCommands(listenerType) +{ + let cmd_cat = ax.create_command("cat", "Read first 2048 bytes of the specified file", "cat C:\\file.exe", "Task: read file"); + cmd_cat.addArgString("path", true); + + let cmd_cd = ax.create_command("cd", "Change current working directory", "cd C:\\Windows", "Task: change working directory"); + cmd_cd.addArgString("path", true); + + let cmd_cp = ax.create_command("cp", "Copy file", "cp src.txt dst.txt", "Task: copy file"); + cmd_cp.addArgString("src", true); + cmd_cp.addArgString("dst", true); + + let cmd_disks = ax.create_command("disks", "Lists mounted drives on current system", "disks", "Task: show mounted disks"); + + let cmd_download = ax.create_command("download", "Download a file", "download C:\\Temp\\file.txt", "Task: download file"); + cmd_download.addArgString("file", true); + + let _cmd_execute_bof = ax.create_command("bof", "Execute Beacon Object File", "execute bof /home/user/whoami.o", "Task: execute BOF"); + _cmd_execute_bof.addArgFile("bof", true, "Path to object file"); + _cmd_execute_bof.addArgString("param_data", false); + let cmd_execute = ax.create_command("execute", "Execute [bof] in the current process's memory"); + cmd_execute.addSubCommands([_cmd_execute_bof]) + + let _cmd_exfil_cancel = ax.create_command("cancel", "Cancels a download", "exfil cancel 1a2b3c4d"); + _cmd_exfil_cancel.addArgString("file_id", true); + let _cmd_exfil_start = ax.create_command("start", "Resumes a download that's has been stoped", "exfil start 1a2b3c4d"); + _cmd_exfil_start.addArgString("file_id", true); + let _cmd_exfil_stop = ax.create_command("stop", "Stops a download that's in-progress", "exfil stop 1a2b3c4d"); + _cmd_exfil_stop.addArgString("file_id", true); + let cmd_exfil = ax.create_command("exfil", "Manage current downloads"); + cmd_exfil.addSubCommands([_cmd_exfil_cancel, _cmd_exfil_start, _cmd_exfil_stop]) + + let cmd_getuid = ax.create_command("getuid", "Prints the User ID associated with the current token", "getuid", "Task: get username of current token"); + + let _cmd_job_list = ax.create_command("list", "List of jobs", "jobs list", "Task: show jobs"); + let _cmd_job_kill = ax.create_command("kill", "Kill a specified job", "jobs kill 1a2b3c4d", "Task: kill job"); + _cmd_job_kill.addArgString("task_id", true); + let cmd_job = ax.create_command("jobs", "Long-running tasks manager"); + cmd_job.addSubCommands([_cmd_job_list, _cmd_job_kill]); + + let _cmd_link_smb = ax.create_command("smb", "Connect to an SMB agent and re-establish control of it", "link smb 192.168.1.2 pipe_a1b2", "Task: Connect to an SMB agent"); + _cmd_link_smb.addArgString("target", true); + _cmd_link_smb.addArgString("pipename", true); + let _cmd_link_tcp = ax.create_command("tcp", "Connect to an TCP agent and re-establish control of it", "link tcp 192.168.1.2 8888", "Task: Connect to an TCP agent"); + _cmd_link_tcp.addArgString("target", true); + _cmd_link_tcp.addArgInt("port", true); + let cmd_link = ax.create_command("link", "Connect to an pivot agents"); + cmd_link.addSubCommands([_cmd_link_smb, _cmd_link_tcp]); + + let cmd_ls = ax.create_command("ls", "Lists files in a folder", "ls C:\\Windows", "Task: list of files in a folder"); + cmd_ls.addArgString("directory", "", "."); + + let _cmd_lportfwd_start = ax.create_command("start", "Start local port forwarding from server via agent", "lportfwd start 127.0.0.1 8080 192.168.1.1 8080"); + _cmd_lportfwd_start.addArgString("lhost", "Listening interface address on server", "0.0.0.0"); + _cmd_lportfwd_start.addArgInt("lport", true, "Listen port on server"); + _cmd_lportfwd_start.addArgString("fwdhost", true, "Remote forwarding address"); + _cmd_lportfwd_start.addArgInt("fwdport", true, "Remote forwarding port"); + let _cmd_lportfwd_stop = ax.create_command("stop", "Stop local port forwarding", "lportfwd stop 8080"); + _cmd_lportfwd_stop.addArgInt("lport", true); + let cmd_lportfwd = ax.create_command("lportfwd", "Managing local port forwarding"); + cmd_lportfwd.addSubCommands([_cmd_lportfwd_start, _cmd_lportfwd_stop]); + + let cmd_mv = ax.create_command("mv", "Move file", "mv src.txt dst.txt", "Task: move file"); + cmd_mv.addArgString("src", true); + cmd_mv.addArgString("dst", true); + + let cmd_mkdir = ax.create_command("mkdir", "Make a directory", "mkdir C:\\Temp", "Task: make directory"); + cmd_mkdir.addArgString("path", true); + + let _cmd_profile_chunksize = ax.create_command("download.chunksize", "Change the exfiltrate data size for download request (default 128000)", "profile download.chunksize 512000", "Task: set download chunk size"); + _cmd_profile_chunksize.addArgInt("size", true); + let _cmd_profile_killdate = ax.create_command("killdate", "Set the date and time for the beacon to stop working", "profile killdate 28.02.2030 12:34:00", "Task: set beacon's killdate"); + _cmd_profile_killdate.addArgString("datetime", true, "Datetime 'DD.MM.YYYY hh:mm:ss' in GMT format. Set 0 to disable the option"); + let _cmd_profile_workingtime = ax.create_command("workingtime", "Set the start and end time of the beacon activity", "profile workingtime 8:00-17:30", "Task: set beacon's workingtime"); + _cmd_profile_workingtime.addArgString("time", true, "Time interval in the format 'HH:mm(start)-HH:mm(end)'. Set 0 to disable the option"); + let cmd_profile = ax.create_command("profile", "Configure the payloads profile for current session"); + cmd_profile.addSubCommands([_cmd_profile_chunksize, _cmd_profile_killdate, _cmd_profile_workingtime]); + + let _cmd_ps_list = ax.create_command("list", "Show process list", "ps list", "Task: show process list"); + let _cmd_ps_kill = ax.create_command("kill", "Kill a process with a given PID", "ps kill 7865", "Task: kill process"); + _cmd_ps_kill.addArgInt("pid", true); + let _cmd_ps_run = ax.create_command("run", "Run a program", "run -s cmd.exe /c whoami /all", "Task: create new process"); + _cmd_ps_run.addArgBool("-s", "Suspend process"); + _cmd_ps_run.addArgBool("-o", "Output to console"); + _cmd_ps_run.addArgString("args", true); + let cmd_ps = ax.create_command("ps", "Process manager"); + cmd_ps.addSubCommands([_cmd_ps_list, _cmd_ps_kill, _cmd_ps_run]); + + let cmd_pwd = ax.create_command("pwd", "Print current working directory", "pwd", "Task: print working directory"); + + let cmd_rev2self = ax.create_command("rev2self", "Revert to your original access token", "rev2self", "Task: revert token"); + + let cmd_rm = ax.create_command("rm", "Remove a file or folder", "rm C:\\Temp\\file.txt", "Task: remove file or directory"); + cmd_rm.addArgString("path", true); + + let _cmd_rportfwd_start = ax.create_command("start", "Start remote port forwarding from agent via server", "rportfwd start 8080 10.10.10.14 8080"); + _cmd_rportfwd_start.addArgInt("lport", true, "Listen port on agent"); + _cmd_rportfwd_start.addArgString("fwdhost", true, "Remote forwarding address"); + _cmd_rportfwd_start.addArgInt("fwdport", true, "Remote forwarding port"); + let _cmd_rportfwd_stop = ax.create_command("stop", "Stop remote port forwarding", "rportfwd stop 8080"); + _cmd_rportfwd_stop.addArgInt("lport", true); + let cmd_rportfwd = ax.create_command("rportfwd", "Managing remote port forwarding"); + cmd_rportfwd.addSubCommands([_cmd_rportfwd_start, _cmd_rportfwd_stop]); + + let cmd_sleep = ax.create_command("sleep", "Sets sleep time", "sleep 30m5s 10"); + cmd_sleep.addArgString("sleep", true, "Time in '%h%m%s' format or number of seconds"); + cmd_sleep.addArgInt("jitter", false, "Max random amount of time in % added to sleep"); + + let _cmd_socks_start = ax.create_command("start", "Start a SOCKS(4a/5) proxy server and listen on a specified port", "socks start 1080 -auth user pass"); + _cmd_socks_start.addArgFlagString("-h", "address", "Listening interface address", "0.0.0.0"); + _cmd_socks_start.addArgInt("port", true, "Listen port"); + _cmd_socks_start.addArgBool("-socks4", "Use SOCKS4 proxy (Default SOCKS5)"); + _cmd_socks_start.addArgBool("-auth", "Enable User/Password authentication for SOCKS5"); + _cmd_socks_start.addArgString("username", false, "Username for SOCKS5 proxy"); + _cmd_socks_start.addArgString("password", false, "Password for SOCKS5 proxy"); + let _cmd_socks_stop = ax.create_command("stop", "Stop a SOCKS proxy server", "socks stop 1080"); + _cmd_socks_stop.addArgInt("port", true); + let cmd_socks = ax.create_command("socks", "Managing socks tunnels"); + cmd_socks.addSubCommands([_cmd_socks_start, _cmd_socks_stop]); + + let _cmd_terminate_thread = ax.create_command("thread", "Terminate the main beacon thread (without terminating the process)", "terminate thread", "Task: terminate agent thread"); + let _cmd_terminate_process = ax.create_command("process", "Terminate the beacon process", "terminate process", "Task: terminate agent process"); + let cmd_terminate = ax.create_command("terminate", "Terminate the session"); + cmd_terminate.addSubCommands([_cmd_terminate_thread, _cmd_terminate_process]); + + let cmd_unlink = ax.create_command("unlink", "Disconnect from an pivot agent", "unlink 1a2b3c4d", "Task: disconnect from an pivot agent"); + cmd_unlink.addArgString("id", true); + + let cmd_upload = ax.create_command("upload", "Upload a file", "upload /tmp/file.txt C:\\Temp\\file.txt", "Task: upload file"); + cmd_upload.addArgFile("local_file", true); + cmd_upload.addArgString("remote_path", false); + + /// Aliases + + let cmd_shell = ax.create_command("shell", "Execute command via cmd.exe", "shell whoami /all"); + cmd_shell.addArgString("cmd_params", true); + cmd_shell.setPreHook(function (id, cmdline, parsed_json, ...parsed_lines) { + let new_cmd = "ps run -o C:\\Windows\\System32\\cmd.exe /c " + parsed_json["cmd_params"]; + ax.execute_alias(id, cmdline, new_cmd); + }); + + let cmd_powershell = ax.create_command("powershell", "Execute command via powershell.exe", "powershell ls"); + cmd_powershell.addArgString("cmd_params", true); + cmd_powershell.setPreHook(function (id, cmdline, parsed_json, ...parsed_lines) { + let new_cmd = "ps run -o C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe -c " + parsed_json["cmd_params"]; + ax.execute_alias(id, cmdline, new_cmd); + }); + + let cmd_interact = ax.create_command("interact", "Set 'sleep 0'", "interact"); + cmd_interact.setPreHook(function (id, cmdline, parsed_json, ...parsed_lines) { + ax.execute_alias(id, cmdline, "sleep 0"); + }); + + if(listenerType == "BeaconHTTP") { + let commands_external = ax.create_commands_group("beacon", [cmd_cat, cmd_cd, cmd_cp, cmd_disks, cmd_download, cmd_execute, cmd_exfil, cmd_getuid, + cmd_job, cmd_link, cmd_ls, cmd_lportfwd, cmd_mv, cmd_mkdir, cmd_profile, cmd_ps, cmd_pwd, cmd_rev2self, cmd_rm, cmd_rportfwd, cmd_sleep, + cmd_socks, cmd_terminate, cmd_unlink, cmd_upload, cmd_shell, cmd_powershell, cmd_interact] ); + + return { commands_windows: commands_external } + } + else if (listenerType == "BeaconSMB" || listenerType == "BeaconTCP") { + let commands_internal = ax.create_commands_group("beacon", [cmd_cat, cmd_cd, cmd_cp, cmd_disks, cmd_download, cmd_execute, cmd_exfil, cmd_getuid, + cmd_job, cmd_link, cmd_ls, cmd_lportfwd, cmd_mv, cmd_mkdir, cmd_profile, cmd_ps, cmd_pwd, cmd_rev2self, cmd_rm, cmd_rportfwd, + cmd_socks, cmd_terminate, cmd_unlink, cmd_upload, cmd_shell, cmd_powershell, cmd_interact] ); + + return { commands_windows: commands_internal } + } + + return ax.create_commands_group("none",[]); +} + +function GenerateUI(listenerType) +{ + let labelArch = form.create_label("Arch:"); + let comboArch = form.create_combo() + comboArch.addItems(["x64", "x86"]); + + let labelFormat = form.create_label("Format:"); + let comboFormat = form.create_combo() + comboFormat.addItems(["Exe", "Service Exe", "DLL", "Shellcode"]); + + let labelSleep = form.create_label("Sleep (Jitter %):"); + let textSleep = form.create_textline("4s"); + textSleep.setPlaceholder("1h 2m 5s") + let spinJitter = form.create_spin(); + spinJitter.setRange(0, 100); + spinJitter.setValue(0); + + if(listenerType != "BeaconHTTP") { + labelSleep.setVisible(false); + textSleep.setVisible(false); + spinJitter.setVisible(false); + } + + let checkKilldate = form.create_check("Set 'killdate'"); + let dateKill = form.create_dateline("dd.MM.yyyy"); + let timeKill = form.create_timeline("HH:mm:ss"); + + let checkWorkingTime = form.create_check("Set 'workingtime'"); + let timeStart = form.create_timeline("HH:mm"); + let timeFinish = form.create_timeline("HH:mm"); + + let labelSvcName = form.create_label("Service Name:"); + labelSvcName.setVisible(false) + let textSvcName = form.create_textline("AgentService"); + textSvcName.setVisible(false); + + let layout = form.create_gridlayout(); + layout.addWidget(labelArch, 0, 0, 1, 1); + layout.addWidget(comboArch, 0, 1, 1, 2); + layout.addWidget(labelFormat, 1, 0, 1, 1); + layout.addWidget(comboFormat, 1, 1, 1, 2); + layout.addWidget(labelSleep, 2, 0, 1, 1); + layout.addWidget(textSleep, 2, 1, 1, 1); + layout.addWidget(spinJitter, 2, 2, 1, 1); + layout.addWidget(checkKilldate, 3, 0, 1, 1); + layout.addWidget(dateKill, 3, 1, 1, 1); + layout.addWidget(timeKill, 3, 2, 1, 1); + layout.addWidget(checkWorkingTime, 4, 0, 1, 1); + layout.addWidget(timeStart, 4, 1, 1, 1); + layout.addWidget(timeFinish, 4, 2, 1, 1); + layout.addWidget(labelSvcName, 5, 0, 1, 1); + layout.addWidget(textSvcName, 5, 1, 1, 2); + + form.connect(comboFormat, "currentTextChanged", function(text) { + if(text == "Service Exe") { + labelSvcName.setVisible(true) + textSvcName.setVisible(true); + } + else { + labelSvcName.setVisible(false) + textSvcName.setVisible(false); + } + }); + + let container = form.create_container() + container.put("arch", comboArch) + container.put("format", comboFormat) + container.put("sleep", textSleep) + container.put("jitter", spinJitter) + container.put("is_killdate", checkKilldate) + container.put("kill_date", dateKill) + container.put("kill_time", timeKill) + container.put("is_workingtime", checkWorkingTime) + container.put("start_time", timeStart) + container.put("end_time", timeFinish) + container.put("svcname", textSvcName) + + let panel = form.create_panel() + panel.setLayout(layout) + + return { + ui_panel: panel, + ui_container: container + } +} diff --git a/Extenders/agent_beacon/config.json b/Extenders/agent_beacon/config.json index 63ed5a0b..00fc07d4 100644 --- a/Extenders/agent_beacon/config.json +++ b/Extenders/agent_beacon/config.json @@ -1,1061 +1,9 @@ { "extender_type": "agent", "extender_file": "agent_beacon.so", + "ax_file": "ax_config.axs", "agent_name": "beacon", "agent_watermark": "be4c0149", - - "listeners": [ - { - "listener_name": "BeaconHTTP", - "configs" : [ - { - "operating_system": "windows", - "handler": "handler_external", - "generate_ui": { - "layout": "glayout", - "elements": [ - { - "type": "label", - "text": "Arch:", - "position": [0, 0, 1, 1] - }, - { - "type": "combo", - "items": ["x64", "x86"], - "id": "arch", - "position": [0, 1, 1, 2] - }, - { - "type": "label", - "text": "Format:", - "position": [1, 0, 1, 1] - }, - { - "type": "combo", - "items": ["Exe", "Service Exe", "DLL", "Shellcode" ], - "id": "format", - "position": [1, 1, 1, 2] - }, - { - "type": "label", - "text": "Sleep (Jitter %):", - "position": [2, 0, 1, 1] - }, - { - "type": "input", - "id": "sleep", - "placeholder": "1h 2m 5s", - "text": "4s", - "position": [2, 1, 1, 1] - }, - { - "type": "spinbox", - "id": "jitter", - "min": 0, - "max": 100, - "position": [2, 2, 1, 1] - }, - { - "type": "checkbox", - "text": "Set 'killdate'", - "id": "is_killdate", - "position": [3, 0, 1, 1] - }, - { - "type": "date_input", - "id": "kill_date", - "format": "dd.MM.yyyy", - "position": [3, 1, 1, 1] - }, - { - "type": "time_input", - "id": "kill_time", - "format": "HH:mm:ss", - "position": [3, 2, 1, 1] - }, - { - "type": "checkbox", - "text": "Set 'workingtime'", - "id": "is_workingtime", - "position": [4, 0, 1, 1] - }, - { - "type": "time_input", - "id": "start_time", - "format": "HH:mm", - "position": [4, 1, 1, 1] - }, - { - "type": "time_input", - "id": "end_time", - "format": "HH:mm", - "position": [4, 2, 1, 1] - }, - { - "type": "label", - "text": "Service Name:", - "position": [5, 0, 1, 1] - }, - { - "type": "input", - "id": "svcname", - "text": "AgentService", - "position": [5, 1, 1, 2] - } - ] - } - } - ] - }, - { - "listener_name": "BeaconSMB", - "configs" : [ - { - "operating_system": "windows", - "handler": "handler_internal", - "generate_ui": { - "layout": "glayout", - "elements": [ - { - "type": "label", - "text": "Arch:", - "position": [0, 0, 1, 1] - }, - { - "type": "combo", - "items": ["x64", "x86"], - "id": "arch", - "position": [0, 1, 1, 2] - }, - { - "type": "label", - "text": "Format:", - "position": [1, 0, 1, 1] - }, - { - "type": "combo", - "items": ["Exe", "Service Exe", "DLL", "Shellcode" ], - "id": "format", - "position": [1, 1, 1, 2] - }, - { - "type": "checkbox", - "text": "Set 'killdate'", - "id": "is_killdate", - "position": [2, 0, 1, 1] - }, - { - "type": "date_input", - "id": "kill_date", - "format": "dd.MM.yyyy", - "position": [2, 1, 1, 1] - }, - { - "type": "time_input", - "id": "kill_time", - "format": "HH:mm:ss", - "position": [2, 2, 1, 1] - }, - { - "type": "label", - "text": "Service Name:", - "position": [3, 0, 1, 1] - }, - { - "type": "input", - "id": "svcname", - "text": "AgentService", - "position": [3, 1, 1, 2] - } - ] - } - } - ] - }, - { - "listener_name": "BeaconTCP", - "configs" : [ - { - "operating_system": "windows", - "handler": "handler_internal", - "generate_ui": { - "layout": "glayout", - "elements": [ - { - "type": "label", - "text": "Arch:", - "position": [0, 0, 1, 1] - }, - { - "type": "combo", - "items": ["x64", "x86"], - "id": "arch", - "position": [0, 1, 1, 2] - }, - { - "type": "label", - "text": "Format:", - "position": [1, 0, 1, 1] - }, - { - "type": "combo", - "items": ["Exe", "Service Exe", "DLL", "Shellcode" ], - "id": "format", - "position": [1, 1, 1, 2] - }, - { - "type": "checkbox", - "text": "Set 'killdate'", - "id": "is_killdate", - "position": [2, 0, 1, 1] - }, - { - "type": "date_input", - "id": "kill_date", - "format": "dd.MM.yyyy", - "position": [2, 1, 1, 1] - }, - { - "type": "time_input", - "id": "kill_time", - "format": "HH:mm:ss", - "position": [2, 2, 1, 1] - }, - { - "type": "label", - "text": "Service Name:", - "position": [3, 0, 1, 1] - }, - { - "type": "input", - "id": "svcname", - "text": "AgentService", - "position": [3, 1, 1, 2] - } - ] - } - } - ] - } - ], - - "handlers": [ - { - "id": "handler_external", - - "commands": [ - { - "command": "cat", - "message": "Task: read file", - "description": "Read first 2048 bytes of the specified file", - "example": "cat c:\\\\file.txt", - "args": [ - "STRING " - ] - }, - { - "command": "cd", - "message": "Task: change working directory", - "description": "Change current working directory", - "example": "cd C:\\Windows\\Temp", - "args": [ - "STRING " - ] - }, - { - "command": "cp", - "message": "Task: copy file", - "description": "Copy file", - "example": "cp src.txt dst.txt", - "args": [ - "STRING ", - "STRING " - ] - }, - { - "command": "disks", - "message": "Task: show mounted disks", - "description": "Lists mounted drives on current system", - "example": "disks" - }, - { - "command": "download", - "message": "Task: download file to teamserver", - "description": "Download a file", - "example": "download C:\\file.txt", - "args": [ - "STRING " - ] - }, - { - "command": "execute", - "description": "Execute [bof] in the current process's memory", - "subcommands": - [ - { - "name": "bof", - "message": "Task: execute BOF", - "description": "Execute Beacon Object File", - "example": "execute bof /home/user/whoami.o", - "args": [ - "FILE {Path to object file}", - "STRING [param_data]" - ] - } - ] - }, - { - "command": "exfil", - "description": "Manage current downloads", - "subcommands": - [ - { - "name": "cancel", - "description": "Cancels a download", - "example": "exfil cancel 1a2b3c4d", - "args": [ - "STRING " - ] - }, - { - "name": "start", - "description": "Resumes a download that's has been stoped", - "example": "exfil start 1a2b3c4d", - "args": [ - "STRING " - ] - }, - { - "name": "stop", - "description": "Stops a download that's in-progress", - "example": "exfil stop 1a2b3c4d", - "args": [ - "STRING " - ] - } - ] - }, - { - "command": "getuid", - "message": "Task: get username of current token", - "description": "Prints the User ID associated with the current token", - "example": "getuid" - }, - { - "command": "jobs", - "description": "Long-running tasks manager", - "subcommands": - [ - { - "name": "list", - "message": "Task: show jobs", - "description": "List of jobs", - "example": "jobs list" - }, - { - "name": "kill", - "message": "Task: kill job", - "description": "Kill a specified job", - "example": "jobs kill 1a2b3c4d", - "args": [ - "STRING " - ] - } - ] - }, - { - "command": "link", - "description": "Connect to an pivot agents", - "subcommands": - [ - { - "name": "smb", - "message": "Task: Connect to an SMB agent", - "description": "Connect to an SMB agent and re-establish control of it", - "example": "link smb 192.168.1.2 pipe_a1b2", - "args": [ - "STRING ", - "STRING " - ] - }, - { - "name": "tcp", - "message": "Task: Connect to an TCP agent", - "description": "Connect to an TCP agent and re-establish control of it", - "example": "link tcp 192.168.1.2 8888", - "args": [ - "STRING ", - "INT " - ] - } - ] - }, - { - "command": "ls", - "message": "Task: list of files in a folder", - "description": "Lists files in a folder", - "example": "ls c:\\users", - "args": [ - "STRING (.)" - ] - }, - { - "command": "lportfwd", - "description": "Managing local port forwarding", - "subcommands": - [ - { - "name": "start", - "description": "Start local port forwarding from server via agent", - "example": "lportfwd start 127.0.0.1 8080 192.168.1.1 8080", - "args": [ - "STRING (0.0.0.0) {Listening interface address on server}", - "INT {Listen port on server}", - "STRING {Remote forwarding address}", - "INT {Remote forwarding port}" - ] - }, - { - "name": "stop", - "description": "Stop local port forwarding", - "example": "lportfwd stop 8080", - "args": [ - "INT " - ] - } - ] - }, - { - "command": "mv", - "message": "Task: move file", - "description": "Move file", - "example": "mv src.txt dst.txt", - "args": [ - "STRING ", - "STRING " - ] - }, - { - "command": "mkdir", - "message": "Task: make directory", - "description": "Make a directory", - "example": "mkdir C:\\dir", - "args": [ - "STRING " - ] - }, - { - "command": "profile", - "description": "Configure the payloads profile for current session", - "subcommands": - [ - { - "name": "download.chunksize", - "message": "Task: set download chunk size", - "description": "Change the exfiltrate data size for download request (default 128000)", - "example": "profile download.chunksize 512000", - "args": [ - "INT " - ] - }, - { - "name": "killdate", - "message": "Task: set beacon's killdate", - "description": "Set the date and time for the beacon to stop working.", - "example": "profile killdate 28.02.2030 12:34:00", - "args": [ - "STRING { Datetime 'DD.MM.YYYY hh:mm:ss' in GMT format. Set 0 to disable the option}" - ] - }, - { - "name": "workingtime", - "message": "Task: set beacon's workingtime", - "description": "Set the start and end time of the beacon activity", - "example": "profile workingtime 8:00-17:30", - "args": [ - "STRING