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) } }