mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
Add CI workflow to check line endings of all source files (#2082)
In addition, all found errors (formatting & line ending) have been fixed in this PR.
This commit is contained in:
parent
4199a01b96
commit
f191de2514
17 changed files with 959 additions and 934 deletions
13
.github/workflows/check-formatting.yml
vendored
13
.github/workflows/check-formatting.yml
vendored
|
@ -8,18 +8,19 @@ on:
|
|||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ubuntu:19.10
|
||||
check:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2.3.3
|
||||
- name: apt-get update
|
||||
run: apt-get update
|
||||
run: sudo apt-get update
|
||||
|
||||
- name: Install clang-format
|
||||
run: apt-get -y install clang-format
|
||||
run: sudo apt-get -y install clang-format dos2unix
|
||||
|
||||
- name: Check formatting
|
||||
run: ./tools/check-format.sh
|
||||
|
||||
- name: Check line-endings
|
||||
run: ./tools/check-line-endings.sh
|
||||
|
|
|
@ -41,7 +41,7 @@ git submodule update --init --recursive
|
|||
The code is formatted using clang format in Qt Creator. [.clang-format](src/.clang-format) contains the style file for clang format.
|
||||
|
||||
### Get it automated with QT Creator + Beautifier + Clang Format
|
||||
1. Download LLVM: https://releases.llvm.org/9.0.0/LLVM-9.0.0-win64.exe
|
||||
1. Download LLVM: https://github.com/llvm/llvm-project/releases/download/llvmorg-11.0.0/LLVM-11.0.0-win64.exe
|
||||
2. During the installation, make sure to add it to your path
|
||||
3. In QT Creator, select `Help` > `About Plugins` > `C++` > `Beautifier` to enable the plugin
|
||||
4. Restart QT Creator
|
||||
|
|
|
@ -1,72 +1,72 @@
|
|||
#include "ChannelChatters.hpp"
|
||||
|
||||
#include "messages/Message.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
ChannelChatters::ChannelChatters(Channel &channel)
|
||||
: channel_(channel)
|
||||
{
|
||||
}
|
||||
|
||||
AccessGuard<const UsernameSet> ChannelChatters::accessChatters() const
|
||||
{
|
||||
return this->chatters_.accessConst();
|
||||
}
|
||||
|
||||
void ChannelChatters::addRecentChatter(const QString &user)
|
||||
{
|
||||
this->chatters_.access()->insert(user);
|
||||
}
|
||||
|
||||
void ChannelChatters::addJoinedUser(const QString &user)
|
||||
{
|
||||
auto joinedUsers = this->joinedUsers_.access();
|
||||
joinedUsers->append(user);
|
||||
|
||||
if (!this->joinedUsersMergeQueued_)
|
||||
{
|
||||
this->joinedUsersMergeQueued_ = true;
|
||||
|
||||
QTimer::singleShot(500, &this->lifetimeGuard_, [this] {
|
||||
auto joinedUsers = this->joinedUsers_.access();
|
||||
|
||||
MessageBuilder builder(systemMessage,
|
||||
"Users joined: " + joinedUsers->join(", "));
|
||||
builder->flags.set(MessageFlag::Collapsed);
|
||||
joinedUsers->clear();
|
||||
this->channel_.addMessage(builder.release());
|
||||
this->joinedUsersMergeQueued_ = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelChatters::addPartedUser(const QString &user)
|
||||
{
|
||||
auto partedUsers = this->partedUsers_.access();
|
||||
partedUsers->append(user);
|
||||
|
||||
if (!this->partedUsersMergeQueued_)
|
||||
{
|
||||
this->partedUsersMergeQueued_ = true;
|
||||
|
||||
QTimer::singleShot(500, &this->lifetimeGuard_, [this] {
|
||||
auto partedUsers = this->partedUsers_.access();
|
||||
|
||||
MessageBuilder builder(systemMessage,
|
||||
"Users parted: " + partedUsers->join(", "));
|
||||
builder->flags.set(MessageFlag::Collapsed);
|
||||
this->channel_.addMessage(builder.release());
|
||||
partedUsers->clear();
|
||||
|
||||
this->partedUsersMergeQueued_ = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
void ChannelChatters::setChatters(UsernameSet &&set)
|
||||
{
|
||||
*this->chatters_.access() = set;
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
#include "ChannelChatters.hpp"
|
||||
|
||||
#include "messages/Message.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
ChannelChatters::ChannelChatters(Channel &channel)
|
||||
: channel_(channel)
|
||||
{
|
||||
}
|
||||
|
||||
AccessGuard<const UsernameSet> ChannelChatters::accessChatters() const
|
||||
{
|
||||
return this->chatters_.accessConst();
|
||||
}
|
||||
|
||||
void ChannelChatters::addRecentChatter(const QString &user)
|
||||
{
|
||||
this->chatters_.access()->insert(user);
|
||||
}
|
||||
|
||||
void ChannelChatters::addJoinedUser(const QString &user)
|
||||
{
|
||||
auto joinedUsers = this->joinedUsers_.access();
|
||||
joinedUsers->append(user);
|
||||
|
||||
if (!this->joinedUsersMergeQueued_)
|
||||
{
|
||||
this->joinedUsersMergeQueued_ = true;
|
||||
|
||||
QTimer::singleShot(500, &this->lifetimeGuard_, [this] {
|
||||
auto joinedUsers = this->joinedUsers_.access();
|
||||
|
||||
MessageBuilder builder(systemMessage,
|
||||
"Users joined: " + joinedUsers->join(", "));
|
||||
builder->flags.set(MessageFlag::Collapsed);
|
||||
joinedUsers->clear();
|
||||
this->channel_.addMessage(builder.release());
|
||||
this->joinedUsersMergeQueued_ = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelChatters::addPartedUser(const QString &user)
|
||||
{
|
||||
auto partedUsers = this->partedUsers_.access();
|
||||
partedUsers->append(user);
|
||||
|
||||
if (!this->partedUsersMergeQueued_)
|
||||
{
|
||||
this->partedUsersMergeQueued_ = true;
|
||||
|
||||
QTimer::singleShot(500, &this->lifetimeGuard_, [this] {
|
||||
auto partedUsers = this->partedUsers_.access();
|
||||
|
||||
MessageBuilder builder(systemMessage,
|
||||
"Users parted: " + partedUsers->join(", "));
|
||||
builder->flags.set(MessageFlag::Collapsed);
|
||||
this->channel_.addMessage(builder.release());
|
||||
partedUsers->clear();
|
||||
|
||||
this->partedUsersMergeQueued_ = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
void ChannelChatters::setChatters(UsernameSet &&set)
|
||||
{
|
||||
*this->chatters_.access() = set;
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -1,37 +1,37 @@
|
|||
#pragma once
|
||||
|
||||
#include "common/Channel.hpp"
|
||||
#include "common/UniqueAccess.hpp"
|
||||
#include "common/UsernameSet.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class ChannelChatters
|
||||
{
|
||||
public:
|
||||
ChannelChatters(Channel &channel);
|
||||
virtual ~ChannelChatters() = default; // add vtable
|
||||
|
||||
AccessGuard<const UsernameSet> accessChatters() const;
|
||||
|
||||
void addRecentChatter(const QString &user);
|
||||
void addJoinedUser(const QString &user);
|
||||
void addPartedUser(const QString &user);
|
||||
void setChatters(UsernameSet &&set);
|
||||
|
||||
private:
|
||||
Channel &channel_;
|
||||
|
||||
// maps 2 char prefix to set of names
|
||||
UniqueAccess<UsernameSet> chatters_;
|
||||
|
||||
// combines multiple joins/parts into one message
|
||||
UniqueAccess<QStringList> joinedUsers_;
|
||||
bool joinedUsersMergeQueued_ = false;
|
||||
UniqueAccess<QStringList> partedUsers_;
|
||||
bool partedUsersMergeQueued_ = false;
|
||||
|
||||
QObject lifetimeGuard_;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
#pragma once
|
||||
|
||||
#include "common/Channel.hpp"
|
||||
#include "common/UniqueAccess.hpp"
|
||||
#include "common/UsernameSet.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class ChannelChatters
|
||||
{
|
||||
public:
|
||||
ChannelChatters(Channel &channel);
|
||||
virtual ~ChannelChatters() = default; // add vtable
|
||||
|
||||
AccessGuard<const UsernameSet> accessChatters() const;
|
||||
|
||||
void addRecentChatter(const QString &user);
|
||||
void addJoinedUser(const QString &user);
|
||||
void addPartedUser(const QString &user);
|
||||
void setChatters(UsernameSet &&set);
|
||||
|
||||
private:
|
||||
Channel &channel_;
|
||||
|
||||
// maps 2 char prefix to set of names
|
||||
UniqueAccess<UsernameSet> chatters_;
|
||||
|
||||
// combines multiple joins/parts into one message
|
||||
UniqueAccess<QStringList> joinedUsers_;
|
||||
bool joinedUsersMergeQueued_ = false;
|
||||
UniqueAccess<QStringList> partedUsers_;
|
||||
bool partedUsersMergeQueued_ = false;
|
||||
|
||||
QObject lifetimeGuard_;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -1,235 +1,235 @@
|
|||
#include "Credentials.hpp"
|
||||
|
||||
#include "debug/AssertInGuiThread.hpp"
|
||||
#include "keychain.h"
|
||||
#include "singletons/Paths.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
#include "util/CombinePath.hpp"
|
||||
#include "util/Overloaded.hpp"
|
||||
|
||||
#include <QSaveFile>
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
#define FORMAT_NAME \
|
||||
([&] { \
|
||||
assert(!provider.contains(":")); \
|
||||
return QString("chatterino:%1:%2").arg(provider).arg(name_); \
|
||||
})()
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
namespace {
|
||||
bool useKeyring()
|
||||
{
|
||||
if (getPaths()->isPortable())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
return getSettings()->useKeyring;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Insecure storage:
|
||||
QString insecurePath()
|
||||
{
|
||||
return combinePath(getPaths()->settingsDirectory, "credentials.json");
|
||||
}
|
||||
|
||||
QJsonDocument loadInsecure()
|
||||
{
|
||||
QFile file(insecurePath());
|
||||
file.open(QIODevice::ReadOnly);
|
||||
return QJsonDocument::fromJson(file.readAll());
|
||||
}
|
||||
|
||||
void storeInsecure(const QJsonDocument &doc)
|
||||
{
|
||||
QSaveFile file(insecurePath());
|
||||
file.open(QIODevice::WriteOnly);
|
||||
file.write(doc.toJson());
|
||||
file.commit();
|
||||
}
|
||||
|
||||
QJsonDocument &insecureInstance()
|
||||
{
|
||||
static auto store = loadInsecure();
|
||||
return store;
|
||||
}
|
||||
|
||||
void queueInsecureSave()
|
||||
{
|
||||
static bool isQueued = false;
|
||||
|
||||
if (!isQueued)
|
||||
{
|
||||
isQueued = true;
|
||||
QTimer::singleShot(200, qApp, [] {
|
||||
storeInsecure(insecureInstance());
|
||||
isQueued = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// QKeychain runs jobs asyncronously, so we have to assure that set/erase
|
||||
// jobs gets executed in order.
|
||||
struct SetJob {
|
||||
QString name;
|
||||
QString credential;
|
||||
};
|
||||
|
||||
struct EraseJob {
|
||||
QString name;
|
||||
};
|
||||
|
||||
using Job = boost::variant<SetJob, EraseJob>;
|
||||
|
||||
static std::queue<Job> &jobQueue()
|
||||
{
|
||||
static std::queue<Job> jobs;
|
||||
return jobs;
|
||||
}
|
||||
|
||||
static void runNextJob()
|
||||
{
|
||||
auto &&queue = jobQueue();
|
||||
|
||||
if (!queue.empty())
|
||||
{
|
||||
// we were gonna use std::visit here but macos is shit
|
||||
|
||||
auto &&item = queue.front();
|
||||
|
||||
if (item.which() == 0) // set job
|
||||
{
|
||||
auto set = boost::get<SetJob>(item);
|
||||
auto job = new QKeychain::WritePasswordJob("chatterino");
|
||||
job->setAutoDelete(true);
|
||||
job->setKey(set.name);
|
||||
job->setTextData(set.credential);
|
||||
QObject::connect(job, &QKeychain::Job::finished, qApp,
|
||||
[](auto) { runNextJob(); });
|
||||
job->start();
|
||||
}
|
||||
else // erase job
|
||||
{
|
||||
auto erase = boost::get<EraseJob>(item);
|
||||
auto job = new QKeychain::DeletePasswordJob("chatterino");
|
||||
job->setAutoDelete(true);
|
||||
job->setKey(erase.name);
|
||||
QObject::connect(job, &QKeychain::Job::finished, qApp,
|
||||
[](auto) { runNextJob(); });
|
||||
job->start();
|
||||
}
|
||||
|
||||
queue.pop();
|
||||
}
|
||||
}
|
||||
|
||||
static void queueJob(Job &&job)
|
||||
{
|
||||
auto &&queue = jobQueue();
|
||||
|
||||
queue.push(std::move(job));
|
||||
if (queue.size() == 1)
|
||||
{
|
||||
runNextJob();
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Credentials &Credentials::instance()
|
||||
{
|
||||
static Credentials creds;
|
||||
return creds;
|
||||
}
|
||||
|
||||
Credentials::Credentials()
|
||||
{
|
||||
}
|
||||
|
||||
void Credentials::get(const QString &provider, const QString &name_,
|
||||
QObject *receiver,
|
||||
std::function<void(const QString &)> &&onLoaded)
|
||||
{
|
||||
assertInGuiThread();
|
||||
|
||||
auto name = FORMAT_NAME;
|
||||
|
||||
if (useKeyring())
|
||||
{
|
||||
auto job = new QKeychain::ReadPasswordJob("chatterino");
|
||||
job->setAutoDelete(true);
|
||||
job->setKey(name);
|
||||
QObject::connect(
|
||||
job, &QKeychain::Job::finished, receiver,
|
||||
[job, onLoaded = std::move(onLoaded)](auto) mutable {
|
||||
onLoaded(job->textData());
|
||||
},
|
||||
Qt::DirectConnection);
|
||||
job->start();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto &instance = insecureInstance();
|
||||
|
||||
onLoaded(instance.object().find(name).value().toString());
|
||||
}
|
||||
}
|
||||
|
||||
void Credentials::set(const QString &provider, const QString &name_,
|
||||
const QString &credential)
|
||||
{
|
||||
assertInGuiThread();
|
||||
|
||||
/// On linux, we try to use a keychain but show a message to disable it when it fails.
|
||||
/// XXX: add said message
|
||||
|
||||
auto name = FORMAT_NAME;
|
||||
|
||||
if (useKeyring())
|
||||
{
|
||||
queueJob(SetJob{name, credential});
|
||||
}
|
||||
else
|
||||
{
|
||||
auto &instance = insecureInstance();
|
||||
|
||||
auto obj = instance.object();
|
||||
obj[name] = credential;
|
||||
instance.setObject(obj);
|
||||
|
||||
queueInsecureSave();
|
||||
}
|
||||
}
|
||||
|
||||
void Credentials::erase(const QString &provider, const QString &name_)
|
||||
{
|
||||
assertInGuiThread();
|
||||
|
||||
auto name = FORMAT_NAME;
|
||||
|
||||
if (useKeyring())
|
||||
{
|
||||
queueJob(EraseJob{name});
|
||||
}
|
||||
else
|
||||
{
|
||||
auto &instance = insecureInstance();
|
||||
|
||||
if (auto it = instance.object().find(name);
|
||||
it != instance.object().end())
|
||||
{
|
||||
instance.object().erase(it);
|
||||
}
|
||||
|
||||
queueInsecureSave();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
#include "Credentials.hpp"
|
||||
|
||||
#include "debug/AssertInGuiThread.hpp"
|
||||
#include "keychain.h"
|
||||
#include "singletons/Paths.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
#include "util/CombinePath.hpp"
|
||||
#include "util/Overloaded.hpp"
|
||||
|
||||
#include <QSaveFile>
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
#define FORMAT_NAME \
|
||||
([&] { \
|
||||
assert(!provider.contains(":")); \
|
||||
return QString("chatterino:%1:%2").arg(provider).arg(name_); \
|
||||
})()
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
namespace {
|
||||
bool useKeyring()
|
||||
{
|
||||
if (getPaths()->isPortable())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
return getSettings()->useKeyring;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Insecure storage:
|
||||
QString insecurePath()
|
||||
{
|
||||
return combinePath(getPaths()->settingsDirectory, "credentials.json");
|
||||
}
|
||||
|
||||
QJsonDocument loadInsecure()
|
||||
{
|
||||
QFile file(insecurePath());
|
||||
file.open(QIODevice::ReadOnly);
|
||||
return QJsonDocument::fromJson(file.readAll());
|
||||
}
|
||||
|
||||
void storeInsecure(const QJsonDocument &doc)
|
||||
{
|
||||
QSaveFile file(insecurePath());
|
||||
file.open(QIODevice::WriteOnly);
|
||||
file.write(doc.toJson());
|
||||
file.commit();
|
||||
}
|
||||
|
||||
QJsonDocument &insecureInstance()
|
||||
{
|
||||
static auto store = loadInsecure();
|
||||
return store;
|
||||
}
|
||||
|
||||
void queueInsecureSave()
|
||||
{
|
||||
static bool isQueued = false;
|
||||
|
||||
if (!isQueued)
|
||||
{
|
||||
isQueued = true;
|
||||
QTimer::singleShot(200, qApp, [] {
|
||||
storeInsecure(insecureInstance());
|
||||
isQueued = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// QKeychain runs jobs asyncronously, so we have to assure that set/erase
|
||||
// jobs gets executed in order.
|
||||
struct SetJob {
|
||||
QString name;
|
||||
QString credential;
|
||||
};
|
||||
|
||||
struct EraseJob {
|
||||
QString name;
|
||||
};
|
||||
|
||||
using Job = boost::variant<SetJob, EraseJob>;
|
||||
|
||||
static std::queue<Job> &jobQueue()
|
||||
{
|
||||
static std::queue<Job> jobs;
|
||||
return jobs;
|
||||
}
|
||||
|
||||
static void runNextJob()
|
||||
{
|
||||
auto &&queue = jobQueue();
|
||||
|
||||
if (!queue.empty())
|
||||
{
|
||||
// we were gonna use std::visit here but macos is shit
|
||||
|
||||
auto &&item = queue.front();
|
||||
|
||||
if (item.which() == 0) // set job
|
||||
{
|
||||
auto set = boost::get<SetJob>(item);
|
||||
auto job = new QKeychain::WritePasswordJob("chatterino");
|
||||
job->setAutoDelete(true);
|
||||
job->setKey(set.name);
|
||||
job->setTextData(set.credential);
|
||||
QObject::connect(job, &QKeychain::Job::finished, qApp,
|
||||
[](auto) { runNextJob(); });
|
||||
job->start();
|
||||
}
|
||||
else // erase job
|
||||
{
|
||||
auto erase = boost::get<EraseJob>(item);
|
||||
auto job = new QKeychain::DeletePasswordJob("chatterino");
|
||||
job->setAutoDelete(true);
|
||||
job->setKey(erase.name);
|
||||
QObject::connect(job, &QKeychain::Job::finished, qApp,
|
||||
[](auto) { runNextJob(); });
|
||||
job->start();
|
||||
}
|
||||
|
||||
queue.pop();
|
||||
}
|
||||
}
|
||||
|
||||
static void queueJob(Job &&job)
|
||||
{
|
||||
auto &&queue = jobQueue();
|
||||
|
||||
queue.push(std::move(job));
|
||||
if (queue.size() == 1)
|
||||
{
|
||||
runNextJob();
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Credentials &Credentials::instance()
|
||||
{
|
||||
static Credentials creds;
|
||||
return creds;
|
||||
}
|
||||
|
||||
Credentials::Credentials()
|
||||
{
|
||||
}
|
||||
|
||||
void Credentials::get(const QString &provider, const QString &name_,
|
||||
QObject *receiver,
|
||||
std::function<void(const QString &)> &&onLoaded)
|
||||
{
|
||||
assertInGuiThread();
|
||||
|
||||
auto name = FORMAT_NAME;
|
||||
|
||||
if (useKeyring())
|
||||
{
|
||||
auto job = new QKeychain::ReadPasswordJob("chatterino");
|
||||
job->setAutoDelete(true);
|
||||
job->setKey(name);
|
||||
QObject::connect(
|
||||
job, &QKeychain::Job::finished, receiver,
|
||||
[job, onLoaded = std::move(onLoaded)](auto) mutable {
|
||||
onLoaded(job->textData());
|
||||
},
|
||||
Qt::DirectConnection);
|
||||
job->start();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto &instance = insecureInstance();
|
||||
|
||||
onLoaded(instance.object().find(name).value().toString());
|
||||
}
|
||||
}
|
||||
|
||||
void Credentials::set(const QString &provider, const QString &name_,
|
||||
const QString &credential)
|
||||
{
|
||||
assertInGuiThread();
|
||||
|
||||
/// On linux, we try to use a keychain but show a message to disable it when it fails.
|
||||
/// XXX: add said message
|
||||
|
||||
auto name = FORMAT_NAME;
|
||||
|
||||
if (useKeyring())
|
||||
{
|
||||
queueJob(SetJob{name, credential});
|
||||
}
|
||||
else
|
||||
{
|
||||
auto &instance = insecureInstance();
|
||||
|
||||
auto obj = instance.object();
|
||||
obj[name] = credential;
|
||||
instance.setObject(obj);
|
||||
|
||||
queueInsecureSave();
|
||||
}
|
||||
}
|
||||
|
||||
void Credentials::erase(const QString &provider, const QString &name_)
|
||||
{
|
||||
assertInGuiThread();
|
||||
|
||||
auto name = FORMAT_NAME;
|
||||
|
||||
if (useKeyring())
|
||||
{
|
||||
queueJob(EraseJob{name});
|
||||
}
|
||||
else
|
||||
{
|
||||
auto &instance = insecureInstance();
|
||||
|
||||
if (auto it = instance.object().find(name);
|
||||
it != instance.object().end())
|
||||
{
|
||||
instance.object().erase(it);
|
||||
}
|
||||
|
||||
queueInsecureSave();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <functional>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class Credentials
|
||||
{
|
||||
public:
|
||||
static Credentials &instance();
|
||||
|
||||
void get(const QString &provider, const QString &name, QObject *receiver,
|
||||
std::function<void(const QString &)> &&onLoaded);
|
||||
void set(const QString &provider, const QString &name,
|
||||
const QString &credential);
|
||||
void erase(const QString &provider, const QString &name);
|
||||
|
||||
private:
|
||||
Credentials();
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <functional>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class Credentials
|
||||
{
|
||||
public:
|
||||
static Credentials &instance();
|
||||
|
||||
void get(const QString &provider, const QString &name, QObject *receiver,
|
||||
std::function<void(const QString &)> &&onLoaded);
|
||||
void set(const QString &provider, const QString &name,
|
||||
const QString &credential);
|
||||
void erase(const QString &provider, const QString &name);
|
||||
|
||||
private:
|
||||
Credentials();
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -1,260 +1,260 @@
|
|||
#include "Irc2.hpp"
|
||||
|
||||
#include <pajlada/serialize.hpp>
|
||||
#include "common/Credentials.hpp"
|
||||
#include "common/SignalVectorModel.hpp"
|
||||
#include "singletons/Paths.hpp"
|
||||
#include "util/CombinePath.hpp"
|
||||
#include "util/RapidjsonHelpers.hpp"
|
||||
#include "util/StandardItemHelper.hpp"
|
||||
|
||||
#include <QSaveFile>
|
||||
#include <QtConcurrent>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
namespace {
|
||||
QString configPath()
|
||||
{
|
||||
return combinePath(getPaths()->settingsDirectory, "irc.json");
|
||||
}
|
||||
|
||||
class Model : public SignalVectorModel<IrcServerData>
|
||||
{
|
||||
public:
|
||||
Model(QObject *parent)
|
||||
: SignalVectorModel<IrcServerData>(6, parent)
|
||||
{
|
||||
}
|
||||
|
||||
// turn a vector item into a model row
|
||||
IrcServerData getItemFromRow(std::vector<QStandardItem *> &row,
|
||||
const IrcServerData &original)
|
||||
{
|
||||
return IrcServerData{
|
||||
row[0]->data(Qt::EditRole).toString(), // host
|
||||
row[1]->data(Qt::EditRole).toInt(), // port
|
||||
row[2]->data(Qt::CheckStateRole).toBool(), // ssl
|
||||
row[3]->data(Qt::EditRole).toString(), // user
|
||||
row[4]->data(Qt::EditRole).toString(), // nick
|
||||
row[5]->data(Qt::EditRole).toString(), // real
|
||||
original.authType, // authType
|
||||
original.connectCommands, // connectCommands
|
||||
original.id, // id
|
||||
};
|
||||
}
|
||||
|
||||
// turns a row in the model into a vector item
|
||||
void getRowFromItem(const IrcServerData &item,
|
||||
std::vector<QStandardItem *> &row)
|
||||
{
|
||||
setStringItem(row[0], item.host, false);
|
||||
setStringItem(row[1], QString::number(item.port));
|
||||
setBoolItem(row[2], item.ssl);
|
||||
setStringItem(row[3], item.user);
|
||||
setStringItem(row[4], item.nick);
|
||||
setStringItem(row[5], item.real);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
inline QString escape(QString str)
|
||||
{
|
||||
return str.replace(":", "::");
|
||||
}
|
||||
|
||||
// This returns a unique id for every server which is understandeable in the systems credential manager.
|
||||
inline QString getCredentialName(const IrcServerData &data)
|
||||
{
|
||||
return escape(QString::number(data.id)) + ":" + escape(data.user) + "@" +
|
||||
escape(data.host);
|
||||
}
|
||||
|
||||
void IrcServerData::getPassword(
|
||||
QObject *receiver, std::function<void(const QString &)> &&onLoaded) const
|
||||
{
|
||||
Credentials::instance().get("irc", getCredentialName(*this), receiver,
|
||||
std::move(onLoaded));
|
||||
}
|
||||
|
||||
void IrcServerData::setPassword(const QString &password)
|
||||
{
|
||||
Credentials::instance().set("irc", getCredentialName(*this), password);
|
||||
}
|
||||
|
||||
Irc::Irc()
|
||||
{
|
||||
this->connections.itemInserted.connect([this](auto &&args) {
|
||||
// make sure only one id can only exist for one server
|
||||
assert(this->servers_.find(args.item.id) == this->servers_.end());
|
||||
|
||||
// add new server
|
||||
if (auto ab = this->abandonedChannels_.find(args.item.id);
|
||||
ab != this->abandonedChannels_.end())
|
||||
{
|
||||
auto server = std::make_unique<IrcServer>(args.item, ab->second);
|
||||
|
||||
// set server of abandoned channels
|
||||
for (auto weak : ab->second)
|
||||
if (auto shared = weak.lock())
|
||||
if (auto ircChannel =
|
||||
dynamic_cast<IrcChannel *>(shared.get()))
|
||||
ircChannel->setServer(server.get());
|
||||
|
||||
// add new server with abandoned channels
|
||||
this->servers_.emplace(args.item.id, std::move(server));
|
||||
this->abandonedChannels_.erase(ab);
|
||||
}
|
||||
else
|
||||
{
|
||||
// add new server
|
||||
this->servers_.emplace(args.item.id,
|
||||
std::make_unique<IrcServer>(args.item));
|
||||
}
|
||||
});
|
||||
|
||||
this->connections.itemRemoved.connect([this](auto &&args) {
|
||||
// restore
|
||||
if (auto server = this->servers_.find(args.item.id);
|
||||
server != this->servers_.end())
|
||||
{
|
||||
auto abandoned = server->second->getChannels();
|
||||
|
||||
// set server of abandoned servers to nullptr
|
||||
for (auto weak : abandoned)
|
||||
if (auto shared = weak.lock())
|
||||
if (auto ircChannel =
|
||||
dynamic_cast<IrcChannel *>(shared.get()))
|
||||
ircChannel->setServer(nullptr);
|
||||
|
||||
this->abandonedChannels_[args.item.id] = abandoned;
|
||||
this->servers_.erase(server);
|
||||
}
|
||||
|
||||
if (args.caller != Irc::noEraseCredentialCaller)
|
||||
{
|
||||
Credentials::instance().erase("irc", getCredentialName(args.item));
|
||||
}
|
||||
});
|
||||
|
||||
this->connections.delayedItemsChanged.connect([this] { this->save(); });
|
||||
}
|
||||
|
||||
QAbstractTableModel *Irc::newConnectionModel(QObject *parent)
|
||||
{
|
||||
auto model = new Model(parent);
|
||||
model->initialize(&this->connections);
|
||||
return model;
|
||||
}
|
||||
|
||||
ChannelPtr Irc::getOrAddChannel(int id, QString name)
|
||||
{
|
||||
if (auto server = this->servers_.find(id); server != this->servers_.end())
|
||||
{
|
||||
return server->second->getOrAddChannel(name);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto channel = std::make_shared<IrcChannel>(name, nullptr);
|
||||
|
||||
this->abandonedChannels_[id].push_back(channel);
|
||||
|
||||
return std::move(channel);
|
||||
}
|
||||
}
|
||||
|
||||
Irc &Irc::instance()
|
||||
{
|
||||
static Irc irc;
|
||||
return irc;
|
||||
}
|
||||
|
||||
int Irc::uniqueId()
|
||||
{
|
||||
int i = this->currentId_ + 1;
|
||||
auto it = this->servers_.find(i);
|
||||
auto it2 = this->abandonedChannels_.find(i);
|
||||
|
||||
while (it != this->servers_.end() || it2 != this->abandonedChannels_.end())
|
||||
{
|
||||
i++;
|
||||
it = this->servers_.find(i);
|
||||
it2 = this->abandonedChannels_.find(i);
|
||||
}
|
||||
|
||||
return (this->currentId_ = i);
|
||||
}
|
||||
|
||||
void Irc::save()
|
||||
{
|
||||
QJsonDocument doc;
|
||||
QJsonObject root;
|
||||
QJsonArray servers;
|
||||
|
||||
for (auto &&conn : this->connections)
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj.insert("host", conn.host);
|
||||
obj.insert("port", conn.port);
|
||||
obj.insert("ssl", conn.ssl);
|
||||
obj.insert("username", conn.user);
|
||||
obj.insert("nickname", conn.nick);
|
||||
obj.insert("realname", conn.real);
|
||||
obj.insert("connectCommands",
|
||||
QJsonArray::fromStringList(conn.connectCommands));
|
||||
obj.insert("id", conn.id);
|
||||
obj.insert("authType", int(conn.authType));
|
||||
|
||||
servers.append(obj);
|
||||
}
|
||||
|
||||
root.insert("servers", servers);
|
||||
doc.setObject(root);
|
||||
|
||||
QSaveFile file(configPath());
|
||||
file.open(QIODevice::WriteOnly);
|
||||
file.write(doc.toJson());
|
||||
file.commit();
|
||||
}
|
||||
|
||||
void Irc::load()
|
||||
{
|
||||
if (this->loaded_)
|
||||
return;
|
||||
this->loaded_ = true;
|
||||
|
||||
QString config = configPath();
|
||||
QFile file(configPath());
|
||||
file.open(QIODevice::ReadOnly);
|
||||
auto object = QJsonDocument::fromJson(file.readAll()).object();
|
||||
|
||||
std::unordered_set<int> ids;
|
||||
|
||||
// load servers
|
||||
for (auto server : object.value("servers").toArray())
|
||||
{
|
||||
auto obj = server.toObject();
|
||||
IrcServerData data;
|
||||
data.host = obj.value("host").toString(data.host);
|
||||
data.port = obj.value("port").toInt(data.port);
|
||||
data.ssl = obj.value("ssl").toBool(data.ssl);
|
||||
data.user = obj.value("username").toString(data.user);
|
||||
data.nick = obj.value("nickname").toString(data.nick);
|
||||
data.real = obj.value("realname").toString(data.real);
|
||||
data.connectCommands =
|
||||
obj.value("connectCommands").toVariant().toStringList();
|
||||
data.id = obj.value("id").toInt(data.id);
|
||||
data.authType =
|
||||
IrcAuthType(obj.value("authType").toInt(int(data.authType)));
|
||||
|
||||
// duplicate id's are not allowed :(
|
||||
if (ids.find(data.id) == ids.end())
|
||||
{
|
||||
ids.insert(data.id);
|
||||
|
||||
this->connections.append(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
#include "Irc2.hpp"
|
||||
|
||||
#include <pajlada/serialize.hpp>
|
||||
#include "common/Credentials.hpp"
|
||||
#include "common/SignalVectorModel.hpp"
|
||||
#include "singletons/Paths.hpp"
|
||||
#include "util/CombinePath.hpp"
|
||||
#include "util/RapidjsonHelpers.hpp"
|
||||
#include "util/StandardItemHelper.hpp"
|
||||
|
||||
#include <QSaveFile>
|
||||
#include <QtConcurrent>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
namespace {
|
||||
QString configPath()
|
||||
{
|
||||
return combinePath(getPaths()->settingsDirectory, "irc.json");
|
||||
}
|
||||
|
||||
class Model : public SignalVectorModel<IrcServerData>
|
||||
{
|
||||
public:
|
||||
Model(QObject *parent)
|
||||
: SignalVectorModel<IrcServerData>(6, parent)
|
||||
{
|
||||
}
|
||||
|
||||
// turn a vector item into a model row
|
||||
IrcServerData getItemFromRow(std::vector<QStandardItem *> &row,
|
||||
const IrcServerData &original)
|
||||
{
|
||||
return IrcServerData{
|
||||
row[0]->data(Qt::EditRole).toString(), // host
|
||||
row[1]->data(Qt::EditRole).toInt(), // port
|
||||
row[2]->data(Qt::CheckStateRole).toBool(), // ssl
|
||||
row[3]->data(Qt::EditRole).toString(), // user
|
||||
row[4]->data(Qt::EditRole).toString(), // nick
|
||||
row[5]->data(Qt::EditRole).toString(), // real
|
||||
original.authType, // authType
|
||||
original.connectCommands, // connectCommands
|
||||
original.id, // id
|
||||
};
|
||||
}
|
||||
|
||||
// turns a row in the model into a vector item
|
||||
void getRowFromItem(const IrcServerData &item,
|
||||
std::vector<QStandardItem *> &row)
|
||||
{
|
||||
setStringItem(row[0], item.host, false);
|
||||
setStringItem(row[1], QString::number(item.port));
|
||||
setBoolItem(row[2], item.ssl);
|
||||
setStringItem(row[3], item.user);
|
||||
setStringItem(row[4], item.nick);
|
||||
setStringItem(row[5], item.real);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
inline QString escape(QString str)
|
||||
{
|
||||
return str.replace(":", "::");
|
||||
}
|
||||
|
||||
// This returns a unique id for every server which is understandeable in the systems credential manager.
|
||||
inline QString getCredentialName(const IrcServerData &data)
|
||||
{
|
||||
return escape(QString::number(data.id)) + ":" + escape(data.user) + "@" +
|
||||
escape(data.host);
|
||||
}
|
||||
|
||||
void IrcServerData::getPassword(
|
||||
QObject *receiver, std::function<void(const QString &)> &&onLoaded) const
|
||||
{
|
||||
Credentials::instance().get("irc", getCredentialName(*this), receiver,
|
||||
std::move(onLoaded));
|
||||
}
|
||||
|
||||
void IrcServerData::setPassword(const QString &password)
|
||||
{
|
||||
Credentials::instance().set("irc", getCredentialName(*this), password);
|
||||
}
|
||||
|
||||
Irc::Irc()
|
||||
{
|
||||
this->connections.itemInserted.connect([this](auto &&args) {
|
||||
// make sure only one id can only exist for one server
|
||||
assert(this->servers_.find(args.item.id) == this->servers_.end());
|
||||
|
||||
// add new server
|
||||
if (auto ab = this->abandonedChannels_.find(args.item.id);
|
||||
ab != this->abandonedChannels_.end())
|
||||
{
|
||||
auto server = std::make_unique<IrcServer>(args.item, ab->second);
|
||||
|
||||
// set server of abandoned channels
|
||||
for (auto weak : ab->second)
|
||||
if (auto shared = weak.lock())
|
||||
if (auto ircChannel =
|
||||
dynamic_cast<IrcChannel *>(shared.get()))
|
||||
ircChannel->setServer(server.get());
|
||||
|
||||
// add new server with abandoned channels
|
||||
this->servers_.emplace(args.item.id, std::move(server));
|
||||
this->abandonedChannels_.erase(ab);
|
||||
}
|
||||
else
|
||||
{
|
||||
// add new server
|
||||
this->servers_.emplace(args.item.id,
|
||||
std::make_unique<IrcServer>(args.item));
|
||||
}
|
||||
});
|
||||
|
||||
this->connections.itemRemoved.connect([this](auto &&args) {
|
||||
// restore
|
||||
if (auto server = this->servers_.find(args.item.id);
|
||||
server != this->servers_.end())
|
||||
{
|
||||
auto abandoned = server->second->getChannels();
|
||||
|
||||
// set server of abandoned servers to nullptr
|
||||
for (auto weak : abandoned)
|
||||
if (auto shared = weak.lock())
|
||||
if (auto ircChannel =
|
||||
dynamic_cast<IrcChannel *>(shared.get()))
|
||||
ircChannel->setServer(nullptr);
|
||||
|
||||
this->abandonedChannels_[args.item.id] = abandoned;
|
||||
this->servers_.erase(server);
|
||||
}
|
||||
|
||||
if (args.caller != Irc::noEraseCredentialCaller)
|
||||
{
|
||||
Credentials::instance().erase("irc", getCredentialName(args.item));
|
||||
}
|
||||
});
|
||||
|
||||
this->connections.delayedItemsChanged.connect([this] { this->save(); });
|
||||
}
|
||||
|
||||
QAbstractTableModel *Irc::newConnectionModel(QObject *parent)
|
||||
{
|
||||
auto model = new Model(parent);
|
||||
model->initialize(&this->connections);
|
||||
return model;
|
||||
}
|
||||
|
||||
ChannelPtr Irc::getOrAddChannel(int id, QString name)
|
||||
{
|
||||
if (auto server = this->servers_.find(id); server != this->servers_.end())
|
||||
{
|
||||
return server->second->getOrAddChannel(name);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto channel = std::make_shared<IrcChannel>(name, nullptr);
|
||||
|
||||
this->abandonedChannels_[id].push_back(channel);
|
||||
|
||||
return std::move(channel);
|
||||
}
|
||||
}
|
||||
|
||||
Irc &Irc::instance()
|
||||
{
|
||||
static Irc irc;
|
||||
return irc;
|
||||
}
|
||||
|
||||
int Irc::uniqueId()
|
||||
{
|
||||
int i = this->currentId_ + 1;
|
||||
auto it = this->servers_.find(i);
|
||||
auto it2 = this->abandonedChannels_.find(i);
|
||||
|
||||
while (it != this->servers_.end() || it2 != this->abandonedChannels_.end())
|
||||
{
|
||||
i++;
|
||||
it = this->servers_.find(i);
|
||||
it2 = this->abandonedChannels_.find(i);
|
||||
}
|
||||
|
||||
return (this->currentId_ = i);
|
||||
}
|
||||
|
||||
void Irc::save()
|
||||
{
|
||||
QJsonDocument doc;
|
||||
QJsonObject root;
|
||||
QJsonArray servers;
|
||||
|
||||
for (auto &&conn : this->connections)
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj.insert("host", conn.host);
|
||||
obj.insert("port", conn.port);
|
||||
obj.insert("ssl", conn.ssl);
|
||||
obj.insert("username", conn.user);
|
||||
obj.insert("nickname", conn.nick);
|
||||
obj.insert("realname", conn.real);
|
||||
obj.insert("connectCommands",
|
||||
QJsonArray::fromStringList(conn.connectCommands));
|
||||
obj.insert("id", conn.id);
|
||||
obj.insert("authType", int(conn.authType));
|
||||
|
||||
servers.append(obj);
|
||||
}
|
||||
|
||||
root.insert("servers", servers);
|
||||
doc.setObject(root);
|
||||
|
||||
QSaveFile file(configPath());
|
||||
file.open(QIODevice::WriteOnly);
|
||||
file.write(doc.toJson());
|
||||
file.commit();
|
||||
}
|
||||
|
||||
void Irc::load()
|
||||
{
|
||||
if (this->loaded_)
|
||||
return;
|
||||
this->loaded_ = true;
|
||||
|
||||
QString config = configPath();
|
||||
QFile file(configPath());
|
||||
file.open(QIODevice::ReadOnly);
|
||||
auto object = QJsonDocument::fromJson(file.readAll()).object();
|
||||
|
||||
std::unordered_set<int> ids;
|
||||
|
||||
// load servers
|
||||
for (auto server : object.value("servers").toArray())
|
||||
{
|
||||
auto obj = server.toObject();
|
||||
IrcServerData data;
|
||||
data.host = obj.value("host").toString(data.host);
|
||||
data.port = obj.value("port").toInt(data.port);
|
||||
data.ssl = obj.value("ssl").toBool(data.ssl);
|
||||
data.user = obj.value("username").toString(data.user);
|
||||
data.nick = obj.value("nickname").toString(data.nick);
|
||||
data.real = obj.value("realname").toString(data.real);
|
||||
data.connectCommands =
|
||||
obj.value("connectCommands").toVariant().toStringList();
|
||||
data.id = obj.value("id").toInt(data.id);
|
||||
data.authType =
|
||||
IrcAuthType(obj.value("authType").toInt(int(data.authType)));
|
||||
|
||||
// duplicate id's are not allowed :(
|
||||
if (ids.find(data.id) == ids.end())
|
||||
{
|
||||
ids.insert(data.id);
|
||||
|
||||
this->connections.append(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -1,67 +1,67 @@
|
|||
#pragma once
|
||||
|
||||
#include <rapidjson/rapidjson.h>
|
||||
#include <common/SignalVector.hpp>
|
||||
|
||||
#include "providers/irc/IrcChannel2.hpp"
|
||||
#include "providers/irc/IrcServer.hpp"
|
||||
|
||||
class QAbstractTableModel;
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
enum class IrcAuthType { Anonymous, Custom, Pass, Sasl };
|
||||
|
||||
struct IrcServerData {
|
||||
QString host;
|
||||
int port = 6697;
|
||||
bool ssl = true;
|
||||
|
||||
QString user;
|
||||
QString nick;
|
||||
QString real;
|
||||
|
||||
IrcAuthType authType = IrcAuthType::Anonymous;
|
||||
void getPassword(QObject *receiver,
|
||||
std::function<void(const QString &)> &&onLoaded) const;
|
||||
void setPassword(const QString &password);
|
||||
|
||||
QStringList connectCommands;
|
||||
|
||||
int id;
|
||||
};
|
||||
|
||||
class Irc
|
||||
{
|
||||
public:
|
||||
Irc();
|
||||
|
||||
static Irc &instance();
|
||||
|
||||
static inline void *const noEraseCredentialCaller =
|
||||
reinterpret_cast<void *>(1);
|
||||
|
||||
SignalVector<IrcServerData> connections;
|
||||
QAbstractTableModel *newConnectionModel(QObject *parent);
|
||||
|
||||
ChannelPtr getOrAddChannel(int serverId, QString name);
|
||||
|
||||
void save();
|
||||
void load();
|
||||
|
||||
int uniqueId();
|
||||
|
||||
private:
|
||||
int currentId_{};
|
||||
bool loaded_{};
|
||||
|
||||
// Servers have a unique id.
|
||||
// When a server gets changed it gets removed and then added again.
|
||||
// So we store the channels of that server in abandonedChannels_ temporarily.
|
||||
// Or if the server got removed permanently then it's still stored there.
|
||||
std::unordered_map<int, std::unique_ptr<IrcServer>> servers_;
|
||||
std::unordered_map<int, std::vector<std::weak_ptr<Channel>>>
|
||||
abandonedChannels_;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
#pragma once
|
||||
|
||||
#include <rapidjson/rapidjson.h>
|
||||
#include <common/SignalVector.hpp>
|
||||
|
||||
#include "providers/irc/IrcChannel2.hpp"
|
||||
#include "providers/irc/IrcServer.hpp"
|
||||
|
||||
class QAbstractTableModel;
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
enum class IrcAuthType { Anonymous, Custom, Pass, Sasl };
|
||||
|
||||
struct IrcServerData {
|
||||
QString host;
|
||||
int port = 6697;
|
||||
bool ssl = true;
|
||||
|
||||
QString user;
|
||||
QString nick;
|
||||
QString real;
|
||||
|
||||
IrcAuthType authType = IrcAuthType::Anonymous;
|
||||
void getPassword(QObject *receiver,
|
||||
std::function<void(const QString &)> &&onLoaded) const;
|
||||
void setPassword(const QString &password);
|
||||
|
||||
QStringList connectCommands;
|
||||
|
||||
int id;
|
||||
};
|
||||
|
||||
class Irc
|
||||
{
|
||||
public:
|
||||
Irc();
|
||||
|
||||
static Irc &instance();
|
||||
|
||||
static inline void *const noEraseCredentialCaller =
|
||||
reinterpret_cast<void *>(1);
|
||||
|
||||
SignalVector<IrcServerData> connections;
|
||||
QAbstractTableModel *newConnectionModel(QObject *parent);
|
||||
|
||||
ChannelPtr getOrAddChannel(int serverId, QString name);
|
||||
|
||||
void save();
|
||||
void load();
|
||||
|
||||
int uniqueId();
|
||||
|
||||
private:
|
||||
int currentId_{};
|
||||
bool loaded_{};
|
||||
|
||||
// Servers have a unique id.
|
||||
// When a server gets changed it gets removed and then added again.
|
||||
// So we store the channels of that server in abandonedChannels_ temporarily.
|
||||
// Or if the server got removed permanently then it's still stored there.
|
||||
std::unordered_map<int, std::unique_ptr<IrcServer>> servers_;
|
||||
std::unordered_map<int, std::vector<std::weak_ptr<Channel>>>
|
||||
abandonedChannels_;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -1,76 +1,76 @@
|
|||
#include "IrcCommands.hpp"
|
||||
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/irc/IrcChannel2.hpp"
|
||||
#include "providers/irc/IrcServer.hpp"
|
||||
#include "util/Overloaded.hpp"
|
||||
#include "util/QStringHash.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
Outcome invokeIrcCommand(const QString &commandName, const QString &allParams,
|
||||
IrcChannel &channel)
|
||||
{
|
||||
if (!channel.server())
|
||||
{
|
||||
return Failure;
|
||||
}
|
||||
|
||||
// STATIC MESSAGES
|
||||
static auto staticMessages = std::unordered_map<QString, QString>{
|
||||
{"join", "/join is not supported. Press ctrl+r to change the "
|
||||
"channel. If required use /raw JOIN #channel."},
|
||||
{"part", "/part is not supported. Press ctrl+r to change the "
|
||||
"channel. If required use /raw PART #channel."},
|
||||
};
|
||||
auto cmd = commandName.toLower();
|
||||
|
||||
if (auto it = staticMessages.find(cmd); it != staticMessages.end())
|
||||
{
|
||||
channel.addMessage(makeSystemMessage(it->second));
|
||||
return Success;
|
||||
}
|
||||
|
||||
// CUSTOM COMMANDS
|
||||
auto params = allParams.split(' ');
|
||||
auto paramsAfter = [&](int i) { return params.mid(i + 1).join(' '); };
|
||||
|
||||
auto sendRaw = [&](QString str) { channel.server()->sendRawMessage(str); };
|
||||
|
||||
if (cmd == "msg")
|
||||
{
|
||||
sendRaw("PRIVMSG " + params[0] + " :" + paramsAfter(0));
|
||||
}
|
||||
else if (cmd == "away")
|
||||
{
|
||||
sendRaw("AWAY" + params[0] + " :" + paramsAfter(0));
|
||||
}
|
||||
else if (cmd == "knock")
|
||||
{
|
||||
sendRaw("KNOCK #" + params[0] + " " + paramsAfter(0));
|
||||
}
|
||||
else if (cmd == "kick")
|
||||
{
|
||||
if (paramsAfter(1).isEmpty())
|
||||
sendRaw("KICK " + params[0] + " " + params[1]);
|
||||
else
|
||||
sendRaw("KICK " + params[0] + " " + params[1] + " :" +
|
||||
paramsAfter(1));
|
||||
}
|
||||
else if (cmd == "wallops")
|
||||
{
|
||||
sendRaw("WALLOPS :" + allParams);
|
||||
}
|
||||
else if (cmd == "raw")
|
||||
{
|
||||
sendRaw(allParams);
|
||||
}
|
||||
else
|
||||
{
|
||||
sendRaw(cmd.toUpper() + " " + allParams);
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
#include "IrcCommands.hpp"
|
||||
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/irc/IrcChannel2.hpp"
|
||||
#include "providers/irc/IrcServer.hpp"
|
||||
#include "util/Overloaded.hpp"
|
||||
#include "util/QStringHash.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
Outcome invokeIrcCommand(const QString &commandName, const QString &allParams,
|
||||
IrcChannel &channel)
|
||||
{
|
||||
if (!channel.server())
|
||||
{
|
||||
return Failure;
|
||||
}
|
||||
|
||||
// STATIC MESSAGES
|
||||
static auto staticMessages = std::unordered_map<QString, QString>{
|
||||
{"join", "/join is not supported. Press ctrl+r to change the "
|
||||
"channel. If required use /raw JOIN #channel."},
|
||||
{"part", "/part is not supported. Press ctrl+r to change the "
|
||||
"channel. If required use /raw PART #channel."},
|
||||
};
|
||||
auto cmd = commandName.toLower();
|
||||
|
||||
if (auto it = staticMessages.find(cmd); it != staticMessages.end())
|
||||
{
|
||||
channel.addMessage(makeSystemMessage(it->second));
|
||||
return Success;
|
||||
}
|
||||
|
||||
// CUSTOM COMMANDS
|
||||
auto params = allParams.split(' ');
|
||||
auto paramsAfter = [&](int i) { return params.mid(i + 1).join(' '); };
|
||||
|
||||
auto sendRaw = [&](QString str) { channel.server()->sendRawMessage(str); };
|
||||
|
||||
if (cmd == "msg")
|
||||
{
|
||||
sendRaw("PRIVMSG " + params[0] + " :" + paramsAfter(0));
|
||||
}
|
||||
else if (cmd == "away")
|
||||
{
|
||||
sendRaw("AWAY" + params[0] + " :" + paramsAfter(0));
|
||||
}
|
||||
else if (cmd == "knock")
|
||||
{
|
||||
sendRaw("KNOCK #" + params[0] + " " + paramsAfter(0));
|
||||
}
|
||||
else if (cmd == "kick")
|
||||
{
|
||||
if (paramsAfter(1).isEmpty())
|
||||
sendRaw("KICK " + params[0] + " " + params[1]);
|
||||
else
|
||||
sendRaw("KICK " + params[0] + " " + params[1] + " :" +
|
||||
paramsAfter(1));
|
||||
}
|
||||
else if (cmd == "wallops")
|
||||
{
|
||||
sendRaw("WALLOPS :" + allParams);
|
||||
}
|
||||
else if (cmd == "raw")
|
||||
{
|
||||
sendRaw(allParams);
|
||||
}
|
||||
else
|
||||
{
|
||||
sendRaw(cmd.toUpper() + " " + allParams);
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "common/Outcome.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class IrcChannel;
|
||||
|
||||
Outcome invokeIrcCommand(const QString &command, const QString ¶ms,
|
||||
IrcChannel &channel);
|
||||
|
||||
} // namespace chatterino
|
||||
#pragma once
|
||||
|
||||
#include "common/Outcome.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class IrcChannel;
|
||||
|
||||
Outcome invokeIrcCommand(const QString &command, const QString ¶ms,
|
||||
IrcChannel &channel);
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "IrcMessageHandler.hpp"
|
||||
#include "IrcMessageHandler.hpp"
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "controllers/accounts/AccountController.hpp"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "common/Aliases.hpp"
|
||||
#include "common/Outcome.hpp"
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
template <class... Ts>
|
||||
struct Overloaded : Ts... {
|
||||
using Ts::operator()...;
|
||||
};
|
||||
|
||||
template <class... Ts>
|
||||
Overloaded(Ts...)->Overloaded<Ts...>;
|
||||
|
||||
} // namespace chatterino
|
||||
#pragma once
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
template <class... Ts>
|
||||
struct Overloaded : Ts... {
|
||||
using Ts::operator()...;
|
||||
};
|
||||
|
||||
template <class... Ts>
|
||||
Overloaded(Ts...) -> Overloaded<Ts...>;
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -1,97 +1,97 @@
|
|||
#include "IrcConnectionEditor.hpp"
|
||||
#include "ui_IrcConnectionEditor.h"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
IrcConnectionEditor::IrcConnectionEditor(const IrcServerData &data, bool isAdd,
|
||||
QWidget *parent)
|
||||
|
||||
: QDialog(parent, Qt::WindowStaysOnTopHint)
|
||||
, ui_(new Ui::IrcConnectionEditor)
|
||||
, data_(data)
|
||||
{
|
||||
this->ui_->setupUi(this);
|
||||
|
||||
this->setWindowTitle(QString(isAdd ? "Add " : "Edit ") + "Irc Connection");
|
||||
|
||||
QObject::connect(this->ui_->userNameLineEdit, &QLineEdit::textChanged, this,
|
||||
[this](const QString &text) {
|
||||
this->ui_->nickNameLineEdit->setPlaceholderText(text);
|
||||
this->ui_->realNameLineEdit->setPlaceholderText(text);
|
||||
});
|
||||
|
||||
this->ui_->serverLineEdit->setText(data.host);
|
||||
this->ui_->portSpinBox->setValue(data.port);
|
||||
this->ui_->securityCheckBox->setChecked(data.ssl);
|
||||
this->ui_->userNameLineEdit->setText(data.user);
|
||||
this->ui_->nickNameLineEdit->setText(data.nick);
|
||||
this->ui_->realNameLineEdit->setText(data.real);
|
||||
this->ui_->connectCommandsEditor->setPlainText(
|
||||
data.connectCommands.join('\n'));
|
||||
|
||||
data.getPassword(this, [this](const QString &password) {
|
||||
this->ui_->passwordLineEdit->setText(password);
|
||||
});
|
||||
|
||||
this->ui_->loginMethodComboBox->setCurrentIndex([&] {
|
||||
switch (data.authType)
|
||||
{
|
||||
case IrcAuthType::Custom:
|
||||
return 1;
|
||||
case IrcAuthType::Pass:
|
||||
return 2;
|
||||
case IrcAuthType::Sasl:
|
||||
return 3;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}());
|
||||
|
||||
QObject::connect(this->ui_->loginMethodComboBox,
|
||||
qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
[this](int index) {
|
||||
if (index == 1) // Custom
|
||||
{
|
||||
this->ui_->connectCommandsEditor->setFocus();
|
||||
}
|
||||
});
|
||||
|
||||
QFont font("Monospace");
|
||||
font.setStyleHint(QFont::TypeWriter);
|
||||
this->ui_->connectCommandsEditor->setFont(font);
|
||||
}
|
||||
|
||||
IrcConnectionEditor::~IrcConnectionEditor()
|
||||
{
|
||||
delete ui_;
|
||||
}
|
||||
|
||||
IrcServerData IrcConnectionEditor::data()
|
||||
{
|
||||
auto data = this->data_;
|
||||
data.host = this->ui_->serverLineEdit->text();
|
||||
data.port = this->ui_->portSpinBox->value();
|
||||
data.ssl = this->ui_->securityCheckBox->isChecked();
|
||||
data.user = this->ui_->userNameLineEdit->text();
|
||||
data.nick = this->ui_->nickNameLineEdit->text();
|
||||
data.real = this->ui_->realNameLineEdit->text();
|
||||
data.connectCommands =
|
||||
this->ui_->connectCommandsEditor->toPlainText().split('\n');
|
||||
data.setPassword(this->ui_->passwordLineEdit->text());
|
||||
data.authType = [this] {
|
||||
switch (this->ui_->loginMethodComboBox->currentIndex())
|
||||
{
|
||||
case 1:
|
||||
return IrcAuthType::Custom;
|
||||
case 2:
|
||||
return IrcAuthType::Pass;
|
||||
case 3:
|
||||
return IrcAuthType::Sasl;
|
||||
default:
|
||||
return IrcAuthType::Anonymous;
|
||||
}
|
||||
}();
|
||||
return data;
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
#include "IrcConnectionEditor.hpp"
|
||||
#include "ui_IrcConnectionEditor.h"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
IrcConnectionEditor::IrcConnectionEditor(const IrcServerData &data, bool isAdd,
|
||||
QWidget *parent)
|
||||
|
||||
: QDialog(parent, Qt::WindowStaysOnTopHint)
|
||||
, ui_(new Ui::IrcConnectionEditor)
|
||||
, data_(data)
|
||||
{
|
||||
this->ui_->setupUi(this);
|
||||
|
||||
this->setWindowTitle(QString(isAdd ? "Add " : "Edit ") + "Irc Connection");
|
||||
|
||||
QObject::connect(this->ui_->userNameLineEdit, &QLineEdit::textChanged, this,
|
||||
[this](const QString &text) {
|
||||
this->ui_->nickNameLineEdit->setPlaceholderText(text);
|
||||
this->ui_->realNameLineEdit->setPlaceholderText(text);
|
||||
});
|
||||
|
||||
this->ui_->serverLineEdit->setText(data.host);
|
||||
this->ui_->portSpinBox->setValue(data.port);
|
||||
this->ui_->securityCheckBox->setChecked(data.ssl);
|
||||
this->ui_->userNameLineEdit->setText(data.user);
|
||||
this->ui_->nickNameLineEdit->setText(data.nick);
|
||||
this->ui_->realNameLineEdit->setText(data.real);
|
||||
this->ui_->connectCommandsEditor->setPlainText(
|
||||
data.connectCommands.join('\n'));
|
||||
|
||||
data.getPassword(this, [this](const QString &password) {
|
||||
this->ui_->passwordLineEdit->setText(password);
|
||||
});
|
||||
|
||||
this->ui_->loginMethodComboBox->setCurrentIndex([&] {
|
||||
switch (data.authType)
|
||||
{
|
||||
case IrcAuthType::Custom:
|
||||
return 1;
|
||||
case IrcAuthType::Pass:
|
||||
return 2;
|
||||
case IrcAuthType::Sasl:
|
||||
return 3;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}());
|
||||
|
||||
QObject::connect(this->ui_->loginMethodComboBox,
|
||||
qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
[this](int index) {
|
||||
if (index == 1) // Custom
|
||||
{
|
||||
this->ui_->connectCommandsEditor->setFocus();
|
||||
}
|
||||
});
|
||||
|
||||
QFont font("Monospace");
|
||||
font.setStyleHint(QFont::TypeWriter);
|
||||
this->ui_->connectCommandsEditor->setFont(font);
|
||||
}
|
||||
|
||||
IrcConnectionEditor::~IrcConnectionEditor()
|
||||
{
|
||||
delete ui_;
|
||||
}
|
||||
|
||||
IrcServerData IrcConnectionEditor::data()
|
||||
{
|
||||
auto data = this->data_;
|
||||
data.host = this->ui_->serverLineEdit->text();
|
||||
data.port = this->ui_->portSpinBox->value();
|
||||
data.ssl = this->ui_->securityCheckBox->isChecked();
|
||||
data.user = this->ui_->userNameLineEdit->text();
|
||||
data.nick = this->ui_->nickNameLineEdit->text();
|
||||
data.real = this->ui_->realNameLineEdit->text();
|
||||
data.connectCommands =
|
||||
this->ui_->connectCommandsEditor->toPlainText().split('\n');
|
||||
data.setPassword(this->ui_->passwordLineEdit->text());
|
||||
data.authType = [this] {
|
||||
switch (this->ui_->loginMethodComboBox->currentIndex())
|
||||
{
|
||||
case 1:
|
||||
return IrcAuthType::Custom;
|
||||
case 2:
|
||||
return IrcAuthType::Pass;
|
||||
case 3:
|
||||
return IrcAuthType::Sasl;
|
||||
default:
|
||||
return IrcAuthType::Anonymous;
|
||||
}
|
||||
}();
|
||||
return data;
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -1,32 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include "providers/irc/Irc2.hpp"
|
||||
#include "widgets/BaseWindow.hpp"
|
||||
|
||||
namespace Ui {
|
||||
class IrcConnectionEditor;
|
||||
}
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
struct IrcServerData;
|
||||
|
||||
class IrcConnectionEditor : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit IrcConnectionEditor(const IrcServerData &data, bool isAdd = false,
|
||||
QWidget *parent = nullptr);
|
||||
~IrcConnectionEditor();
|
||||
|
||||
IrcServerData data();
|
||||
|
||||
private:
|
||||
Ui::IrcConnectionEditor *ui_;
|
||||
IrcServerData data_;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
#pragma once
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include "providers/irc/Irc2.hpp"
|
||||
#include "widgets/BaseWindow.hpp"
|
||||
|
||||
namespace Ui {
|
||||
class IrcConnectionEditor;
|
||||
}
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
struct IrcServerData;
|
||||
|
||||
class IrcConnectionEditor : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit IrcConnectionEditor(const IrcServerData &data, bool isAdd = false,
|
||||
QWidget *parent = nullptr);
|
||||
~IrcConnectionEditor();
|
||||
|
||||
IrcServerData data();
|
||||
|
||||
private:
|
||||
Ui::IrcConnectionEditor *ui_;
|
||||
IrcServerData data_;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -4,12 +4,14 @@ set -eu
|
|||
|
||||
fail="0"
|
||||
|
||||
clang-format --version
|
||||
|
||||
while read -r file; do
|
||||
if ! diff -u <(cat "$file") <(clang-format "$file"); then
|
||||
echo "$file differs!!!!!!!"
|
||||
fail="1"
|
||||
fi
|
||||
done < <(find src/ \( -iname "*.hpp" -o -iname "*.cpp" \))
|
||||
done < <(find src/ -type f \( -iname "*.hpp" -o -iname "*.cpp" \))
|
||||
|
||||
if [ "$fail" = "1" ]; then
|
||||
echo "At least one file is poorly formatted - check the output above"
|
||||
|
|
22
tools/check-line-endings.sh
Executable file
22
tools/check-line-endings.sh
Executable file
|
@ -0,0 +1,22 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
|
||||
fail="0"
|
||||
|
||||
dos2unix --version
|
||||
|
||||
while read -r file; do
|
||||
num_dos_line_endings=$(dos2unix -id "$file" | awk '/[0-9]+/{print $(NF-1)}')
|
||||
if [ "$num_dos_line_endings" -gt "0" ]; then
|
||||
>&2 echo "File '$file' contains $num_dos_line_endings DOS line-endings, it should only be using unix line-endings!"
|
||||
fail="1"
|
||||
fi
|
||||
done < <(find src/ -type f \( -iname "*.hpp" -o -iname "*.cpp" \))
|
||||
|
||||
if [ "$fail" = "1" ]; then
|
||||
>&2 echo "At least one file is not using unix line-endings - check the output above"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
>&2 echo "Every file seems to be using unix line-endings. Good job!"
|
Loading…
Reference in a new issue