diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b66c08e..7768d1b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,44 +1,45 @@ # set Kaidans sources (used in main cmake file) set(KAIDAN_SOURCES src/main.cpp src/Kaidan.cpp src/ClientWorker.cpp src/AvatarFileStorage.cpp src/Database.cpp src/RosterModel.cpp src/RosterManager.cpp src/RegistrationManager.cpp src/MessageHandler.cpp src/MessageModel.cpp src/Notifications.cpp src/PresenceCache.cpp src/DiscoveryManager.cpp src/VCardManager.cpp src/LogHandler.cpp src/StatusBar.cpp src/UploadManager.cpp src/EmojiModel.cpp src/TransferCache.cpp src/DownloadManager.cpp src/Utils.cpp src/QrCodeScanner.cpp # needed to trigger moc generation src/Enums.h # kaidan QXmpp extensions (need to be merged into QXmpp upstream) src/qxmpp-exts/QXmppHttpUploadIq.cpp src/qxmpp-exts/QXmppUploadRequestManager.cpp src/qxmpp-exts/QXmppUploadManager.cpp src/qxmpp-exts/QXmppColorGenerator.cpp + src/qxmpp-exts/QXmppUri.cpp # hsluv-c required for color generation src/hsluv-c/hsluv.c ) if(NOT ANDROID AND NOT IOS) set(KAIDAN_SOURCES ${KAIDAN_SOURCES} src/singleapp/singleapplication.cpp src/singleapp/singleapplication_p.cpp ) endif() diff --git a/src/qxmpp-exts/QXmppUri.cpp b/src/qxmpp-exts/QXmppUri.cpp new file mode 100644 index 0000000..2dbfb66 --- /dev/null +++ b/src/qxmpp-exts/QXmppUri.cpp @@ -0,0 +1,242 @@ +/* + * Kaidan - A user-friendly XMPP client for every device! + * + * Copyright (C) 2016-2019 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 "QXmppUri.h" +#include + +const QStringList ACTION_STRINGS = QStringList() + << "" + << "command" + << "disco" + << "invite" + << "join" + << "message" + << "pubsub" + << "recvfile" + << "register" + << "remove" + << "roster" + << "sendfile" + << "subscribe" + << "unregister" + << "unsubscribe" + << "vcard"; + +const QStringList MESSAGE_TYPE_STRINGS = QStringList() + << "error" + << "normal" + << "chat" + << "groupchat" + << "headline"; + +const QStringList MESSAGE_ATTRIBUTES = QStringList() + << "subject" + << "body" + << "thread" + << "from" + << "id" + << "type"; + +void helperToAddPair(QList> &pairs, const QString &key, + const QString &val) +{ + if (!val.isEmpty()) + pairs << qMakePair(key, val); +} + +QString helperToGetQueryItemValue(const QUrlQuery &query, const QString &key) +{ + return query.queryItemValue(key, QUrl::FullyDecoded); +} + +QXmppUri::QXmppUri() +{ +} + +/// Parses the URI from a string. + +QXmppUri::QXmppUri(QString input) +{ + // We're replacing ';' with '&', so we can reuse the QUrlQuery. + QUrl url(input.replace(";", "&")); + if (!url.isValid() || url.scheme() != "xmpp") + return; + + // set JID + setJid(url.path()); + + if (!input.contains("?")) + return; + + QUrlQuery query(url.query()); + // check that there are query items (key-value pairs) + if (!query.queryItems().size()) + return; + + m_actionType = static_cast( + ACTION_STRINGS.indexOf(query.queryItems().first().first) + ); + + switch (m_actionType) { + case Message: + m_message.setSubject(helperToGetQueryItemValue(query, "subject")); + m_message.setBody(helperToGetQueryItemValue(query, "body")); + m_message.setThread(helperToGetQueryItemValue(query, "thread")); + m_message.setId(helperToGetQueryItemValue(query, "id")); + m_message.setFrom(helperToGetQueryItemValue(query, "from")); + if (!helperToGetQueryItemValue(query, "type").isEmpty()) + m_message.setType(static_cast( + MESSAGE_TYPE_STRINGS.indexOf(helperToGetQueryItemValue(query, "type")) + )); + else + m_hasMessageType = false; + break; + default: + break; + } +} + +/// Decodes the URI to a string +/// +/// \returns Full XMPP URI + +QString QXmppUri::toString() +{ + QUrl url; + url.setScheme("xmpp"); + url.setPath(m_jid); + + // Create query items (parameters) + QUrlQuery query; + QList> queryItems; + + switch (m_actionType) { + case Message: + helperToAddPair(queryItems, "body", m_message.body()); + helperToAddPair(queryItems, "from", m_message.from()); + helperToAddPair(queryItems, "id", m_message.id()); + helperToAddPair(queryItems, "thread", m_message.thread()); + if (m_hasMessageType) + helperToAddPair(queryItems, "type", MESSAGE_TYPE_STRINGS.at( + int(m_message.type()))); + helperToAddPair(queryItems, "subject", m_message.subject()); + break; + default: + break; + } + query.setQueryItems(queryItems); + + QString output = url.toEncoded(); + if (m_actionType != None) { + // add action + output += "?"; + output += ACTION_STRINGS.at(int(m_actionType)); + + // add parameters + QString queryStr = QUrl::toPercentEncoding(query.toString().replace("&", ";"), ";="); + if (!query.isEmpty()) { + output += ";"; + output += queryStr; + } + } + + return output; +} + +/// Returns the JID this URI is about +/// +/// This can also be e.g. a MUC room in case of a Join action. + +QString QXmppUri::jid() const +{ + return m_jid; +} + +/// Sets the JID this XMPP URI links to + +void QXmppUri::setJid(const QString &jid) +{ + m_jid = jid; +} + +/// Returns the action type of this XMPP URI. +/// +/// This is None in case no action is included. + +QXmppUri::ActionType QXmppUri::actionType() const +{ + return m_actionType; +} + +/// Sets the action type of this XMPP URI, e.g. Join for an URI ending with +/// \c ?join". + +void QXmppUri::setActionType(const ActionType &queryType) +{ + m_actionType = queryType; +} + +/// In case the URI has a message query, this can be used to get the attached +/// message content directly as \c QXmppMessage. + +QXmppMessage QXmppUri::message() const +{ + return m_message; +} + +/// Sets the attached message for a Message action. +/// +/// Supported properties are: body, from, id, thread, type, subject. +/// If you want to include the message type, ensure that \c hasMessageType is +/// set to true. + +void QXmppUri::setMessage(const QXmppMessage &message) +{ + setActionType(Message); + m_message = message; +} + +/// Returns true, if the attached message's type is included. +/// +/// This is required because \c QXmppMessage has no option to set no type. + +bool QXmppUri::hasMessageType() const +{ + return m_hasMessageType; +} + +/// Sets whether to include the message's type. +/// +/// This is required because \c QXmppMessage has no option to set no type. + +void QXmppUri::setHasMessageType(bool hasMessageType) +{ + m_hasMessageType = hasMessageType; +} diff --git a/src/qxmpp-exts/QXmppUri.h b/src/qxmpp-exts/QXmppUri.h new file mode 100644 index 0000000..63fa6f7 --- /dev/null +++ b/src/qxmpp-exts/QXmppUri.h @@ -0,0 +1,85 @@ +/* + * Kaidan - A user-friendly XMPP client for every device! + * + * Copyright (C) 2016-2019 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 QXMPPURI_H +#define QXMPPURI_H + +#include + +class QXmppUri +{ +public: + enum ActionType { + None, + Command, + Disco, + Invite, + Join, + Message, + PubSub, + RecvFile, + Register, + Remove, + Roster, + SendFile, + Subscribe, + Unregister, + Unsubscribe, + VCard, + }; + + QXmppUri(); + QXmppUri(QString uri); + + QString toString(); + + QString jid() const; + void setJid(const QString &jid); + + ActionType actionType() const; + void setActionType(const ActionType &actionType); + + // 'message' query + QXmppMessage message() const; + void setMessage(const QXmppMessage&); + + bool hasMessageType() const; + void setHasMessageType(bool hasMessageType); + +private: + QString m_jid; + ActionType m_actionType = None; + + // message + QXmppMessage m_message; + bool m_hasMessageType = false; +}; + +#endif // QXMPPURI_H