From 2bc3e2790639519e0e9218c283663fe1f5afccc1 Mon Sep 17 00:00:00 2001 From: Syping Date: Thu, 29 Apr 2021 16:11:51 +0200 Subject: [PATCH] initial commit --- .gitmodules | 4 + CMakeLists.txt | 27 ++++++ src/3rdparty/qxmpp | 1 + src/xmppbot/main.cpp | 183 +++++++++++++++++++++++++++++++++++++ src/xmppbot/unixsocket.cpp | 46 ++++++++++ src/xmppbot/unixsocket.h | 45 +++++++++ 6 files changed, 306 insertions(+) create mode 100644 .gitmodules create mode 100644 CMakeLists.txt create mode 160000 src/3rdparty/qxmpp create mode 100644 src/xmppbot/main.cpp create mode 100644 src/xmppbot/unixsocket.cpp create mode 100644 src/xmppbot/unixsocket.h diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..8e5f797 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "src/3rdparty/qxmpp"] +path = src/3rdparty/qxmpp +url = https://github.com/qxmpp-project/qxmpp.git +branch = 1.4 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c313867 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.7) + +project(xmppbot LANGUAGES CXX) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network REQUIRED) + +add_subdirectory(src/3rdparty/qxmpp) +add_executable(xmppbot + src/xmppbot/main.cpp + src/xmppbot/unixsocket.cpp + src/xmppbot/unixsocket.h +) +add_dependencies(xmppbot qxmpp) + +target_link_libraries(xmppbot Qt${QT_VERSION_MAJOR}::Network qxmpp) + +install(TARGETS xmppbot DESTINATION bin) diff --git a/src/3rdparty/qxmpp b/src/3rdparty/qxmpp new file mode 160000 index 0000000..6d778d8 --- /dev/null +++ b/src/3rdparty/qxmpp @@ -0,0 +1 @@ +Subproject commit 6d778d84fd2faac6a899a1c70f2dcb9bd4e0b164 diff --git a/src/xmppbot/main.cpp b/src/xmppbot/main.cpp new file mode 100644 index 0000000..57b576c --- /dev/null +++ b/src/xmppbot/main.cpp @@ -0,0 +1,183 @@ +/***************************************************************************** +* xmppbot Simple unix socket based XMPP bot +* Copyright (C) 2021 Syping +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* This software is provided as-is, no warranties are given to you, we are not +* responsible for anything with use of the software, you are self responsible. +*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "unixsocket.h" +#include "QXmppClient.h" +#include "QXmppMessage.h" + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + app.setApplicationName("xmppbot"); + app.setApplicationVersion("0.1"); + + QCommandLineParser commandLineParser; + commandLineParser.addPositionalArgument("config", QCoreApplication::translate("xmppbot", "Configuration file.")); + commandLineParser.addHelpOption(); + commandLineParser.addVersionOption(); + commandLineParser.process(app); + + QString settingsPath = QLatin1String("settings.ini"); + const QStringList args = commandLineParser.positionalArguments(); + if (args.length() == 1) { + const QString a_settingsPath = args.at(0); + if (QFile::exists(a_settingsPath)) { + settingsPath = a_settingsPath; + } + else { + QTextStream(stderr) << "xmppbot: " << a_settingsPath << " not found!" << Qt::endl; + return 1; + } + } + + QXmppClient client; + + bool loginSet = false; + QString jid, jpw; + QHash h_msg; + QHash h_run; + if (QFile::exists(settingsPath)) { + QSettings settings(settingsPath, QSettings::IniFormat); + for (const QString &group : settings.childGroups()) { + settings.beginGroup(group); + for (const QString &key : settings.childKeys()) { + if (key == "Password") { + if (!loginSet) { + jid = group; + jpw = settings.value(key, QString()).toString(); + loginSet = true; + } + else { + QTextStream(stderr) << "xmppbot: Login password can only be set once!" << Qt::endl; + return 1; + } + } + if (key == "UnixSocket") { + UnixSocket *unixSocket = new UnixSocket(&client, jid, group); + const QString permission = settings.value("SocketPermission", QString()).toString(); + if (permission == "UG" || permission == "UserGroup") { + unixSocket->setSocketOptions(QLocalServer::UserAccessOption | QLocalServer::GroupAccessOption); + } + if (permission == "UO" || permission == "UserOther") { + unixSocket->setSocketOptions(QLocalServer::UserAccessOption | QLocalServer::OtherAccessOption); + } + if (permission == "U" || permission == "User") { + unixSocket->setSocketOptions(QLocalServer::UserAccessOption); + } + if (permission == "GO" || permission == "GroupOther") { + unixSocket->setSocketOptions(QLocalServer::GroupAccessOption | QLocalServer::OtherAccessOption); + } + if (permission == "G" || permission == "Group") { + unixSocket->setSocketOptions(QLocalServer::GroupAccessOption); + } + if (permission == "O" || permission == "Other") { + unixSocket->setSocketOptions(QLocalServer::OtherAccessOption); + } + if (permission == "A" || permission == "All" || permission == "UGO" || permission == "UserGroupOther") { + unixSocket->setSocketOptions(QLocalServer::WorldAccessOption); + } + const QString socketPath = settings.value(key, QString()).toString(); + bool listen = unixSocket->listen(socketPath); +#ifdef Q_OS_UNIX + if (!listen) { + QLocalServer::removeServer(socketPath); + listen = unixSocket->listen(socketPath); + } +#endif + if (listen) { + QTextStream(stderr) << "xmppbot: Account socket " << group << " initialised" << Qt::endl; + const QString incoming = settings.value("Incoming", QString()).toString(); + if (incoming.startsWith("message:")) { + QTextStream(stderr) << "xmppbot: Account message incoming " << group << " initialised" << Qt::endl; + h_msg.insert(group, incoming.mid(8)); + } + if (incoming.startsWith("run:")) { + QTextStream(stderr) << "xmppbot: Account run incoming " << group << " initialised" << Qt::endl; + h_run.insert(group, incoming.mid(4)); + } + } + else { + delete unixSocket; + } + } + } + settings.endGroup(); + } + } + else { + QTextStream(stderr) << "xmppbot: Can't initialise without settings.ini!" << Qt::endl; + return 1; + } + + if (jid.isEmpty() || jpw.isEmpty()) { + QTextStream(stderr) << "xmppbot: Can't initialise without XMPP account!" << Qt::endl; + return 1; + } + + QObject::connect(&client, &QXmppClient::connected, [&]() { + QTextStream(stderr) << "xmppbot: Account " << jid << " connected" << Qt::endl; + QXmppPresence xmppPresence(QXmppPresence::Available); + client.setClientPresence(xmppPresence); + }); + + QObject::connect(&client, &QXmppClient::disconnected, [&]() { + QTextStream(stderr) << "xmppbot: Account " << jid << " disconnected" << Qt::endl; + client.connectToServer(jid, jpw); + }); + + QObject::connect(&client, &QXmppClient::messageReceived, [&](const QXmppMessage &xmppMessage) { + const QString body = xmppMessage.body(); + if (body.isEmpty()) + return; + const QString from = xmppMessage.from(); + QString from_jid; + for (const QChar &val : from) { + if (val == '/') + break; + from_jid += val; + } + const QString msg = h_msg.value(from_jid, QString()); + if (!msg.isEmpty()) { + QXmppMessage xmppMessage(jid, from, msg); + client.sendPacket(xmppMessage); + } + const QString run = h_run.value(from_jid, QString()); + if (!run.isEmpty()) { + qint64 pid; + bool isStarted = QProcess::startDetached(run, QStringList() << from << xmppMessage.to() << xmppMessage.body(), QString(), &pid); + if (isStarted) { + QTextStream(stderr) << "xmppbot: Account " << from_jid << " executed pid " << pid << Qt::endl; + } + } + }); + + client.connectToServer(jid, jpw); + + QTextStream(stderr) << "xmppbot: Account login " << jid << " initialised" << Qt::endl; + + return app.exec(); +} diff --git a/src/xmppbot/unixsocket.cpp b/src/xmppbot/unixsocket.cpp new file mode 100644 index 0000000..5c6f507 --- /dev/null +++ b/src/xmppbot/unixsocket.cpp @@ -0,0 +1,46 @@ +/***************************************************************************** +* xmppbot Simple unix socket based XMPP bot +* Copyright (C) 2021 Syping +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* This software is provided as-is, no warranties are given to you, we are not +* responsible for anything with use of the software, you are self responsible. +*****************************************************************************/ + +#include + +#include "unixsocket.h" +#include "QXmppMessage.h" + +UnixSocket::UnixSocket(QXmppClient *client, const QString &from, const QString &to) : client(client), from(from), to(to) +{ +} + +void UnixSocket::readyRead() +{ + QLocalSocket *localSocket = qobject_cast(sender()); + if (localSocket) { + const QString message = QString::fromUtf8(localSocket->readAll()); + if (!message.isEmpty()) { + QXmppMessage xmppMessage(from, to, message); + client->sendPacket(xmppMessage); + } + } +} + +void UnixSocket::incomingConnection(quintptr socketDescriptor) +{ + QLocalSocket *localSocket = new QLocalSocket(this); + localSocket->setSocketDescriptor(socketDescriptor); + QObject::connect(localSocket, &QLocalSocket::readyRead, this, &UnixSocket::readyRead); + QObject::connect(localSocket, &QLocalSocket::disconnected, localSocket, &QLocalSocket::deleteLater); +} diff --git a/src/xmppbot/unixsocket.h b/src/xmppbot/unixsocket.h new file mode 100644 index 0000000..687aafe --- /dev/null +++ b/src/xmppbot/unixsocket.h @@ -0,0 +1,45 @@ +/***************************************************************************** +* xmppbot Simple unix socket based XMPP bot +* Copyright (C) 2021 Syping +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* This software is provided as-is, no warranties are given to you, we are not +* responsible for anything with use of the software, you are self responsible. +*****************************************************************************/ + +#ifndef UNIXSOCKET_H +#define UNIXSOCKET_H + +#include +#include + +#include "QXmppClient.h" + +class UnixSocket : public QLocalServer +{ + Q_OBJECT +public: + UnixSocket(QXmppClient *client, const QString &from, const QString &to); + +private slots: + void readyRead(); + +private: + QXmppClient *client; + QString from; + QString to; + +protected: + void incomingConnection(quintptr socketDescriptor); +}; + +#endif // UNIXSOCKET_H