Merge branch 'master' into git_is_pepega

This commit is contained in:
Mm2PL
2020-01-01 21:06:29 +01:00
committed by GitHub
174 changed files with 2445 additions and 1112 deletions
+34
View File
@@ -0,0 +1,34 @@
#include "Args.hpp"
namespace chatterino {
Args::Args(const QStringList &args)
{
for (auto &&arg : args)
{
if (arg == "--crash-recovery")
{
this->crashRecovery = true;
}
else if (arg == "--version")
{
this->printVersion = true;
}
}
}
static Args *instance = nullptr;
void initArgs(const QStringList &args)
{
instance = new Args(args);
}
const Args &getArgs()
{
assert(instance);
return *instance;
}
} // namespace chatterino
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include <QStringList>
namespace chatterino {
/// Command line arguments passed to Chatterino.
class Args
{
public:
Args(const QStringList &args);
bool printVersion{};
bool crashRecovery{};
};
void initArgs(const QStringList &args);
const Args &getArgs();
} // namespace chatterino
+1 -1
View File
@@ -178,7 +178,7 @@ void Channel::addOrReplaceTimeout(MessagePtr message)
}
// XXX: Might need the following line
// WindowManager::getInstance().repaintVisibleChatWidgets(this);
// WindowManager::instance().repaintVisibleChatWidgets(this);
}
void Channel::disableAllMessages()
+12
View File
@@ -0,0 +1,12 @@
#include "common/ChatterinoSetting.hpp"
#include "BaseSettings.hpp"
namespace chatterino {
void _registerSetting(std::weak_ptr<pajlada::Settings::SettingData> setting)
{
_actuallyRegisterSetting(setting);
}
} // namespace chatterino
+88
View File
@@ -0,0 +1,88 @@
#pragma once
#include <QString>
#include <pajlada/settings.hpp>
namespace chatterino {
void _registerSetting(std::weak_ptr<pajlada::Settings::SettingData> setting);
template <typename Type>
class ChatterinoSetting : public pajlada::Settings::Setting<Type>
{
public:
ChatterinoSetting(const std::string &path)
: pajlada::Settings::Setting<Type>(path)
{
_registerSetting(this->getData());
}
ChatterinoSetting(const std::string &path, const Type &defaultValue)
: pajlada::Settings::Setting<Type>(path, defaultValue)
{
_registerSetting(this->getData());
}
template <typename T2>
ChatterinoSetting &operator=(const T2 &newValue)
{
this->setValue(newValue);
return *this;
}
ChatterinoSetting &operator=(Type &&newValue) noexcept
{
pajlada::Settings::Setting<Type>::operator=(newValue);
return *this;
}
using pajlada::Settings::Setting<Type>::operator==;
using pajlada::Settings::Setting<Type>::operator!=;
using pajlada::Settings::Setting<Type>::operator Type;
};
using BoolSetting = ChatterinoSetting<bool>;
using FloatSetting = ChatterinoSetting<float>;
using DoubleSetting = ChatterinoSetting<double>;
using IntSetting = ChatterinoSetting<int>;
using StringSetting = ChatterinoSetting<std::string>;
using QStringSetting = ChatterinoSetting<QString>;
template <typename Enum>
class EnumSetting
: public ChatterinoSetting<typename std::underlying_type<Enum>::type>
{
using Underlying = typename std::underlying_type<Enum>::type;
public:
using ChatterinoSetting<Underlying>::ChatterinoSetting;
EnumSetting(const std::string &path, const Enum &defaultValue)
: ChatterinoSetting<Underlying>(path, Underlying(defaultValue))
{
_registerSetting(this->getData());
}
template <typename T2>
EnumSetting<Enum> &operator=(Enum newValue)
{
this->setValue(Underlying(newValue));
return *this;
}
operator Enum()
{
return Enum(this->getValue());
}
Enum getEnum()
{
return Enum(this->getValue());
}
};
} // namespace chatterino
+14 -5
View File
@@ -1,16 +1,15 @@
#pragma once
#include "common/Aliases.hpp"
#include "common/Outcome.hpp"
#include "common/ProviderId.hpp"
#include <QString>
#include <QWidget>
#include <boost/optional.hpp>
#include <boost/preprocessor.hpp>
#include <string>
#include "common/Aliases.hpp"
#include "common/Outcome.hpp"
#include "common/ProviderId.hpp"
namespace chatterino {
enum class HighlightState {
@@ -46,4 +45,14 @@ enum class CopyMode {
OnlyTextAndEmotes,
};
struct DeleteLater {
void operator()(QObject *obj)
{
obj->deleteLater();
}
};
template <typename T>
using QObjectPtr = std::unique_ptr<T, DeleteLater>;
} // namespace chatterino
+10 -7
View File
@@ -143,7 +143,7 @@ namespace {
}
} // namespace
Credentials &Credentials::getInstance()
Credentials &Credentials::instance()
{
static Credentials creds;
return creds;
@@ -166,11 +166,12 @@ void Credentials::get(const QString &provider, const QString &name_,
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);
QObject::connect(
job, &QKeychain::Job::finished, receiver,
[job, onLoaded = std::move(onLoaded)](auto) mutable {
onLoaded(job->textData());
},
Qt::DirectConnection);
job->start();
}
else
@@ -199,7 +200,9 @@ void Credentials::set(const QString &provider, const QString &name_,
{
auto &instance = insecureInstance();
instance.object()[name] = credential;
auto obj = instance.object();
obj[name] = credential;
instance.setObject(obj);
queueInsecureSave();
}
+1 -1
View File
@@ -8,7 +8,7 @@ namespace chatterino {
class Credentials
{
public:
static Credentials &getInstance();
static Credentials &instance();
void get(const QString &provider, const QString &name, QObject *receiver,
std::function<void(const QString &)> &&onLoaded);
+2 -4
View File
@@ -48,13 +48,11 @@ void DownloadManager::onFinished(QNetworkReply *reply)
{
switch (reply->error())
{
case QNetworkReply::NoError:
{
case QNetworkReply::NoError: {
qDebug("file is downloaded successfully.");
}
break;
default:
{
default: {
qDebug() << reply->errorString().toLatin1();
};
}
+33
View File
@@ -1,5 +1,7 @@
#include "common/Env.hpp"
#include <QVariant>
namespace chatterino {
namespace {
@@ -15,6 +17,33 @@ namespace {
return defaultValue;
}
uint16_t readPortEnv(const char *envName, uint16_t defaultValue)
{
auto envString = std::getenv(envName);
if (envString != nullptr)
{
bool ok;
auto val = QString(envString).toUShort(&ok);
if (ok)
{
return val;
}
}
return defaultValue;
}
uint16_t readBoolEnv(const char *envName, bool defaultValue)
{
auto envString = std::getenv(envName);
if (envString != nullptr)
{
return QVariant(QString(envString)).toBool();
}
return defaultValue;
}
} // namespace
Env::Env()
@@ -30,6 +59,10 @@ Env::Env()
"https://braize.pajlada.com/chatterino/twitchemotes/set/%1/"))
, imageUploaderUrl(readStringEnv("CHATTERINO2_IMAGE_PASTE_SITE_URL",
"https://i.nuuls.com/upload"))
, twitchServerHost(
readStringEnv("CHATTERINO2_TWITCH_SERVER_HOST", "irc.chat.twitch.tv"))
, twitchServerPort(readPortEnv("CHATTERINO2_TWITCH_SERVER_PORT", 6697))
, twitchServerSecure(readBoolEnv("CHATTERINO2_TWITCH_SERVER_SECURE", true))
{
}
+3
View File
@@ -15,6 +15,9 @@ public:
const QString linkResolverUrl;
const QString twitchEmoteSetResolverUrl;
const QString imageUploaderUrl;
const QString twitchServerHost;
const uint16_t twitchServerPort;
const bool twitchServerSecure;
};
} // namespace chatterino
+82
View File
@@ -0,0 +1,82 @@
#pragma once
#include <type_traits>
namespace chatterino {
template <typename T, typename Q = typename std::underlying_type<T>::type>
class FlagsEnum
{
public:
FlagsEnum()
: value_(static_cast<T>(0))
{
}
FlagsEnum(T value)
: value_(value)
{
}
FlagsEnum(std::initializer_list<T> flags)
{
for (auto flag : flags)
{
this->set(flag);
}
}
bool operator==(const FlagsEnum<T> &other)
{
return this->value_ == other.value_;
}
bool operator!=(const FlagsEnum &other)
{
return this->value_ != other.value_;
}
void set(T flag)
{
reinterpret_cast<Q &>(this->value_) |= static_cast<Q>(flag);
}
void unset(T flag)
{
reinterpret_cast<Q &>(this->value_) &= ~static_cast<Q>(flag);
}
void set(T flag, bool value)
{
if (value)
this->set(flag);
else
this->unset(flag);
}
bool has(T flag) const
{
return static_cast<Q>(this->value_) & static_cast<Q>(flag);
}
bool hasAny(FlagsEnum flags) const
{
return static_cast<Q>(this->value_) & static_cast<Q>(flags.value_);
}
bool hasAll(FlagsEnum<T> flags) const
{
return (static_cast<Q>(this->value_) & static_cast<Q>(flags.value_)) &&
static_cast<Q>(flags->value);
}
bool hasNone(std::initializer_list<T> flags) const
{
return !this->hasAny(flags);
}
private:
T value_{};
};
} // namespace chatterino
+176 -42
View File
@@ -1,67 +1,201 @@
#include "common/LinkParser.hpp"
#include <QFile>
#include <QMap>
#include <QRegularExpression>
#include <QString>
#include <QStringRef>
#include <QTextStream>
// ip 0.0.0.0 - 224.0.0.0
#define IP \
"(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" \
"(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" \
"(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))"
#define PORT "(?::\\d{2,5})"
#define WEB_CHAR1 "[_a-z\\x{00a1}-\\x{ffff}0-9]"
#define WEB_CHAR2 "[a-z\\x{00a1}-\\x{ffff}0-9]"
#define SPOTIFY_1 "(?:artist|album|track|user:[^:]+:playlist):[a-zA-Z0-9]+"
#define SPOTIFY_2 "user:[^:]+"
#define SPOTIFY_3 "search:(?:[-\\w$\\.+!*'(),]+|%[a-fA-F0-9]{2})+"
#define SPOTIFY_PARAMS "(?:" SPOTIFY_1 "|" SPOTIFY_2 "|" SPOTIFY_3 ")"
#define SPOTIFY_LINK "(?x-mi:(spotify:" SPOTIFY_PARAMS "))"
#define WEB_PROTOCOL "(?:(?:https?|ftps?)://)?"
#define WEB_USER "(?:\\S+(?::\\S*)?@)?"
#define WEB_HOST "(?:(?:" WEB_CHAR1 "-*)*" WEB_CHAR2 "+)"
#define WEB_DOMAIN "(?:\\.(?:" WEB_CHAR2 "-*)*" WEB_CHAR2 "+)*"
#define WEB_TLD "(?:" + tldData + ")"
#define WEB_RESOURCE_PATH "(?:[/?#]\\S*)"
#define WEB_LINK \
WEB_PROTOCOL WEB_USER "(?:" IP "|" WEB_HOST WEB_DOMAIN "\\." WEB_TLD PORT \
"?" WEB_RESOURCE_PATH "?)"
#define LINK "^(?:" SPOTIFY_LINK "|" WEB_LINK ")$"
namespace chatterino {
namespace {
QSet<QString> &tlds()
{
static QSet<QString> tlds = [] {
QFile file(":/tlds.txt");
file.open(QFile::ReadOnly);
QTextStream stream(&file);
stream.setCodec("UTF-8");
int safetyMax = 20000;
QSet<QString> set;
while (!stream.atEnd())
{
auto line = stream.readLine();
set.insert(line);
if (safetyMax-- == 0)
break;
}
return set;
}();
return tlds;
}
bool isValidHostname(QStringRef &host)
{
int index = host.lastIndexOf('.');
return index != -1 &&
tlds().contains(host.mid(index + 1).toString().toLower());
}
bool isValidIpv4(QStringRef &host)
{
static auto exp = QRegularExpression("^\\d{1,3}(?:\\.\\d{1,3}){3}$");
return exp.match(host).hasMatch();
}
#ifdef C_MATCH_IPV6_LINK
bool isValidIpv6(QStringRef &host)
{
static auto exp = QRegularExpression("^\\[[a-fA-F0-9:%]+\\]$");
return exp.match(host).hasMatch();
}
#endif
} // namespace
LinkParser::LinkParser(const QString &unparsedString)
{
static QRegularExpression linkRegex = [] {
static QRegularExpression newLineRegex("\r?\n");
QFile file(":/tlds.txt");
file.open(QFile::ReadOnly);
QTextStream tlds(&file);
tlds.setCodec("UTF-8");
this->match_ = unparsedString;
// tldData gets injected into the LINK macro
auto tldData = tlds.readAll().replace(newLineRegex, "|");
(void)tldData;
// This is not implemented with a regex to increase performance.
// We keep removing parts of the url until there's either nothing left or we fail.
QStringRef l(&unparsedString);
return QRegularExpression(LINK,
QRegularExpression::CaseInsensitiveOption);
}();
bool hasHttp = false;
this->match_ = linkRegex.match(unparsedString);
// Protocol `https?://`
if (l.startsWith("https://", Qt::CaseInsensitive))
{
hasHttp = true;
l = l.mid(8);
}
else if (l.startsWith("http://", Qt::CaseInsensitive))
{
hasHttp = true;
l = l.mid(7);
}
// Http basic auth `user:password`.
// Not supported for security reasons (misleading links)
// Host `a.b.c.com`
QStringRef host = l;
bool lastWasDot = true;
bool inIpv6 = false;
for (int i = 0; i < l.size(); i++)
{
if (l[i] == '.')
{
if (lastWasDot == true) // no double dots ..
goto error;
lastWasDot = true;
}
else
{
lastWasDot = false;
}
if (l[i] == ':' && !inIpv6)
{
host = l.mid(0, i);
l = l.mid(i + 1);
goto parsePort;
}
else if (l[i] == '/')
{
host = l.mid(0, i);
l = l.mid(i + 1);
goto parsePath;
}
else if (l[i] == '?')
{
host = l.mid(0, i);
l = l.mid(i + 1);
goto parseQuery;
}
else if (l[i] == '#')
{
host = l.mid(0, i);
l = l.mid(i + 1);
goto parseAnchor;
}
// ipv6
if (l[i] == '[')
{
if (i == 0)
inIpv6 = true;
else
goto error;
}
else if (l[i] == ']')
{
inIpv6 = false;
}
}
if (lastWasDot)
goto error;
else
goto done;
parsePort:
// Port `:12345`
for (int i = 0; i < std::min<int>(5, l.size()); i++)
{
if (l[i] == '/')
goto parsePath;
else if (l[i] == '?')
goto parseQuery;
else if (l[i] == '#')
goto parseAnchor;
if (!l[i].isDigit())
goto error;
}
goto done;
parsePath:
parseQuery:
parseAnchor:
// we accept everything in the path/query/anchor
done:
// check host
this->hasMatch_ = isValidHostname(host) || isValidIpv4(host)
#ifdef C_MATCH_IPV6_LINK
|| (hasHttp && isValidIpv6(host))
#endif
;
if (this->hasMatch_)
{
this->match_ = unparsedString;
}
return;
error:
hasMatch_ = false;
}
bool LinkParser::hasMatch() const
{
return this->match_.hasMatch();
return this->hasMatch_;
}
QString LinkParser::getCaptured() const
{
return this->match_.captured();
return this->match_;
}
} // namespace chatterino
+2 -1
View File
@@ -14,7 +14,8 @@ public:
QString getCaptured() const;
private:
QRegularExpressionMatch match_;
bool hasMatch_{false};
QString match_;
};
} // namespace chatterino
+1 -1
View File
@@ -27,7 +27,7 @@ Modes::Modes()
}
}
const Modes &Modes::getInstance()
const Modes &Modes::instance()
{
static Modes instance;
return instance;
+1 -1
View File
@@ -7,7 +7,7 @@ class Modes
public:
Modes();
static const Modes &getInstance();
static const Modes &instance();
bool isNightly{};
bool isPortable{};
+51
View File
@@ -0,0 +1,51 @@
#pragma once
namespace chatterino {
struct SuccessTag {
};
struct FailureTag {
};
const SuccessTag Success{};
const FailureTag Failure{};
class Outcome
{
public:
Outcome(SuccessTag)
: success_(true)
{
}
Outcome(FailureTag)
: success_(false)
{
}
explicit operator bool() const
{
return this->success_;
}
bool operator!() const
{
return !this->success_;
}
bool operator==(const Outcome &other) const
{
return this->success_ == other.success_;
}
bool operator!=(const Outcome &other) const
{
return !this->operator==(other);
}
private:
bool success_;
};
} // namespace chatterino
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#include <boost/noncopyable.hpp>
namespace chatterino {
class Settings;
class Paths;
class Singleton : boost::noncopyable
{
public:
virtual ~Singleton() = default;
virtual void initialize(Settings &settings, Paths &paths)
{
(void)(settings);
(void)(paths);
}
virtual void save()
{
}
};
} // namespace chatterino
+9 -13
View File
@@ -16,50 +16,46 @@ Version::Version()
this->commitHash_ =
QString(FROM_EXTERNAL_DEFINE(CHATTERINO_GIT_HASH)).remove('"');
// Date of build
// Date of build, this is depended on the format not changing
#ifdef CHATTERINO_NIGHTLY_VERSION_STRING
this->dateOfBuild_ =
QString(FROM_EXTERNAL_DEFINE(CHATTERINO_NIGHTLY_VERSION_STRING))
.remove('"');
.remove('"')
.split(' ')[0];
#endif
// "Full" version string, as displayed in window title
this->fullVersion_ = "Chatterino ";
if (Modes::getInstance().isNightly)
if (Modes::instance().isNightly)
{
this->fullVersion_ += "Nightly ";
}
this->fullVersion_ += this->version_;
if (Modes::getInstance().isNightly)
{
this->fullVersion_ += this->dateOfBuild_;
}
}
const Version &Version::getInstance()
const Version &Version::instance()
{
static Version instance;
return instance;
}
const QString &Version::getVersion() const
const QString &Version::version() const
{
return this->version_;
}
const QString &Version::getFullVersion() const
const QString &Version::fullVersion() const
{
return this->fullVersion_;
}
const QString &Version::getCommitHash() const
const QString &Version::commitHash() const
{
return this->commitHash_;
}
const QString &Version::getDateOfBuild() const
const QString &Version::dateOfBuild() const
{
return this->dateOfBuild_;
}
+8 -7
View File
@@ -1,8 +1,9 @@
#pragma once
#include <QString>
#include <QtGlobal>
#define CHATTERINO_VERSION "2.1.4"
#define CHATTERINO_VERSION "2.1.7"
#if defined(Q_OS_WIN)
# define CHATTERINO_OS "win"
@@ -19,12 +20,12 @@ namespace chatterino {
class Version
{
public:
static const Version &getInstance();
static const Version &instance();
const QString &getVersion() const;
const QString &getCommitHash() const;
const QString &getDateOfBuild() const;
const QString &getFullVersion() const;
const QString &version() const;
const QString &commitHash() const;
const QString &dateOfBuild() const;
const QString &fullVersion() const;
private:
Version();
@@ -35,4 +36,4 @@ private:
QString fullVersion_;
};
};
}; // namespace chatterino