mirror-chatterino2/src/singletons/NativeMessaging.cpp

245 lines
7 KiB
C++
Raw Normal View History

2018-07-15 14:03:41 +02:00
#include "singletons/NativeMessaging.hpp"
2018-04-09 22:59:19 +02:00
2018-06-26 14:09:39 +02:00
#include "Application.hpp"
#include "providers/twitch/TwitchServer.hpp"
2018-06-28 19:46:45 +02:00
#include "singletons/Paths.hpp"
2018-06-26 14:09:39 +02:00
#include "util/PostToThread.hpp"
2018-04-09 22:59:19 +02:00
#include <QCoreApplication>
#include <QFile>
2018-06-13 13:27:10 +02:00
#include <QJsonArray>
2018-04-09 22:59:19 +02:00
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <boost/interprocess/ipc/message_queue.hpp>
2018-04-12 00:40:18 +02:00
namespace ipc = boost::interprocess;
2018-04-09 22:59:19 +02:00
#ifdef Q_OS_WIN
2018-08-15 22:46:20 +02:00
# include <QProcess>
2018-08-15 22:46:20 +02:00
# include <Windows.h>
# include "singletons/WindowManager.hpp"
# include "widgets/AttachedWindow.hpp"
2018-04-09 22:59:19 +02:00
#endif
#include <iostream>
2018-06-23 22:17:57 +02:00
#define EXTENSION_ID "glknmaideaikkmemifbfkhnomoknepka"
2018-04-09 22:59:19 +02:00
#define MESSAGE_SIZE 1024
namespace chatterino {
2018-08-02 14:23:27 +02:00
void registerNmManifest(Paths &paths, const QString &manifestFilename,
2018-08-06 21:17:03 +02:00
const QString &registryKeyName,
const QJsonDocument &document);
2018-04-09 22:59:19 +02:00
2018-08-02 14:23:27 +02:00
void registerNmHost(Paths &paths)
{
2018-08-02 14:23:27 +02:00
if (paths.isPortable()) return;
2018-05-28 18:25:19 +02:00
2018-06-13 13:27:10 +02:00
auto getBaseDocument = [&] {
QJsonObject obj;
obj.insert("name", "com.chatterino.chatterino");
obj.insert("description", "Browser interaction with chatterino.");
obj.insert("path", QCoreApplication::applicationFilePath());
obj.insert("type", "stdio");
return obj;
};
// chrome
2018-06-13 13:27:10 +02:00
{
QJsonDocument document;
auto obj = getBaseDocument();
2018-08-06 21:17:03 +02:00
QJsonArray allowed_origins_arr = {"chrome-extension://" EXTENSION_ID
"/"};
2018-06-13 13:27:10 +02:00
obj.insert("allowed_origins", allowed_origins_arr);
document.setObject(obj);
2018-08-06 21:17:03 +02:00
registerNmManifest(paths, "/native-messaging-manifest-chrome.json",
"HKCU\\Software\\Google\\Chrome\\NativeMessagingHost"
"s\\com.chatterino.chatterino",
document);
2018-06-13 13:27:10 +02:00
}
2018-04-09 22:59:19 +02:00
// firefox
2018-06-13 13:27:10 +02:00
{
QJsonDocument document;
2018-06-13 13:27:10 +02:00
auto obj = getBaseDocument();
QJsonArray allowed_extensions = {"chatterino_native@chatterino.com"};
obj.insert("allowed_extensions", allowed_extensions);
document.setObject(obj);
2018-04-09 22:59:19 +02:00
2018-08-06 21:17:03 +02:00
registerNmManifest(paths, "/native-messaging-manifest-firefox.json",
"HKCU\\Software\\Mozilla\\NativeMessagingHosts\\com."
"chatterino.chatterino",
document);
2018-06-13 13:27:10 +02:00
}
2018-04-09 22:59:19 +02:00
}
2018-08-02 14:23:27 +02:00
void registerNmManifest(Paths &paths, const QString &manifestFilename,
2018-08-06 21:17:03 +02:00
const QString &registryKeyName,
const QJsonDocument &document)
2018-04-09 22:59:19 +02:00
{
2018-08-02 14:23:27 +02:00
(void)registryKeyName;
2018-04-09 22:59:19 +02:00
2018-08-02 14:23:27 +02:00
// save the manifest
QString manifestPath = paths.miscDirectory + manifestFilename;
QFile file(manifestPath);
file.open(QIODevice::WriteOnly | QIODevice::Truncate);
file.write(document.toJson());
file.flush();
#ifdef Q_OS_WIN
// clang-format off
QProcess::execute("REG ADD \"" + registryKeyName + "\" /ve /t REG_SZ /d \"" + manifestPath + "\" /f");
// clang-format on
#endif
}
2018-04-09 22:59:19 +02:00
2018-08-02 14:23:27 +02:00
std::string &getNmQueueName(Paths &paths)
{
2018-08-06 21:17:03 +02:00
static std::string name =
"chatterino_gui" + paths.applicationFilePathHash.toStdString();
2018-08-02 14:23:27 +02:00
return name;
2018-04-09 22:59:19 +02:00
}
2018-08-02 14:23:27 +02:00
// CLIENT
void NativeMessagingClient::sendMessage(const QByteArray &array)
2018-04-09 22:59:19 +02:00
{
try {
ipc::message_queue messageQueue(ipc::open_only, "chatterino_gui");
2018-04-09 22:59:19 +02:00
messageQueue.try_send(array.data(), array.size(), 1);
} catch (ipc::interprocess_exception &ex) {
qDebug() << "send to gui process:" << ex.what();
2018-04-09 22:59:19 +02:00
}
}
2018-08-02 14:23:27 +02:00
void NativeMessagingClient::writeToCout(const QByteArray &array)
{
auto *data = array.data();
auto size = uint32_t(array.size());
std::cout.write(reinterpret_cast<char *>(&size), 4);
std::cout.write(data, size);
std::cout.flush();
}
// SERVER
void NativeMessagingServer::start()
{
this->thread.start();
}
void NativeMessagingServer::ReceiverThread::run()
2018-04-09 22:59:19 +02:00
{
ipc::message_queue::remove("chatterino_gui");
2018-08-06 21:17:03 +02:00
ipc::message_queue messageQueue(ipc::open_or_create, "chatterino_gui", 100,
MESSAGE_SIZE);
2018-04-09 22:59:19 +02:00
while (true) {
try {
2018-08-06 21:17:03 +02:00
std::unique_ptr<char> buf(
static_cast<char *>(malloc(MESSAGE_SIZE)));
2018-04-09 22:59:19 +02:00
ipc::message_queue::size_type retSize;
unsigned int priority;
2018-05-28 08:34:54 +02:00
messageQueue.receive(buf.get(), MESSAGE_SIZE, retSize, priority);
2018-04-09 22:59:19 +02:00
2018-08-06 21:17:03 +02:00
QJsonDocument document = QJsonDocument::fromJson(
QByteArray::fromRawData(buf.get(), retSize));
this->handleMessage(document.object());
2018-04-09 22:59:19 +02:00
} catch (ipc::interprocess_exception &ex) {
qDebug() << "received from gui process:" << ex.what();
2018-04-09 22:59:19 +02:00
}
}
}
2018-08-06 21:17:03 +02:00
void NativeMessagingServer::ReceiverThread::handleMessage(
const QJsonObject &root)
{
auto app = getApp();
2018-04-20 19:54:45 +02:00
QString action = root.value("action").toString();
if (action.isNull()) {
qDebug() << "NM action was null";
return;
}
if (action == "select") {
QString _type = root.value("type").toString();
bool attach = root.value("attach").toBool();
2018-04-20 19:54:45 +02:00
QString name = root.value("name").toString();
2018-06-13 13:27:10 +02:00
qDebug() << attach;
2018-05-28 19:49:37 +02:00
#ifdef USEWINSDK
2018-06-26 17:06:17 +02:00
AttachedWindow::GetArgs args;
2018-05-28 18:25:19 +02:00
args.winId = root.value("winId").toString();
args.yOffset = root.value("yOffset").toInt(-1);
args.width = root.value("size").toObject().value("width").toInt(-1);
args.height = root.value("size").toObject().value("height").toInt(-1);
if (_type.isNull() || args.winId.isNull()) {
qDebug() << "NM type, name or winId missing";
attach = false;
2018-04-20 19:54:45 +02:00
return;
}
2018-05-28 19:49:37 +02:00
#endif
2018-04-20 19:54:45 +02:00
if (_type == "twitch") {
2018-06-26 17:06:17 +02:00
postToThread([=] {
2018-05-28 18:25:19 +02:00
if (!name.isEmpty()) {
2018-08-10 19:00:14 +02:00
app->twitch.server->watchingChannel.reset(
2018-05-28 18:25:19 +02:00
app->twitch.server->getOrAddChannel(name));
}
if (attach) {
2018-04-26 20:58:32 +02:00
#ifdef USEWINSDK
2018-06-13 13:27:10 +02:00
// if (args.height != -1) {
2018-08-06 21:17:03 +02:00
auto *window =
AttachedWindow::get(::GetForegroundWindow(), args);
2018-06-13 13:27:10 +02:00
if (!name.isEmpty()) {
2018-08-06 21:17:03 +02:00
window->setChannel(
app->twitch.server->getOrAddChannel(name));
2018-05-28 18:25:19 +02:00
}
2018-06-13 13:27:10 +02:00
// }
2018-05-28 18:25:19 +02:00
// window->show();
2018-04-26 20:58:32 +02:00
#endif
}
2018-04-20 19:54:45 +02:00
});
2018-04-20 19:54:45 +02:00
} else {
qDebug() << "NM unknown channel type";
}
} else if (action == "detach") {
QString winId = root.value("winId").toString();
if (winId.isNull()) {
qDebug() << "NM winId missing";
return;
}
2018-04-26 20:58:32 +02:00
#ifdef USEWINSDK
2018-06-26 17:06:17 +02:00
postToThread([winId] {
2018-05-30 17:17:27 +02:00
qDebug() << "NW detach";
2018-06-26 17:06:17 +02:00
AttachedWindow::detach(winId);
2018-05-30 17:17:27 +02:00
});
2018-04-26 20:58:32 +02:00
#endif
} else {
qDebug() << "NM unknown action " + action;
2018-04-20 19:54:45 +02:00
}
2018-06-13 13:27:10 +02:00
}
2018-04-09 22:59:19 +02:00
} // namespace chatterino