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:
pajlada 2020-10-18 15:54:48 +02:00 committed by GitHub
parent 4199a01b96
commit f191de2514
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 959 additions and 934 deletions

View file

@ -8,18 +8,19 @@ on:
pull_request: pull_request:
jobs: jobs:
build: check:
runs-on: ubuntu-latest runs-on: ubuntu-20.04
container:
image: ubuntu:19.10
steps: steps:
- uses: actions/checkout@v2.3.3 - uses: actions/checkout@v2.3.3
- name: apt-get update - name: apt-get update
run: apt-get update run: sudo apt-get update
- name: Install clang-format - name: Install clang-format
run: apt-get -y install clang-format run: sudo apt-get -y install clang-format dos2unix
- name: Check formatting - name: Check formatting
run: ./tools/check-format.sh run: ./tools/check-format.sh
- name: Check line-endings
run: ./tools/check-line-endings.sh

View file

@ -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. 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 ### 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 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 3. In QT Creator, select `Help` > `About Plugins` > `C++` > `Beautifier` to enable the plugin
4. Restart QT Creator 4. Restart QT Creator

View file

@ -1,72 +1,72 @@
#include "ChannelChatters.hpp" #include "ChannelChatters.hpp"
#include "messages/Message.hpp" #include "messages/Message.hpp"
#include "messages/MessageBuilder.hpp" #include "messages/MessageBuilder.hpp"
namespace chatterino { namespace chatterino {
ChannelChatters::ChannelChatters(Channel &channel) ChannelChatters::ChannelChatters(Channel &channel)
: channel_(channel) : channel_(channel)
{ {
} }
AccessGuard<const UsernameSet> ChannelChatters::accessChatters() const AccessGuard<const UsernameSet> ChannelChatters::accessChatters() const
{ {
return this->chatters_.accessConst(); return this->chatters_.accessConst();
} }
void ChannelChatters::addRecentChatter(const QString &user) void ChannelChatters::addRecentChatter(const QString &user)
{ {
this->chatters_.access()->insert(user); this->chatters_.access()->insert(user);
} }
void ChannelChatters::addJoinedUser(const QString &user) void ChannelChatters::addJoinedUser(const QString &user)
{ {
auto joinedUsers = this->joinedUsers_.access(); auto joinedUsers = this->joinedUsers_.access();
joinedUsers->append(user); joinedUsers->append(user);
if (!this->joinedUsersMergeQueued_) if (!this->joinedUsersMergeQueued_)
{ {
this->joinedUsersMergeQueued_ = true; this->joinedUsersMergeQueued_ = true;
QTimer::singleShot(500, &this->lifetimeGuard_, [this] { QTimer::singleShot(500, &this->lifetimeGuard_, [this] {
auto joinedUsers = this->joinedUsers_.access(); auto joinedUsers = this->joinedUsers_.access();
MessageBuilder builder(systemMessage, MessageBuilder builder(systemMessage,
"Users joined: " + joinedUsers->join(", ")); "Users joined: " + joinedUsers->join(", "));
builder->flags.set(MessageFlag::Collapsed); builder->flags.set(MessageFlag::Collapsed);
joinedUsers->clear(); joinedUsers->clear();
this->channel_.addMessage(builder.release()); this->channel_.addMessage(builder.release());
this->joinedUsersMergeQueued_ = false; this->joinedUsersMergeQueued_ = false;
}); });
} }
} }
void ChannelChatters::addPartedUser(const QString &user) void ChannelChatters::addPartedUser(const QString &user)
{ {
auto partedUsers = this->partedUsers_.access(); auto partedUsers = this->partedUsers_.access();
partedUsers->append(user); partedUsers->append(user);
if (!this->partedUsersMergeQueued_) if (!this->partedUsersMergeQueued_)
{ {
this->partedUsersMergeQueued_ = true; this->partedUsersMergeQueued_ = true;
QTimer::singleShot(500, &this->lifetimeGuard_, [this] { QTimer::singleShot(500, &this->lifetimeGuard_, [this] {
auto partedUsers = this->partedUsers_.access(); auto partedUsers = this->partedUsers_.access();
MessageBuilder builder(systemMessage, MessageBuilder builder(systemMessage,
"Users parted: " + partedUsers->join(", ")); "Users parted: " + partedUsers->join(", "));
builder->flags.set(MessageFlag::Collapsed); builder->flags.set(MessageFlag::Collapsed);
this->channel_.addMessage(builder.release()); this->channel_.addMessage(builder.release());
partedUsers->clear(); partedUsers->clear();
this->partedUsersMergeQueued_ = false; this->partedUsersMergeQueued_ = false;
}); });
} }
} }
void ChannelChatters::setChatters(UsernameSet &&set) void ChannelChatters::setChatters(UsernameSet &&set)
{ {
*this->chatters_.access() = set; *this->chatters_.access() = set;
} }
} // namespace chatterino } // namespace chatterino

View file

@ -1,37 +1,37 @@
#pragma once #pragma once
#include "common/Channel.hpp" #include "common/Channel.hpp"
#include "common/UniqueAccess.hpp" #include "common/UniqueAccess.hpp"
#include "common/UsernameSet.hpp" #include "common/UsernameSet.hpp"
namespace chatterino { namespace chatterino {
class ChannelChatters class ChannelChatters
{ {
public: public:
ChannelChatters(Channel &channel); ChannelChatters(Channel &channel);
virtual ~ChannelChatters() = default; // add vtable virtual ~ChannelChatters() = default; // add vtable
AccessGuard<const UsernameSet> accessChatters() const; AccessGuard<const UsernameSet> accessChatters() const;
void addRecentChatter(const QString &user); void addRecentChatter(const QString &user);
void addJoinedUser(const QString &user); void addJoinedUser(const QString &user);
void addPartedUser(const QString &user); void addPartedUser(const QString &user);
void setChatters(UsernameSet &&set); void setChatters(UsernameSet &&set);
private: private:
Channel &channel_; Channel &channel_;
// maps 2 char prefix to set of names // maps 2 char prefix to set of names
UniqueAccess<UsernameSet> chatters_; UniqueAccess<UsernameSet> chatters_;
// combines multiple joins/parts into one message // combines multiple joins/parts into one message
UniqueAccess<QStringList> joinedUsers_; UniqueAccess<QStringList> joinedUsers_;
bool joinedUsersMergeQueued_ = false; bool joinedUsersMergeQueued_ = false;
UniqueAccess<QStringList> partedUsers_; UniqueAccess<QStringList> partedUsers_;
bool partedUsersMergeQueued_ = false; bool partedUsersMergeQueued_ = false;
QObject lifetimeGuard_; QObject lifetimeGuard_;
}; };
} // namespace chatterino } // namespace chatterino

View file

@ -1,235 +1,235 @@
#include "Credentials.hpp" #include "Credentials.hpp"
#include "debug/AssertInGuiThread.hpp" #include "debug/AssertInGuiThread.hpp"
#include "keychain.h" #include "keychain.h"
#include "singletons/Paths.hpp" #include "singletons/Paths.hpp"
#include "singletons/Settings.hpp" #include "singletons/Settings.hpp"
#include "util/CombinePath.hpp" #include "util/CombinePath.hpp"
#include "util/Overloaded.hpp" #include "util/Overloaded.hpp"
#include <QSaveFile> #include <QSaveFile>
#include <boost/variant.hpp> #include <boost/variant.hpp>
#define FORMAT_NAME \ #define FORMAT_NAME \
([&] { \ ([&] { \
assert(!provider.contains(":")); \ assert(!provider.contains(":")); \
return QString("chatterino:%1:%2").arg(provider).arg(name_); \ return QString("chatterino:%1:%2").arg(provider).arg(name_); \
})() })()
namespace chatterino { namespace chatterino {
namespace { namespace {
bool useKeyring() bool useKeyring()
{ {
if (getPaths()->isPortable()) if (getPaths()->isPortable())
{ {
return false; return false;
} }
else else
{ {
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
return getSettings()->useKeyring; return getSettings()->useKeyring;
#else #else
return true; return true;
#endif #endif
} }
} }
// Insecure storage: // Insecure storage:
QString insecurePath() QString insecurePath()
{ {
return combinePath(getPaths()->settingsDirectory, "credentials.json"); return combinePath(getPaths()->settingsDirectory, "credentials.json");
} }
QJsonDocument loadInsecure() QJsonDocument loadInsecure()
{ {
QFile file(insecurePath()); QFile file(insecurePath());
file.open(QIODevice::ReadOnly); file.open(QIODevice::ReadOnly);
return QJsonDocument::fromJson(file.readAll()); return QJsonDocument::fromJson(file.readAll());
} }
void storeInsecure(const QJsonDocument &doc) void storeInsecure(const QJsonDocument &doc)
{ {
QSaveFile file(insecurePath()); QSaveFile file(insecurePath());
file.open(QIODevice::WriteOnly); file.open(QIODevice::WriteOnly);
file.write(doc.toJson()); file.write(doc.toJson());
file.commit(); file.commit();
} }
QJsonDocument &insecureInstance() QJsonDocument &insecureInstance()
{ {
static auto store = loadInsecure(); static auto store = loadInsecure();
return store; return store;
} }
void queueInsecureSave() void queueInsecureSave()
{ {
static bool isQueued = false; static bool isQueued = false;
if (!isQueued) if (!isQueued)
{ {
isQueued = true; isQueued = true;
QTimer::singleShot(200, qApp, [] { QTimer::singleShot(200, qApp, [] {
storeInsecure(insecureInstance()); storeInsecure(insecureInstance());
isQueued = false; isQueued = false;
}); });
} }
} }
// QKeychain runs jobs asyncronously, so we have to assure that set/erase // QKeychain runs jobs asyncronously, so we have to assure that set/erase
// jobs gets executed in order. // jobs gets executed in order.
struct SetJob { struct SetJob {
QString name; QString name;
QString credential; QString credential;
}; };
struct EraseJob { struct EraseJob {
QString name; QString name;
}; };
using Job = boost::variant<SetJob, EraseJob>; using Job = boost::variant<SetJob, EraseJob>;
static std::queue<Job> &jobQueue() static std::queue<Job> &jobQueue()
{ {
static std::queue<Job> jobs; static std::queue<Job> jobs;
return jobs; return jobs;
} }
static void runNextJob() static void runNextJob()
{ {
auto &&queue = jobQueue(); auto &&queue = jobQueue();
if (!queue.empty()) if (!queue.empty())
{ {
// we were gonna use std::visit here but macos is shit // we were gonna use std::visit here but macos is shit
auto &&item = queue.front(); auto &&item = queue.front();
if (item.which() == 0) // set job if (item.which() == 0) // set job
{ {
auto set = boost::get<SetJob>(item); auto set = boost::get<SetJob>(item);
auto job = new QKeychain::WritePasswordJob("chatterino"); auto job = new QKeychain::WritePasswordJob("chatterino");
job->setAutoDelete(true); job->setAutoDelete(true);
job->setKey(set.name); job->setKey(set.name);
job->setTextData(set.credential); job->setTextData(set.credential);
QObject::connect(job, &QKeychain::Job::finished, qApp, QObject::connect(job, &QKeychain::Job::finished, qApp,
[](auto) { runNextJob(); }); [](auto) { runNextJob(); });
job->start(); job->start();
} }
else // erase job else // erase job
{ {
auto erase = boost::get<EraseJob>(item); auto erase = boost::get<EraseJob>(item);
auto job = new QKeychain::DeletePasswordJob("chatterino"); auto job = new QKeychain::DeletePasswordJob("chatterino");
job->setAutoDelete(true); job->setAutoDelete(true);
job->setKey(erase.name); job->setKey(erase.name);
QObject::connect(job, &QKeychain::Job::finished, qApp, QObject::connect(job, &QKeychain::Job::finished, qApp,
[](auto) { runNextJob(); }); [](auto) { runNextJob(); });
job->start(); job->start();
} }
queue.pop(); queue.pop();
} }
} }
static void queueJob(Job &&job) static void queueJob(Job &&job)
{ {
auto &&queue = jobQueue(); auto &&queue = jobQueue();
queue.push(std::move(job)); queue.push(std::move(job));
if (queue.size() == 1) if (queue.size() == 1)
{ {
runNextJob(); runNextJob();
} }
} }
} // namespace } // namespace
Credentials &Credentials::instance() Credentials &Credentials::instance()
{ {
static Credentials creds; static Credentials creds;
return creds; return creds;
} }
Credentials::Credentials() Credentials::Credentials()
{ {
} }
void Credentials::get(const QString &provider, const QString &name_, void Credentials::get(const QString &provider, const QString &name_,
QObject *receiver, QObject *receiver,
std::function<void(const QString &)> &&onLoaded) std::function<void(const QString &)> &&onLoaded)
{ {
assertInGuiThread(); assertInGuiThread();
auto name = FORMAT_NAME; auto name = FORMAT_NAME;
if (useKeyring()) if (useKeyring())
{ {
auto job = new QKeychain::ReadPasswordJob("chatterino"); auto job = new QKeychain::ReadPasswordJob("chatterino");
job->setAutoDelete(true); job->setAutoDelete(true);
job->setKey(name); job->setKey(name);
QObject::connect( QObject::connect(
job, &QKeychain::Job::finished, receiver, job, &QKeychain::Job::finished, receiver,
[job, onLoaded = std::move(onLoaded)](auto) mutable { [job, onLoaded = std::move(onLoaded)](auto) mutable {
onLoaded(job->textData()); onLoaded(job->textData());
}, },
Qt::DirectConnection); Qt::DirectConnection);
job->start(); job->start();
} }
else else
{ {
auto &instance = insecureInstance(); auto &instance = insecureInstance();
onLoaded(instance.object().find(name).value().toString()); onLoaded(instance.object().find(name).value().toString());
} }
} }
void Credentials::set(const QString &provider, const QString &name_, void Credentials::set(const QString &provider, const QString &name_,
const QString &credential) const QString &credential)
{ {
assertInGuiThread(); assertInGuiThread();
/// On linux, we try to use a keychain but show a message to disable it when it fails. /// On linux, we try to use a keychain but show a message to disable it when it fails.
/// XXX: add said message /// XXX: add said message
auto name = FORMAT_NAME; auto name = FORMAT_NAME;
if (useKeyring()) if (useKeyring())
{ {
queueJob(SetJob{name, credential}); queueJob(SetJob{name, credential});
} }
else else
{ {
auto &instance = insecureInstance(); auto &instance = insecureInstance();
auto obj = instance.object(); auto obj = instance.object();
obj[name] = credential; obj[name] = credential;
instance.setObject(obj); instance.setObject(obj);
queueInsecureSave(); queueInsecureSave();
} }
} }
void Credentials::erase(const QString &provider, const QString &name_) void Credentials::erase(const QString &provider, const QString &name_)
{ {
assertInGuiThread(); assertInGuiThread();
auto name = FORMAT_NAME; auto name = FORMAT_NAME;
if (useKeyring()) if (useKeyring())
{ {
queueJob(EraseJob{name}); queueJob(EraseJob{name});
} }
else else
{ {
auto &instance = insecureInstance(); auto &instance = insecureInstance();
if (auto it = instance.object().find(name); if (auto it = instance.object().find(name);
it != instance.object().end()) it != instance.object().end())
{ {
instance.object().erase(it); instance.object().erase(it);
} }
queueInsecureSave(); queueInsecureSave();
} }
} }
} // namespace chatterino } // namespace chatterino

View file

@ -1,23 +1,23 @@
#pragma once #pragma once
#include <QString> #include <QString>
#include <functional> #include <functional>
namespace chatterino { namespace chatterino {
class Credentials class Credentials
{ {
public: public:
static Credentials &instance(); static Credentials &instance();
void get(const QString &provider, const QString &name, QObject *receiver, void get(const QString &provider, const QString &name, QObject *receiver,
std::function<void(const QString &)> &&onLoaded); std::function<void(const QString &)> &&onLoaded);
void set(const QString &provider, const QString &name, void set(const QString &provider, const QString &name,
const QString &credential); const QString &credential);
void erase(const QString &provider, const QString &name); void erase(const QString &provider, const QString &name);
private: private:
Credentials(); Credentials();
}; };
} // namespace chatterino } // namespace chatterino

View file

@ -1,260 +1,260 @@
#include "Irc2.hpp" #include "Irc2.hpp"
#include <pajlada/serialize.hpp> #include <pajlada/serialize.hpp>
#include "common/Credentials.hpp" #include "common/Credentials.hpp"
#include "common/SignalVectorModel.hpp" #include "common/SignalVectorModel.hpp"
#include "singletons/Paths.hpp" #include "singletons/Paths.hpp"
#include "util/CombinePath.hpp" #include "util/CombinePath.hpp"
#include "util/RapidjsonHelpers.hpp" #include "util/RapidjsonHelpers.hpp"
#include "util/StandardItemHelper.hpp" #include "util/StandardItemHelper.hpp"
#include <QSaveFile> #include <QSaveFile>
#include <QtConcurrent> #include <QtConcurrent>
namespace chatterino { namespace chatterino {
namespace { namespace {
QString configPath() QString configPath()
{ {
return combinePath(getPaths()->settingsDirectory, "irc.json"); return combinePath(getPaths()->settingsDirectory, "irc.json");
} }
class Model : public SignalVectorModel<IrcServerData> class Model : public SignalVectorModel<IrcServerData>
{ {
public: public:
Model(QObject *parent) Model(QObject *parent)
: SignalVectorModel<IrcServerData>(6, parent) : SignalVectorModel<IrcServerData>(6, parent)
{ {
} }
// turn a vector item into a model row // turn a vector item into a model row
IrcServerData getItemFromRow(std::vector<QStandardItem *> &row, IrcServerData getItemFromRow(std::vector<QStandardItem *> &row,
const IrcServerData &original) const IrcServerData &original)
{ {
return IrcServerData{ return IrcServerData{
row[0]->data(Qt::EditRole).toString(), // host row[0]->data(Qt::EditRole).toString(), // host
row[1]->data(Qt::EditRole).toInt(), // port row[1]->data(Qt::EditRole).toInt(), // port
row[2]->data(Qt::CheckStateRole).toBool(), // ssl row[2]->data(Qt::CheckStateRole).toBool(), // ssl
row[3]->data(Qt::EditRole).toString(), // user row[3]->data(Qt::EditRole).toString(), // user
row[4]->data(Qt::EditRole).toString(), // nick row[4]->data(Qt::EditRole).toString(), // nick
row[5]->data(Qt::EditRole).toString(), // real row[5]->data(Qt::EditRole).toString(), // real
original.authType, // authType original.authType, // authType
original.connectCommands, // connectCommands original.connectCommands, // connectCommands
original.id, // id original.id, // id
}; };
} }
// turns a row in the model into a vector item // turns a row in the model into a vector item
void getRowFromItem(const IrcServerData &item, void getRowFromItem(const IrcServerData &item,
std::vector<QStandardItem *> &row) std::vector<QStandardItem *> &row)
{ {
setStringItem(row[0], item.host, false); setStringItem(row[0], item.host, false);
setStringItem(row[1], QString::number(item.port)); setStringItem(row[1], QString::number(item.port));
setBoolItem(row[2], item.ssl); setBoolItem(row[2], item.ssl);
setStringItem(row[3], item.user); setStringItem(row[3], item.user);
setStringItem(row[4], item.nick); setStringItem(row[4], item.nick);
setStringItem(row[5], item.real); setStringItem(row[5], item.real);
} }
}; };
} // namespace } // namespace
inline QString escape(QString str) inline QString escape(QString str)
{ {
return str.replace(":", "::"); return str.replace(":", "::");
} }
// This returns a unique id for every server which is understandeable in the systems credential manager. // This returns a unique id for every server which is understandeable in the systems credential manager.
inline QString getCredentialName(const IrcServerData &data) inline QString getCredentialName(const IrcServerData &data)
{ {
return escape(QString::number(data.id)) + ":" + escape(data.user) + "@" + return escape(QString::number(data.id)) + ":" + escape(data.user) + "@" +
escape(data.host); escape(data.host);
} }
void IrcServerData::getPassword( void IrcServerData::getPassword(
QObject *receiver, std::function<void(const QString &)> &&onLoaded) const QObject *receiver, std::function<void(const QString &)> &&onLoaded) const
{ {
Credentials::instance().get("irc", getCredentialName(*this), receiver, Credentials::instance().get("irc", getCredentialName(*this), receiver,
std::move(onLoaded)); std::move(onLoaded));
} }
void IrcServerData::setPassword(const QString &password) void IrcServerData::setPassword(const QString &password)
{ {
Credentials::instance().set("irc", getCredentialName(*this), password); Credentials::instance().set("irc", getCredentialName(*this), password);
} }
Irc::Irc() Irc::Irc()
{ {
this->connections.itemInserted.connect([this](auto &&args) { this->connections.itemInserted.connect([this](auto &&args) {
// make sure only one id can only exist for one server // make sure only one id can only exist for one server
assert(this->servers_.find(args.item.id) == this->servers_.end()); assert(this->servers_.find(args.item.id) == this->servers_.end());
// add new server // add new server
if (auto ab = this->abandonedChannels_.find(args.item.id); if (auto ab = this->abandonedChannels_.find(args.item.id);
ab != this->abandonedChannels_.end()) ab != this->abandonedChannels_.end())
{ {
auto server = std::make_unique<IrcServer>(args.item, ab->second); auto server = std::make_unique<IrcServer>(args.item, ab->second);
// set server of abandoned channels // set server of abandoned channels
for (auto weak : ab->second) for (auto weak : ab->second)
if (auto shared = weak.lock()) if (auto shared = weak.lock())
if (auto ircChannel = if (auto ircChannel =
dynamic_cast<IrcChannel *>(shared.get())) dynamic_cast<IrcChannel *>(shared.get()))
ircChannel->setServer(server.get()); ircChannel->setServer(server.get());
// add new server with abandoned channels // add new server with abandoned channels
this->servers_.emplace(args.item.id, std::move(server)); this->servers_.emplace(args.item.id, std::move(server));
this->abandonedChannels_.erase(ab); this->abandonedChannels_.erase(ab);
} }
else else
{ {
// add new server // add new server
this->servers_.emplace(args.item.id, this->servers_.emplace(args.item.id,
std::make_unique<IrcServer>(args.item)); std::make_unique<IrcServer>(args.item));
} }
}); });
this->connections.itemRemoved.connect([this](auto &&args) { this->connections.itemRemoved.connect([this](auto &&args) {
// restore // restore
if (auto server = this->servers_.find(args.item.id); if (auto server = this->servers_.find(args.item.id);
server != this->servers_.end()) server != this->servers_.end())
{ {
auto abandoned = server->second->getChannels(); auto abandoned = server->second->getChannels();
// set server of abandoned servers to nullptr // set server of abandoned servers to nullptr
for (auto weak : abandoned) for (auto weak : abandoned)
if (auto shared = weak.lock()) if (auto shared = weak.lock())
if (auto ircChannel = if (auto ircChannel =
dynamic_cast<IrcChannel *>(shared.get())) dynamic_cast<IrcChannel *>(shared.get()))
ircChannel->setServer(nullptr); ircChannel->setServer(nullptr);
this->abandonedChannels_[args.item.id] = abandoned; this->abandonedChannels_[args.item.id] = abandoned;
this->servers_.erase(server); this->servers_.erase(server);
} }
if (args.caller != Irc::noEraseCredentialCaller) if (args.caller != Irc::noEraseCredentialCaller)
{ {
Credentials::instance().erase("irc", getCredentialName(args.item)); Credentials::instance().erase("irc", getCredentialName(args.item));
} }
}); });
this->connections.delayedItemsChanged.connect([this] { this->save(); }); this->connections.delayedItemsChanged.connect([this] { this->save(); });
} }
QAbstractTableModel *Irc::newConnectionModel(QObject *parent) QAbstractTableModel *Irc::newConnectionModel(QObject *parent)
{ {
auto model = new Model(parent); auto model = new Model(parent);
model->initialize(&this->connections); model->initialize(&this->connections);
return model; return model;
} }
ChannelPtr Irc::getOrAddChannel(int id, QString name) ChannelPtr Irc::getOrAddChannel(int id, QString name)
{ {
if (auto server = this->servers_.find(id); server != this->servers_.end()) if (auto server = this->servers_.find(id); server != this->servers_.end())
{ {
return server->second->getOrAddChannel(name); return server->second->getOrAddChannel(name);
} }
else else
{ {
auto channel = std::make_shared<IrcChannel>(name, nullptr); auto channel = std::make_shared<IrcChannel>(name, nullptr);
this->abandonedChannels_[id].push_back(channel); this->abandonedChannels_[id].push_back(channel);
return std::move(channel); return std::move(channel);
} }
} }
Irc &Irc::instance() Irc &Irc::instance()
{ {
static Irc irc; static Irc irc;
return irc; return irc;
} }
int Irc::uniqueId() int Irc::uniqueId()
{ {
int i = this->currentId_ + 1; int i = this->currentId_ + 1;
auto it = this->servers_.find(i); auto it = this->servers_.find(i);
auto it2 = this->abandonedChannels_.find(i); auto it2 = this->abandonedChannels_.find(i);
while (it != this->servers_.end() || it2 != this->abandonedChannels_.end()) while (it != this->servers_.end() || it2 != this->abandonedChannels_.end())
{ {
i++; i++;
it = this->servers_.find(i); it = this->servers_.find(i);
it2 = this->abandonedChannels_.find(i); it2 = this->abandonedChannels_.find(i);
} }
return (this->currentId_ = i); return (this->currentId_ = i);
} }
void Irc::save() void Irc::save()
{ {
QJsonDocument doc; QJsonDocument doc;
QJsonObject root; QJsonObject root;
QJsonArray servers; QJsonArray servers;
for (auto &&conn : this->connections) for (auto &&conn : this->connections)
{ {
QJsonObject obj; QJsonObject obj;
obj.insert("host", conn.host); obj.insert("host", conn.host);
obj.insert("port", conn.port); obj.insert("port", conn.port);
obj.insert("ssl", conn.ssl); obj.insert("ssl", conn.ssl);
obj.insert("username", conn.user); obj.insert("username", conn.user);
obj.insert("nickname", conn.nick); obj.insert("nickname", conn.nick);
obj.insert("realname", conn.real); obj.insert("realname", conn.real);
obj.insert("connectCommands", obj.insert("connectCommands",
QJsonArray::fromStringList(conn.connectCommands)); QJsonArray::fromStringList(conn.connectCommands));
obj.insert("id", conn.id); obj.insert("id", conn.id);
obj.insert("authType", int(conn.authType)); obj.insert("authType", int(conn.authType));
servers.append(obj); servers.append(obj);
} }
root.insert("servers", servers); root.insert("servers", servers);
doc.setObject(root); doc.setObject(root);
QSaveFile file(configPath()); QSaveFile file(configPath());
file.open(QIODevice::WriteOnly); file.open(QIODevice::WriteOnly);
file.write(doc.toJson()); file.write(doc.toJson());
file.commit(); file.commit();
} }
void Irc::load() void Irc::load()
{ {
if (this->loaded_) if (this->loaded_)
return; return;
this->loaded_ = true; this->loaded_ = true;
QString config = configPath(); QString config = configPath();
QFile file(configPath()); QFile file(configPath());
file.open(QIODevice::ReadOnly); file.open(QIODevice::ReadOnly);
auto object = QJsonDocument::fromJson(file.readAll()).object(); auto object = QJsonDocument::fromJson(file.readAll()).object();
std::unordered_set<int> ids; std::unordered_set<int> ids;
// load servers // load servers
for (auto server : object.value("servers").toArray()) for (auto server : object.value("servers").toArray())
{ {
auto obj = server.toObject(); auto obj = server.toObject();
IrcServerData data; IrcServerData data;
data.host = obj.value("host").toString(data.host); data.host = obj.value("host").toString(data.host);
data.port = obj.value("port").toInt(data.port); data.port = obj.value("port").toInt(data.port);
data.ssl = obj.value("ssl").toBool(data.ssl); data.ssl = obj.value("ssl").toBool(data.ssl);
data.user = obj.value("username").toString(data.user); data.user = obj.value("username").toString(data.user);
data.nick = obj.value("nickname").toString(data.nick); data.nick = obj.value("nickname").toString(data.nick);
data.real = obj.value("realname").toString(data.real); data.real = obj.value("realname").toString(data.real);
data.connectCommands = data.connectCommands =
obj.value("connectCommands").toVariant().toStringList(); obj.value("connectCommands").toVariant().toStringList();
data.id = obj.value("id").toInt(data.id); data.id = obj.value("id").toInt(data.id);
data.authType = data.authType =
IrcAuthType(obj.value("authType").toInt(int(data.authType))); IrcAuthType(obj.value("authType").toInt(int(data.authType)));
// duplicate id's are not allowed :( // duplicate id's are not allowed :(
if (ids.find(data.id) == ids.end()) if (ids.find(data.id) == ids.end())
{ {
ids.insert(data.id); ids.insert(data.id);
this->connections.append(data); this->connections.append(data);
} }
} }
} }
} // namespace chatterino } // namespace chatterino

View file

@ -1,67 +1,67 @@
#pragma once #pragma once
#include <rapidjson/rapidjson.h> #include <rapidjson/rapidjson.h>
#include <common/SignalVector.hpp> #include <common/SignalVector.hpp>
#include "providers/irc/IrcChannel2.hpp" #include "providers/irc/IrcChannel2.hpp"
#include "providers/irc/IrcServer.hpp" #include "providers/irc/IrcServer.hpp"
class QAbstractTableModel; class QAbstractTableModel;
namespace chatterino { namespace chatterino {
enum class IrcAuthType { Anonymous, Custom, Pass, Sasl }; enum class IrcAuthType { Anonymous, Custom, Pass, Sasl };
struct IrcServerData { struct IrcServerData {
QString host; QString host;
int port = 6697; int port = 6697;
bool ssl = true; bool ssl = true;
QString user; QString user;
QString nick; QString nick;
QString real; QString real;
IrcAuthType authType = IrcAuthType::Anonymous; IrcAuthType authType = IrcAuthType::Anonymous;
void getPassword(QObject *receiver, void getPassword(QObject *receiver,
std::function<void(const QString &)> &&onLoaded) const; std::function<void(const QString &)> &&onLoaded) const;
void setPassword(const QString &password); void setPassword(const QString &password);
QStringList connectCommands; QStringList connectCommands;
int id; int id;
}; };
class Irc class Irc
{ {
public: public:
Irc(); Irc();
static Irc &instance(); static Irc &instance();
static inline void *const noEraseCredentialCaller = static inline void *const noEraseCredentialCaller =
reinterpret_cast<void *>(1); reinterpret_cast<void *>(1);
SignalVector<IrcServerData> connections; SignalVector<IrcServerData> connections;
QAbstractTableModel *newConnectionModel(QObject *parent); QAbstractTableModel *newConnectionModel(QObject *parent);
ChannelPtr getOrAddChannel(int serverId, QString name); ChannelPtr getOrAddChannel(int serverId, QString name);
void save(); void save();
void load(); void load();
int uniqueId(); int uniqueId();
private: private:
int currentId_{}; int currentId_{};
bool loaded_{}; bool loaded_{};
// Servers have a unique id. // Servers have a unique id.
// When a server gets changed it gets removed and then added again. // When a server gets changed it gets removed and then added again.
// So we store the channels of that server in abandonedChannels_ temporarily. // So we store the channels of that server in abandonedChannels_ temporarily.
// Or if the server got removed permanently then it's still stored there. // 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::unique_ptr<IrcServer>> servers_;
std::unordered_map<int, std::vector<std::weak_ptr<Channel>>> std::unordered_map<int, std::vector<std::weak_ptr<Channel>>>
abandonedChannels_; abandonedChannels_;
}; };
} // namespace chatterino } // namespace chatterino

View file

@ -1,76 +1,76 @@
#include "IrcCommands.hpp" #include "IrcCommands.hpp"
#include "messages/MessageBuilder.hpp" #include "messages/MessageBuilder.hpp"
#include "providers/irc/IrcChannel2.hpp" #include "providers/irc/IrcChannel2.hpp"
#include "providers/irc/IrcServer.hpp" #include "providers/irc/IrcServer.hpp"
#include "util/Overloaded.hpp" #include "util/Overloaded.hpp"
#include "util/QStringHash.hpp" #include "util/QStringHash.hpp"
namespace chatterino { namespace chatterino {
Outcome invokeIrcCommand(const QString &commandName, const QString &allParams, Outcome invokeIrcCommand(const QString &commandName, const QString &allParams,
IrcChannel &channel) IrcChannel &channel)
{ {
if (!channel.server()) if (!channel.server())
{ {
return Failure; return Failure;
} }
// STATIC MESSAGES // STATIC MESSAGES
static auto staticMessages = std::unordered_map<QString, QString>{ static auto staticMessages = std::unordered_map<QString, QString>{
{"join", "/join is not supported. Press ctrl+r to change the " {"join", "/join is not supported. Press ctrl+r to change the "
"channel. If required use /raw JOIN #channel."}, "channel. If required use /raw JOIN #channel."},
{"part", "/part is not supported. Press ctrl+r to change the " {"part", "/part is not supported. Press ctrl+r to change the "
"channel. If required use /raw PART #channel."}, "channel. If required use /raw PART #channel."},
}; };
auto cmd = commandName.toLower(); auto cmd = commandName.toLower();
if (auto it = staticMessages.find(cmd); it != staticMessages.end()) if (auto it = staticMessages.find(cmd); it != staticMessages.end())
{ {
channel.addMessage(makeSystemMessage(it->second)); channel.addMessage(makeSystemMessage(it->second));
return Success; return Success;
} }
// CUSTOM COMMANDS // CUSTOM COMMANDS
auto params = allParams.split(' '); auto params = allParams.split(' ');
auto paramsAfter = [&](int i) { return params.mid(i + 1).join(' '); }; auto paramsAfter = [&](int i) { return params.mid(i + 1).join(' '); };
auto sendRaw = [&](QString str) { channel.server()->sendRawMessage(str); }; auto sendRaw = [&](QString str) { channel.server()->sendRawMessage(str); };
if (cmd == "msg") if (cmd == "msg")
{ {
sendRaw("PRIVMSG " + params[0] + " :" + paramsAfter(0)); sendRaw("PRIVMSG " + params[0] + " :" + paramsAfter(0));
} }
else if (cmd == "away") else if (cmd == "away")
{ {
sendRaw("AWAY" + params[0] + " :" + paramsAfter(0)); sendRaw("AWAY" + params[0] + " :" + paramsAfter(0));
} }
else if (cmd == "knock") else if (cmd == "knock")
{ {
sendRaw("KNOCK #" + params[0] + " " + paramsAfter(0)); sendRaw("KNOCK #" + params[0] + " " + paramsAfter(0));
} }
else if (cmd == "kick") else if (cmd == "kick")
{ {
if (paramsAfter(1).isEmpty()) if (paramsAfter(1).isEmpty())
sendRaw("KICK " + params[0] + " " + params[1]); sendRaw("KICK " + params[0] + " " + params[1]);
else else
sendRaw("KICK " + params[0] + " " + params[1] + " :" + sendRaw("KICK " + params[0] + " " + params[1] + " :" +
paramsAfter(1)); paramsAfter(1));
} }
else if (cmd == "wallops") else if (cmd == "wallops")
{ {
sendRaw("WALLOPS :" + allParams); sendRaw("WALLOPS :" + allParams);
} }
else if (cmd == "raw") else if (cmd == "raw")
{ {
sendRaw(allParams); sendRaw(allParams);
} }
else else
{ {
sendRaw(cmd.toUpper() + " " + allParams); sendRaw(cmd.toUpper() + " " + allParams);
} }
return Success; return Success;
} }
} // namespace chatterino } // namespace chatterino

View file

@ -1,12 +1,12 @@
#pragma once #pragma once
#include "common/Outcome.hpp" #include "common/Outcome.hpp"
namespace chatterino { namespace chatterino {
class IrcChannel; class IrcChannel;
Outcome invokeIrcCommand(const QString &command, const QString &params, Outcome invokeIrcCommand(const QString &command, const QString &params,
IrcChannel &channel); IrcChannel &channel);
} // namespace chatterino } // namespace chatterino

View file

@ -1,4 +1,4 @@
#include "IrcMessageHandler.hpp" #include "IrcMessageHandler.hpp"
#include "Application.hpp" #include "Application.hpp"
#include "controllers/accounts/AccountController.hpp" #include "controllers/accounts/AccountController.hpp"

View file

@ -1,4 +1,4 @@
#pragma once #pragma once
#include "common/Aliases.hpp" #include "common/Aliases.hpp"
#include "common/Outcome.hpp" #include "common/Outcome.hpp"

View file

@ -1,13 +1,13 @@
#pragma once #pragma once
namespace chatterino { namespace chatterino {
template <class... Ts> template <class... Ts>
struct Overloaded : Ts... { struct Overloaded : Ts... {
using Ts::operator()...; using Ts::operator()...;
}; };
template <class... Ts> template <class... Ts>
Overloaded(Ts...)->Overloaded<Ts...>; Overloaded(Ts...) -> Overloaded<Ts...>;
} // namespace chatterino } // namespace chatterino

View file

@ -1,97 +1,97 @@
#include "IrcConnectionEditor.hpp" #include "IrcConnectionEditor.hpp"
#include "ui_IrcConnectionEditor.h" #include "ui_IrcConnectionEditor.h"
namespace chatterino { namespace chatterino {
IrcConnectionEditor::IrcConnectionEditor(const IrcServerData &data, bool isAdd, IrcConnectionEditor::IrcConnectionEditor(const IrcServerData &data, bool isAdd,
QWidget *parent) QWidget *parent)
: QDialog(parent, Qt::WindowStaysOnTopHint) : QDialog(parent, Qt::WindowStaysOnTopHint)
, ui_(new Ui::IrcConnectionEditor) , ui_(new Ui::IrcConnectionEditor)
, data_(data) , data_(data)
{ {
this->ui_->setupUi(this); this->ui_->setupUi(this);
this->setWindowTitle(QString(isAdd ? "Add " : "Edit ") + "Irc Connection"); this->setWindowTitle(QString(isAdd ? "Add " : "Edit ") + "Irc Connection");
QObject::connect(this->ui_->userNameLineEdit, &QLineEdit::textChanged, this, QObject::connect(this->ui_->userNameLineEdit, &QLineEdit::textChanged, this,
[this](const QString &text) { [this](const QString &text) {
this->ui_->nickNameLineEdit->setPlaceholderText(text); this->ui_->nickNameLineEdit->setPlaceholderText(text);
this->ui_->realNameLineEdit->setPlaceholderText(text); this->ui_->realNameLineEdit->setPlaceholderText(text);
}); });
this->ui_->serverLineEdit->setText(data.host); this->ui_->serverLineEdit->setText(data.host);
this->ui_->portSpinBox->setValue(data.port); this->ui_->portSpinBox->setValue(data.port);
this->ui_->securityCheckBox->setChecked(data.ssl); this->ui_->securityCheckBox->setChecked(data.ssl);
this->ui_->userNameLineEdit->setText(data.user); this->ui_->userNameLineEdit->setText(data.user);
this->ui_->nickNameLineEdit->setText(data.nick); this->ui_->nickNameLineEdit->setText(data.nick);
this->ui_->realNameLineEdit->setText(data.real); this->ui_->realNameLineEdit->setText(data.real);
this->ui_->connectCommandsEditor->setPlainText( this->ui_->connectCommandsEditor->setPlainText(
data.connectCommands.join('\n')); data.connectCommands.join('\n'));
data.getPassword(this, [this](const QString &password) { data.getPassword(this, [this](const QString &password) {
this->ui_->passwordLineEdit->setText(password); this->ui_->passwordLineEdit->setText(password);
}); });
this->ui_->loginMethodComboBox->setCurrentIndex([&] { this->ui_->loginMethodComboBox->setCurrentIndex([&] {
switch (data.authType) switch (data.authType)
{ {
case IrcAuthType::Custom: case IrcAuthType::Custom:
return 1; return 1;
case IrcAuthType::Pass: case IrcAuthType::Pass:
return 2; return 2;
case IrcAuthType::Sasl: case IrcAuthType::Sasl:
return 3; return 3;
default: default:
return 0; return 0;
} }
}()); }());
QObject::connect(this->ui_->loginMethodComboBox, QObject::connect(this->ui_->loginMethodComboBox,
qOverload<int>(&QComboBox::currentIndexChanged), this, qOverload<int>(&QComboBox::currentIndexChanged), this,
[this](int index) { [this](int index) {
if (index == 1) // Custom if (index == 1) // Custom
{ {
this->ui_->connectCommandsEditor->setFocus(); this->ui_->connectCommandsEditor->setFocus();
} }
}); });
QFont font("Monospace"); QFont font("Monospace");
font.setStyleHint(QFont::TypeWriter); font.setStyleHint(QFont::TypeWriter);
this->ui_->connectCommandsEditor->setFont(font); this->ui_->connectCommandsEditor->setFont(font);
} }
IrcConnectionEditor::~IrcConnectionEditor() IrcConnectionEditor::~IrcConnectionEditor()
{ {
delete ui_; delete ui_;
} }
IrcServerData IrcConnectionEditor::data() IrcServerData IrcConnectionEditor::data()
{ {
auto data = this->data_; auto data = this->data_;
data.host = this->ui_->serverLineEdit->text(); data.host = this->ui_->serverLineEdit->text();
data.port = this->ui_->portSpinBox->value(); data.port = this->ui_->portSpinBox->value();
data.ssl = this->ui_->securityCheckBox->isChecked(); data.ssl = this->ui_->securityCheckBox->isChecked();
data.user = this->ui_->userNameLineEdit->text(); data.user = this->ui_->userNameLineEdit->text();
data.nick = this->ui_->nickNameLineEdit->text(); data.nick = this->ui_->nickNameLineEdit->text();
data.real = this->ui_->realNameLineEdit->text(); data.real = this->ui_->realNameLineEdit->text();
data.connectCommands = data.connectCommands =
this->ui_->connectCommandsEditor->toPlainText().split('\n'); this->ui_->connectCommandsEditor->toPlainText().split('\n');
data.setPassword(this->ui_->passwordLineEdit->text()); data.setPassword(this->ui_->passwordLineEdit->text());
data.authType = [this] { data.authType = [this] {
switch (this->ui_->loginMethodComboBox->currentIndex()) switch (this->ui_->loginMethodComboBox->currentIndex())
{ {
case 1: case 1:
return IrcAuthType::Custom; return IrcAuthType::Custom;
case 2: case 2:
return IrcAuthType::Pass; return IrcAuthType::Pass;
case 3: case 3:
return IrcAuthType::Sasl; return IrcAuthType::Sasl;
default: default:
return IrcAuthType::Anonymous; return IrcAuthType::Anonymous;
} }
}(); }();
return data; return data;
} }
} // namespace chatterino } // namespace chatterino

View file

@ -1,32 +1,32 @@
#pragma once #pragma once
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include "providers/irc/Irc2.hpp" #include "providers/irc/Irc2.hpp"
#include "widgets/BaseWindow.hpp" #include "widgets/BaseWindow.hpp"
namespace Ui { namespace Ui {
class IrcConnectionEditor; class IrcConnectionEditor;
} }
namespace chatterino { namespace chatterino {
struct IrcServerData; struct IrcServerData;
class IrcConnectionEditor : public QDialog class IrcConnectionEditor : public QDialog
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit IrcConnectionEditor(const IrcServerData &data, bool isAdd = false, explicit IrcConnectionEditor(const IrcServerData &data, bool isAdd = false,
QWidget *parent = nullptr); QWidget *parent = nullptr);
~IrcConnectionEditor(); ~IrcConnectionEditor();
IrcServerData data(); IrcServerData data();
private: private:
Ui::IrcConnectionEditor *ui_; Ui::IrcConnectionEditor *ui_;
IrcServerData data_; IrcServerData data_;
}; };
} // namespace chatterino } // namespace chatterino

View file

@ -4,12 +4,14 @@ set -eu
fail="0" fail="0"
clang-format --version
while read -r file; do while read -r file; do
if ! diff -u <(cat "$file") <(clang-format "$file"); then if ! diff -u <(cat "$file") <(clang-format "$file"); then
echo "$file differs!!!!!!!" echo "$file differs!!!!!!!"
fail="1" fail="1"
fi fi
done < <(find src/ \( -iname "*.hpp" -o -iname "*.cpp" \)) done < <(find src/ -type f \( -iname "*.hpp" -o -iname "*.cpp" \))
if [ "$fail" = "1" ]; then if [ "$fail" = "1" ]; then
echo "At least one file is poorly formatted - check the output above" echo "At least one file is poorly formatted - check the output above"

22
tools/check-line-endings.sh Executable file
View 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!"