From b95369f254a03dc8f8cd8bfb9ae284bcf07ff971 Mon Sep 17 00:00:00 2001
From: Syping <schiedelrafael@keppe.org>
Date: Fri, 6 Aug 2021 04:48:24 +0200
Subject: [PATCH] xmppbot 0.5

---
 CMakeLists.txt                   |   2 +-
 src/xmppbot/main.cpp             | 160 ++++++++++++++++++-------------
 src/xmppbot/xmppbotlua.cpp       | 105 +++++++++++++++++++-
 src/xmppbot/xmppbotlua.h         |  12 ++-
 src/xmppbot/xmppbotluathread.cpp |  25 +++--
 src/xmppbot/xmppbotluathread.h   |   6 +-
 6 files changed, 233 insertions(+), 77 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 48aa5f9..2c36b8c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,6 @@
 cmake_minimum_required(VERSION 3.7)
 
-project(xmppbot VERSION 0.4 LANGUAGES CXX)
+project(xmppbot VERSION 0.5 LANGUAGES CXX)
 
 set(CMAKE_INCLUDE_CURRENT_DIR ON)
 
diff --git a/src/xmppbot/main.cpp b/src/xmppbot/main.cpp
index 252c8e4..cbfa02b 100644
--- a/src/xmppbot/main.cpp
+++ b/src/xmppbot/main.cpp
@@ -39,7 +39,7 @@ int main(int argc, char *argv[])
 {
     QCoreApplication app(argc, argv);
     app.setApplicationName(QLatin1String("xmppbot"));
-    app.setApplicationVersion(QLatin1String("0.4"));
+    app.setApplicationVersion(QLatin1String("0.5"));
 
     QCommandLineParser commandLineParser;
     commandLineParser.addPositionalArgument(QLatin1String("config"), QCoreApplication::translate("xmppbot", "Configuration file."));
@@ -64,7 +64,7 @@ int main(int argc, char *argv[])
     app.setProperty("XmppClient", QVariant::fromValue<QXmppClient*>(&client));
 
     bool loginSet = false;
-    QString jid, jpw;
+    QString jid, jpw, script;
     QHash<QString, QString> h_msg;
     QHash<QString, QString> h_lua;
     QHash<QString, QString> h_run;
@@ -72,76 +72,81 @@ int main(int argc, char *argv[])
         QSettings settings(settingsPath, QSettings::IniFormat);
         for (const QString &group : settings.childGroups()) {
             settings.beginGroup(group);
-            for (const QString &key : settings.childKeys()) {
-                if (key == QLatin1String("Password")) {
-                    if (!loginSet) {
-                        jid = group;
-                        const QString instance = settings.value(QLatin1String("Instance"), QString()).toString();
-                        if (!instance.isEmpty()) {
-                            jid += QLatin1String("/") + instance;
+            if (group == QLatin1String("xmppbot")) {
+                script = settings.value(QLatin1String("Script"), QString()).toString();
+            }
+            else {
+                for (const QString &key : settings.childKeys()) {
+                    if (key == QLatin1String("Password")) {
+                        if (!loginSet) {
+                            jid = group;
+                            const QString instance = settings.value(QLatin1String("Instance"), QString()).toString();
+                            if (!instance.isEmpty()) {
+                                jid += QLatin1String("/") + instance;
+                            }
+                            jpw = settings.value(key, QString()).toString();
+                            loginSet = true;
+                        }
+                        else {
+                            QTextStream(stderr) << "xmppbot: Login password can only be set once!" << xendl;
+                            return 1;
                         }
-                        jpw = settings.value(key, QString()).toString();
-                        loginSet = true;
                     }
-                    else {
-                        QTextStream(stderr) << "xmppbot: Login password can only be set once!" << xendl;
-                        return 1;
+                    if (key == QLatin1String("Incoming")) {
+                        const QString incoming = settings.value("Incoming", QString()).toString();
+                        if (incoming.startsWith(QLatin1String("message:"))) {
+                            QTextStream(stderr) << QLatin1String("xmppbot: Account message incoming ") << group << QLatin1String(" initialised") << xendl;
+                            h_msg.insert(group, incoming.mid(8));
+                        }
+                        if (incoming.startsWith(QLatin1String("lua:"))) {
+                            QTextStream(stderr) << QLatin1String("xmppbot: Account lua incoming ") << group << QLatin1String(" initialised") << xendl;
+                            h_lua.insert(group, incoming.mid(4));
+                        }
+                        if (incoming.startsWith(QLatin1String("run:"))) {
+                            QTextStream(stderr) << QLatin1String("xmppbot: Account run incoming ") << group << QLatin1String(" initialised") << xendl;
+                            h_run.insert(group, incoming.mid(4));
+                        }
                     }
-                }
-                if (key == QLatin1String("Incoming")) {
-                    const QString incoming = settings.value("Incoming", QString()).toString();
-                    if (incoming.startsWith(QLatin1String("message:"))) {
-                        QTextStream(stderr) << QLatin1String("xmppbot: Account message incoming ") << group << QLatin1String(" initialised") << xendl;
-                        h_msg.insert(group, incoming.mid(8));
-                    }
-                    if (incoming.startsWith(QLatin1String("lua:"))) {
-                        QTextStream(stderr) << QLatin1String("xmppbot: Account lua incoming ") << group << QLatin1String(" initialised") << xendl;
-                        h_lua.insert(group, incoming.mid(4));
-                    }
-                    if (incoming.startsWith(QLatin1String("run:"))) {
-                        QTextStream(stderr) << QLatin1String("xmppbot: Account run incoming ") << group << QLatin1String(" initialised") << xendl;
-                        h_run.insert(group, incoming.mid(4));
-                    }
-                }
-                if (key == QLatin1String(XmppSocketType)) {
-                    XmppSocket *xmppSocket = new XmppSocket(&client, jid, group);
+                    if (key == QLatin1String(XmppSocketType)) {
+                        XmppSocket *xmppSocket = new XmppSocket(&client, jid, group);
 #ifdef Q_OS_UNIX
-                    const QString permission = settings.value(QLatin1String("SocketPermission"), QString()).toString();
-                    if (permission == QLatin1String("UG") || permission == QLatin1String("UserGroup")) {
-                        xmppSocket->setSocketOptions(QLocalServer::UserAccessOption | QLocalServer::GroupAccessOption);
-                    }
-                    if (permission == QLatin1String("UO") || permission == QLatin1String("UserOther")) {
-                        xmppSocket->setSocketOptions(QLocalServer::UserAccessOption | QLocalServer::OtherAccessOption);
-                    }
-                    if (permission == QLatin1String("U") || permission == QLatin1String("User")) {
-                        xmppSocket->setSocketOptions(QLocalServer::UserAccessOption);
-                    }
-                    if (permission == QLatin1String("GO") || permission == QLatin1String("GroupOther")) {
-                        xmppSocket->setSocketOptions(QLocalServer::GroupAccessOption | QLocalServer::OtherAccessOption);
-                    }
-                    if (permission == QLatin1String("G") || permission == QLatin1String("Group")) {
-                        xmppSocket->setSocketOptions(QLocalServer::GroupAccessOption);
-                    }
-                    if (permission == QLatin1String("O") || permission == QLatin1String("Other")) {
-                        xmppSocket->setSocketOptions(QLocalServer::OtherAccessOption);
-                    }
-                    if (permission == QLatin1String("A") || permission == QLatin1String("All") || permission == QLatin1String("UGO") || permission == QLatin1String("UserGroupOther")) {
-                        xmppSocket->setSocketOptions(QLocalServer::WorldAccessOption);
-                    }
+                        const QString permission = settings.value(QLatin1String("SocketPermission"), QString()).toString();
+                        if (permission == QLatin1String("UG") || permission == QLatin1String("UserGroup")) {
+                            xmppSocket->setSocketOptions(QLocalServer::UserAccessOption | QLocalServer::GroupAccessOption);
+                        }
+                        if (permission == QLatin1String("UO") || permission == QLatin1String("UserOther")) {
+                            xmppSocket->setSocketOptions(QLocalServer::UserAccessOption | QLocalServer::OtherAccessOption);
+                        }
+                        if (permission == QLatin1String("U") || permission == QLatin1String("User")) {
+                            xmppSocket->setSocketOptions(QLocalServer::UserAccessOption);
+                        }
+                        if (permission == QLatin1String("GO") || permission == QLatin1String("GroupOther")) {
+                            xmppSocket->setSocketOptions(QLocalServer::GroupAccessOption | QLocalServer::OtherAccessOption);
+                        }
+                        if (permission == QLatin1String("G") || permission == QLatin1String("Group")) {
+                            xmppSocket->setSocketOptions(QLocalServer::GroupAccessOption);
+                        }
+                        if (permission == QLatin1String("O") || permission == QLatin1String("Other")) {
+                            xmppSocket->setSocketOptions(QLocalServer::OtherAccessOption);
+                        }
+                        if (permission == QLatin1String("A") || permission == QLatin1String("All") || permission == QLatin1String("UGO") || permission == QLatin1String("UserGroupOther")) {
+                            xmppSocket->setSocketOptions(QLocalServer::WorldAccessOption);
+                        }
 #endif
-                    const QString socketPath = settings.value(key, QString()).toString();
-                    bool listen = xmppSocket->listen(socketPath);
+                        const QString socketPath = settings.value(key, QString()).toString();
+                        bool listen = xmppSocket->listen(socketPath);
 #ifdef Q_OS_UNIX
-                    if (!listen) {
-                        QLocalServer::removeServer(socketPath);
-                        listen = xmppSocket->listen(socketPath);
-                    }
+                        if (!listen) {
+                            QLocalServer::removeServer(socketPath);
+                            listen = xmppSocket->listen(socketPath);
+                        }
 #endif
-                    if (listen) {
-                        QTextStream(stderr) << QLatin1String("xmppbot: Account socket ") << group << QLatin1String(" initialised") << xendl;
-                    }
-                    else {
-                        delete xmppSocket;
+                        if (listen) {
+                            QTextStream(stderr) << QLatin1String("xmppbot: Account socket ") << group << QLatin1String(" initialised") << xendl;
+                        }
+                        else {
+                            delete xmppSocket;
+                        }
                     }
                 }
             }
@@ -160,12 +165,22 @@ int main(int argc, char *argv[])
 
     QTextStream(stderr) << QLatin1String("xmppbot: Account login ") << jid << QLatin1String(" initialised") << xendl;
 
+    XmppBotLuaThread xmppBotLuaGlobalThread(script, QLatin1String("jidInitialised"), QVariantList() << jid, true);
+    if (!script.isEmpty() && QFile::exists(script)) {
+        xmppBotLuaGlobalThread.start();
+    }
+
     QObject::connect(&client, &QXmppClient::stateChanged, [&](QXmppClient::State state) {
         switch (state) {
         case QXmppClient::ConnectedState: {
             QTextStream(stderr) << QLatin1String("xmppbot: Account ") << jid << QLatin1String(" connected") << xendl;
             QXmppPresence xmppPresence(QXmppPresence::Available);
             client.setClientPresence(xmppPresence);
+            if (xmppBotLuaGlobalThread.isRunning()) {
+                const QString lua_function = QLatin1String("jidConnected");
+                const QVariantList lua_args = QVariantList() << jid;
+                QMetaObject::invokeMethod(&xmppBotLuaGlobalThread, "executeLuaFunction", Qt::QueuedConnection, Q_ARG(QString, lua_function), Q_ARG(QVariantList, lua_args));
+            }
             break;
         }
         case QXmppClient::ConnectingState:
@@ -175,6 +190,11 @@ int main(int argc, char *argv[])
             QTimer::singleShot(5000, &client, [&]() {
                 client.connectToServer(jid, jpw);
             });
+            if (xmppBotLuaGlobalThread.isRunning()) {
+                const QString lua_function = QLatin1String("jidDisconnected");
+                const QVariantList lua_args = QVariantList() << jid;
+                QMetaObject::invokeMethod(&xmppBotLuaGlobalThread, "executeLuaFunction", Qt::QueuedConnection, Q_ARG(QString, lua_function), Q_ARG(QVariantList, lua_args));
+            }
             break;
         default:
             break;
@@ -211,6 +231,11 @@ int main(int argc, char *argv[])
                 QTextStream(stderr) << QLatin1String("xmppbot: Account ") << from_jid << QLatin1String(" executed pid ") << pid << xendl;
             }
         }
+        if (xmppBotLuaGlobalThread.isRunning()) {
+            const QString lua_function = QLatin1String("messageReceived");
+            const QVariantList lua_args = QVariantList() << from << xmppMessage.to() << xmppMessage.body();
+            QMetaObject::invokeMethod(&xmppBotLuaGlobalThread, "executeLuaFunction", Qt::QueuedConnection, Q_ARG(QString, lua_function), Q_ARG(QVariantList, lua_args));
+        }
     });
 
     QObject::connect(&client, &QXmppClient::presenceReceived, [&](const QXmppPresence &xmppPresence) {
@@ -227,6 +252,11 @@ int main(int argc, char *argv[])
             QObject::connect(xmppBotLuaThread, &XmppBotLuaThread::finished, xmppBotLuaThread, &XmppBotLuaThread::deleteLater);
             xmppBotLuaThread->start();
         }
+        if (xmppBotLuaGlobalThread.isRunning()) {
+            const QString lua_function = QLatin1String("presenceReceived");
+            const QVariantList lua_args = QVariantList() << from << static_cast<int>(xmppPresence.type()) << static_cast<int>(xmppPresence.availableStatusType()) << xmppPresence.statusText();
+            QMetaObject::invokeMethod(&xmppBotLuaGlobalThread, "executeLuaFunction", Qt::QueuedConnection, Q_ARG(QString, lua_function), Q_ARG(QVariantList, lua_args));
+        }
     });
 
     client.connectToServer(jid, jpw);
diff --git a/src/xmppbot/xmppbotlua.cpp b/src/xmppbot/xmppbotlua.cpp
index 76ea812..88bb99f 100644
--- a/src/xmppbot/xmppbotlua.cpp
+++ b/src/xmppbot/xmppbotlua.cpp
@@ -17,6 +17,10 @@
 *****************************************************************************/
 
 #include <QCoreApplication>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QProcess>
 #include <QTextStream>
 
 #include "xmppbot.h"
@@ -30,11 +34,11 @@ XmppBotLua::XmppBotLua(QObject *parent) : QObject(parent)
     L = luaL_newstate();
     luaL_openlibs(L);
 
-    // Functions
+    // XMPP Functions
     pushFunction("jid", jid);
     pushFunction("jin", jin);
     pushFunction("sendMessage", sendMessage);
-    pushFunction("setPresence", setPresence);
+    pushFunction("setClientPresence", setClientPresence);
 
     // XMPP Presence
     pushVariant("PresenceAvailable", static_cast<int>(QXmppPresence::Available));
@@ -48,6 +52,15 @@ XmppBotLua::XmppBotLua(QObject *parent) : QObject(parent)
     pushVariant("StatusSnooze", static_cast<int>(QXmppPresence::XA));
     pushVariant("StatusBusy", static_cast<int>(QXmppPresence::DND));
     pushVariant("StatusChat", static_cast<int>(QXmppPresence::Chat));
+
+    // JSON
+    pushFunction("jsonToTable", jsonToTable);
+    pushFunction("tableToJson", tableToJson);
+    pushVariant("JsonCompact", static_cast<int>(QJsonDocument::Compact));
+    pushVariant("JsonIndented", static_cast<int>(QJsonDocument::Indented));
+
+    // Process
+    pushFunction("executeProcess", executeProcess);
 }
 
 XmppBotLua::~XmppBotLua()
@@ -371,7 +384,7 @@ int XmppBotLua::sendMessage(lua_State *L_p)
     return 1;
 }
 
-int XmppBotLua::setPresence(lua_State *L_p)
+int XmppBotLua::setClientPresence(lua_State *L_p)
 {
     bool presenceSet = false;
     if (getArgumentCount(L_p) >= 2 && getArgumentCount(L_p) <= 3) {
@@ -390,3 +403,89 @@ int XmppBotLua::setPresence(lua_State *L_p)
     pushVariant(L_p, presenceSet);
     return 1;
 }
+
+int XmppBotLua::jsonToTable(lua_State *L_p)
+{
+    if (getArgumentCount(L_p) >= 1) {
+        const QJsonDocument jsonDocument = QJsonDocument::fromJson(getVariant(L_p, 1).toString().toUtf8());
+        if (jsonDocument.isObject()) {
+            pushVariant(L_p, jsonDocument.object().toVariantMap());
+            return 1;
+        }
+        else if (jsonDocument.isArray()) {
+            pushVariant(L_p, jsonDocument.array().toVariantList());
+            return 1;
+        }
+    }
+    return 0;
+}
+
+int XmppBotLua::tableToJson(lua_State *L_p)
+{
+    if (getArgumentCount(L_p) >= 1) {
+        QJsonDocument::JsonFormat jsonFormat = QJsonDocument::Compact;
+        if (getArgumentCount(L_p) >= 2) {
+            jsonFormat = static_cast<QJsonDocument::JsonFormat>(getVariant(L_p, 2).toInt());
+        }
+        pushVariant(L_p, QString::fromUtf8(QJsonDocument(QJsonObject::fromVariantMap(getVariant(L_p, 1).toMap())).toJson(jsonFormat)));
+        return 1;
+    }
+    return 0;
+}
+
+int XmppBotLua::executeProcess(lua_State *L_p)
+{
+    if (getArgumentCount(L_p) >= 1) {
+        int processReturn = 0;
+        bool runInBackground = false;
+        bool processSuccessed = false;
+        if (getArgumentCount(L_p) >= 2) {
+            QStringList processArguments;
+            QString processPath = getVariant(L_p, 1).toString();
+            QVariant argument = getVariant(L_p, 2);
+            if (static_cast<QMetaType::Type>(argument.type()) == QMetaType::QVariantMap) {
+                const QVariantMap argumentMap = argument.toMap();
+                for (auto it = argumentMap.constBegin(); it != argumentMap.constEnd(); it++) {
+                    processArguments << it.value().toString();
+                }
+            }
+            else if (argument.type() == QVariant::Bool) {
+                runInBackground = argument.toBool();
+            }
+            else {
+                processArguments << argument.toString();
+            }
+            if (getArgumentCount(L_p) >= 3) {
+                if (argument.type() == QVariant::Bool) {
+                    processArguments << argument.toString();
+                }
+                runInBackground = getVariant(L_p, 3).toBool();
+            }
+            if (runInBackground) {
+                processSuccessed = QProcess::startDetached(processPath, processArguments);
+            }
+            else {
+                processReturn = QProcess::execute(processPath, processArguments);
+            }
+        }
+        else {
+#if QT_VERSION >= 0x050F00
+            processReturn = system(getVariant(L_p, 1).toString().toUtf8().constData());
+#else
+            processReturn = QProcess::execute(getVariant(L_p, 1).toString());
+#endif
+        }
+        if (runInBackground && !processSuccessed) {
+            processReturn = -2;
+        }
+        else if (!runInBackground && processReturn == 0) {
+            processSuccessed = true;
+        }
+        pushVariant(L_p, processSuccessed);
+        pushVariant(L_p, processReturn);
+        return 2;
+    }
+    pushVariant(L_p, false);
+    pushVariant(L_p, -2);
+    return 2;
+}
diff --git a/src/xmppbot/xmppbotlua.h b/src/xmppbot/xmppbotlua.h
index fa23901..18bbb6d 100644
--- a/src/xmppbot/xmppbotlua.h
+++ b/src/xmppbot/xmppbotlua.h
@@ -70,10 +70,20 @@ public:
     static int getArgumentCount(lua_State *L_p);
 
 private:
+    // XMPP
     static int jid(lua_State *L_p);
     static int jin(lua_State *L_p);
     static int sendMessage(lua_State *L_p);
-    static int setPresence(lua_State *L_p);
+    static int setClientPresence(lua_State *L_p);
+
+    // JSON
+    static int jsonToTable(lua_State *L_p);
+    static int tableToJson(lua_State *L_p);
+
+    // Process
+    static int executeProcess(lua_State *L_p);
+
+    // Lua
     lua_State *L;
 };
 
diff --git a/src/xmppbot/xmppbotluathread.cpp b/src/xmppbot/xmppbotluathread.cpp
index 5109a48..21b0b99 100644
--- a/src/xmppbot/xmppbotluathread.cpp
+++ b/src/xmppbot/xmppbotluathread.cpp
@@ -16,13 +16,15 @@
 * responsible for anything with use of the software, you are self responsible.
 *****************************************************************************/
 
+#include <QCoreApplication>
+#include <QEventLoop>
 #include <QFile>
 
 #include "xmppbotlua.h"
 #include "xmppbotluathread.h"
 
-XmppBotLuaThread::XmppBotLuaThread(const QString &filePath, const QString &lua_function, const QVariantList &lua_args) :
-    filePath(filePath), lua_function(lua_function), lua_args(lua_args)
+XmppBotLuaThread::XmppBotLuaThread(const QString &filePath, const QString &lua_function, const QVariantList &lua_args, const bool &lua_globalthread) :
+    filePath(filePath), lua_function(lua_function), lua_args(lua_args), lua_globalthread(lua_globalthread)
 {
 }
 
@@ -36,9 +38,20 @@ void XmppBotLuaThread::run()
             scriptFile.close();
         }
     }
-    if (!script.isEmpty()) {
-        XmppBotLua xmppBotLua;
-        xmppBotLua.executeLuaScript(script);
-        xmppBotLua.executeLuaFunction(lua_function.toUtf8().constData(), lua_args);
+
+    if (script.isEmpty())
+        return;
+
+    XmppBotLua xmppBotLua;
+    xmppBotLua.executeLuaScript(script);
+    xmppBotLua.executeLuaFunction(lua_function.toUtf8().constData(), lua_args);
+
+    if (lua_globalthread) {
+        QObject::connect(this, &XmppBotLuaThread::executeLuaFunction, this, [&](const QString &lua_function, const QVariantList &lua_args) {
+            xmppBotLua.executeLuaFunction(lua_function.toUtf8().constData(), lua_args);
+        });
+        QEventLoop threadLoop;
+        QObject::connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, &threadLoop, &QEventLoop::quit);
+        threadLoop.exec();
     }
 }
diff --git a/src/xmppbot/xmppbotluathread.h b/src/xmppbot/xmppbotluathread.h
index c82a0bb..bdc2ff5 100644
--- a/src/xmppbot/xmppbotluathread.h
+++ b/src/xmppbot/xmppbotluathread.h
@@ -26,7 +26,7 @@ class XmppBotLuaThread : public QThread
 {
     Q_OBJECT
 public:
-    XmppBotLuaThread(const QString &filePath, const QString &lua_function, const QVariantList &lua_args);
+    XmppBotLuaThread(const QString &filePath, const QString &lua_function, const QVariantList &lua_args, const bool &lua_globalthread = false);
 
 protected:
     void run();
@@ -35,6 +35,10 @@ private:
     QString filePath;
     QString lua_function;
     QVariantList lua_args;
+    bool lua_globalthread;
+
+signals:
+    void executeLuaFunction(const QString &lua_function, const QVariantList &lua_args);
 };
 
 #endif // XMPPBOTLUATHREAD_H