diff --git a/src/ClientWorker.cpp b/src/ClientWorker.cpp
index aeba8ad..721f39c 100644
--- a/src/ClientWorker.cpp
+++ b/src/ClientWorker.cpp
@@ -1,204 +1,216 @@
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2016-2020 Kaidan developers and contributors
* (see the LICENSE file for a full list of copyright authors)
*
* Kaidan is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the author of Kaidan gives
* permission to link the code of its release with the OpenSSL
* project's "OpenSSL" library (or with modified versions of it that
* use the same license as the "OpenSSL" library), and distribute the
* linked executables. You must obey the GNU General Public License in
* all respects for all of the code used other than "OpenSSL". If you
* modify this file, you may extend this exception to your version of
* the file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*
* Kaidan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kaidan. If not, see .
*/
#include "ClientWorker.h"
// Qt
#include
#include
#include
#include
// QXmpp
#include
#include
#include
#include
// Kaidan
#include "DiscoveryManager.h"
#include "DownloadManager.h"
#include "Kaidan.h"
#include "LogHandler.h"
#include "MessageHandler.h"
#include "RegistrationManager.h"
#include "RosterManager.h"
#include "UploadManager.h"
#include "VCardManager.h"
ClientWorker::ClientWorker(Caches *caches, Kaidan *kaidan, bool enableLogging, QGuiApplication *app,
QObject* parent)
: QObject(parent), caches(caches), kaidan(kaidan), enableLogging(enableLogging), app(app)
{
client = new QXmppClient(this);
logger = new LogHandler(client, this);
logger->enableLogging(enableLogging);
vCardManager = new VCardManager(client, caches->avatarStorage, this);
registrationManager = new RegistrationManager(kaidan, caches->settings);
rosterManager = new RosterManager(kaidan, client, caches->rosterModel,
caches->avatarStorage, vCardManager, this);
msgHandler = new MessageHandler(kaidan, client, caches->msgModel, this);
discoManager = new DiscoveryManager(client, this);
uploadManager = new UploadManager(kaidan, client, caches->msgModel, rosterManager,
caches->transferCache, this);
downloadManager = new DownloadManager(kaidan, caches->transferCache,
caches->msgModel, this);
client->addExtension(registrationManager);
connect(client, &QXmppClient::presenceReceived,
caches->presCache, &PresenceCache::updatePresence);
connect(client, &QXmppClient::disconnected,
caches->presCache, &PresenceCache::clear);
connect(this, &ClientWorker::credentialsUpdated, this, &ClientWorker::setCredentials);
// publish kaidan version
client->versionManager().setClientName(APPLICATION_DISPLAY_NAME);
client->versionManager().setClientVersion(VERSION_STRING);
client->versionManager().setClientOs(QSysInfo::prettyProductName());
// Client State Indication
connect(app, &QGuiApplication::applicationStateChanged, this, &ClientWorker::setCsiState);
}
void ClientWorker::main()
{
// initialize random generator
qsrand(time(nullptr));
connect(client, &QXmppClient::stateChanged, kaidan, &Kaidan::setConnectionState);
connect(client, &QXmppClient::connected, this, &ClientWorker::onConnect);
connect(client, &QXmppClient::error, this, &ClientWorker::onConnectionError);
connect(this, &ClientWorker::connectRequested, this, &ClientWorker::xmppConnect);
connect(this, &ClientWorker::disconnectRequested, client, &QXmppClient::disconnectFromServer);
}
void ClientWorker::xmppConnect()
{
QXmppConfiguration config;
config.setJid(creds.jid);
config.setResource(creds.jidResource.append(".").append(generateRandomString()));
config.setPassword(creds.password);
config.setAutoAcceptSubscriptions(false);
config.setStreamSecurityMode(QXmppConfiguration::TLSRequired);
config.setAutoReconnectionEnabled(true); // will automatically reconnect
// on first try we must be sure that we connect successfully
// otherwise this could end in a reconnection loop
if (creds.isFirstTry)
config.setAutoReconnectionEnabled(false);
client->connectToServer(config, QXmppPresence(QXmppPresence::Available));
}
void ClientWorker::onConnect()
{
// no mutex needed, because this is called from updateClient()
qDebug() << "[client] Connected successfully to server";
// Emit signal, that logging in with these credentials has worked for the first time
if (creds.isFirstTry)
emit kaidan->logInWorked();
// accept credentials and save them
creds.isFirstTry = false;
caches->settings->setValue(KAIDAN_SETTINGS_AUTH_JID, creds.jid);
caches->settings->setValue(KAIDAN_SETTINGS_AUTH_PASSWD,
QString::fromUtf8(creds.password.toUtf8().toBase64()));
// after first log in we always want to automatically reconnect
client->configuration().setAutoReconnectionEnabled(true);
}
void ClientWorker::onConnectionError(QXmppClient::Error error)
{
// no mutex needed, because this is called from updateClient()
qDebug() << "[client] Disconnected:" << error;
// Check if first time connecting with these credentials
if (creds.isFirstTry || error == QXmppClient::XmppStreamError) {
// always request new credentials, when failed to connect on first time
emit kaidan->newCredentialsNeeded();
}
- if (error == QXmppClient::NoError) {
- emit disconnReasonChanged(DisconnReason::ConnUserDisconnected);
- } else if (error == QXmppClient::KeepAliveError) {
- emit disconnReasonChanged(DisconnReason::ConnKeepAliveError);
- } else if (error == QXmppClient::XmppStreamError) {
- QXmppStanza::Error::Condition xError = client->xmppStreamError();
- qDebug() << xError;
- if (xError == QXmppStanza::Error::NotAuthorized) {
- emit disconnReasonChanged(DisconnReason::ConnAuthenticationFailed);
- } else {
- emit disconnReasonChanged(DisconnReason::ConnNotConnected);
+ QXmppStanza::Error::Condition xmppStreamError;
+ QAbstractSocket::SocketError socketError;
+
+ switch (error) {
+ case QXmppClient::NoError:
+ emit connectionErrorChanged(ClientWorker::UserDisconnected);
+ break;
+ case QXmppClient::KeepAliveError:
+ emit connectionErrorChanged(ClientWorker::KeepAliveError);
+ break;
+ case QXmppClient::XmppStreamError:
+ xmppStreamError = client->xmppStreamError();
+ qDebug() << xmppStreamError;
+ if (xmppStreamError == QXmppStanza::Error::NotAuthorized) {
+ emit connectionErrorChanged(ClientWorker::AuthenticationFailed);
+ } else {
+ emit connectionErrorChanged(ClientWorker::NotConnected);
+ }
+ break;
+ case QXmppClient::SocketError:
+ socketError = client->socketError();
+ switch (socketError) {
+ case QAbstractSocket::ConnectionRefusedError:
+ case QAbstractSocket::RemoteHostClosedError:
+ emit connectionErrorChanged(ClientWorker::ConnectionRefused);
+ break;
+ case QAbstractSocket::HostNotFoundError:
+ emit connectionErrorChanged(ClientWorker::DnsError);
+ break;
+ case QAbstractSocket::SocketAccessError:
+ emit connectionErrorChanged(ClientWorker::NoNetworkPermission);
+ break;
+ case QAbstractSocket::SocketTimeoutError:
+ emit connectionErrorChanged(ClientWorker::KeepAliveError);
+ break;
+ case QAbstractSocket::SslHandshakeFailedError:
+ case QAbstractSocket::SslInternalError:
+ emit connectionErrorChanged(ClientWorker::TlsFailed);
+ break;
+ default:
+ emit connectionErrorChanged(ClientWorker::NotConnected);
+ }
+ break;
}
- } else if (error == QXmppClient::SocketError) {
- qDebug() << "[client] Socket Error:" << client->socketErrorString();
-
- QAbstractSocket::SocketError sError = client->socketError();
- if (sError == QAbstractSocket::ConnectionRefusedError ||
- sError == QAbstractSocket::RemoteHostClosedError) {
- emit disconnReasonChanged(DisconnReason::ConnConnectionRefused);
- } else if (sError == QAbstractSocket::HostNotFoundError) {
- emit disconnReasonChanged(DisconnReason::ConnDnsError);
- } else if (sError == QAbstractSocket::SocketAccessError) {
- emit disconnReasonChanged(DisconnReason::ConnNoNetworkPermission);
- } else if (sError == QAbstractSocket::SocketTimeoutError) {
- emit disconnReasonChanged(DisconnReason::ConnKeepAliveError);
- } else if (sError == QAbstractSocket::SslHandshakeFailedError ||
- sError == QAbstractSocket::SslInternalError) {
- emit disconnReasonChanged(DisconnReason::ConnTlsFailed);
- } else {
- emit disconnReasonChanged(DisconnReason::ConnNotConnected);
- }
- }
}
QString ClientWorker::generateRandomString(unsigned int length) const
{
const QString resourceChars(KAIDAN_RESOURCE_RANDOM_CHARS);
QString randomString;
for (unsigned int i = 0; i < length; ++i)
randomString.append(resourceChars.at(qrand() % resourceChars.length()));
return randomString;
}
VCardManager *ClientWorker::getVCardManager() const
{
return vCardManager;
}
void ClientWorker::setCsiState(Qt::ApplicationState state)
{
if (state == Qt::ApplicationActive)
client->setActive(true);
else
client->setActive(false);
}
diff --git a/src/ClientWorker.h b/src/ClientWorker.h
index ddfd30a..cd56fab 100644
--- a/src/ClientWorker.h
+++ b/src/ClientWorker.h
@@ -1,194 +1,214 @@
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2016-2020 Kaidan developers and contributors
* (see the LICENSE file for a full list of copyright authors)
*
* Kaidan is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the author of Kaidan gives
* permission to link the code of its release with the OpenSSL
* project's "OpenSSL" library (or with modified versions of it that
* use the same license as the "OpenSSL" library), and distribute the
* linked executables. You must obey the GNU General Public License in
* all respects for all of the code used other than "OpenSSL". If you
* modify this file, you may extend this exception to your version of
* the file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*
* Kaidan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kaidan. If not, see .
*/
#ifndef CLIENTWORKER_H
#define CLIENTWORKER_H
// Qt
#include
#include
#include
class QGuiApplication;
// QXmpp
#include
// Kaidan
#include "AvatarFileStorage.h"
#include "Database.h"
#include "Enums.h"
#include "Globals.h"
#include "MessageModel.h"
#include "PresenceCache.h"
#include "RosterModel.h"
#include "TransferCache.h"
class LogHandler;
class Kaidan;
class ClientWorker;
class RegistrationManager;
class RosterManager;
class MessageHandler;
class DiscoveryManager;
class VCardManager;
class UploadManager;
class DownloadManager;
-using namespace Enums;
-
/**
* The ClientWorker is used as a QObject-based worker on the ClientThread.
*/
class ClientWorker : public QObject
{
Q_OBJECT
public:
+ /**
+ * enumeration of possible connection errors
+ */
+ enum ConnectionError {
+ NoError,
+ UserDisconnected,
+ AuthenticationFailed,
+ NotConnected,
+ TlsFailed,
+ TlsNotAvailable,
+ DnsError,
+ ConnectionRefused,
+ NoSupportedAuth,
+ KeepAliveError,
+ NoNetworkPermission,
+ RegistrationUnsupported
+ };
+ Q_ENUM(ConnectionError)
+
struct Caches {
Caches(Kaidan *kaidan, RosterDb *rosterDb, MessageDb *msgDb,
QObject *parent = nullptr)
: msgModel(new MessageModel(kaidan, msgDb, parent)),
rosterModel(new RosterModel(rosterDb, parent)),
avatarStorage(new AvatarFileStorage(parent)),
presCache(new PresenceCache(parent)),
transferCache(new TransferCache(parent)),
settings(new QSettings(APPLICATION_NAME, APPLICATION_NAME))
{
rosterModel->setMessageModel(msgModel);
}
~Caches()
{
delete msgModel;
delete rosterModel;
delete avatarStorage;
delete presCache;
delete transferCache;
delete settings;
}
MessageModel *msgModel;
RosterModel *rosterModel;
AvatarFileStorage *avatarStorage;
PresenceCache *presCache;
TransferCache* transferCache;
QSettings *settings;
};
struct Credentials {
QString jid;
QString jidResource;
QString password;
// if never connected successfully before with these credentials
bool isFirstTry;
};
/**
* @param caches All caches running in the main thread for communication with the UI.
* @param kaidan Main back-end class, running in the main thread.
* @param enableLogging If logging of the XMPP stream should be done.
* @param app The QGuiApplication to determine if the window is active.
* @param parent Optional QObject-based parent.
*/
ClientWorker(Caches *caches, Kaidan *kaidan, bool enableLogging, QGuiApplication *app,
QObject *parent = nullptr);
VCardManager *getVCardManager() const;
public slots:
/**
* Main function of the client thread
*/
void main();
/**
* Sets the new credentials for next connect.
*
* @param creds The new credentials for the next connect
*/
void setCredentials(ClientWorker::Credentials creds)
{
this->creds = creds;
}
/**
* Connects the client with the server.
*/
void xmppConnect();
signals:
// emitted by 'Kaidan' to us:
void connectRequested();
void disconnectRequested();
void credentialsUpdated(ClientWorker::Credentials creds);
- // emitted by us:
- // connection state is directly connected (client -> kaidan) without this step
- void disconnReasonChanged(Enums::DisconnectionReason reason);
+ /**
+ * Emitted when the client failed to connect to the server.
+ *
+ * @param error new connection error
+ */
+ void connectionErrorChanged(ClientWorker::ConnectionError error);
private slots:
/**
* Notifys via signal that the client has connected.
*/
void onConnect();
/**
- * Shows error reason
+ * Sets a new connection error.
*/
void onConnectionError(QXmppClient::Error error);
/**
* Uses the QGuiApplication state to reduce network traffic when window is minimized
*/
void setCsiState(Qt::ApplicationState state);
private:
/**
* Generates a random alphanumeric string
*
* @param length The length of the generated string
*/
QString generateRandomString(unsigned int length = 4) const;
Caches *caches;
Kaidan *kaidan;
QXmppClient *client;
LogHandler *logger;
Credentials creds;
bool enableLogging;
QGuiApplication *app;
RegistrationManager *registrationManager;
RosterManager *rosterManager;
MessageHandler *msgHandler;
DiscoveryManager *discoManager;
VCardManager *vCardManager;
UploadManager *uploadManager;
DownloadManager *downloadManager;
};
#endif // CLIENTWORKER_H
diff --git a/src/Enums.h b/src/Enums.h
index 0f023d8..6589426 100644
--- a/src/Enums.h
+++ b/src/Enums.h
@@ -1,136 +1,114 @@
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2016-2020 Kaidan developers and contributors
* (see the LICENSE file for a full list of copyright authors)
*
* Kaidan is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the author of Kaidan gives
* permission to link the code of its release with the OpenSSL
* project's "OpenSSL" library (or with modified versions of it that
* use the same license as the "OpenSSL" library), and distribute the
* linked executables. You must obey the GNU General Public License in
* all respects for all of the code used other than "OpenSSL". If you
* modify this file, you may extend this exception to your version of
* the file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*
* Kaidan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kaidan. If not, see .
*/
#ifndef ENUMS_H
#define ENUMS_H
#include
#include
#include
#include
#define ENABLE_IF(...) typename std::enable_if<__VA_ARGS__>::type* = nullptr
template struct make_void { typedef void type; };
template using void_t = typename make_void::type;
// primary template handles types that have no nested ::enum_type member, like standard enum
template >
struct has_enum_type : std::false_type { };
// specialization recognizes types that do have a nested ::enum_type member, like QFlags enum
template
struct has_enum_type> : std::true_type { };
namespace Enums {
Q_NAMESPACE
/**
* Enumeration of possible connection states.
*/
enum class ConnectionState {
StateDisconnected = QXmppClient::DisconnectedState,
StateConnecting = QXmppClient::ConnectingState,
StateConnected = QXmppClient::ConnectedState
};
Q_ENUM_NS(ConnectionState)
- /**
- * Enumeration of possible disconnection reasons
- */
- enum class DisconnectionReason {
- ConnNoError,
- ConnUserDisconnected,
- ConnAuthenticationFailed,
- ConnNotConnected,
- ConnTlsFailed,
- ConnTlsNotAvailable,
- ConnDnsError,
- ConnConnectionRefused,
- ConnNoSupportedAuth,
- ConnKeepAliveError,
- ConnNoNetworkPermission
- };
- Q_ENUM_NS(DisconnectionReason)
-
- // Alias, so that qDebug outputs the full name, but it can be
- // abrieviated in the code
- using DisconnReason = DisconnectionReason;
-
/**
* Enumeration of different media/message types
*/
enum class MessageType {
MessageUnknown = -1,
MessageText,
MessageFile,
MessageImage,
MessageVideo,
MessageAudio,
MessageDocument,
MessageGeoLocation
};
Q_ENUM_NS(MessageType)
/**
* Enumeration of contact availability states
*/
enum class AvailabilityTypes {
PresError,
PresUnavailable,
PresOnline,
PresAway,
PresXA,
PresDND,
PresChat,
PresInvisible
};
Q_ENUM_NS(AvailabilityTypes)
template ::value && std::is_enum::value)>
QString toString(const T flag) {
static const QMetaEnum e = QMetaEnum::fromType();
return QString::fromLatin1(e.valueToKey(static_cast(flag)));
}
template ::value)>
QString toString(const T flags) {
static const QMetaEnum e = QMetaEnum::fromType();
return QString::fromLatin1(e.valueToKeys(static_cast(flags)));
}
}
// Needed workaround to trigger older CMake auto moc versions to generate moc
// sources for this file (it only contains Q_NAMESPACE, which is new).
#if 0
Q_OBJECT
#endif
#endif // ENUMS_H
diff --git a/src/Kaidan.cpp b/src/Kaidan.cpp
index 6ee9ba0..3f3ccff 100644
--- a/src/Kaidan.cpp
+++ b/src/Kaidan.cpp
@@ -1,279 +1,279 @@
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2016-2020 Kaidan developers and contributors
* (see the LICENSE file for a full list of copyright authors)
*
* Kaidan is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the author of Kaidan gives
* permission to link the code of its release with the OpenSSL
* project's "OpenSSL" library (or with modified versions of it that
* use the same license as the "OpenSSL" library), and distribute the
* linked executables. You must obey the GNU General Public License in
* all respects for all of the code used other than "OpenSSL". If you
* modify this file, you may extend this exception to your version of
* the file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*
* Kaidan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kaidan. If not, see .
*/
#include "Kaidan.h"
// Qt
#include
#include
#include
#include
// QXmpp
#include "qxmpp-exts/QXmppColorGenerator.h"
#include "qxmpp-exts/QXmppUri.h"
// Kaidan
#include "AvatarFileStorage.h"
#include "Database.h"
#include "MessageDb.h"
#include "MessageModel.h"
#include "PresenceCache.h"
#include "QmlUtils.h"
#include "RosterDb.h"
#include "RosterModel.h"
Kaidan *Kaidan::s_instance = nullptr;
Kaidan::Kaidan(QGuiApplication *app, bool enableLogging, QObject *parent)
: QObject(parent),
m_database(new Database()),
m_dbThrd(new QThread()),
m_msgDb(new MessageDb()),
m_rosterDb(new RosterDb(m_database)),
m_cltThrd(new QThread())
{
Q_ASSERT(!Kaidan::s_instance);
Kaidan::s_instance = this;
// Database setup
m_database->moveToThread(m_dbThrd);
m_msgDb->moveToThread(m_dbThrd);
m_rosterDb->moveToThread(m_dbThrd);
connect(m_dbThrd, &QThread::started, m_database, &Database::openDatabase);
m_dbThrd->setObjectName("SqlDatabase");
m_dbThrd->start();
// Caching components
m_caches = new ClientWorker::Caches(this, m_rosterDb, m_msgDb, this);
// Connect the avatar changed signal of the avatarStorage with the NOTIFY signal
// of the Q_PROPERTY for the avatar storage (so all avatars are updated in QML)
connect(m_caches->avatarStorage, &AvatarFileStorage::avatarIdsChanged,
this, &Kaidan::avatarStorageChanged);
//
// Load settings
//
creds.jid = m_caches->settings->value(KAIDAN_SETTINGS_AUTH_JID).toString();
creds.jidResource = m_caches->settings->value(KAIDAN_SETTINGS_AUTH_RESOURCE)
.toString();
creds.password = QString(QByteArray::fromBase64(m_caches->settings->value(
KAIDAN_SETTINGS_AUTH_PASSWD).toString().toUtf8()));
// use Kaidan as resource, if no set
if (creds.jidResource.isEmpty())
setJidResource(APPLICATION_DISPLAY_NAME);
creds.isFirstTry = false;
//
// Start ClientWorker on new thread
//
m_client = new ClientWorker(m_caches, this, enableLogging, app);
m_client->setCredentials(creds);
m_client->moveToThread(m_cltThrd);
- connect(m_client, &ClientWorker::disconnReasonChanged, this, &Kaidan::setDisconnReason);
+ connect(m_client, &ClientWorker::connectionErrorChanged, this, &Kaidan::setConnectionError);
connect(m_cltThrd, &QThread::started, m_client, &ClientWorker::main);
m_client->setObjectName("XmppClient");
m_cltThrd->start();
}
Kaidan::~Kaidan()
{
delete m_caches;
delete m_database;
Kaidan::s_instance = nullptr;
}
void Kaidan::start()
{
if (creds.jid.isEmpty() || creds.password.isEmpty())
emit newCredentialsNeeded();
else
mainConnect();
}
void Kaidan::mainConnect()
{
if (connectionState != ConnectionState::StateDisconnected) {
qWarning() << "[main] Tried to connect, even if still connected!"
<< "Requesting disconnect.";
emit m_client->disconnectRequested();
}
emit m_client->credentialsUpdated(creds);
emit m_client->connectRequested();
}
void Kaidan::mainDisconnect(bool openLogInPage)
{
// disconnect the client if connected or connecting
if (connectionState != ConnectionState::StateDisconnected)
emit m_client->disconnectRequested();
if (openLogInPage) {
// clear password
m_caches->settings->remove(KAIDAN_SETTINGS_AUTH_PASSWD);
setPassword(QString());
// trigger log in page
emit newCredentialsNeeded();
}
}
void Kaidan::setConnectionState(QXmppClient::State state)
{
if (this->connectionState != static_cast(state)) {
this->connectionState = static_cast(state);
emit connectionStateChanged();
// Open the possibly cached URI when connected.
// This is needed because the XMPP URIs can't be opened when Kaidan is not connected.
if (connectionState == ConnectionState::StateConnected && !openUriCache.isEmpty()) {
// delay is needed because sometimes the RosterPage needs to be loaded first
QTimer::singleShot(300, [=] () {
emit xmppUriReceived(openUriCache);
openUriCache = "";
});
}
}
}
-void Kaidan::setDisconnReason(DisconnectionReason reason)
+void Kaidan::setConnectionError(ClientWorker::ConnectionError error)
{
- disconnReason = reason;
- emit disconnReasonChanged();
+ connectionError = error;
+ emit connectionErrorChanged();
}
bool Kaidan::notificationsMuted(const QString &jid)
{
return m_caches->settings->value(QString("muted/") + jid, false).toBool();
}
void Kaidan::setNotificationsMuted(const QString &jid, bool muted)
{
m_caches->settings->setValue(QString("muted/") + jid, muted);
emit notificationsMutedChanged(jid);
}
void Kaidan::setJid(const QString &jid)
{
creds.jid = jid;
// credentials were modified -> first try
creds.isFirstTry = true;
emit jidChanged();
}
void Kaidan::setJidResource(const QString &jidResource)
{
// JID resource won't influence the authentication, so we don't need
// to set the first try flag and can save it.
creds.jidResource = jidResource;
m_caches->settings->setValue(KAIDAN_SETTINGS_AUTH_RESOURCE, jidResource);
emit jidResourceChanged();
}
void Kaidan::setPassword(const QString &password)
{
creds.password = password;
// credentials were modified -> first try
creds.isFirstTry = true;
emit passwordChanged();
}
-quint8 Kaidan::getDisconnReason() const
+quint8 Kaidan::getConnectionError() const
{
- return static_cast(disconnReason);
+ return static_cast(connectionError);
}
void Kaidan::addOpenUri(const QString &uri)
{
if (!QXmppUri::isXmppUri(uri))
return;
if (connectionState == ConnectionState::StateConnected) {
emit xmppUriReceived(uri);
} else {
//: The link is an XMPP-URI (i.e. 'xmpp:kaidan@muc.kaidan.im?join' for joining a chat)
emit passiveNotificationRequested(tr("The link will be opened after you have connected."));
openUriCache = uri;
}
}
void Kaidan::loginByUri(const QString &uri)
{
// input does not start with 'xmpp:'
if (!QXmppUri::isXmppUri(uri)) {
notifyLoginUriNotFound();
return;
}
// parse
QXmppUri parsedUri(uri);
// no JID provided
if (parsedUri.jid().isEmpty()) {
notifyLoginUriNotFound();
return;
}
setJid(parsedUri.jid());
// URI has no login action or no password
if (!parsedUri.hasAction(QXmppUri::Action::Login) || parsedUri.password().isEmpty()) {
// reset password
setPassword(QString());
emit passiveNotificationRequested(tr("No password found. Please enter it."));
return;
}
setPassword(parsedUri.password());
// try to connect
mainConnect();
}
void Kaidan::notifyLoginUriNotFound()
{
qWarning() << "[main]" << "No valid login URI found.";
emit passiveNotificationRequested(tr("No valid login QR code found."));
}
ClientWorker *Kaidan::getClient() const
{
return m_client;
}
Kaidan *Kaidan::instance()
{
return s_instance;
}
diff --git a/src/Kaidan.h b/src/Kaidan.h
index 16265c3..86eacd4 100644
--- a/src/Kaidan.h
+++ b/src/Kaidan.h
@@ -1,435 +1,431 @@
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2016-2020 Kaidan developers and contributors
* (see the LICENSE file for a full list of copyright authors)
*
* Kaidan is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the author of Kaidan gives
* permission to link the code of its release with the OpenSSL
* project's "OpenSSL" library (or with modified versions of it that
* use the same license as the "OpenSSL" library), and distribute the
* linked executables. You must obey the GNU General Public License in
* all respects for all of the code used other than "OpenSSL". If you
* modify this file, you may extend this exception to your version of
* the file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*
* Kaidan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kaidan. If not, see .
*/
#ifndef KAIDAN_H
#define KAIDAN_H
// Qt
#include
#include
#include
// Kaidan
#include "ClientWorker.h"
-#include "Enums.h"
#include "Globals.h"
class QGuiApplication;
class Database;
class QXmppClient;
-using namespace Enums;
-
/**
* @class Kaidan Kaidan's Back-End Class
*
* @brief This class will initiate the complete back-end, including the @see Database
* connection, viewing models (@see MessageModel, @see RosterModel), etc.
*
* This class will run in the main thread, the XMPP connection and the database managers
* run in other threads.
*/
class Kaidan : public QObject
{
Q_OBJECT
Q_PROPERTY(RosterModel* rosterModel READ getRosterModel CONSTANT)
Q_PROPERTY(MessageModel* messageModel READ getMessageModel CONSTANT)
Q_PROPERTY(AvatarFileStorage* avatarStorage READ getAvatarStorage NOTIFY avatarStorageChanged)
Q_PROPERTY(PresenceCache* presenceCache READ getPresenceCache CONSTANT)
Q_PROPERTY(TransferCache* transferCache READ getTransferCache CONSTANT)
Q_PROPERTY(QSettings* settings READ getSettings CONSTANT)
Q_PROPERTY(quint8 connectionState READ getConnectionState NOTIFY connectionStateChanged)
- Q_PROPERTY(quint8 disconnReason READ getDisconnReason NOTIFY disconnReasonChanged)
+ Q_PROPERTY(quint8 connectionError READ getConnectionError NOTIFY connectionErrorChanged)
Q_PROPERTY(QString jid READ getJid WRITE setJid NOTIFY jidChanged)
Q_PROPERTY(QString jidResource READ getJidResource WRITE setJidResource NOTIFY jidResourceChanged)
Q_PROPERTY(QString password READ getPassword WRITE setPassword NOTIFY passwordChanged)
Q_PROPERTY(bool uploadServiceFound READ getUploadServiceFound NOTIFY uploadServiceFoundChanged)
public:
static Kaidan *instance();
Kaidan(QGuiApplication *app, bool enableLogging = true, QObject *parent = nullptr);
~Kaidan();
/**
* Start connection (called from QML when ready)
*/
Q_INVOKABLE void start();
/**
* Connect to the XMPP server
*
* If you haven't set a username and password, they are used from the
* last successful login (the settings file).
*/
Q_INVOKABLE void mainConnect();
/**
* Disconnect from XMPP server
*
* This will disconnect the client from the server. When disconnected,
* the connectionStateChanged signal will be emitted.
*
* @param openLogInPage If true, the newCredentialsNeeded signal will be
* emitted.
*/
Q_INVOKABLE void mainDisconnect(bool openLogInPage = false);
/**
* Returns the current ConnectionState
*/
Q_INVOKABLE quint8 getConnectionState() const
{
return (quint8) connectionState;
}
/**
- * Returns the last disconnection reason
+ * Returns the last connection error.
*/
- Q_INVOKABLE quint8 getDisconnReason() const;
+ Q_INVOKABLE quint8 getConnectionError() const;
/**
* Set own JID used for connection
*
* To really change the JID of the current connection, you'll need to
* reconnect.
*/
void setJid(const QString &jid);
/**
* Get the current JID
*/
QString getJid() const
{
return creds.jid;
}
/**
* Set a optional custom JID resource (device name)
*/
void setJidResource(const QString &jidResource);
/**
* Get the JID resoruce
*/
QString getJidResource() const
{
return creds.jidResource;
}
/**
* Set the password for next connection
*/
void setPassword(const QString &password);
/**
* Get the currently used password
*/
QString getPassword() const
{
return creds.password;
}
RosterModel* getRosterModel() const
{
return m_caches->rosterModel;
}
MessageModel* getMessageModel() const
{
return m_caches->msgModel;
}
AvatarFileStorage* getAvatarStorage() const
{
return m_caches->avatarStorage;
}
PresenceCache* getPresenceCache() const
{
return m_caches->presCache;
}
TransferCache* getTransferCache() const
{
return m_caches->transferCache;
}
QSettings* getSettings() const
{
return m_caches->settings;
}
ClientWorker *getClient() const;
/**
* Adds XMPP URI to open as soon as possible
*/
void addOpenUri(const QString &uri);
/**
* Connects to the server by the parsed credentials (bare JID and password)
* from a given XMPP URI (e.g. from scanning a QR code)
* like "xmpp:user@example.org?login;password=abc"
*/
Q_INVOKABLE void loginByUri(const QString &uri);
/**
* Returns whether an HTTP File Upload service has been found
*/
bool getUploadServiceFound() const
{
return uploadServiceFound;
}
signals:
void avatarStorageChanged();
/**
* Emitted, when the client's connection state has changed (e.g. when
* successfully connected or when disconnected)
*/
void connectionStateChanged();
/**
- * Emitted, when the client failed to connect and gives the reason in
- * a DisconnectionReason enumatrion.
+ * Emitted when the client failed to connect.
*/
- void disconnReasonChanged();
+ void connectionErrorChanged();
/**
* Emitted when the JID was changed
*/
void jidChanged();
/**
* Emitted when the JID resouce (device name) has changed
*/
void jidResourceChanged();
/**
* Emitted when the used password for logging in has changed
*/
void passwordChanged();
/**
* Emitted when there are no (correct) credentials and new are needed
*
* The client will be in disconnected state, when this is emitted.
*/
void newCredentialsNeeded();
/**
* Emitted when log in worked with new credentials
*
* The client will be in connected state, when this is emitted.
*/
void logInWorked();
/**
* Show passive notification
*/
void passiveNotificationRequested(QString text);
/**
* Emitted, whan a subscription request was received
*/
void subscriptionRequestReceived(QString from, QString msg);
/**
* Incoming subscription request was accepted or declined by the user
*/
void subscriptionRequestAnswered(QString jid, bool accepted);
/**
* Request vCard of any JID
*
* Is required when the avatar (or other information) of a JID are
* requested and the JID is not in the roster.
*/
void vCardRequested(const QString &jid);
/**
* XMPP URI received
*
* Is called when Kaidan was used to open an XMPP URI (i.e. 'xmpp:kaidan@muc.kaidan.im?join')
*/
void xmppUriReceived(QString uri);
/**
* The upload progress of a file upload has changed
*/
void uploadProgressMade(QString msgId, quint64 sent, quint64 total);
/**
* An HTTP File Upload service was discovered
*/
void uploadServiceFoundChanged();
/**
* Send a text message to any JID
*
* Currently only contacts are displayed on the RosterPage (there is no
* way to view a list of all chats -> for contacts and non-contacts), so
* you should only send messages to JIDs from your roster, otherwise you
* won't be able to see the message history.
*/
void sendMessage(QString jid, QString message, bool isSpoiler, QString spoilerHint);
/**
* Correct the last message
*
* To get/check the last message id, use `kaidan.messageModel.lastMessageId(jid)`
*/
void correctMessage(QString toJid, QString msgId, QString message);
/**
* Upload and send file
*/
void sendFile(const QString &jid, const QUrl &fileUrl, const QString &body);
/**
* Add a contact to your roster
*
* @param nick A simple nick name for the new contact, which should be
* used to display in the roster.
*/
void addContact(QString jid, QString nick, QString msg);
/**
* Remove a contact from your roster
*
* Only the JID is needed.
*/
void removeContact(QString jid);
/**
* Change a contact's name
*/
void renameContact(const QString &jid, const QString &newContactName);
/**
* Downloads an attached media file of a message
*
* @param msgId The message
* @param url the media url from the message
*/
void downloadMedia(QString msgId, QString url);
/**
* Changes the user's password on the server
*
* @param newPassword The new password
*/
void changePassword(const QString &newPassword);
/**
* Emitted, when changing the password has succeeded.
*/
void passwordChangeSucceeded();
/**
* Emitted, when changing the password has failed.
*/
void passwordChangeFailed();
/**
* Emitted, when a contact was muted/unmuted.
*/
void notificationsMutedChanged(const QString& jid);
public slots:
/**
* Set current connection state
*/
void setConnectionState(QXmppClient::State state);
/**
- * Sets the disconnection error/reason
+ * Sets a new connection error.
*/
- void setDisconnReason(Enums::DisconnectionReason reason);
+ void setConnectionError(ClientWorker::ConnectionError error);
/**
* Receives messages from another instance of the application
*/
void receiveMessage(quint32, const QByteArray &msg)
{
// currently we only send XMPP URIs
addOpenUri(msg);
}
/**
* Enables HTTP File Upload to be used (will be called from UploadManager)
*/
void setUploadServiceFound(bool enabled)
{
uploadServiceFound = enabled;
emit uploadServiceFoundChanged();
}
/**
* Returns whether notifications are enabled for the given contact.
*/
bool notificationsMuted(const QString& jid);
/**
* Sets the notifications to muted/unmuted.
*
* @param muted true if notifications should be muted.
* @param jid contains the current chatpartner's jid.
*/
void setNotificationsMuted(const QString& jid, bool muted);
private:
void connectDatabases();
/**
* Notifies if no login URI was found
*/
void notifyLoginUriNotFound();
Database *m_database;
QThread *m_dbThrd;
MessageDb *m_msgDb;
RosterDb *m_rosterDb;
QThread *m_cltThrd;
ClientWorker::Caches *m_caches;
ClientWorker *m_client;
ClientWorker::Credentials creds;
QString openUriCache;
bool uploadServiceFound = false;
ConnectionState connectionState = ConnectionState::StateDisconnected;
- DisconnReason disconnReason = DisconnReason::ConnNoError;
+ ClientWorker::ConnectionError connectionError = ClientWorker::NoError;
static Kaidan *s_instance;
};
#endif
diff --git a/src/main.cpp b/src/main.cpp
index f2be323..0b7e789 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,387 +1,389 @@
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2016-2020 Kaidan developers and contributors
* (see the LICENSE file for a full list of copyright authors)
*
* Kaidan is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the author of Kaidan gives
* permission to link the code of its release with the OpenSSL
* project's "OpenSSL" library (or with modified versions of it that
* use the same license as the "OpenSSL" library), and distribute the
* linked executables. You must obey the GNU General Public License in
* all respects for all of the code used other than "OpenSSL". If you
* modify this file, you may extend this exception to your version of
* the file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*
* Kaidan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kaidan. If not, see .
*/
// Qt
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// QXmpp
#include "qxmpp-exts/QXmppUploadManager.h"
#include
// Kaidan
#include "AvatarFileStorage.h"
#include "EmojiModel.h"
#include "Enums.h"
#include "Kaidan.h"
#include "Message.h"
#include "MessageModel.h"
#include "PresenceCache.h"
#include "QmlUtils.h"
#include "RosterModel.h"
#include "RosterFilterProxyModel.h"
#include "StatusBar.h"
#include "UploadManager.h"
#include "EmojiModel.h"
#include "Utils.h"
#include "QrCodeScannerFilter.h"
#include "VCardModel.h"
#include "CameraModel.h"
#include "AudioDeviceModel.h"
#include "MediaSettingModel.h"
#include "MediaUtils.h"
#include "MediaRecorder.h"
#ifdef STATIC_BUILD
#include "static_plugins.h"
#endif
#ifndef QAPPLICATION_CLASS
#define QAPPLICATION_CLASS QApplication
#endif
#include QT_STRINGIFY(QAPPLICATION_CLASS)
#if !defined(Q_OS_IOS) && !defined(Q_OS_ANDROID)
// SingleApplication (Qt5 replacement for QtSingleApplication)
#include "singleapp/singleapplication.h"
#endif
#ifdef STATIC_BUILD
#define KIRIGAMI_BUILD_TYPE_STATIC
#include "./3rdparty/kirigami/src/kirigamiplugin.h"
#endif
#ifdef Q_OS_ANDROID
#include
#endif
#ifdef Q_OS_WIN
#include
#endif
enum CommandLineParseResult {
CommandLineOk,
CommandLineError,
CommandLineVersionRequested,
CommandLineHelpRequested
};
CommandLineParseResult parseCommandLine(QCommandLineParser &parser, QString *errorMessage)
{
// application description
parser.setApplicationDescription(QString(APPLICATION_DISPLAY_NAME) +
" - " + QString(APPLICATION_DESCRIPTION));
// add all possible arguments
QCommandLineOption helpOption = parser.addHelpOption();
QCommandLineOption versionOption = parser.addVersionOption();
parser.addOption({"disable-xml-log", "Disable output of full XMPP XML stream."});
parser.addOption({{"m", "multiple"}, "Allow multiple instances to be started."});
parser.addPositionalArgument("xmpp-uri", "An XMPP-URI to open (i.e. join a chat).",
"[xmpp-uri]");
// parse arguments
if (!parser.parse(QGuiApplication::arguments())) {
*errorMessage = parser.errorText();
return CommandLineError;
}
// check for special cases
if (parser.isSet(versionOption))
return CommandLineVersionRequested;
if (parser.isSet(helpOption))
return CommandLineHelpRequested;
// if nothing special happened, return OK
return CommandLineOk;
}
Q_DECL_EXPORT int main(int argc, char *argv[])
{
#ifdef Q_OS_WIN
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
}
#endif
// initialize random generator
qsrand(time(nullptr));
//
// App
//
#ifdef UBUNTU_TOUCH
qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "true");
qputenv("QT_QUICK_CONTROLS_MOBILE", "true");
#endif
#ifdef APPIMAGE
qputenv("OPENSSL_CONF", "");
#endif
// name, display name, description
QGuiApplication::setApplicationName(APPLICATION_NAME);
QGuiApplication::setApplicationDisplayName(APPLICATION_DISPLAY_NAME);
QGuiApplication::setApplicationVersion(VERSION_STRING);
// attributes
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
// create a qt app
#if defined(Q_OS_IOS) || defined(Q_OS_ANDROID)
QGuiApplication app(argc, argv);
#else
SingleApplication app(argc, argv, true);
#endif
#ifdef APPIMAGE
QFileInfo executable(QCoreApplication::applicationFilePath());
if (executable.isSymLink()) {
executable.setFile(executable.symLinkTarget());
}
QString gstreamerPluginsPath;
// Try to use deployed plugins if any...
#if defined(TARGET_GSTREAMER_PLUGINS)
gstreamerPluginsPath = QString::fromLocal8Bit(TARGET_GSTREAMER_PLUGINS);
if (!gstreamerPluginsPath.isEmpty()) {
gstreamerPluginsPath = QDir::cleanPath(QString::fromLatin1("%1/../..%2")
.arg(executable.absolutePath(), gstreamerPluginsPath));
}
#else
qFatal("Please provide the unified directory containing the gstreamer plugins and gst-plugin-scanner.");
#endif
#if defined(QT_DEBUG)
qputenv("GST_DEBUG", "ERROR:5,WARNING:5,INFO:5,DEBUG:5,LOG:5");
#endif
qputenv("GST_PLUGIN_PATH_1_0", QByteArray());
qputenv("GST_PLUGIN_SYSTEM_PATH_1_0", gstreamerPluginsPath.toLocal8Bit());
qputenv("GST_PLUGIN_SCANNER_1_0", QString::fromLatin1("%1/gst-plugin-scanner").arg(gstreamerPluginsPath).toLocal8Bit());
#endif // APPIMAGE
// register qMetaTypes
qRegisterMetaType("RosterItem");
qRegisterMetaType("RosterModel*");
qRegisterMetaType("Message");
qRegisterMetaType("MessageModel*");
qRegisterMetaType("AvatarFileStorage*");
qRegisterMetaType("PresenceCache*");
qRegisterMetaType("QXmppPresence");
qRegisterMetaType("Credentials");
qRegisterMetaType("Qt::ApplicationState");
qRegisterMetaType("QXmppClient::State");
qRegisterMetaType("MessageType");
- qRegisterMetaType("DisconnectionReason");
qRegisterMetaType("TransferJob*");
qRegisterMetaType("QmlUtils*");
qRegisterMetaType>("QVector");
qRegisterMetaType>("QVector");
qRegisterMetaType>("QHash");
qRegisterMetaType>("std::function");
qRegisterMetaType>("std::function");
qRegisterMetaType("ClientWorker::Credentials");
qRegisterMetaType("QXmppVCardIq");
qRegisterMetaType();
qRegisterMetaType();
qRegisterMetaType();
qRegisterMetaType();
qRegisterMetaType();
qRegisterMetaType();
qRegisterMetaType();
+
// Enums for c++ member calls using enums
qRegisterMetaType();
- qRegisterMetaType();
+ qRegisterMetaType();
qRegisterMetaType();
qRegisterMetaType();
qRegisterMetaType();
qRegisterMetaType();
qRegisterMetaType();
qRegisterMetaType();
qRegisterMetaType();
qRegisterMetaType();
qRegisterMetaType();
qRegisterMetaType();
// Qt-Translator
QTranslator qtTranslator;
qtTranslator.load("qt_" + QLocale::system().name(),
QLibraryInfo::location(QLibraryInfo::TranslationsPath));
QCoreApplication::installTranslator(&qtTranslator);
// Kaidan-Translator
QTranslator kaidanTranslator;
// load the systems locale or none from resources
kaidanTranslator.load(QLocale::system().name(), ":/i18n");
QCoreApplication::installTranslator(&kaidanTranslator);
//
// Command line arguments
//
// create parser and add a description
QCommandLineParser parser;
// parse the arguments
QString commandLineErrorMessage;
switch (parseCommandLine(parser, &commandLineErrorMessage)) {
case CommandLineError:
qWarning() << commandLineErrorMessage;
return 1;
case CommandLineVersionRequested:
parser.showVersion();
return 0;
case CommandLineHelpRequested:
parser.showHelp();
return 0;
case CommandLineOk:
break;
}
#if !defined(Q_OS_IOS) && !defined(Q_OS_ANDROID)
// check if another instance already runs
if (app.isSecondary() && !parser.isSet("multiple")) {
qDebug().noquote() << QString("Another instance of %1 is already running.")
.arg(APPLICATION_DISPLAY_NAME)
<< "You can enable multiple instances by specifying '--multiple'.";
// send a possible link to the primary instance
if (!parser.positionalArguments().isEmpty())
app.sendMessage(parser.positionalArguments().first().toUtf8());
return 0;
}
#endif
//
// Kaidan back-end
//
Kaidan kaidan(&app, !parser.isSet("disable-xml-log"));
#if !defined(Q_OS_IOS) && !defined(Q_OS_ANDROID)
// receive messages from other instances of Kaidan
Kaidan::connect(&app, &SingleApplication::receivedMessage,
&kaidan, &Kaidan::receiveMessage);
#endif
// open the XMPP-URI/link (if given)
if (!parser.positionalArguments().isEmpty())
kaidan.addOpenUri(parser.positionalArguments().first());
//
// QML-GUI
//
if (QIcon::themeName().isEmpty()) {
QIcon::setThemeName("breeze");
}
QQmlApplicationEngine engine;
// QtQuickControls2 Style
if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) {
#ifdef Q_OS_WIN
const QString defaultStyle = QStringLiteral("Universal");
#else
const QString defaultStyle = QStringLiteral("Material");
#endif
qDebug() << "QT_QUICK_CONTROLS_STYLE not set, setting to" << defaultStyle;
qputenv("QT_QUICK_CONTROLS_STYLE", defaultStyle.toLatin1());
}
// QML type bindings
#ifdef STATIC_BUILD
KirigamiPlugin::getInstance().registerTypes();
#endif
qmlRegisterType("StatusBar", 0, 1, "StatusBar");
qmlRegisterType("EmojiModel", 0, 1, "EmojiModel");
qmlRegisterType("EmojiModel", 0, 1, "EmojiProxyModel");
qmlRegisterType(APPLICATION_ID, 1, 0, "QrCodeScannerFilter");
qmlRegisterType(APPLICATION_ID, 1, 0, "VCardModel");
qmlRegisterType(APPLICATION_ID, 1, 0, "RosterFilterProxyModel");
qmlRegisterType(APPLICATION_ID, 1, 0, "CameraModel");
qmlRegisterType(APPLICATION_ID, 1, 0, "AudioDeviceModel");
qmlRegisterType(APPLICATION_ID, 1, 0, "MediaSettingsContainerModel");
qmlRegisterType(APPLICATION_ID, 1, 0, "MediaSettingsResolutionModel");
qmlRegisterType(APPLICATION_ID, 1, 0, "MediaSettingsQualityModel");
qmlRegisterType(APPLICATION_ID, 1, 0, "MediaSettingsImageCodecModel");
qmlRegisterType(APPLICATION_ID, 1, 0, "MediaSettingsAudioCodecModel");
qmlRegisterType(APPLICATION_ID, 1, 0, "MediaSettingsAudioSampleRateModel");
qmlRegisterType(APPLICATION_ID, 1, 0, "MediaSettingsVideoCodecModel");
qmlRegisterType(APPLICATION_ID, 1, 0, "MediaSettingsVideoFrameRateModel");
qmlRegisterType(APPLICATION_ID, 1, 0, "MediaRecorder");
qmlRegisterUncreatableType("EmojiModel", 0, 1, "QAbstractItemModel", "Used by proxy models");
qmlRegisterUncreatableType("EmojiModel", 0, 1, "Emoji", "Used by emoji models");
qmlRegisterUncreatableType(APPLICATION_ID, 1, 0, "TransferJob", "TransferJob type usable");
qmlRegisterUncreatableType(APPLICATION_ID, 1, 0, "QMimeType", "QMimeType type usable");
qmlRegisterUncreatableType(APPLICATION_ID, 1, 0, "CameraInfo", "CameraInfo type usable");
qmlRegisterUncreatableType(APPLICATION_ID, 1, 0, "AudioDeviceInfo", "AudioDeviceInfo type usable");
qmlRegisterUncreatableType(APPLICATION_ID, 1, 0, "MediaSettings", "MediaSettings type usable");
qmlRegisterUncreatableType(APPLICATION_ID, 1, 0, "CommonEncoderSettings", "CommonEncoderSettings type usable");
qmlRegisterUncreatableType(APPLICATION_ID, 1, 0, "ImageEncoderSettings", "ImageEncoderSettings type usable");
qmlRegisterUncreatableType(APPLICATION_ID, 1, 0, "AudioEncoderSettings", "AudioEncoderSettings type usable");
qmlRegisterUncreatableType(APPLICATION_ID, 1, 0, "VideoEncoderSettings", "VideoEncoderSettings type usable");
+ qmlRegisterUncreatableType(APPLICATION_ID, 1, 0, "ClientWorker", "Cannot create object; only enums defined!");
+
qmlRegisterUncreatableMetaObject(Enums::staticMetaObject, APPLICATION_ID,
1, 0, "Enums", "Can't create object; only enums defined!");
qmlRegisterSingletonType("MediaUtils", 0, 1, "MediaUtilsInstance", [](QQmlEngine *, QJSEngine *) {
QObject *instance = new MediaUtils(qApp);
return instance;
});
qmlRegisterSingletonType(APPLICATION_ID, 1, 0, "Utils", [](QQmlEngine *, QJSEngine *) {
return static_cast(QmlUtils::instance());
});
engine.rootContext()->setContextProperty("kaidan", &kaidan);
engine.load(QUrl("qrc:/qml/main.qml"));
- if(engine.rootObjects().isEmpty())
+ if (engine.rootObjects().isEmpty())
return -1;
#ifdef Q_OS_ANDROID
QtAndroid::hideSplashScreen();
#endif
// enter qt main loop
return app.exec();
}
diff --git a/src/qml/LoginPage.qml b/src/qml/LoginPage.qml
index b2d2243..1c106e4 100644
--- a/src/qml/LoginPage.qml
+++ b/src/qml/LoginPage.qml
@@ -1,168 +1,168 @@
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2016-2020 Kaidan developers and contributors
* (see the LICENSE file for a full list of copyright authors)
*
* Kaidan is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the author of Kaidan gives
* permission to link the code of its release with the OpenSSL
* project's "OpenSSL" library (or with modified versions of it that
* use the same license as the "OpenSSL" library), and distribute the
* linked executables. You must obey the GNU General Public License in
* all respects for all of the code used other than "OpenSSL". If you
* modify this file, you may extend this exception to your version of
* the file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*
* Kaidan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kaidan. If not, see .
*/
import QtQuick 2.7
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.3 as Controls
import org.kde.kirigami 2.8 as Kirigami
import im.kaidan.kaidan 1.0
import "elements"
Kirigami.Page {
title: qsTr("Log in")
contextualActions: [
Kirigami.Action {
text: qsTr("Log in using a QR-Code")
icon.name: "view-barcode"
onTriggered: pageStack.layers.push(qrCodeScannerPage)
}
]
ColumnLayout {
anchors.fill: parent
Kirigami.Heading {
text: qsTr("Log in to your XMPP account")
wrapMode: Text.WordWrap
Layout.fillWidth: true
horizontalAlignment: Qt.AlignHCenter
}
ColumnLayout {
width: parent.width
Layout.fillWidth: true
// For desktop or tablet devices
Layout.alignment: Qt.AlignCenter
Layout.maximumWidth: Kirigami.Units.gridUnit * 25
// JID field
Controls.Label {
id: jidLabel
text: qsTr("Your Jabber-ID:")
}
Controls.TextField {
id: jidField
text: kaidan.jid
placeholderText: qsTr("user@example.org")
Layout.fillWidth: true
selectByMouse: true
inputMethodHints: Qt.ImhEmailCharactersOnly
}
// Password field
Controls.Label {
text: qsTr("Your Password:")
}
Controls.TextField {
id: passField
text: kaidan.password
echoMode: TextInput.Password
selectByMouse: true
Layout.fillWidth: true
}
// Connect button
CenteredAdaptiveHighlightedButton {
id: connectButton
label.text: qsTr("Connect")
state: kaidan.connectionState !== Enums.StateDisconnected ? "connecting" : ""
states: [
State {
name: "connecting"
PropertyChanges {
target: connectButton
label.text: "" + qsTr("Connecting…") + ""
label.color: "black"
label.textFormat: Text.StyledText
enabled: false
}
}
]
onClicked: {
// connect to given account data
kaidan.jid = jidField.text
kaidan.password = passField.text
kaidan.mainConnect()
}
}
// connect when return was pressed
Keys.onPressed: {
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
if (jidField.activeFocus)
passField.forceActiveFocus()
else
connectButton.clicked()
}
}
}
// placeholder
Item {
Layout.preferredHeight: Kirigami.Units.gridUnit * 3
}
}
function handleConnectionError() {
- var error = kaidan.disconnReason
- if (error === Enums.ConnAuthenticationFailed) {
+ var error = kaidan.connectionError
+ if (error === ClientWorker.AuthenticationFailed) {
passiveNotification(qsTr("Invalid username or password."))
- } else if (error === Enums.ConnNotConnected) {
+ } else if (error === ClientWorker.NotConnected) {
passiveNotification(qsTr("Cannot connect to the server. Please check your internet connection."))
- } else if (error === Enums.ConnTlsNotAvailable) {
+ } else if (error === ClientWorker.TlsNotAvailable) {
passiveNotification(qsTr("The server doesn't support secure connections."))
- } else if (error === Enums.ConnTlsFailed) {
+ } else if (error === ClientWorker.TlsFailed) {
passiveNotification(qsTr("Error while trying to connect securely."))
- } else if (error === Enums.ConnDnsError) {
+ } else if (error === ClientWorker.DnsError) {
passiveNotification(qsTr("Could not resolve the server's address. Please check your JID again."))
- } else if (error === Enums.ConnConnectionRefused) {
+ } else if (error === ClientWorker.ConnectionRefused) {
passiveNotification(qsTr("Could not connect to the server."))
- } else if (error === Enums.ConnNoSupportedAuth) {
+ } else if (error === ClientWorker.NoSupportedAuth) {
passiveNotification(qsTr("Authentification protocol not supported by the server."))
} else {
passiveNotification(qsTr("An unknown error occured; see log for details."))
}
}
Component.onCompleted: {
- kaidan.disconnReasonChanged.connect(handleConnectionError)
+ kaidan.connectionErrorChanged.connect(handleConnectionError)
}
Component.onDestruction: {
- kaidan.disconnReasonChanged.disconnect(handleConnectionError)
+ kaidan.connectionErrorChanged.disconnect(handleConnectionError)
}
}