diff options
123 files changed, 3447 insertions, 436 deletions
@@ -7,6 +7,9 @@ Manjeet Dahiya <manjeetdahiya@gmail.com> Jeremy LainĂ© <jeremy.laine@m4x.org> * Co-author of QXmpp. +Linus Jahn <lnj@kaidan.im> + * Current lead developer of QXmpp. + Ian Reinhart Geiser <geiseri@kde.org> * Initial author of Jabber-RPC support. diff --git a/CHANGELOG.md b/CHANGELOG.md index 96239d57..d6dce3a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +QXmpp 1.1.0 (UNRELEASED) +------------------------ + +New features: + - Add support for SCRAM-SHA-1 and SCRAM-SHA-256 (#183, @jlaine) + - Add XEP-0334: Message Processing Hints (v0.3.0) (#212, @lnjX, @jaragont, + @sam-truscott) + - Add XEP-0367: Message Attaching (v0.3.0) (#196, @lnjX) + - Add XEP-0380: Explicit Message Encryption (v0.3.0) (#199, @lnjX) + - Add XEP-0382: Spoiler messages (v0.2.0) (#195, @lnjX) + QXmpp 1.0.1 (Oct 14, 2019) -------------------------- diff --git a/CMakeLists.txt b/CMakeLists.txt index 99ab6ce9..12a11706 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,11 +1,10 @@ +cmake_minimum_required(VERSION 3.3) project(qxmpp) -cmake_minimum_required(VERSION 3.2) - set(VERSION_MAJOR 1) set(VERSION_MINOR 0) set(VERSION_PATCH 1) -set(SO_VERSION 1) +set(SO_VERSION 2) set(VERSION_STRING ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) mark_as_advanced(VERSION_MAJOR VERSION_MINOR VERSION_PATCH VERSION_STRING) @@ -1,7 +1,7 @@ [](https://travis-ci.org/qxmpp-project/qxmpp) [](https://codecov.io/gh/qxmpp-project/qxmpp) -ABOUT QXMPP +About QXmpp =========== QXmpp is a cross-platform C++ XMPP client and server library. It is written @@ -21,7 +21,7 @@ libraries such as speex and theora enable additional features. QXmpp is released under the terms of the GNU Lesser General Public License, version 2.1 or later. -BUILDING QXMPP +Building QXmpp ============== QXmpp requires Qt 5.0 or higher with SSL enabled. @@ -45,7 +45,7 @@ You can pass the following arguments to CMake: WITH_THEORA to enable theora video codec (default: false) WITH_VPX to enable vpx video codec (default: false) -INSTALLING QXMPP +Installing QXmpp ================ After building QXmpp, you can install the Headers, Libraries @@ -55,7 +55,7 @@ Install from command line: cmake --build . --target install -EXAMPLES +Examples ======== Look at the example directory for various examples. Here is a description of @@ -71,18 +71,14 @@ This is a very simple bot which echoes the message sent to it. Run this example, send it a message from a friend of this bot and you will receive the message back. This example shows how to receive and send messages. -* *GuiClient* -This is a full fledged Graphical XMPP client. This example will uses most of -the part of this library. - -DOCUMENTATION +Documentation ============= You can find the API documentation for the latest QXmpp version here: http://doc.qxmpp.org/ -SUPPORTED PLATFORMS +Supported Platforms =================== It should work on all the platforms supported by Qt. For a complete list of @@ -90,7 +86,7 @@ platforms support by Qt, see: https://doc.qt.io/qt-5/supported-platforms.html -HOW TO REPORT A BUG +How to report a bug =================== If you think you have found a bug in QXmpp, we would like to hear about @@ -99,7 +95,7 @@ is already know at: https://github.com/qxmpp-project/qxmpp/issues -DISCUSSION GROUP +Discussion Group ================ Join QXmpp Discussion Group for queries, discussions and updates. diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index 97a4e1de..b5765cd0 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -13,3 +13,7 @@ GENERATE_LATEX = NO HTML_TIMESTAMP = NO QUIET = YES RECURSIVE = YES +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = YES +PREDEFINED = "QXMPP_DEPRECATED_SINCE(major, minor)=" diff --git a/doc/xep.doc b/doc/xep.doc index 1a5bd147..27da33c3 100644 --- a/doc/xep.doc +++ b/doc/xep.doc @@ -40,11 +40,18 @@ Complete: - XEP-0308: Last Message Correction - XEP-0313: Message Archive Management - XEP-0319: Last User Interaction in Presence +- XEP-0334: Message Processing Hints (v0.3.0) - XEP-0352: Client State Indication +- XEP-0367: Message Attaching (v0.3.0) +- XEP-0380: Explicit Message Encryption (v0.3.0) +- XEP-0382: Spoiler messages (v0.2.0) Ongoing: - XEP-0009: Jabber-RPC (API is not finalized yet) - XEP-0060: Publish-Subscribe (Only basic IQ implemented) - XEP-0077: In-Band Registration (Only basic IQ implemented) +- XEP-0363: HTTP File Upload (v0.9.0) +- XEP-0369: Mediated Information eXchange (MIX) (Only IQ queries implemented) (v0.14.2) +- XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements (Only IQ queries implemented) (v0.4.0) */ diff --git a/examples/example_1_echoClient/example_1_echoClient.h b/examples/example_1_echoClient/example_1_echoClient.h index b10d74a0..b02d8394 100644 --- a/examples/example_1_echoClient/example_1_echoClient.h +++ b/examples/example_1_echoClient/example_1_echoClient.h @@ -32,8 +32,8 @@ class echoClient : public QXmppClient Q_OBJECT public: - echoClient(QObject *parent = 0); - ~echoClient(); + echoClient(QObject *parent = nullptr); + ~echoClient() override; public slots: void messageReceived(const QXmppMessage&); diff --git a/examples/example_2_rosterHandling/example_2_rosterHandling.h b/examples/example_2_rosterHandling/example_2_rosterHandling.h index b53d5451..06bcc6d1 100644 --- a/examples/example_2_rosterHandling/example_2_rosterHandling.h +++ b/examples/example_2_rosterHandling/example_2_rosterHandling.h @@ -32,8 +32,8 @@ class xmppClient : public QXmppClient Q_OBJECT public: - xmppClient(QObject *parent = 0); - ~xmppClient(); + xmppClient(QObject *parent = nullptr); + ~xmppClient() override; public slots: void clientConnected(); diff --git a/examples/example_3_transferHandling/example_3_transferHandling.cpp b/examples/example_3_transferHandling/example_3_transferHandling.cpp index f6621ba2..dc4c81c8 100644 --- a/examples/example_3_transferHandling/example_3_transferHandling.cpp +++ b/examples/example_3_transferHandling/example_3_transferHandling.cpp @@ -35,7 +35,7 @@ #include "example_3_transferHandling.h" xmppClient::xmppClient(QObject *parent) - : QXmppClient(parent), transferManager(0) + : QXmppClient(parent), transferManager(nullptr) { bool check; Q_UNUSED(check); diff --git a/examples/example_3_transferHandling/example_3_transferHandling.h b/examples/example_3_transferHandling/example_3_transferHandling.h index 969f7a80..e8ade267 100644 --- a/examples/example_3_transferHandling/example_3_transferHandling.h +++ b/examples/example_3_transferHandling/example_3_transferHandling.h @@ -33,7 +33,7 @@ class xmppClient : public QXmppClient Q_OBJECT public: - xmppClient(QObject *parent = 0); + xmppClient(QObject *parent = nullptr); void setRecipient(const QString &recipient); private slots: diff --git a/examples/example_5_rpcInterface/remoteinterface.h b/examples/example_5_rpcInterface/remoteinterface.h index cc1fb9e8..9c63d3f8 100644 --- a/examples/example_5_rpcInterface/remoteinterface.h +++ b/examples/example_5_rpcInterface/remoteinterface.h @@ -7,9 +7,9 @@ class RemoteInterface : public QXmppInvokable { Q_OBJECT public: - RemoteInterface(QObject *parent = 0); + RemoteInterface(QObject *parent = nullptr); - bool isAuthorized( const QString &jid ) const; + bool isAuthorized( const QString &jid ) const override; // RPC Interface public slots: diff --git a/examples/example_6_rpcClient/rpcClient.h b/examples/example_6_rpcClient/rpcClient.h index 126e8568..d68b7382 100644 --- a/examples/example_6_rpcClient/rpcClient.h +++ b/examples/example_6_rpcClient/rpcClient.h @@ -35,8 +35,8 @@ class rpcClient : public QXmppClient Q_OBJECT public: - rpcClient(QObject *parent = 0); - ~rpcClient(); + rpcClient(QObject *parent = nullptr); + ~rpcClient() override; private slots: void slotInvokeRemoteMethod(); diff --git a/examples/example_7_archiveHandling/example_7_archiveHandling.h b/examples/example_7_archiveHandling/example_7_archiveHandling.h index 70623e8a..a1cc5f7c 100644 --- a/examples/example_7_archiveHandling/example_7_archiveHandling.h +++ b/examples/example_7_archiveHandling/example_7_archiveHandling.h @@ -43,8 +43,8 @@ public: PageBackwards }; - xmppClient(QObject *parent = 0); - ~xmppClient(); + xmppClient(QObject *parent = nullptr); + ~xmppClient() override; void setPageDirection(PageDirection direction); void setPageSize(int size); diff --git a/examples/example_8_server/example_8_server.cpp b/examples/example_8_server/example_8_server.cpp index a9346a58..f1d00cef 100644 --- a/examples/example_8_server/example_8_server.cpp +++ b/examples/example_8_server/example_8_server.cpp @@ -33,7 +33,7 @@ class passwordChecker : public QXmppPasswordChecker { /// Retrieves the password for the given username. - QXmppPasswordReply::Error getPassword(const QXmppPasswordRequest &request, QString &password) + QXmppPasswordReply::Error getPassword(const QXmppPasswordRequest &request, QString &password) override { if (request.username() == USERNAME) { @@ -45,7 +45,7 @@ class passwordChecker : public QXmppPasswordChecker }; /// Returns true as we implemented getPassword(). - bool hasGetPassword() const + bool hasGetPassword() const override { return true; }; diff --git a/examples/example_9_vCard/example_9_vCard.h b/examples/example_9_vCard/example_9_vCard.h index 854baba7..24fc4b6c 100644 --- a/examples/example_9_vCard/example_9_vCard.h +++ b/examples/example_9_vCard/example_9_vCard.h @@ -34,8 +34,8 @@ class xmppClient : public QXmppClient Q_OBJECT public: - xmppClient(QObject *parent = 0); - ~xmppClient(); + xmppClient(QObject *parent = nullptr); + ~xmppClient() override; public slots: void clientConnected(); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 976f069e..6af69786 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,12 +19,15 @@ set(INSTALL_HEADER_FILES base/QXmppDiscoveryIq.h base/QXmppElement.h base/QXmppEntityTimeIq.h + base/QXmppHttpUploadIq.h base/QXmppIbbIq.h base/QXmppIq.h base/QXmppJingleIq.h base/QXmppLogger.h base/QXmppMamIq.h base/QXmppMessage.h + base/QXmppMixIq.h + base/QXmppMixItem.h base/QXmppMucIq.h base/QXmppNonSASLAuth.h base/QXmppPingIq.h @@ -93,12 +96,15 @@ set(SOURCE_FILES base/QXmppDiscoveryIq.cpp base/QXmppElement.cpp base/QXmppEntityTimeIq.cpp + base/QXmppHttpUploadIq.cpp base/QXmppIbbIq.cpp base/QXmppIq.cpp base/QXmppJingleIq.cpp base/QXmppLogger.cpp base/QXmppMamIq.cpp base/QXmppMessage.cpp + base/QXmppMixIq.cpp + base/QXmppMixItem.cpp base/QXmppMucIq.cpp base/QXmppNonSASLAuth.cpp base/QXmppPingIq.cpp diff --git a/src/base/QXmppArchiveIq.h b/src/base/QXmppArchiveIq.h index 1f82174a..a631e096 100644 --- a/src/base/QXmppArchiveIq.h +++ b/src/base/QXmppArchiveIq.h @@ -111,8 +111,8 @@ public: static bool isArchiveChatIq(const QDomElement &element); protected: - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: @@ -153,8 +153,8 @@ public: protected: /// \cond - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: @@ -186,8 +186,8 @@ public: static bool isArchiveRemoveIq(const QDomElement &element); protected: - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: @@ -218,8 +218,8 @@ public: static bool isArchiveRetrieveIq(const QDomElement &element); protected: - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: @@ -239,8 +239,8 @@ public: static bool isArchivePrefIq(const QDomElement &element); protected: - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond }; diff --git a/src/base/QXmppBindIq.h b/src/base/QXmppBindIq.h index c1434d54..fec856c9 100644 --- a/src/base/QXmppBindIq.h +++ b/src/base/QXmppBindIq.h @@ -48,8 +48,8 @@ public: protected: /// \cond - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: diff --git a/src/base/QXmppByteStreamIq.h b/src/base/QXmppByteStreamIq.h index 67a25fea..c94f1d9b 100644 --- a/src/base/QXmppByteStreamIq.h +++ b/src/base/QXmppByteStreamIq.h @@ -78,8 +78,8 @@ public: protected: /// \cond - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: diff --git a/src/base/QXmppCodec_p.h b/src/base/QXmppCodec_p.h index 8ee51f4f..d7f80d7e 100644 --- a/src/base/QXmppCodec_p.h +++ b/src/base/QXmppCodec_p.h @@ -60,8 +60,8 @@ class QXmppG711aCodec : public QXmppCodec public: QXmppG711aCodec(int clockrate); - qint64 encode(QDataStream &input, QDataStream &output); - qint64 decode(QDataStream &input, QDataStream &output); + qint64 encode(QDataStream &input, QDataStream &output) override; + qint64 decode(QDataStream &input, QDataStream &output) override; private: int m_frequency; @@ -76,8 +76,8 @@ class QXmppG711uCodec : public QXmppCodec public: QXmppG711uCodec(int clockrate); - qint64 encode(QDataStream &input, QDataStream &output); - qint64 decode(QDataStream &input, QDataStream &output); + qint64 encode(QDataStream &input, QDataStream &output) override; + qint64 decode(QDataStream &input, QDataStream &output) override; private: int m_frequency; diff --git a/src/base/QXmppConstants.cpp b/src/base/QXmppConstants.cpp index 513476de..92ff8689 100644 --- a/src/base/QXmppConstants.cpp +++ b/src/base/QXmppConstants.cpp @@ -37,6 +37,8 @@ const char* ns_stanza = "urn:ietf:params:xml:ns:xmpp-stanzas"; const char* ns_rpc = "jabber:iq:rpc"; // XEP-0020: Feature Negotiation const char* ns_feature_negotiation = "http://jabber.org/protocol/feature-neg"; +// XEP-0027: Current Jabber OpenPGP Usage +const char* ns_legacy_openpgp = "jabber:x:encrypted"; // XEP-0030: Service Discovery const char* ns_disco_info = "http://jabber.org/protocol/disco#info"; const char* ns_disco_items = "http://jabber.org/protocol/disco#items"; @@ -63,6 +65,7 @@ const char* ns_oob = "jabber:x:oob"; const char *ns_xhtml_im = "http://jabber.org/protocol/xhtml-im"; // XEP-0077: In-Band Registration const char* ns_register = "jabber:iq:register"; +const char* ns_register_feature = "http://jabber.org/features/iq-register"; // XEP-0078: Non-SASL Authentication const char* ns_auth = "jabber:iq:auth"; const char* ns_authFeature = "http://jabber.org/features/iq-auth"; @@ -130,5 +133,34 @@ const char* ns_mam = "urn:xmpp:mam:1"; const char* ns_idle = "urn:xmpp:idle:1"; // XEP-0333: Chat Markers const char* ns_chat_markers = "urn:xmpp:chat-markers:0"; +// XEP-0334: Message Processing Hints +const char* ns_message_processing_hints = "urn:xmpp:hints"; // XEP-0352: Client State Indication const char* ns_csi = "urn:xmpp:csi:0"; +// XEP-0363: HTTP File Upload +const char* ns_http_upload = "urn:xmpp:http:upload:0"; +// XEP-0364: Current Off-the-Record Messaging Usage +const char* ns_otr = "urn:xmpp:otr:0"; +// XEP-0367: Message Attaching +const char* ns_message_attaching = "urn:xmpp:message-attaching:1"; +// XEP-0369: Mediated Information eXchange (MIX) +const char* ns_mix = "urn:xmpp:mix:core:1"; +const char* ns_mix_create_channel = "urn:xmpp:mix:core:1#create-channel"; +const char* ns_mix_searchable = "urn:xmpp:mix:core:1#searchable"; +const char* ns_mix_node_messages = "urn:xmpp:mix:nodes:messages"; +const char* ns_mix_node_participants = "urn:xmpp:mix:nodes:participants"; +const char* ns_mix_node_presence = "urn:xmpp:mix:nodes:presence"; +const char* ns_mix_node_config = "urn:xmpp:mix:nodes:config"; +const char* ns_mix_node_info = "urn:xmpp:mix:nodes:info"; +// XEP-0373: OpenPGP for XMPP +const char* ns_ox = "urn:xmpp:openpgp:0"; +// XEP-0380: Explicit Message Encryption +const char* ns_eme = "urn:xmpp:eme:0"; +// XEP-0382: Spoiler messages +const char* ns_spoiler = "urn:xmpp:spoiler:0"; +// XEP-0384: OMEMO Encryption +const char* ns_omemo = "eu.siacs.conversations.axolotl"; +// XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements +const char* ns_mix_pam = "urn:xmpp:mix:pam:1"; +const char* ns_mix_roster = "urn:xmpp:mix:roster:0"; +const char* ns_mix_presence = "urn:xmpp:presence:0"; diff --git a/src/base/QXmppConstants_p.h b/src/base/QXmppConstants_p.h index 1de6355d..ba39ee05 100644 --- a/src/base/QXmppConstants_p.h +++ b/src/base/QXmppConstants_p.h @@ -49,6 +49,8 @@ extern const char* ns_stanza; extern const char* ns_rpc; // XEP-0020: Feature Negotiation extern const char* ns_feature_negotiation; +// XEP-0027: Current Jabber OpenPGP Usage +extern const char* ns_legacy_openpgp; // XEP-0030: Service Discovery extern const char* ns_disco_info; extern const char* ns_disco_items; @@ -75,6 +77,7 @@ extern const char* ns_oob; extern const char *ns_xhtml_im; // XEP-0077: In-Band Registration extern const char* ns_register; +extern const char* ns_register_feature; // XEP-0078: Non-SASL Authentication extern const char* ns_auth; extern const char* ns_authFeature; @@ -142,7 +145,36 @@ extern const char* ns_mam; extern const char* ns_idle; // XEP-0333: Char Markers extern const char* ns_chat_markers; +// XEP-0334: Message Processing Hints: +extern const char* ns_message_processing_hints; // XEP-0352: Client State Indication extern const char* ns_csi; +// XEP-0363: HTTP File Upload +extern const char* ns_http_upload; +// XEP-0364: Current Off-the-Record Messaging Usage +extern const char* ns_otr; +// XEP-0367: Message Attaching +extern const char* ns_message_attaching; +// XEP-0369: Mediated Information eXchange (MIX) +extern const char* ns_mix; +extern const char* ns_mix_create_channel; +extern const char* ns_mix_searchable; +extern const char* ns_mix_node_messages; +extern const char* ns_mix_node_participants; +extern const char* ns_mix_node_presence; +extern const char* ns_mix_node_config; +extern const char* ns_mix_node_info; +// XEP-0373: OpenPGP for XMPP +extern const char* ns_ox; +// XEP-0380: Explicit Message Encryption +extern const char* ns_eme; +// XEP-0382: Spoiler messages +extern const char* ns_spoiler; +// XEP-0384: OMEMO Encryption +extern const char* ns_omemo; +// XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements +extern const char* ns_mix_pam; +extern const char* ns_mix_roster; +extern const char* ns_mix_presence; #endif // QXMPPCONSTANTS_H diff --git a/src/base/QXmppDataForm.cpp b/src/base/QXmppDataForm.cpp index b500785c..0e909259 100644 --- a/src/base/QXmppDataForm.cpp +++ b/src/base/QXmppDataForm.cpp @@ -46,7 +46,7 @@ static field_type field_types[] = { {QXmppDataForm::Field::TextMultiField, "text-multi"}, {QXmppDataForm::Field::TextPrivateField, "text-private"}, {QXmppDataForm::Field::TextSingleField, "text-single"}, - {static_cast<QXmppDataForm::Field::Type>(-1), NULL}, + {static_cast<QXmppDataForm::Field::Type>(-1), nullptr}, }; class QXmppDataFormMediaPrivate : public QSharedData diff --git a/src/base/QXmppDiscoveryIq.cpp b/src/base/QXmppDiscoveryIq.cpp index 2f8af810..98fa195e 100644 --- a/src/base/QXmppDiscoveryIq.cpp +++ b/src/base/QXmppDiscoveryIq.cpp @@ -199,9 +199,9 @@ QByteArray QXmppDiscoveryIq::verificationString() const { QString S; QList<QXmppDiscoveryIq::Identity> sortedIdentities = m_identities; - qSort(sortedIdentities.begin(), sortedIdentities.end(), identityLessThan); + std::sort(sortedIdentities.begin(), sortedIdentities.end(), identityLessThan); QStringList sortedFeatures = m_features; - qSort(sortedFeatures); + std::sort(sortedFeatures.begin(), sortedFeatures.end()); sortedFeatures.removeDuplicates(); foreach (const QXmppDiscoveryIq::Identity &identity, sortedIdentities) S += QString("%1/%2/%3/%4<").arg(identity.category(), identity.type(), identity.language(), identity.name()); @@ -219,7 +219,7 @@ QByteArray QXmppDiscoveryIq::verificationString() const S += field.value().toString() + QLatin1String("<"); QStringList keys = fieldMap.keys(); - qSort(keys); + std::sort(keys.begin(), keys.end()); foreach (const QString &key, keys) { const QXmppDataForm::Field field = fieldMap.value(key); S += key + QLatin1String("<"); diff --git a/src/base/QXmppDiscoveryIq.h b/src/base/QXmppDiscoveryIq.h index 8de64dc7..edc2669d 100644 --- a/src/base/QXmppDiscoveryIq.h +++ b/src/base/QXmppDiscoveryIq.h @@ -99,8 +99,8 @@ public: protected: /// \cond - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: diff --git a/src/base/QXmppElement.cpp b/src/base/QXmppElement.cpp index cd977817..7d0e33fa 100644 --- a/src/base/QXmppElement.cpp +++ b/src/base/QXmppElement.cpp @@ -46,12 +46,12 @@ public: }; QXmppElementPrivate::QXmppElementPrivate() - : counter(1), parent(NULL) + : counter(1), parent(nullptr) { } QXmppElementPrivate::QXmppElementPrivate(const QDomElement &element) - : counter(1), parent(NULL) + : counter(1), parent(nullptr) { if (element.isNull()) return; @@ -207,7 +207,7 @@ void QXmppElement::removeChild(const QXmppElement &child) d->children.removeAll(child.d); child.d->counter.deref(); - child.d->parent = NULL; + child.d->parent = nullptr; } QString QXmppElement::tagName() const diff --git a/src/base/QXmppElement.h b/src/base/QXmppElement.h index e7cc5596..a3647d0d 100644 --- a/src/base/QXmppElement.h +++ b/src/base/QXmppElement.h @@ -34,7 +34,7 @@ class QDomElement; class QXmppElement; class QXmppElementPrivate; -typedef QList<QXmppElement> QXmppElementList; +using QXmppElementList = QList<QXmppElement>; class QXMPP_EXPORT QXmppElement { public: diff --git a/src/base/QXmppEntityTimeIq.h b/src/base/QXmppEntityTimeIq.h index a5b7e2d5..1379ebce 100644 --- a/src/base/QXmppEntityTimeIq.h +++ b/src/base/QXmppEntityTimeIq.h @@ -44,8 +44,8 @@ public: protected: /// \cond - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: diff --git a/src/base/QXmppGlobal.h.in b/src/base/QXmppGlobal.h.in index f562eb04..992e02b1 100644 --- a/src/base/QXmppGlobal.h.in +++ b/src/base/QXmppGlobal.h.in @@ -4,6 +4,7 @@ * Author: * Manjeet Dahiya * Niels Ole Salscheider + * Linus Jahn * * Source: * https://github.com/qxmpp-project/qxmpp @@ -52,5 +53,14 @@ inline QLatin1String QXmppVersion() return QLatin1String("@VERSION_STRING@"); } +// This sets which deprecated functions should still be usable +// It works exactly like QT_DISABLE_DEPRECATED_BEFORE +#ifndef QXMPP_DISABLE_DEPRECATED_BEFORE +# define QXMPP_DISABLE_DEPRECATED_BEFORE 0x@VERSION_MAJOR@ << 16 +#endif + +// This works exactly like QT_DEPRECATED_SINCE, but checks QXMPP_DISABLE_DEPRECATED_BEFORE instead. +#define QXMPP_DEPRECATED_SINCE(major, minor) (QT_VERSION_CHECK(major, minor, 0) > QXMPP_DISABLE_DEPRECATED_BEFORE) + #endif //QXMPPGLOBAL_H diff --git a/src/base/QXmppHttpUploadIq.cpp b/src/base/QXmppHttpUploadIq.cpp new file mode 100644 index 00000000..4754cd3e --- /dev/null +++ b/src/base/QXmppHttpUploadIq.cpp @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2008-2019 The QXmpp developers + * + * Authors: + * Linus Jahn <lnj@kaidan.im> + * + * Source: + * https://github.com/qxmpp-project/qxmpp + * + * This file is a part of QXmpp library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + */ + +#include <QDomElement> +#include <QMimeDatabase> + +#include "QXmppHttpUploadIq.h" +#include "QXmppConstants_p.h" + +class QXmppHttpUploadRequestIqPrivate +{ +public: + QString fileName; + qint64 size; + QMimeType contentType; +}; + +QXmppHttpUploadRequestIq::QXmppHttpUploadRequestIq() + : d(new QXmppHttpUploadRequestIqPrivate()) +{ +} + +QXmppHttpUploadRequestIq::~QXmppHttpUploadRequestIq() +{ + delete d; +} + +/// Returns the file name of the file to be uploaded. + +QString QXmppHttpUploadRequestIq::fileName() const +{ + return d->fileName; +} + +/// Sets the file name. The upload service will use this to create the upload/ +/// download URLs. This may also differ from the actual file name to get a +/// different URL. It's not required to replace special characters (this is the +/// server's job). + +void QXmppHttpUploadRequestIq::setFileName(const QString &fileName) +{ + d->fileName = fileName; +} + +/// Returns the file's size in bytes. + +qint64 QXmppHttpUploadRequestIq::size() const +{ + return d->size; +} + +/// Sets the file's size in bytes. + +void QXmppHttpUploadRequestIq::setSize(qint64 size) +{ + d->size = size; +} + +/// Returns the (optional) MIME-type of the file. + +QMimeType QXmppHttpUploadRequestIq::contentType() const +{ + return d->contentType; +} + +/// Sets the MIME-type of the file. This is optional. + +void QXmppHttpUploadRequestIq::setContentType(const QMimeType &type) +{ + d->contentType = type; +} + +bool QXmppHttpUploadRequestIq::isHttpUploadRequestIq(const QDomElement &element) +{ + if (element.tagName() == "iq") { + QDomElement request = element.firstChildElement("request"); + return !request.isNull() && request.namespaceURI() == ns_http_upload; + } + return false; +} + +/// \cond +void QXmppHttpUploadRequestIq::parseElementFromChild(const QDomElement &element) +{ + QDomElement request = element.firstChildElement("request"); + d->fileName = request.attribute("filename"); + d->size = request.attribute("size").toLongLong(); + if (request.hasAttribute("content-type")) { + QMimeDatabase mimeDb; + QMimeType type = mimeDb.mimeTypeForName(request.attribute("content-type")); + if (!type.isDefault() && type.isValid()) + d->contentType = type; + } +} + +void QXmppHttpUploadRequestIq::toXmlElementFromChild(QXmlStreamWriter *writer) const +{ + writer->writeStartElement("request"); + writer->writeAttribute("xmlns", ns_http_upload); + // filename and size are required + writer->writeAttribute("filename", d->fileName); + writer->writeAttribute("size", QString::number(d->size)); + // content-type is optional + if (!d->contentType.isDefault() && d->contentType.isValid()) + writer->writeAttribute("content-type", d->contentType.name()); + writer->writeEndElement(); +} +/// \endcond + +class QXmppHttpUploadSlotIqPrivate +{ +public: + QUrl putUrl; + QUrl getUrl; + QMap<QString, QString> putHeaders; +}; + +QXmppHttpUploadSlotIq::QXmppHttpUploadSlotIq() + : d(new QXmppHttpUploadSlotIqPrivate()) +{ +} + +QXmppHttpUploadSlotIq::~QXmppHttpUploadSlotIq() +{ + delete d; +} + +/// Returns the URL for uploading via. HTTP PUT. + +QUrl QXmppHttpUploadSlotIq::putUrl() const +{ + return d->putUrl; +} + +/// Sets the URL the client should use for uploading. + +void QXmppHttpUploadSlotIq::setPutUrl(const QUrl &putUrl) +{ + d->putUrl = putUrl; +} + +/// Returns the URL to where the file will be served. + +QUrl QXmppHttpUploadSlotIq::getUrl() const +{ + return d->getUrl; +} + +/// Sets the download URL. + +void QXmppHttpUploadSlotIq::setGetUrl(const QUrl &getUrl) +{ + d->getUrl = getUrl; +} + +/// Returns a map of header fields (header name -> value) that need to be +/// included in the PUT (upload) request. This won't contain any other fields +/// than: "Authorization", "Cookie" or "Expires". + +QMap<QString, QString> QXmppHttpUploadSlotIq::putHeaders() const +{ + return d->putHeaders; +} + +/// Sets the header fields the client needs to include in the PUT (upload) +/// request. All fields other than "Authorization", "Cookie" or "Expires" will +/// be ignored. + +void QXmppHttpUploadSlotIq::setPutHeaders(const QMap<QString, QString> &putHeaders) +{ + d->putHeaders.clear(); + for (QString &name : putHeaders.keys()) { + if (name == "Authorization" || name == "Cookie" || name == "Expires") + d->putHeaders[name] = putHeaders[name]; + } +} + +bool QXmppHttpUploadSlotIq::isHttpUploadSlotIq(const QDomElement &element) +{ + if (element.tagName() == "iq") { + QDomElement slot = element.firstChildElement("slot"); + return !slot.isNull() && slot.namespaceURI() == ns_http_upload; + } + return false; +} + +/// \cond +void QXmppHttpUploadSlotIq::parseElementFromChild(const QDomElement &element) +{ + QDomElement slot = element.firstChildElement("slot"); + QDomElement put = slot.firstChildElement("put"); + d->getUrl = QUrl::fromEncoded(slot.firstChildElement("get").attribute("url").toUtf8()); + d->putUrl = QUrl::fromEncoded(put.attribute("url").toUtf8()); + if (put.hasChildNodes()) { + QMap<QString, QString> headers; + QDomElement header = put.firstChildElement("header"); + while (!header.isNull()) { + headers[header.attribute("name")] = header.text(); + + header = header.nextSiblingElement("header"); + } + + setPutHeaders(headers); + } +} + +void QXmppHttpUploadSlotIq::toXmlElementFromChild(QXmlStreamWriter *writer) const +{ + writer->writeStartElement("slot"); + writer->writeAttribute("xmlns", ns_http_upload); + + writer->writeStartElement("put"); + writer->writeAttribute("url", d->putUrl.toEncoded()); + if (!d->putHeaders.isEmpty()) { + for (const QString &name : d->putHeaders.keys()) { + writer->writeStartElement("header"); + writer->writeAttribute("name", name); + writer->writeCharacters(d->putHeaders[name]); + writer->writeEndElement(); + } + } + writer->writeEndElement(); + + writer->writeStartElement("get"); + writer->writeAttribute("url", d->getUrl.toEncoded()); + writer->writeEndElement(); + + writer->writeEndElement(); +} +/// \endcond diff --git a/src/base/QXmppHttpUploadIq.h b/src/base/QXmppHttpUploadIq.h new file mode 100644 index 00000000..63c2da23 --- /dev/null +++ b/src/base/QXmppHttpUploadIq.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2008-2019 The QXmpp developers + * + * Authors: + * Linus Jahn <lnj@kaidan.im> + * + * Source: + * https://github.com/qxmpp-project/qxmpp + * + * This file is a part of QXmpp library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + */ + +#ifndef QXMPPHTTPUPLOADIQ_H +#define QXMPPHTTPUPLOADIQ_H + +#include <QMap> +#include <QMimeType> +#include <QUrl> + +#include "QXmppIq.h" + +class QXmppHttpUploadRequestIqPrivate; +class QXmppHttpUploadSlotIqPrivate; + +/// \brief Represents an HTTP File Upload IQ for requesting an upload slot as +/// defined by XEP-0363: HTTP File Upload [v0.9.0]. +/// +/// \ingroup Stanzas + +class QXMPP_EXPORT QXmppHttpUploadRequestIq : public QXmppIq +{ +public: + QXmppHttpUploadRequestIq(); + ~QXmppHttpUploadRequestIq() override; + + QString fileName() const; + void setFileName(const QString &filename); + + qint64 size() const; + void setSize(qint64 size); + + QMimeType contentType() const; + void setContentType(const QMimeType &type); + + static bool isHttpUploadRequestIq(const QDomElement &element); + +protected: + /// \cond + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; + /// \endcond + +private: + QXmppHttpUploadRequestIqPrivate* const d; +}; + +/// \brief Represents an HTTP File Upload IQ result for receiving an upload slot as +/// defined by XEP-0363: HTTP File Upload [v0.9.0]. +/// +/// \ingroup Stanzas + +class QXMPP_EXPORT QXmppHttpUploadSlotIq : public QXmppIq +{ +public: + QXmppHttpUploadSlotIq(); + ~QXmppHttpUploadSlotIq() override; + + QUrl putUrl() const; + void setPutUrl(const QUrl &putUrl); + + QUrl getUrl() const; + void setGetUrl(const QUrl &getUrl); + + QMap<QString, QString> putHeaders() const; + void setPutHeaders(const QMap<QString, QString> &putHeaders); + + static bool isHttpUploadSlotIq(const QDomElement &element); + +protected: + /// \cond + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; + /// \endcond + +private: + QXmppHttpUploadSlotIqPrivate* const d; +}; + +#endif // QXMPPHTTPUPLOADIQ_H diff --git a/src/base/QXmppIbbIq.h b/src/base/QXmppIbbIq.h index 732936c7..92828095 100644 --- a/src/base/QXmppIbbIq.h +++ b/src/base/QXmppIbbIq.h @@ -42,8 +42,8 @@ public: protected: /// \cond - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: @@ -63,8 +63,8 @@ public: protected: /// \cond - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: @@ -89,8 +89,8 @@ public: protected: /// \cond - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: diff --git a/src/base/QXmppIq.h b/src/base/QXmppIq.h index c147bb78..e7451e7c 100644 --- a/src/base/QXmppIq.h +++ b/src/base/QXmppIq.h @@ -53,18 +53,18 @@ public: QXmppIq(QXmppIq::Type type = QXmppIq::Get); QXmppIq(const QXmppIq &other); - ~QXmppIq(); + ~QXmppIq() override; QXmppIq& operator=(const QXmppIq &other); QXmppIq::Type type() const; void setType(QXmppIq::Type); - bool isXmppStanza() const; + bool isXmppStanza() const override; /// \cond - void parse(const QDomElement &element); - void toXml(QXmlStreamWriter *writer) const; + void parse(const QDomElement &element) override; + void toXml(QXmlStreamWriter *writer) const override; protected: virtual void parseElementFromChild(const QDomElement &element); diff --git a/src/base/QXmppJingleIq.cpp b/src/base/QXmppJingleIq.cpp index c652887e..4fcd5bec 100644 --- a/src/base/QXmppJingleIq.cpp +++ b/src/base/QXmppJingleIq.cpp @@ -576,7 +576,7 @@ QString QXmppJingleIq::Content::toSdp() const QHostAddress localRtpAddress = QHostAddress::Any; quint16 localRtpPort = 0; QList<QXmppJingleCandidate> sortedCandidates = d->transportCandidates; - qSort(sortedCandidates.begin(), sortedCandidates.end(), candidateLessThan); + std::sort(sortedCandidates.begin(), sortedCandidates.end(), candidateLessThan); foreach (const QXmppJingleCandidate &candidate, sortedCandidates) { if (candidate.component() == RTP_COMPONENT) { localRtpAddress = candidate.host(); diff --git a/src/base/QXmppJingleIq.h b/src/base/QXmppJingleIq.h index ea5d84ef..75a37e04 100644 --- a/src/base/QXmppJingleIq.h +++ b/src/base/QXmppJingleIq.h @@ -138,7 +138,7 @@ public: void parse(const QDomElement &element); void toXml(QXmlStreamWriter *writer) const; - static QXmppJingleCandidate::Type typeFromString(const QString &typeStr, bool *ok = 0); + static QXmppJingleCandidate::Type typeFromString(const QString &typeStr, bool *ok = nullptr); static QString typeToString(QXmppJingleCandidate::Type type); /// \endcond @@ -289,7 +289,7 @@ public: QXmppJingleIq(); QXmppJingleIq(const QXmppJingleIq &other); - ~QXmppJingleIq(); + ~QXmppJingleIq() override; QXmppJingleIq& operator=(const QXmppJingleIq &other); @@ -322,8 +322,8 @@ public: protected: /// \cond - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: diff --git a/src/base/QXmppLogger.cpp b/src/base/QXmppLogger.cpp index 4b602b38..f429b969 100644 --- a/src/base/QXmppLogger.cpp +++ b/src/base/QXmppLogger.cpp @@ -32,7 +32,7 @@ #include "QXmppLogger.h" -QXmppLogger* QXmppLogger::m_logger = 0; +QXmppLogger* QXmppLogger::m_logger = nullptr; static const char *typeName(QXmppLogger::MessageType type) { @@ -116,7 +116,7 @@ public: QXmppLoggerPrivate::QXmppLoggerPrivate() : loggingType(QXmppLogger::NoLogging) - , logFile(0) + , logFile(nullptr) , logFilePath("QXmppClientLog.log") , messageTypes(QXmppLogger::AnyMessage) { @@ -268,7 +268,7 @@ void QXmppLogger::reopen() { if (d->logFile) { delete d->logFile; - d->logFile = 0; + d->logFile = nullptr; } } diff --git a/src/base/QXmppLogger.h b/src/base/QXmppLogger.h index a75623b1..912dbd4c 100644 --- a/src/base/QXmppLogger.h +++ b/src/base/QXmppLogger.h @@ -74,8 +74,8 @@ public: }; Q_DECLARE_FLAGS(MessageTypes, MessageType) - QXmppLogger(QObject *parent = 0); - ~QXmppLogger(); + QXmppLogger(QObject *parent = nullptr); + ~QXmppLogger() override; static QXmppLogger* getLogger(); @@ -113,11 +113,11 @@ class QXMPP_EXPORT QXmppLoggable : public QObject Q_OBJECT public: - QXmppLoggable(QObject *parent = 0); + QXmppLoggable(QObject *parent = nullptr); protected: /// \cond - virtual void childEvent(QChildEvent *event); + void childEvent(QChildEvent *event) override; /// \endcond /// Logs a debugging message. diff --git a/src/base/QXmppMamIq.h b/src/base/QXmppMamIq.h index 78a6f943..65f0b3f5 100644 --- a/src/base/QXmppMamIq.h +++ b/src/base/QXmppMamIq.h @@ -47,8 +47,8 @@ public: static bool isMamQueryIq(const QDomElement &element); protected: - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; private: QXmppDataForm m_form; @@ -72,8 +72,8 @@ public: static bool isMamResultIq(const QDomElement &element); protected: - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; private: QXmppResultSetReply m_resultSetReply; diff --git a/src/base/QXmppMessage.cpp b/src/base/QXmppMessage.cpp index f5869bf4..9806edf0 100644 --- a/src/base/QXmppMessage.cpp +++ b/src/base/QXmppMessage.cpp @@ -55,6 +55,31 @@ static const char* marker_types[] = { "acknowledged" }; +static const QStringList ENCRYPTION_NAMESPACES = { + QString(), + QString(), + ns_otr, + ns_legacy_openpgp, + ns_ox, + ns_omemo +}; + +static const QStringList HINT_TYPES = { + QStringLiteral("no-permanent-store"), + QStringLiteral("no-store"), + QStringLiteral("no-copy"), + QStringLiteral("store") +}; + +static const QStringList ENCRYPTION_NAMES = { + QString(), + QString(), + QStringLiteral("OTR"), + QStringLiteral("Legacy OpenPGP"), + QStringLiteral("OpenPGP for XMPP (OX)"), + QStringLiteral("OMEMO") +}; + static const char *ns_xhtml = "http://www.w3.org/1999/xhtml"; enum StampType @@ -102,6 +127,24 @@ public: // XEP-0308: Last Message Correction QString replaceId; + + // XEP-0334: Message Processing Hints + quint8 hints; + + // XEP-0367: Message Attaching + QString attachId; + + // XEP-0369: Mediated Information eXchange (MIX) + QString mixUserJid; + QString mixUserNick; + + // XEP-0380: Explicit Message Encryption + QString encryptionMethod; + QString encryptionName; + + // XEP-0382: Spoiler messages + bool isSpoiler = false; + QString spoilerHint; }; /// Constructs a QXmppMessage. @@ -112,7 +155,7 @@ public: /// \param thread QXmppMessage::QXmppMessage(const QString& from, const QString& to, const - QString& body, const QString& thread) + QString& body, const QString& thread) : QXmppStanza(from, to) , d(new QXmppMessagePrivate) { @@ -128,6 +171,8 @@ QXmppMessage::QXmppMessage(const QString& from, const QString& to, const d->marker = NoMarker; d->privatemsg = false; + + d->hints = 0; } /// Constructs a copy of \a other. @@ -513,6 +558,199 @@ void QXmppMessage::setReplaceId(const QString &replaceId) d->replaceId = replaceId; } +/// Returns true if the message contains the hint passed, as defined in +/// XEP-0334: Message Processing Hints + +bool QXmppMessage::hasHint(const Hint hint) const +{ + return d->hints & hint; +} + +/// Adds a hint to the message, as defined in XEP-0334: Message Processing +/// Hints + +void QXmppMessage::addHint(const Hint hint) +{ + d->hints |= hint; +} + +/// Removes a hint from the message, as defined in XEP-0334: Message Processing +/// Hints + +void QXmppMessage::removeHint(const Hint hint) +{ + d->hints &= ~hint; +} + +/// Removes all hints from the message, as defined in XEP-0334: Message +/// Processing Hints + +void QXmppMessage::removeAllHints() +{ + d->hints = 0; +} + +/// Returns the message id this message is linked/attached to. See XEP-0367: +/// Message Attaching for details. + +QString QXmppMessage::attachId() const +{ + return d->attachId; +} + +/// Sets the id of the attached message as in XEP-0367: Message Attaching. This +/// can be used for a "reply to" or "reaction" function. +/// +/// The used message id depends on the message context, see the Business rules +/// section of the XEP for details about when to use which id. + +void QXmppMessage::setAttachId(const QString &attachId) +{ + d->attachId = attachId; +} + +/// Returns the actual JID of a MIX channel participant. + +QString QXmppMessage::mixUserJid() const +{ + return d->mixUserJid; +} + +/// Sets the actual JID of a MIX channel participant. + +void QXmppMessage::setMixUserJid(const QString& mixUserJid) +{ + d->mixUserJid = mixUserJid; +} + +/// Returns the MIX participant's nickname. + +QString QXmppMessage::mixUserNick() const +{ + return d->mixUserNick; +} + +/// Sets the MIX participant's nickname. + +void QXmppMessage::setMixUserNick(const QString& mixUserNick) +{ + d->mixUserNick = mixUserNick; +} + +/// Returns the encryption method this message is advertised to be encrypted +/// with. +/// +/// \note QXmppMessage::NoEncryption does not necesserily mean that the message +/// is not encrypted; it may also be that the author of the message does not +/// support XEP-0380: Explicit Message Encryption. +/// +/// \note If this returns QXmppMessage::UnknownEncryption, you can still get +/// the namespace of the encryption with \c encryptionMethodNs() and possibly +/// also a name with \c encryptionName(). + +QXmppMessage::EncryptionMethod QXmppMessage::encryptionMethod() const +{ + if (d->encryptionMethod.isEmpty()) + return QXmppMessage::NoEncryption; + + int index = ENCRYPTION_NAMESPACES.indexOf(d->encryptionMethod); + if (index < 0) + return QXmppMessage::UnknownEncryption; + return static_cast<QXmppMessage::EncryptionMethod>(index); +} + +/// Advertises that this message is encrypted with the given encryption method. +/// See XEP-0380: Explicit Message Encryption for details. + +void QXmppMessage::setEncryptionMethod(QXmppMessage::EncryptionMethod method) +{ + d->encryptionMethod = ENCRYPTION_NAMESPACES.at(int(method)); +} + +/// Returns the namespace of the advertised encryption method via. XEP-0380: +/// Explicit Message Encryption. + +QString QXmppMessage::encryptionMethodNs() const +{ + return d->encryptionMethod; +} + +/// Sets the namespace of the encryption method this message advertises to be +/// encrypted with. See XEP-0380: Explicit Message Encryption for details. + +void QXmppMessage::setEncryptionMethodNs(const QString &encryptionMethod) +{ + d->encryptionMethod = encryptionMethod; +} + +/// Returns the associated name of the encryption method this message +/// advertises to be encrypted with. See XEP-0380: Explicit Message Encryption +/// for details. + +QString QXmppMessage::encryptionName() const +{ + if (!d->encryptionName.isEmpty()) + return d->encryptionName; + return ENCRYPTION_NAMES.at(int(encryptionMethod())); +} + +/// Sets the name of the encryption method for XEP-0380: Explicit Message +/// Encryption. +/// +/// \note This should only be used, if the encryption method is custom and is +/// not one of the methods listed in the XEP. + +void QXmppMessage::setEncryptionName(const QString &encryptionName) +{ + d->encryptionName = encryptionName; +} + +/// Returns true, if this is a spoiler message according to XEP-0382: Spoiler +/// messages. The spoiler hint however can still be empty. +/// +/// A spoiler message's content should not be visible to the user by default. + +bool QXmppMessage::isSpoiler() const +{ + return d->isSpoiler; +} + +/// Sets whether this is a spoiler message as specified in XEP-0382: Spoiler +/// messages. +/// +/// The content of spoiler messages will not be displayed by default to the +/// user. However, clients not supporting spoiler messages will still display +/// the content as usual. + +void QXmppMessage::setIsSpoiler(bool isSpoiler) +{ + d->isSpoiler = isSpoiler; +} + +/// Returns the spoiler hint as specified in XEP-0382: Spoiler messages. +/// +/// The hint may be empty, even if isSpoiler is true. + +QString QXmppMessage::spoilerHint() const +{ + return d->spoilerHint; +} + +/// Sets a spoiler hint for XEP-0382: Spoiler messages. If the spoiler hint +/// is not empty, isSpoiler will be set to true. +/// +/// A spoiler hint is optional for spoiler messages. +/// +/// Keep in mind that the spoiler hint is not displayed at all by clients not +/// supporting spoiler messages. + +void QXmppMessage::setSpoilerHint(const QString &spoilerHint) +{ + d->spoilerHint = spoilerHint; + if (!spoilerHint.isEmpty()) + d->isSpoiler = true; +} + /// \cond void QXmppMessage::parse(const QDomElement &element) { @@ -654,6 +892,24 @@ void QXmppMessage::parse(const QDomElement &element) else { extensions << QXmppElement(xElement); } + // XEP-0334: Message Processing Hints + } else if (xElement.namespaceURI() == ns_message_processing_hints && HINT_TYPES.contains(xElement.tagName())) { + addHint(Hint(1 << HINT_TYPES.indexOf(xElement.tagName()))); + // XEP-0367: Message Attaching + } else if (xElement.tagName() == "attach-to" && xElement.namespaceURI() == ns_message_attaching) { + d->attachId = xElement.attribute("id"); + // XEP-0369: Mediated Information eXchange (MIX) + } else if (xElement.tagName() == "mix" && xElement.namespaceURI() == ns_mix) { + d->mixUserJid = xElement.firstChildElement("jid").text(); + d->mixUserNick = xElement.firstChildElement("nick").text(); + // XEP-0380: Explicit Message Encryption + } else if (xElement.tagName() == "encryption" && xElement.namespaceURI() == ns_eme) { + d->encryptionMethod = xElement.attribute("namespace"); + d->encryptionName = xElement.attribute("name"); + // XEP-0382: Spoiler messages + } else if (xElement.tagName() == "spoiler" && xElement.namespaceURI() == ns_spoiler) { + d->isSpoiler = true; + d->spoilerHint = xElement.text(); } else if (!knownElems.contains(qMakePair(xElement.tagName(), xElement.namespaceURI())) && !knownElems.contains(qMakePair(xElement.tagName(), QString()))) { // other extensions @@ -791,6 +1047,49 @@ void QXmppMessage::toXml(QXmlStreamWriter *xmlWriter) const xmlWriter->writeEndElement(); } + // XEP-0334: Message Processing Hints + for (quint8 i = 0; i < HINT_TYPES.size(); i++) { + if (hasHint(Hint(1 << i))) { + xmlWriter->writeStartElement(HINT_TYPES.at(i)); + xmlWriter->writeAttribute("xmlns", ns_message_processing_hints); + xmlWriter->writeEndElement(); + } + } + + // XEP-0367: Message Attaching + if (!d->attachId.isEmpty()) { + xmlWriter->writeStartElement("attach-to"); + xmlWriter->writeAttribute("xmlns", ns_message_attaching); + xmlWriter->writeAttribute("id", d->attachId); + xmlWriter->writeEndElement(); + } + + // XEP-0369: Mediated Information eXchange (MIX) + if (!d->mixUserJid.isEmpty() || !d->mixUserNick.isEmpty()) { + xmlWriter->writeStartElement("mix"); + xmlWriter->writeAttribute("xmlns", ns_mix); + helperToXmlAddTextElement(xmlWriter, "jid", d->mixUserJid); + helperToXmlAddTextElement(xmlWriter, "nick", d->mixUserNick); + xmlWriter->writeEndElement(); + } + + // XEP-0380: Explicit Message Encryption + if (!d->encryptionMethod.isEmpty()) { + xmlWriter->writeStartElement("encryption"); + xmlWriter->writeAttribute("xmlns", ns_eme); + xmlWriter->writeAttribute("namespace", d->encryptionMethod); + helperToXmlAddAttribute(xmlWriter, "name", d->encryptionName); + xmlWriter->writeEndElement(); + } + + // XEP-0382: Spoiler messages + if (d->isSpoiler) { + xmlWriter->writeStartElement("spoiler"); + xmlWriter->writeAttribute("xmlns", ns_spoiler); + xmlWriter->writeCharacters(d->spoilerHint); + xmlWriter->writeEndElement(); + } + // other extensions QXmppStanza::extensionsToXml(xmlWriter); diff --git a/src/base/QXmppMessage.h b/src/base/QXmppMessage.h index e0c3d831..2743ca13 100644 --- a/src/base/QXmppMessage.h +++ b/src/base/QXmppMessage.h @@ -21,7 +21,6 @@ * */ - #ifndef QXMPPMESSAGE_H #define QXMPPMESSAGE_H @@ -33,7 +32,6 @@ class QXmppMessagePrivate; /// \brief The QXmppMessage class represents an XMPP message. /// /// \ingroup Stanzas -/// class QXMPP_EXPORT QXmppMessage : public QXmppStanza { @@ -48,8 +46,8 @@ public: Headline }; - /// This enum describes a chat state as defined by - /// XEP-0085 : Chat State Notifications. + /// This enum describes a chat state as defined by XEP-0085: Chat State + /// Notifications. enum State { None = 0, ///< The message does not contain any chat state information. @@ -60,8 +58,7 @@ public: Paused ///< User had been composing but now has stopped. }; - /// This enum describes a chat marker as defined by - /// XEP-0333 : Char Markers + /// This enum describes a chat marker as defined by XEP-0333: Chat Markers. enum Marker { NoMarker = 0, Received, @@ -69,11 +66,31 @@ public: Acknowledged }; + /// XEP-0334: Message Processing Hints + enum Hint { + NoPermanentStore = 1 << 0, ///< Do not allow permanent storage + NoStore = 1 << 1, ///< Do not store at all + NoCopy = 1 << 2, ///< Do not copy the message + Store = 1 << 3 ///< Do store the message + }; + + /// This enum describes different end-to-end encryption methods. These can + /// be used to mark a message explicitly as encrypted with a specific + /// algothim. See XEP-0380: Explicit Message Encryption for details. + enum EncryptionMethod { + NoEncryption, ///< No encryption + UnknownEncryption, ///< Unknown encryption + OTR, ///< XEP-0364: Current Off-the-Record Messaging Usage + LegacyOpenPGP, ///< XEP-0027: Current Jabber OpenPGP Usage + OX, ///< XEP-0373: OpenPGP for XMPP + OMEMO ///< XEP-0384: OMEMO Encryption + }; + QXmppMessage(const QString& from = QString(), const QString& to = QString(), const QString& body = QString(), const QString& thread = QString()); QXmppMessage(const QXmppMessage &other); - ~QXmppMessage(); + ~QXmppMessage() override; QXmppMessage& operator=(const QXmppMessage &other); @@ -116,7 +133,7 @@ public: QString xhtml() const; void setXhtml(const QString &xhtml); - // XEP-0333 + // XEP-0333: Chat Markers bool isMarkable() const; void setMarkable(const bool); @@ -133,7 +150,7 @@ public: bool isPrivate() const; void setPrivate(const bool); - bool isXmppStanza() const; + bool isXmppStanza() const override; // XEP-0066: Out of Band Data QString outOfBandUrl() const; @@ -143,9 +160,42 @@ public: QString replaceId() const; void setReplaceId(const QString&); + // XEP-0334: Message Processing Hints + bool hasHint(const Hint hint) const; + void addHint(const Hint hint); + void removeHint(const Hint hint); + void removeAllHints(); + + // XEP-0367: Message Attaching + QString attachId() const; + void setAttachId(const QString&); + + // XEP-0369: Mediated Information eXchange (MIX) + QString mixUserJid() const; + void setMixUserJid(const QString&); + + QString mixUserNick() const; + void setMixUserNick(const QString&); + + // XEP-0380: Explicit Message Encryption + EncryptionMethod encryptionMethod() const; + void setEncryptionMethod(EncryptionMethod); + QString encryptionMethodNs() const; + void setEncryptionMethodNs(const QString&); + + QString encryptionName() const; + void setEncryptionName(const QString&); + + // XEP-0382: Spoiler messages + bool isSpoiler() const; + void setIsSpoiler(bool); + + QString spoilerHint() const; + void setSpoilerHint(const QString&); + /// \cond - void parse(const QDomElement &element); - void toXml(QXmlStreamWriter *writer) const; + void parse(const QDomElement &element) override; + void toXml(QXmlStreamWriter *writer) const override; /// \endcond private: diff --git a/src/base/QXmppMixIq.cpp b/src/base/QXmppMixIq.cpp new file mode 100644 index 00000000..7cc316e2 --- /dev/null +++ b/src/base/QXmppMixIq.cpp @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2008-2019 The QXmpp developers + * + * Author: + * Linus Jahn <lnj@kaidan.im> + * + * Source: + * https://github.com/qxmpp-project/qxmpp + * + * This file is a part of QXmpp library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + */ + +#include "QXmppMixIq.h" +#include "QXmppDataForm.h" +#include "QXmppConstants_p.h" +#include "QXmppUtils.h" +#include <QDomElement> + +static const QStringList MIX_ACTION_TYPES = QStringList() << "" + << "client-join" << "client-leave" << "join" << "leave" + << "update-subscription" << "setnick" << "create" << "destroy"; + +class QXmppMixIqPrivate : QSharedData +{ +public: + QString jid; + QString channelName; + QStringList nodes; + QString nick; + QXmppMixIq::Type actionType = QXmppMixIq::None; +}; + +QXmppMixIq::QXmppMixIq() + : d(new QXmppMixIqPrivate) +{ +} + +QXmppMixIq::~QXmppMixIq() +{ +} + +/// Returns the channel JID. It also contains a participant id for Join/ +/// ClientJoin results. + +QString QXmppMixIq::jid() const +{ + return d->jid; +} + +/// Sets the channel JID. For results of Join/ClientJoin queries this also +/// needs to contain a participant id. + +void QXmppMixIq::setJid(const QString& jid) +{ + d->jid = jid; +} + +/// Returns the channel name (the name part of the channel JID). This may still +/// be empty, if a JID was set. + +QString QXmppMixIq::channelName() const +{ + return d->channelName; +} + +/// Sets the channel name for creating/destroying specific channels. When you +/// create a new channel, this can also be left empty to let the server +/// generate a name. + +void QXmppMixIq::setChannelName(const QString& channelName) +{ + d->channelName = channelName; +} + +/// Returns the list of nodes to subscribe to. + +QStringList QXmppMixIq::nodes() const +{ + return d->nodes; +} + +/// Sets the nodes to subscribe to. Note that for UpdateSubscription queries +/// you only need to include the new subscriptions. + +void QXmppMixIq::setNodes(const QStringList& nodes) +{ + d->nodes = nodes; +} + +/// Returns the user's nickname in the channel. + +QString QXmppMixIq::nick() const +{ + return d->nick; +} + +/// Sets the nickname for the channel. + +void QXmppMixIq::setNick(const QString& nick) +{ + d->nick = nick; +} + +/// Returns the MIX channel action type. + +QXmppMixIq::Type QXmppMixIq::actionType() const +{ + return d->actionType; +} + +/// Sets the channel action. + +void QXmppMixIq::setActionType(QXmppMixIq::Type type) +{ + d->actionType = type; +} + +/// \cond +bool QXmppMixIq::isMixIq(const QDomElement& element) +{ + const QDomElement& child = element.firstChildElement(); + return !child.isNull() && (child.namespaceURI() == ns_mix + || child.namespaceURI() == ns_mix_pam); +} + +void QXmppMixIq::parseElementFromChild(const QDomElement& element) +{ + QDomElement child = element.firstChildElement(); + // determine action type + d->actionType = (QXmppMixIq::Type) MIX_ACTION_TYPES.indexOf(child.tagName()); + + if (child.namespaceURI() == ns_mix_pam) { + if (child.hasAttribute("channel")) + d->jid = child.attribute("channel"); + + child = child.firstChildElement(); + } + + if (!child.isNull() && child.namespaceURI() == ns_mix) { + if (child.hasAttribute("jid")) + d->jid = child.attribute("jid"); + if (child.hasAttribute("channel")) + d->channelName = child.attribute("channel"); + + QDomElement subChild = child.firstChildElement(); + while (!subChild.isNull()) { + if (subChild.tagName() == "subscribe") + d->nodes << subChild.attribute("node"); + else if (subChild.tagName() == "nick") + d->nick = subChild.text(); + + subChild = subChild.nextSiblingElement(); + } + } +} + +void QXmppMixIq::toXmlElementFromChild(QXmlStreamWriter* writer) const +{ + if (d->actionType == None) + return; + + writer->writeStartElement(MIX_ACTION_TYPES.at(d->actionType)); + if (d->actionType == ClientJoin || d->actionType == ClientLeave) { + writer->writeAttribute("xmlns", ns_mix_pam); + if (type() == Set) + helperToXmlAddAttribute(writer, "channel", d->jid); + + if (d->actionType == ClientJoin) + writer->writeStartElement("join"); + else if (d->actionType == ClientLeave) + writer->writeStartElement("leave"); + } + + writer->writeAttribute("xmlns", ns_mix); + helperToXmlAddAttribute(writer, "channel", d->channelName); + if (type() == Result) + helperToXmlAddAttribute(writer, "jid", d->jid); + + for (const auto &node : d->nodes) { + writer->writeStartElement("subscribe"); + writer->writeAttribute("node", node); + writer->writeEndElement(); + } + if (!d->nick.isEmpty()) + writer->writeTextElement("nick", d->nick); + + writer->writeEndElement(); + if (d->actionType == ClientJoin || d->actionType == ClientLeave) + writer->writeEndElement(); +} +/// \endcond diff --git a/src/base/QXmppMixIq.h b/src/base/QXmppMixIq.h new file mode 100644 index 00000000..4e3b71f8 --- /dev/null +++ b/src/base/QXmppMixIq.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2008-2019 The QXmpp developers + * + * Author: + * Linus Jahn <lnj@kaidan.im> + * + * Source: + * https://github.com/qxmpp-project/qxmpp + * + * This file is a part of QXmpp library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + */ + +#ifndef QXMPPMIXIQ_H +#define QXMPPMIXIQ_H + +#include "QXmppIq.h" + +class QXmppMixIqPrivate; + +/// \brief The QXmppMixIq class represents an IQ used to do actions on a MIX +/// channel as defined by XEP-0369: Mediated Information eXchange (MIX) and +/// XEP-0405: Mediated Information eXchange (MIX): Participant Server +/// Requirements. +/// +/// \ingroup Stanzas + +class QXMPP_EXPORT QXmppMixIq : public QXmppIq +{ +public: + enum Type { + None, + ClientJoin, + ClientLeave, + Join, + Leave, + UpdateSubscription, + SetNick, + Create, + Destroy + }; + + QXmppMixIq(); + ~QXmppMixIq() override; + + QXmppMixIq::Type actionType() const; + void setActionType(QXmppMixIq::Type); + + QString jid() const; + void setJid(const QString&); + + QString channelName() const; + void setChannelName(const QString&); + + QStringList nodes() const; + void setNodes(const QStringList&); + + QString nick() const; + void setNick(const QString&); + + /// \cond + static bool isMixIq(const QDomElement&); + /// \endcond + +protected: + /// \cond + void parseElementFromChild(const QDomElement&) override; + void toXmlElementFromChild(QXmlStreamWriter*) const override; + /// \endcond + +private: + QXmppMixIqPrivate *d; +}; + +#endif // QXMPPMIXIQ_H diff --git a/src/base/QXmppMixItem.cpp b/src/base/QXmppMixItem.cpp new file mode 100644 index 00000000..5b1d7c00 --- /dev/null +++ b/src/base/QXmppMixItem.cpp @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2008-2019 The QXmpp developers + * + * Author: + * Linus Jahn <lnj@kaidan.im> + * + * Source: + * https://github.com/qxmpp-project/qxmpp + * + * This file is a part of QXmpp library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + */ + +#include "QXmppMixItem.h" +#include "QXmppDataForm.h" +#include "QXmppConstants_p.h" +#include "QXmppUtils.h" +#include <QDomElement> +#include <QBuffer> + +class QXmppMixInfoItemPrivate : QSharedData +{ +public: + QString name; + QString description; + QStringList contactJids; +}; + +QXmppMixInfoItem::QXmppMixInfoItem() + : d(new QXmppMixInfoItemPrivate) +{ +} + +/// Returns the user-specified name of the MIX channel. This is not the name +/// part of the channel's JID. + +QString QXmppMixInfoItem::name() const +{ + return d->name; +} + +/// Sets the name of the channel. + +void QXmppMixInfoItem::setName(const QString& name) +{ + d->name = name; +} + +/// Returns the description of the channel. This string might be very long. + +QString QXmppMixInfoItem::description() const +{ + return d->description; +} + +/// Sets the longer channel description. + +void QXmppMixInfoItem::setDescription(const QString& description) +{ + d->description = description; +} + +/// Returns a list of JIDs that are responsible for this channel. + +QStringList QXmppMixInfoItem::contactJids() const +{ + return d->contactJids; +} + +/// Sets a list of public JIDs that are responsible for this channel. + +void QXmppMixInfoItem::setContactJids(const QStringList& contactJids) +{ + d->contactJids = contactJids; +} + +/// Returns true, if the given dom element is a MIX channel info item. + +bool QXmppMixInfoItem::isMixChannelInfo(const QDomElement& element) +{ + QXmppDataForm form; + form.parse(element); + for (const auto &field : form.fields()) { + if (field.key() == "FORM_TYPE") + return field.value() == ns_mix; + } + return false; +} + +void QXmppMixInfoItem::parse(const QXmppElement& element) +{ + QXmppDataForm form; + form.parse(element.sourceDomElement()); + + for (auto& field : form.fields()) { + if (field.key() == "Name") + d->name = field.value().toString(); + else if (field.key() == "Description") + d->description = field.value().toString(); + else if (field.key() == "Contact") + d->contactJids = field.value().toStringList(); + } +} + +QXmppElement QXmppMixInfoItem::toElement() const +{ + QXmppDataForm form; + form.setType(QXmppDataForm::Result); + QList<QXmppDataForm::Field> fields; + + QXmppDataForm::Field formType; + formType.setType(QXmppDataForm::Field::HiddenField); + formType.setKey("FORM_TYPE"); + formType.setValue(ns_mix); + fields << formType; + + QXmppDataForm::Field nameField; + nameField.setKey("Name"); + nameField.setValue(d->name); + fields << nameField; + + QXmppDataForm::Field descriptionField; + descriptionField.setKey("Description"); + descriptionField.setValue(d->description); + fields << descriptionField; + + QXmppDataForm::Field contactsField; + contactsField.setKey("Contact"); + contactsField.setValue(d->contactJids); + contactsField.setType(QXmppDataForm::Field::JidMultiField); + fields << contactsField; + + form.setFields(fields); + + // FIXME: this is too complicated; maybe don't use QXmppElement in QXmppPubSubItem? + QBuffer buffer; + buffer.open(QIODevice::ReadWrite); + QXmlStreamWriter writer(&buffer); + form.toXml(&writer); + + QDomDocument doc; + doc.setContent(buffer.data()); + return QXmppElement(doc.documentElement()); +} + +class QXmppMixParticipantItemPrivate : QSharedData +{ +public: + QString nick; + QString jid; +}; + +QXmppMixParticipantItem::QXmppMixParticipantItem() + : d(new QXmppMixParticipantItemPrivate) +{ +} + +/// Returns the participant's nickname. + +QString QXmppMixParticipantItem::nick() const +{ + return d->nick; +} + +/// Sets the participants nickname. + +void QXmppMixParticipantItem::setNick(const QString& nick) +{ + d->nick = nick; +} + +/// Returns the participant's JID. + +QString QXmppMixParticipantItem::jid() const +{ + return d->jid; +} + +/// Sets the participant's JID. + +void QXmppMixParticipantItem::setJid(const QString& jid) +{ + d->jid = jid; +} + +void QXmppMixParticipantItem::parse(const QXmppElement& itemContent) +{ + d->nick = itemContent.firstChildElement("nick").value(); + d->jid = itemContent.firstChildElement("jid").value(); +} + +QXmppElement QXmppMixParticipantItem::toElement() const +{ + QXmppElement element; + element.setTagName("participant"); + element.setAttribute("xmlns", ns_mix); + + QXmppElement jid; + jid.setTagName("jid"); + jid.setValue(d->jid); + element.appendChild(jid); + + QXmppElement nick; + nick.setTagName("nick"); + nick.setValue(d->nick); + element.appendChild(nick); + + return element; +} + +/// Returns true, if this dom element is a MIX participant item. + +bool QXmppMixParticipantItem::isMixParticipantItem(const QDomElement &element) +{ + return element.tagName() == "participant" && element.namespaceURI() == ns_mix; +} diff --git a/src/base/QXmppMixItem.h b/src/base/QXmppMixItem.h new file mode 100644 index 00000000..d4f046c1 --- /dev/null +++ b/src/base/QXmppMixItem.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2008-2019 The QXmpp developers + * + * Author: + * Linus Jahn <lnj@kaidan.im> + * + * Source: + * https://github.com/qxmpp-project/qxmpp + * + * This file is a part of QXmpp library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + */ + +#ifndef QXMPPMIXITEM_H +#define QXMPPMIXITEM_H + +#include "QXmppElement.h" + +class QXmppMixInfoItemPrivate; +class QXmppMixParticipantItemPrivate; + +/// \brief The QXmppMixInfoItem class represents a PubSub item of a MIX +/// channel containing channel information as defined by XEP-0369: Mediated +/// Information eXchange (MIX). +/// +/// \ingroup Stanzas + +class QXMPP_EXPORT QXmppMixInfoItem +{ +public: + QXmppMixInfoItem(); + + QString name() const; + void setName(const QString&); + + QString description() const; + void setDescription(const QString&); + + QStringList contactJids() const; + void setContactJids(const QStringList&); + + void parse(const QXmppElement& itemContent); + QXmppElement toElement() const; + + static bool isMixChannelInfo(const QDomElement&); + +private: + QXmppMixInfoItemPrivate *d; +}; + +/// \brief The QXmppMixParticipantItem class represents a PubSub item of a MIX +/// channel participant as defined by XEP-0369: Mediated Information eXchange +/// (MIX). +/// +/// \ingroup Stanzas + +class QXMPP_EXPORT QXmppMixParticipantItem +{ +public: + QXmppMixParticipantItem(); + + QString nick() const; + void setNick(const QString&); + + QString jid() const; + void setJid(const QString&); + + void parse(const QXmppElement& itemContent); + QXmppElement toElement() const; + + static bool isMixParticipantItem(const QDomElement&); + +private: + QXmppMixParticipantItemPrivate *d; +}; + +#endif // QXMPPMIXITEM_H diff --git a/src/base/QXmppMucIq.h b/src/base/QXmppMucIq.h index 7a87eddc..b9b715f8 100644 --- a/src/base/QXmppMucIq.h +++ b/src/base/QXmppMucIq.h @@ -113,8 +113,8 @@ public: protected: /// \cond - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: @@ -141,8 +141,8 @@ public: protected: /// \cond - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: diff --git a/src/base/QXmppNonSASLAuth.h b/src/base/QXmppNonSASLAuth.h index e888d4c2..4a457fe6 100644 --- a/src/base/QXmppNonSASLAuth.h +++ b/src/base/QXmppNonSASLAuth.h @@ -47,8 +47,8 @@ public: protected: /// \cond - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: diff --git a/src/base/QXmppPingIq.h b/src/base/QXmppPingIq.h index c23381c5..3139dc13 100644 --- a/src/base/QXmppPingIq.h +++ b/src/base/QXmppPingIq.h @@ -30,7 +30,7 @@ class QXMPP_EXPORT QXmppPingIq : public QXmppIq { public: QXmppPingIq(); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; static bool isPingIq(const QDomElement &element); }; diff --git a/src/base/QXmppPresence.cpp b/src/base/QXmppPresence.cpp index f8a27f43..ad0b1c60 100644 --- a/src/base/QXmppPresence.cpp +++ b/src/base/QXmppPresence.cpp @@ -80,6 +80,10 @@ public: // XEP-0319: Last User Interaction in Presence QDateTime lastUserInteraction; + + // XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements + QString mixUserJid; + QString mixUserNick; }; /// Constructs a QXmppPresence. @@ -265,6 +269,11 @@ void QXmppPresence::parse(const QDomElement &element) d->lastUserInteraction = QXmppUtils::datetimeFromString(since); } } + // XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements + else if (xElement.tagName() == "mix" && xElement.namespaceURI() == ns_mix_presence) { + d->mixUserJid = xElement.firstChildElement("jid").text(); + d->mixUserNick = xElement.firstChildElement("nick").text(); + } else if (xElement.tagName() != "addresses" && xElement.tagName() != "error" && xElement.tagName() != "show" && xElement.tagName() != "status" && xElement.tagName() != "priority") @@ -361,6 +370,17 @@ void QXmppPresence::toXml(QXmlStreamWriter *xmlWriter) const xmlWriter->writeEndElement(); } + // XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements + if (!d->mixUserJid.isEmpty() || !d->mixUserNick.isEmpty()) { + xmlWriter->writeStartElement("mix"); + xmlWriter->writeAttribute("xmlns", ns_mix_presence); + if (!d->mixUserJid.isEmpty()) + xmlWriter->writeTextElement("jid", d->mixUserJid); + if (!d->mixUserNick.isEmpty()) + xmlWriter->writeTextElement("nick", d->mixUserNick); + xmlWriter->writeEndElement(); + } + // other extensions QXmppStanza::extensionsToXml(xmlWriter); @@ -522,7 +542,35 @@ void QXmppPresence::setLastUserInteraction(const QDateTime& lastUserInteraction) d->lastUserInteraction = lastUserInteraction; } -/// Indicates if the QXmppStanza is a stanza in the XMPP sense (i. e. a message, +/// Returns the actual (full) JID of the MIX channel participant. + +QString QXmppPresence::mixUserJid() const +{ + return d->mixUserJid; +} + +/// Sets the actual (full) JID of the MIX channel participant. + +void QXmppPresence::setMixUserJid(const QString& mixUserJid) +{ + d->mixUserJid = mixUserJid; +} + +/// Returns the MIX participant's nickname. + +QString QXmppPresence::mixUserNick() const +{ + return d->mixUserNick; +} + +/// Sets the MIX participant's nickname. + +void QXmppPresence::setMixUserNick(const QString& mixUserNick) +{ + d->mixUserNick = mixUserNick; +} + +/// Indicates if the QXmppStanza is a stanza in the XMPP sence (i. e. a message, /// iq or presence) bool QXmppPresence::isXmppStanza() const diff --git a/src/base/QXmppPresence.h b/src/base/QXmppPresence.h index bda272af..d6ab980d 100644 --- a/src/base/QXmppPresence.h +++ b/src/base/QXmppPresence.h @@ -75,7 +75,7 @@ public: QXmppPresence(QXmppPresence::Type type = QXmppPresence::Available); QXmppPresence(const QXmppPresence &other); - ~QXmppPresence(); + ~QXmppPresence() override; QXmppPresence& operator=(const QXmppPresence &other); @@ -92,8 +92,8 @@ public: void setStatusText(const QString& statusText); /// \cond - void parse(const QDomElement &element); - void toXml(QXmlStreamWriter *writer) const; + void parse(const QDomElement &element) override; + void toXml(QXmlStreamWriter *writer) const override; /// \endcond // XEP-0045: Multi-User Chat @@ -132,7 +132,14 @@ public: QDateTime lastUserInteraction() const; void setLastUserInteraction(const QDateTime&); - bool isXmppStanza() const; + // XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements + QString mixUserJid() const; + void setMixUserJid(const QString&); + + QString mixUserNick() const; + void setMixUserNick(const QString&); + + bool isXmppStanza() const override; private: QSharedDataPointer<QXmppPresencePrivate> d; diff --git a/src/base/QXmppPubSubIq.h b/src/base/QXmppPubSubIq.h index 77cd234e..741b25da 100644 --- a/src/base/QXmppPubSubIq.h +++ b/src/base/QXmppPubSubIq.h @@ -92,8 +92,8 @@ public: protected: /// \cond - void parseElementFromChild(const QDomElement&); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement&) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: diff --git a/src/base/QXmppRegisterIq.h b/src/base/QXmppRegisterIq.h index c51035da..47fe12cc 100644 --- a/src/base/QXmppRegisterIq.h +++ b/src/base/QXmppRegisterIq.h @@ -59,8 +59,8 @@ public: protected: /// \cond - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: diff --git a/src/base/QXmppRosterIq.cpp b/src/base/QXmppRosterIq.cpp index 081cb753..3dfb32b4 100644 --- a/src/base/QXmppRosterIq.cpp +++ b/src/base/QXmppRosterIq.cpp @@ -29,20 +29,39 @@ #include "QXmppConstants_p.h" #include "QXmppUtils.h" +class QXmppRosterIqPrivate : public QSharedData +{ +public: + QList<QXmppRosterIq::Item> items; + // XEP-0237 Roster Versioning + QString version; + // XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements + bool mixAnnotate = false; +}; + +QXmppRosterIq::QXmppRosterIq() + : d(new QXmppRosterIqPrivate) +{ +} + +QXmppRosterIq::~QXmppRosterIq() +{ +} + /// Adds an item to the roster IQ. /// /// \param item void QXmppRosterIq::addItem(const Item& item) { - m_items.append(item); + d->items.append(item); } /// Returns the roster IQ's items. QList<QXmppRosterIq::Item> QXmppRosterIq::items() const { - return m_items; + return d->items; } /// Returns the roster version of IQ. @@ -52,7 +71,7 @@ QList<QXmppRosterIq::Item> QXmppRosterIq::items() const QString QXmppRosterIq::version() const { - return m_version; + return d->version; } /// Sets the roster version of IQ. @@ -62,7 +81,22 @@ QString QXmppRosterIq::version() const void QXmppRosterIq::setVersion(const QString &version) { - m_version = version; + d->version = version; +} + +/// Whether to annotate which items are MIX channels. + +bool QXmppRosterIq::mixAnnotate() const +{ + return d->mixAnnotate; +} + +/// Sets whether to include which roster items are MIX channels. This MUST only +/// be enabled in get requests. + +void QXmppRosterIq::setMixAnnotate(bool mixAnnotate) +{ + d->mixAnnotate = mixAnnotate; } /// \cond @@ -74,16 +108,20 @@ bool QXmppRosterIq::isRosterIq(const QDomElement &element) void QXmppRosterIq::parseElementFromChild(const QDomElement &element) { QDomElement queryElement = element.firstChildElement("query"); - QDomElement itemElement = queryElement.firstChildElement("item"); - setVersion(queryElement.attribute("ver")); + + QDomElement itemElement = queryElement.firstChildElement("item"); while(!itemElement.isNull()) { QXmppRosterIq::Item item; item.parse(itemElement); - m_items.append(item); - itemElement = itemElement.nextSiblingElement(); + d->items.append(item); + itemElement = itemElement.nextSiblingElement("item"); } + + QDomElement annotateElement = queryElement.firstChildElement("annotate"); + setMixAnnotate(!annotateElement.isNull() && annotateElement.namespaceURI() + == ns_mix_roster); } void QXmppRosterIq::toXmlElementFromChild(QXmlStreamWriter *writer) const @@ -92,19 +130,52 @@ void QXmppRosterIq::toXmlElementFromChild(QXmlStreamWriter *writer) const writer->writeAttribute( "xmlns", ns_roster); // XEP-0237 roster versioning - If the server does not advertise support for roster versioning, the client MUST NOT include the 'ver' attribute. - if(!version().isEmpty()) - writer->writeAttribute( "ver", version()); - for(int i = 0; i < m_items.count(); ++i) - m_items.at(i).toXml(writer); + if (!version().isEmpty()) + writer->writeAttribute("ver", version()); + + // XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements + if (d->mixAnnotate) { + writer->writeStartElement("annotate"); + writer->writeAttribute("xmlns", ns_mix_roster); + writer->writeEndElement(); + } + + for (int i = 0; i < d->items.count(); ++i) + d->items.at(i).toXml(writer); writer->writeEndElement(); } /// \endcond +class QXmppRosterIq::ItemPrivate : public QSharedData +{ +public: + QString bareJid; + Item::SubscriptionType type; + QString name; + // can be subscribe/unsubscribe (attribute "ask") + QString subscriptionStatus; + QSet<QString> groups; + // XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements + bool isMixChannel = false; + QString mixParticipantId; +}; + /// Constructs a new roster entry. QXmppRosterIq::Item::Item() - : m_type(NotSet) + : d(new ItemPrivate) { + d->type = NotSet; +} + +QXmppRosterIq::Item::~Item() +{ +} + +QXmppRosterIq::Item& QXmppRosterIq::Item::operator=(const Item &other) +{ + d = other.d; + return *this; } /// Returns the bareJid of the roster entry. @@ -114,7 +185,7 @@ QXmppRosterIq::Item::Item() QString QXmppRosterIq::Item::bareJid() const { - return m_bareJid; + return d->bareJid; } /// Sets the bareJid of the roster entry. @@ -124,7 +195,7 @@ QString QXmppRosterIq::Item::bareJid() const void QXmppRosterIq::Item::setBareJid(const QString &bareJid) { - m_bareJid = bareJid; + d->bareJid = bareJid; } /// Returns the groups of the roster entry. @@ -134,7 +205,7 @@ void QXmppRosterIq::Item::setBareJid(const QString &bareJid) QSet<QString> QXmppRosterIq::Item::groups() const { - return m_groups; + return d->groups; } /// Sets the groups of the roster entry. @@ -144,7 +215,7 @@ QSet<QString> QXmppRosterIq::Item::groups() const void QXmppRosterIq::Item::setGroups(const QSet<QString>& groups) { - m_groups = groups; + d->groups = groups; } /// Returns the name of the roster entry. @@ -154,7 +225,7 @@ void QXmppRosterIq::Item::setGroups(const QSet<QString>& groups) QString QXmppRosterIq::Item::name() const { - return m_name; + return d->name; } /// Sets the name of the roster entry. @@ -164,7 +235,7 @@ QString QXmppRosterIq::Item::name() const void QXmppRosterIq::Item::setName(const QString &name) { - m_name = name; + d->name = name; } /// Returns the subscription status of the roster entry. It is the "ask" @@ -177,7 +248,7 @@ void QXmppRosterIq::Item::setName(const QString &name) QString QXmppRosterIq::Item::subscriptionStatus() const { - return m_subscriptionStatus; + return d->subscriptionStatus; } /// Sets the subscription status of the roster entry. It is the "ask" @@ -189,7 +260,7 @@ QString QXmppRosterIq::Item::subscriptionStatus() const void QXmppRosterIq::Item::setSubscriptionStatus(const QString &status) { - m_subscriptionStatus = status; + d->subscriptionStatus = status; } /// Returns the subscription type of the roster entry. @@ -198,7 +269,7 @@ void QXmppRosterIq::Item::setSubscriptionStatus(const QString &status) QXmppRosterIq::Item::SubscriptionType QXmppRosterIq::Item::subscriptionType() const { - return m_type; + return d->type; } /// Sets the subscription type of the roster entry. @@ -208,12 +279,12 @@ QXmppRosterIq::Item::SubscriptionType void QXmppRosterIq::Item::setSubscriptionType(SubscriptionType type) { - m_type = type; + d->type = type; } QString QXmppRosterIq::Item::getSubscriptionTypeStr() const { - switch(m_type) + switch(d->type) { case NotSet: return ""; @@ -253,36 +324,80 @@ void QXmppRosterIq::Item::setSubscriptionTypeFromStr(const QString& type) qWarning("QXmppRosterIq::Item::setTypeFromStr(): invalid type"); } +/// Returns whether this is a MIX channel. + +bool QXmppRosterIq::Item::isMixChannel() const +{ + return d->isMixChannel; +} + +/// Sets whether this is a MIX channel. + +void QXmppRosterIq::Item::setIsMixChannel(bool isMixChannel) +{ + d->isMixChannel = isMixChannel; +} + +/// Returns the participant id for this MIX channel. + +QString QXmppRosterIq::Item::mixParticipantId() const +{ + return d->mixParticipantId; +} + +/// Sets the participant id for this MIX channel. + +void QXmppRosterIq::Item::setMixParticipantId(const QString& participantId) +{ + d->mixParticipantId = participantId; +} + /// \cond void QXmppRosterIq::Item::parse(const QDomElement &element) { - m_name = element.attribute("name"); - m_bareJid = element.attribute("jid"); + d->name = element.attribute("name"); + d->bareJid = element.attribute("jid"); setSubscriptionTypeFromStr(element.attribute("subscription")); setSubscriptionStatus(element.attribute("ask")); QDomElement groupElement = element.firstChildElement("group"); while(!groupElement.isNull()) { - m_groups << groupElement.text(); + d->groups << groupElement.text(); groupElement = groupElement.nextSiblingElement("group"); } + + // XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements + QDomElement channelElement = element.firstChildElement("channel"); + if (!channelElement.isNull() && channelElement.namespaceURI() == ns_mix_roster) { + d->isMixChannel = true; + d->mixParticipantId = channelElement.attribute("participant-id"); + } } void QXmppRosterIq::Item::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement("item"); - helperToXmlAddAttribute(writer,"jid", m_bareJid); - helperToXmlAddAttribute(writer,"name", m_name); + helperToXmlAddAttribute(writer,"jid", d->bareJid); + helperToXmlAddAttribute(writer,"name", d->name); helperToXmlAddAttribute(writer,"subscription", getSubscriptionTypeStr()); helperToXmlAddAttribute(writer, "ask", subscriptionStatus()); - QSet<QString>::const_iterator i = m_groups.constBegin(); - while(i != m_groups.constEnd()) + QSet<QString>::const_iterator i = d->groups.constBegin(); + while(i != d->groups.constEnd()) { helperToXmlAddTextElement(writer,"group", *i); ++i; } + + // XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements + if (d->isMixChannel) { + writer->writeStartElement("channel"); + writer->writeAttribute("xmlns", ns_mix_roster); + helperToXmlAddAttribute(writer, "participant-id", d->mixParticipantId); + writer->writeEndElement(); + } + writer->writeEndElement(); } /// \endcond diff --git a/src/base/QXmppRosterIq.h b/src/base/QXmppRosterIq.h index b2883557..4d0205d4 100644 --- a/src/base/QXmppRosterIq.h +++ b/src/base/QXmppRosterIq.h @@ -30,6 +30,8 @@ #include <QList> #include <QSet> +class QXmppRosterIqPrivate; + /// \brief The QXmppRosterIq class represents a roster IQ. /// /// \ingroup Stanzas @@ -37,6 +39,7 @@ class QXMPP_EXPORT QXmppRosterIq : public QXmppIq { public: + class ItemPrivate; /// \brief The QXmppRosterIq::Item class represents a roster entry. class QXMPP_EXPORT Item @@ -59,6 +62,10 @@ public: }; Item(); + ~Item(); + + Item& operator=(const Item &other); + QString bareJid() const; QSet<QString> groups() const; QString name() const; @@ -71,6 +78,13 @@ public: void setSubscriptionStatus(const QString&); void setSubscriptionType(SubscriptionType); + // XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements + bool isMixChannel() const; + void setIsMixChannel(bool); + + QString mixParticipantId() const; + void setMixParticipantId(const QString&); + /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *writer) const; @@ -80,34 +94,34 @@ public: QString getSubscriptionTypeStr() const; void setSubscriptionTypeFromStr(const QString&); - QString m_bareJid; - SubscriptionType m_type; - QString m_name; - // can be subscribe/unsubscribe (attribute "ask") - QString m_subscriptionStatus; - QSet<QString> m_groups; + ItemPrivate *d; }; + QXmppRosterIq(); + ~QXmppRosterIq() override; + QString version() const; void setVersion(const QString&); void addItem(const Item&); QList<Item> items() const; + // XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements + bool mixAnnotate() const; + void setMixAnnotate(bool); + /// \cond static bool isRosterIq(const QDomElement &element); /// \endcond protected: /// \cond - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: - QList<Item> m_items; - // XEP-0237 Roster Versioning - QString m_version; + QXmppRosterIqPrivate *d; }; #endif // QXMPPROSTERIQ_H diff --git a/src/base/QXmppRpcIq.h b/src/base/QXmppRpcIq.h index ce406d4f..1edd31aa 100644 --- a/src/base/QXmppRpcIq.h +++ b/src/base/QXmppRpcIq.h @@ -60,8 +60,8 @@ public: protected: /// \cond - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: @@ -92,8 +92,8 @@ public: protected: /// \cond - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: @@ -117,8 +117,8 @@ public: protected: /// \cond - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: diff --git a/src/base/QXmppRtpChannel.cpp b/src/base/QXmppRtpChannel.cpp index 61bf10f7..f80c7c4d 100644 --- a/src/base/QXmppRtpChannel.cpp +++ b/src/base/QXmppRtpChannel.cpp @@ -213,12 +213,12 @@ QXmppRtpAudioChannelPrivate::QXmppRtpAudioChannelPrivate() , incomingMaximum(0) , incomingPos(0) , incomingSequence(0) - , outgoingCodec(0) + , outgoingCodec(nullptr) , outgoingMarker(true) , outgoingPayloadNumbered(false) , outgoingSequence(1) , outgoingStamp(0) - , outgoingTimer(0) + , outgoingTimer(nullptr) { qRegisterMetaType<QXmppRtpAudioChannel::Tone>("QXmppRtpAudioChannel::Tone"); } @@ -240,7 +240,7 @@ QXmppCodec *QXmppRtpAudioChannelPrivate::codecForPayloadType(const QXmppJinglePa else if (payloadType.name().toLower() == "opus") return new QXmppOpusCodec(payloadType.clockrate(), payloadType.channels()); #endif - return 0; + return nullptr; } /// Constructs a new RTP audio channel with the given \a parent. @@ -349,7 +349,7 @@ void QXmppRtpAudioChannel::datagramReceived(const QByteArray &ba) d->incomingSequence = packet.sequence(); // get or create codec - QXmppCodec *codec = 0; + QXmppCodec *codec = nullptr; const quint8 packetType = packet.type(); if (!d->incomingCodecs.contains(packetType)) { foreach (const QXmppJinglePayloadType &payload, m_incomingPayloadTypes) { @@ -497,7 +497,7 @@ void QXmppRtpAudioChannel::payloadTypesChanged() // delete outgoing codec if (d->outgoingCodec) { delete d->outgoingCodec; - d->outgoingCodec = 0; + d->outgoingCodec = nullptr; } // create outgoing codec @@ -797,7 +797,7 @@ public: }; QXmppRtpVideoChannelPrivate::QXmppRtpVideoChannelPrivate() - : encoder(0), + : encoder(nullptr), outgoingId(0), outgoingSequence(1), outgoingStamp(0) @@ -925,7 +925,7 @@ void QXmppRtpVideoChannel::payloadTypesChanged() delete decoder; d->decoders.clear(); foreach (const QXmppJinglePayloadType &payload, m_incomingPayloadTypes) { - QXmppVideoDecoder *decoder = 0; + QXmppVideoDecoder *decoder = nullptr; if (false) {} #ifdef QXMPP_USE_THEORA @@ -945,10 +945,10 @@ void QXmppRtpVideoChannel::payloadTypesChanged() // refresh encoder if (d->encoder) { delete d->encoder; - d->encoder = 0; + d->encoder = nullptr; } foreach (const QXmppJinglePayloadType &payload, m_outgoingPayloadTypes) { - QXmppVideoEncoder *encoder = 0; + QXmppVideoEncoder *encoder = nullptr; if (false) {} #ifdef QXMPP_USE_THEORA diff --git a/src/base/QXmppRtpChannel.h b/src/base/QXmppRtpChannel.h index 0e0789da..9b68c7bc 100644 --- a/src/base/QXmppRtpChannel.h +++ b/src/base/QXmppRtpChannel.h @@ -98,16 +98,16 @@ public: Tone_D ///< Tone for the D key. }; - QXmppRtpAudioChannel(QObject *parent = 0); - ~QXmppRtpAudioChannel(); + QXmppRtpAudioChannel(QObject *parent = nullptr); + ~QXmppRtpAudioChannel() override; - qint64 bytesAvailable() const; - void close(); - bool isSequential() const; - QIODevice::OpenMode openMode() const; + qint64 bytesAvailable() const override; + void close() override; + bool isSequential() const override; + QIODevice::OpenMode openMode() const override; QXmppJinglePayloadType payloadType() const; - qint64 pos() const; - bool seek(qint64 pos); + qint64 pos() const override; + bool seek(qint64 pos) override; signals: /// \brief This signal is emitted when a datagram needs to be sent. @@ -143,9 +143,9 @@ protected: emit logMessage(QXmppLogger::SentMessage, qxmpp_loggable_trace(message)); } - void payloadTypesChanged(); - qint64 readData(char * data, qint64 maxSize); - qint64 writeData(const char * data, qint64 maxSize); + void payloadTypesChanged() override; + qint64 readData(char * data, qint64 maxSize) override; + qint64 writeData(const char * data, qint64 maxSize) override; /// \endcond private slots: @@ -263,11 +263,11 @@ class QXMPP_EXPORT QXmppRtpVideoChannel : public QXmppLoggable, public QXmppRtpC Q_OBJECT public: - QXmppRtpVideoChannel(QObject *parent = 0); - ~QXmppRtpVideoChannel(); + QXmppRtpVideoChannel(QObject *parent = nullptr); + ~QXmppRtpVideoChannel() override; - void close(); - QIODevice::OpenMode openMode() const; + void close() override; + QIODevice::OpenMode openMode() const override; // incoming stream QXmppVideoFormat decoderFormat() const; @@ -287,7 +287,7 @@ public slots: protected: /// \cond - void payloadTypesChanged(); + void payloadTypesChanged() override; /// \endcond private: diff --git a/src/base/QXmppSasl.cpp b/src/base/QXmppSasl.cpp index d6dbb04a..497fadf5 100644 --- a/src/base/QXmppSasl.cpp +++ b/src/base/QXmppSasl.cpp @@ -24,9 +24,10 @@ #include <cstdlib> -#include <QCryptographicHash> #include <QDomElement> +#include <QMessageAuthenticationCode> #include <QStringList> +#include <QtEndian> #include <QUrlQuery> #include "QXmppSasl_p.h" @@ -49,6 +50,36 @@ static QByteArray calculateDigest(const QByteArray &method, const QByteArray &di return QCryptographicHash::hash(KD, QCryptographicHash::Md5).toHex(); } +// Perform PBKFD2 key derivation, code taken from Qt 5.12 + +static QByteArray deriveKeyPbkdf2(QCryptographicHash::Algorithm algorithm, + const QByteArray &data, const QByteArray &salt, + int iterations, quint64 dkLen) +{ + QByteArray key; + quint32 currentIteration = 1; + QMessageAuthenticationCode hmac(algorithm, data); + QByteArray index(4, Qt::Uninitialized); + while (quint64(key.length()) < dkLen) { + hmac.addData(salt); + qToBigEndian(currentIteration, reinterpret_cast<uchar*>(index.data())); + hmac.addData(index); + QByteArray u = hmac.result(); + hmac.reset(); + QByteArray tkey = u; + for (int iter = 1; iter < iterations; iter++) { + hmac.addData(u); + u = hmac.result(); + hmac.reset(); + std::transform(tkey.cbegin(), tkey.cend(), u.cbegin(), tkey.begin(), + std::bit_xor<char>()); + } + key += tkey; + currentIteration++; + } + return key.left(dkLen); +} + static QByteArray generateNonce() { if (!forcedNonce.isEmpty()) @@ -61,6 +92,17 @@ static QByteArray generateNonce() return nonce.toBase64(); } +static QMap<char, QByteArray> parseGS2(const QByteArray &ba) +{ + QMap<char, QByteArray> map; + foreach (const QByteArray &keyValue, ba.split(',')) { + if (keyValue.size() >= 2 && keyValue[1] == '=') { + map[keyValue[0]] = keyValue.mid(2); + } + } + return map; +} + QXmppSaslAuth::QXmppSaslAuth(const QString &mechanism, const QByteArray &value) : m_mechanism(mechanism) , m_value(value) @@ -230,7 +272,9 @@ QXmppSaslClient::~QXmppSaslClient() QStringList QXmppSaslClient::availableMechanisms() { - return QStringList() << "PLAIN" << "DIGEST-MD5" << "ANONYMOUS" << "X-FACEBOOK-PLATFORM" << "X-MESSENGER-OAUTH2" << "X-OAUTH2"; + return QStringList() << "SCRAM-SHA-256" << "SCRAM-SHA-1" << "DIGEST-MD5" + << "PLAIN" << "ANONYMOUS" + << "X-FACEBOOK-PLATFORM" << "X-MESSENGER-OAUTH2" << "X-OAUTH2"; } /// Creates an SASL client for the given mechanism. @@ -243,6 +287,10 @@ QXmppSaslClient* QXmppSaslClient::create(const QString &mechanism, QObject *pare return new QXmppSaslClientDigestMd5(parent); } else if (mechanism == "ANONYMOUS") { return new QXmppSaslClientAnonymous(parent); + } else if (mechanism == "SCRAM-SHA-1") { + return new QXmppSaslClientScram(QCryptographicHash::Sha1, parent); + } else if (mechanism == "SCRAM-SHA-256") { + return new QXmppSaslClientScram(QCryptographicHash::Sha256, parent); } else if (mechanism == "X-FACEBOOK-PLATFORM") { return new QXmppSaslClientFacebook(parent); } else if (mechanism == "X-MESSENGER-OAUTH2") { @@ -250,7 +298,7 @@ QXmppSaslClient* QXmppSaslClient::create(const QString &mechanism, QObject *pare } else if (mechanism == "X-OAUTH2") { return new QXmppSaslClientGoogle(parent); } else { - return 0; + return nullptr; } } @@ -443,7 +491,7 @@ bool QXmppSaslClientFacebook::respond(const QByteArray &challenge, QByteArray &r QUrlQuery responseUrl; responseUrl.addQueryItem("access_token", password()); responseUrl.addQueryItem("api_key", username()); - responseUrl.addQueryItem("call_id", 0); + responseUrl.addQueryItem("call_id", nullptr); responseUrl.addQueryItem("method", requestUrl.queryItemValue("method")); responseUrl.addQueryItem("nonce", requestUrl.queryItemValue("nonce")); responseUrl.addQueryItem("v", "1.0"); @@ -507,6 +555,76 @@ bool QXmppSaslClientPlain::respond(const QByteArray &challenge, QByteArray &resp } } +QXmppSaslClientScram::QXmppSaslClientScram(QCryptographicHash::Algorithm algorithm, QObject *parent) + : QXmppSaslClient(parent) + , m_algorithm(algorithm) + , m_step(0) +{ + Q_ASSERT(m_algorithm == QCryptographicHash::Sha1 || m_algorithm == QCryptographicHash::Sha256); + m_nonce = generateNonce(); + + if (m_algorithm == QCryptographicHash::Sha256) { + m_dklen = 32; + m_mechanism = "SCRAM-SHA-256"; + } else { + m_dklen = 20; + m_mechanism = "SCRAM-SHA-1"; + } +} + +QString QXmppSaslClientScram::mechanism() const +{ + return m_mechanism; +} + +bool QXmppSaslClientScram::respond(const QByteArray &challenge, QByteArray &response) +{ + Q_UNUSED(challenge); + if (m_step == 0) { + m_gs2Header = "n,,"; + m_clientFirstMessageBare = "n=" + username().toUtf8() + ",r=" + m_nonce; + + response = m_gs2Header + m_clientFirstMessageBare; + m_step++; + return true; + } else if (m_step == 1) { + // validate input + const QMap<char, QByteArray> input = parseGS2(challenge); + const QByteArray nonce = input.value('r'); + const QByteArray salt = QByteArray::fromBase64(input.value('s')); + const int iterations = input.value('i').toInt(); + if (!nonce.startsWith(m_nonce) || salt.isEmpty() || iterations < 1) { + return false; + } + + // calculate proofs + const QByteArray clientFinalMessageBare = "c=" + m_gs2Header.toBase64() + ",r=" + nonce; + const QByteArray saltedPassword = deriveKeyPbkdf2(m_algorithm, password().toUtf8(), salt, + iterations, m_dklen); + const QByteArray clientKey = QMessageAuthenticationCode::hash("Client Key", saltedPassword, m_algorithm); + const QByteArray storedKey = QCryptographicHash::hash(clientKey, m_algorithm); + const QByteArray authMessage = m_clientFirstMessageBare + "," + challenge + "," + clientFinalMessageBare; + QByteArray clientProof = QMessageAuthenticationCode::hash(authMessage, storedKey, m_algorithm); + std::transform(clientProof.cbegin(), clientProof.cend(), clientKey.cbegin(), + clientProof.begin(), std::bit_xor<char>()); + + const QByteArray serverKey = QMessageAuthenticationCode::hash("Server Key", saltedPassword, m_algorithm); + m_serverSignature = QMessageAuthenticationCode::hash(authMessage, serverKey, m_algorithm); + + response = clientFinalMessageBare + ",p=" + clientProof.toBase64(); + m_step++; + return true; + } else if (m_step == 2) { + const QMap<char, QByteArray> input = parseGS2(challenge); + response = QByteArray(); + m_step++; + return QByteArray::fromBase64(input.value('v')) == m_serverSignature; + } else { + warning("QXmppSaslClientPlain : Invalid step"); + return false; + } +} + QXmppSaslClientWindowsLive::QXmppSaslClientWindowsLive(QObject *parent) : QXmppSaslClient(parent) , m_step(0) @@ -563,7 +681,7 @@ QXmppSaslServer* QXmppSaslServer::create(const QString &mechanism, QObject *pare } else if (mechanism == "ANONYMOUS") { return new QXmppSaslServerAnonymous(parent); } else { - return 0; + return nullptr; } } diff --git a/src/base/QXmppSasl_p.h b/src/base/QXmppSasl_p.h index ac8d911b..1d460f01 100644 --- a/src/base/QXmppSasl_p.h +++ b/src/base/QXmppSasl_p.h @@ -26,6 +26,7 @@ #define QXMPPSASL_P_H #include <QByteArray> +#include <QCryptographicHash> #include <QMap> #include "QXmppGlobal.h" @@ -51,8 +52,8 @@ class QXmppSaslServerPrivate; class QXMPP_AUTOTEST_EXPORT QXmppSaslClient : public QXmppLoggable { public: - QXmppSaslClient(QObject *parent = 0); - virtual ~QXmppSaslClient(); + QXmppSaslClient(QObject *parent = nullptr); + ~QXmppSaslClient() override; QString host() const; void setHost(const QString &host); @@ -70,7 +71,7 @@ public: virtual bool respond(const QByteArray &challenge, QByteArray &response) = 0; static QStringList availableMechanisms(); - static QXmppSaslClient* create(const QString &mechanism, QObject *parent = 0); + static QXmppSaslClient* create(const QString &mechanism, QObject *parent = nullptr); private: QXmppSaslClientPrivate *d; @@ -86,8 +87,8 @@ public: InputNeeded = 3 }; - QXmppSaslServer(QObject *parent = 0); - virtual ~QXmppSaslServer(); + QXmppSaslServer(QObject *parent = nullptr); + ~QXmppSaslServer() override; QString username() const; void setUsername(const QString &username); @@ -104,7 +105,7 @@ public: virtual QString mechanism() const = 0; virtual Response respond(const QByteArray &challenge, QByteArray &response) = 0; - static QXmppSaslServer* create(const QString &mechanism, QObject *parent = 0); + static QXmppSaslServer* create(const QString &mechanism, QObject *parent = nullptr); private: QXmppSaslServerPrivate *d; @@ -132,8 +133,8 @@ public: void setValue(const QByteArray &value); /// \cond - void parse(const QDomElement &element); - void toXml(QXmlStreamWriter *writer) const; + void parse(const QDomElement &element) override; + void toXml(QXmlStreamWriter *writer) const override; /// \endcond private: @@ -150,8 +151,8 @@ public: void setValue(const QByteArray &value); /// \cond - void parse(const QDomElement &element); - void toXml(QXmlStreamWriter *writer) const; + void parse(const QDomElement &element) override; + void toXml(QXmlStreamWriter *writer) const override; /// \endcond private: @@ -167,8 +168,8 @@ public: void setCondition(const QString &condition); /// \cond - void parse(const QDomElement &element); - void toXml(QXmlStreamWriter *writer) const; + void parse(const QDomElement &element) override; + void toXml(QXmlStreamWriter *writer) const override; /// \endcond private: @@ -184,8 +185,8 @@ public: void setValue(const QByteArray &value); /// \cond - void parse(const QDomElement &element); - void toXml(QXmlStreamWriter *writer) const; + void parse(const QDomElement &element) override; + void toXml(QXmlStreamWriter *writer) const override; /// \endcond private: @@ -198,17 +199,17 @@ public: QXmppSaslSuccess(); /// \cond - void parse(const QDomElement &element); - void toXml(QXmlStreamWriter *writer) const; + void parse(const QDomElement &element) override; + void toXml(QXmlStreamWriter *writer) const override; /// \endcond }; class QXmppSaslClientAnonymous : public QXmppSaslClient { public: - QXmppSaslClientAnonymous(QObject *parent = 0); - QString mechanism() const; - bool respond(const QByteArray &challenge, QByteArray &response); + QXmppSaslClientAnonymous(QObject *parent = nullptr); + QString mechanism() const override; + bool respond(const QByteArray &challenge, QByteArray &response) override; private: int m_step; @@ -217,9 +218,9 @@ private: class QXmppSaslClientDigestMd5 : public QXmppSaslClient { public: - QXmppSaslClientDigestMd5(QObject *parent = 0); - QString mechanism() const; - bool respond(const QByteArray &challenge, QByteArray &response); + QXmppSaslClientDigestMd5(QObject *parent = nullptr); + QString mechanism() const override; + bool respond(const QByteArray &challenge, QByteArray &response) override; private: QByteArray m_cnonce; @@ -232,9 +233,9 @@ private: class QXmppSaslClientFacebook : public QXmppSaslClient { public: - QXmppSaslClientFacebook(QObject *parent = 0); - QString mechanism() const; - bool respond(const QByteArray &challenge, QByteArray &response); + QXmppSaslClientFacebook(QObject *parent = nullptr); + QString mechanism() const override; + bool respond(const QByteArray &challenge, QByteArray &response) override; private: int m_step; @@ -243,9 +244,9 @@ private: class QXmppSaslClientGoogle : public QXmppSaslClient { public: - QXmppSaslClientGoogle(QObject *parent = 0); - QString mechanism() const; - bool respond(const QByteArray &challenge, QByteArray &response); + QXmppSaslClientGoogle(QObject *parent = nullptr); + QString mechanism() const override; + bool respond(const QByteArray &challenge, QByteArray &response) override; private: int m_step; @@ -254,20 +255,38 @@ private: class QXmppSaslClientPlain : public QXmppSaslClient { public: - QXmppSaslClientPlain(QObject *parent = 0); - QString mechanism() const; - bool respond(const QByteArray &challenge, QByteArray &response); + QXmppSaslClientPlain(QObject *parent = nullptr); + QString mechanism() const override; + bool respond(const QByteArray &challenge, QByteArray &response) override; private: int m_step; }; +class QXmppSaslClientScram : public QXmppSaslClient +{ +public: + QXmppSaslClientScram(QCryptographicHash::Algorithm algorithm, QObject *parent = nullptr); + QString mechanism() const override; + bool respond(const QByteArray &challenge, QByteArray &response) override; + +private: + QCryptographicHash::Algorithm m_algorithm; + int m_step; + int m_dklen; + QString m_mechanism; + QByteArray m_gs2Header; + QByteArray m_clientFirstMessageBare; + QByteArray m_serverSignature; + QByteArray m_nonce; +}; + class QXmppSaslClientWindowsLive : public QXmppSaslClient { public: - QXmppSaslClientWindowsLive(QObject *parent = 0); - QString mechanism() const; - bool respond(const QByteArray &challenge, QByteArray &response); + QXmppSaslClientWindowsLive(QObject *parent = nullptr); + QString mechanism() const override; + bool respond(const QByteArray &challenge, QByteArray &response) override; private: int m_step; @@ -276,10 +295,10 @@ private: class QXmppSaslServerAnonymous : public QXmppSaslServer { public: - QXmppSaslServerAnonymous(QObject *parent = 0); - QString mechanism() const; + QXmppSaslServerAnonymous(QObject *parent = nullptr); + QString mechanism() const override; - Response respond(const QByteArray &challenge, QByteArray &response); + Response respond(const QByteArray &challenge, QByteArray &response) override; private: int m_step; @@ -288,10 +307,10 @@ private: class QXmppSaslServerDigestMd5 : public QXmppSaslServer { public: - QXmppSaslServerDigestMd5(QObject *parent = 0); - QString mechanism() const; + QXmppSaslServerDigestMd5(QObject *parent = nullptr); + QString mechanism() const override; - Response respond(const QByteArray &challenge, QByteArray &response); + Response respond(const QByteArray &challenge, QByteArray &response) override; private: QByteArray m_cnonce; @@ -304,10 +323,10 @@ private: class QXmppSaslServerFacebook : public QXmppSaslServer { public: - QXmppSaslServerFacebook(QObject *parent = 0); - QString mechanism() const; + QXmppSaslServerFacebook(QObject *parent = nullptr); + QString mechanism() const override; - Response respond(const QByteArray &challenge, QByteArray &response); + Response respond(const QByteArray &challenge, QByteArray &response) override; private: int m_step; @@ -316,10 +335,10 @@ private: class QXmppSaslServerPlain : public QXmppSaslServer { public: - QXmppSaslServerPlain(QObject *parent = 0); - QString mechanism() const; + QXmppSaslServerPlain(QObject *parent = nullptr); + QString mechanism() const override; - Response respond(const QByteArray &challenge, QByteArray &response); + Response respond(const QByteArray &challenge, QByteArray &response) override; private: int m_step; diff --git a/src/base/QXmppSessionIq.h b/src/base/QXmppSessionIq.h index 7a1b444b..7cc375fb 100644 --- a/src/base/QXmppSessionIq.h +++ b/src/base/QXmppSessionIq.h @@ -42,7 +42,7 @@ public: private: /// \cond - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond }; diff --git a/src/base/QXmppSocks.h b/src/base/QXmppSocks.h index 114c7ab9..31af8ca9 100644 --- a/src/base/QXmppSocks.h +++ b/src/base/QXmppSocks.h @@ -36,7 +36,7 @@ class QXMPP_EXPORT QXmppSocksClient : public QTcpSocket Q_OBJECT public: - QXmppSocksClient(const QString &proxyHost, quint16 proxyPort, QObject *parent=0); + QXmppSocksClient(const QString &proxyHost, quint16 proxyPort, QObject *parent=nullptr); void connectToHost(const QString &hostName, quint16 hostPort); signals: @@ -59,7 +59,7 @@ class QXMPP_EXPORT QXmppSocksServer : public QObject Q_OBJECT public: - QXmppSocksServer(QObject *parent=0); + QXmppSocksServer(QObject *parent=nullptr); void close(); bool listen(quint16 port = 0); diff --git a/src/base/QXmppStream.cpp b/src/base/QXmppStream.cpp index 000f3b75..e3fa5bfd 100644 --- a/src/base/QXmppStream.cpp +++ b/src/base/QXmppStream.cpp @@ -61,7 +61,7 @@ public: }; QXmppStreamPrivate::QXmppStreamPrivate() - : socket(0), streamManagementEnabled(false), lastOutgoingSequenceNumber(0), lastIncomingSequenceNumber(0) + : socket(nullptr), streamManagementEnabled(false), lastOutgoingSequenceNumber(0), lastIncomingSequenceNumber(0) { } diff --git a/src/base/QXmppStream.h b/src/base/QXmppStream.h index d05b29fa..271b3400 100644 --- a/src/base/QXmppStream.h +++ b/src/base/QXmppStream.h @@ -44,7 +44,7 @@ class QXMPP_EXPORT QXmppStream : public QXmppLoggable public: QXmppStream(QObject *parent); - ~QXmppStream(); + ~QXmppStream() override; virtual bool isConnected() const; bool sendPacket(const QXmppStanza&); diff --git a/src/base/QXmppStreamFeatures.cpp b/src/base/QXmppStreamFeatures.cpp index 214cbd9f..8079962a 100644 --- a/src/base/QXmppStreamFeatures.cpp +++ b/src/base/QXmppStreamFeatures.cpp @@ -116,6 +116,16 @@ void QXmppStreamFeatures::setClientStateIndicationMode(QXmppStreamFeatures::Mode m_csiMode = mode; } +QXmppStreamFeatures::Mode QXmppStreamFeatures::registerMode() const +{ + return m_registerMode; +} + +void QXmppStreamFeatures::setRegisterMode(const QXmppStreamFeatures::Mode ®isterMode) +{ + m_registerMode = registerMode; +} + /// \cond bool QXmppStreamFeatures::isStreamFeatures(const QDomElement &element) { @@ -148,6 +158,7 @@ void QXmppStreamFeatures::parse(const QDomElement &element) m_tlsMode = readFeature(element, "starttls", ns_tls); m_streamManagementMode = readFeature(element, "sm", ns_stream_management); m_csiMode = readFeature(element, "csi", ns_csi); + m_registerMode = readFeature(element, "register", ns_register_feature); // parse advertised compression methods QDomElement compression = element.firstChildElement("compression"); @@ -194,6 +205,7 @@ void QXmppStreamFeatures::toXml(QXmlStreamWriter *writer) const writeFeature(writer, "starttls", ns_tls, m_tlsMode); writeFeature(writer, "sm", ns_stream_management, m_streamManagementMode); writeFeature(writer, "csi", ns_csi, m_csiMode); + writeFeature(writer, "register", ns_register_feature, m_registerMode); if (!m_compressionMethods.isEmpty()) { diff --git a/src/base/QXmppStreamFeatures.h b/src/base/QXmppStreamFeatures.h index 952e8c84..5a697b83 100644 --- a/src/base/QXmppStreamFeatures.h +++ b/src/base/QXmppStreamFeatures.h @@ -73,9 +73,17 @@ public: /// \pa mode The mode to set. void setClientStateIndicationMode(Mode mode); + /// Returns the mode for XEP-0077: In-Band Registration + Mode registerMode() const; + + /// Sets the mode for XEP-0077: In-Band Registration + /// + /// \pa mode The mode to set. + void setRegisterMode(const Mode ®isterMode); + /// \cond - void parse(const QDomElement &element); - void toXml(QXmlStreamWriter *writer) const; + void parse(const QDomElement &element) override; + void toXml(QXmlStreamWriter *writer) const override; /// \endcond static bool isStreamFeatures(const QDomElement &element); @@ -87,6 +95,7 @@ private: Mode m_tlsMode; Mode m_streamManagementMode; Mode m_csiMode; + Mode m_registerMode; QStringList m_authMechanisms; QStringList m_compressionMethods; }; diff --git a/src/base/QXmppStreamInitiationIq_p.h b/src/base/QXmppStreamInitiationIq_p.h index efff0d29..8f227020 100644 --- a/src/base/QXmppStreamInitiationIq_p.h +++ b/src/base/QXmppStreamInitiationIq_p.h @@ -70,8 +70,8 @@ public: protected: /// \cond - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: diff --git a/src/base/QXmppStun.cpp b/src/base/QXmppStun.cpp index 7ab90c8b..36fc4d26 100644 --- a/src/base/QXmppStun.cpp +++ b/src/base/QXmppStun.cpp @@ -1683,8 +1683,8 @@ CandidatePair::CandidatePair(int component, bool controlling, QObject *parent) : QXmppLoggable(parent) , nominated(false) , nominating(false) - , transport(0) - , transaction(0) + , transport(nullptr) + , transaction(nullptr) , m_component(component) , m_controlling(controlling) , m_state(WaitingState) @@ -1790,14 +1790,14 @@ private: }; QXmppIceComponentPrivate::QXmppIceComponentPrivate(int component_, QXmppIcePrivate *config_, QXmppIceComponent *qq) - : activePair(0) + : activePair(nullptr) , component(component_) , config(config_) - , fallbackPair(0) + , fallbackPair(nullptr) , gatheringState(QXmppIceConnection::NewGatheringState) , peerReflexivePriority(0) - , timer(0) - , turnAllocation(0) + , timer(nullptr) + , turnAllocation(nullptr) , turnConfigured(false) , q(qq) { @@ -1834,7 +1834,7 @@ bool QXmppIceComponentPrivate::addRemoteCandidate(const QXmppJingleCandidate &ca fallbackPair = pair; } - qSort(pairs.begin(), pairs.end(), candidatePairPtrLessThan); + std::sort(pairs.begin(), pairs.end(), candidatePairPtrLessThan); return true; } @@ -1845,7 +1845,7 @@ CandidatePair* QXmppIceComponentPrivate::findPair(QXmppStunTransaction *transact if (pair->transaction == transaction) return pair; } - return 0; + return nullptr; } void QXmppIceComponentPrivate::performCheck(CandidatePair *pair, bool nominate) @@ -2029,7 +2029,7 @@ void QXmppIceComponent::close() transport->disconnectFromHost(); d->turnAllocation->disconnectFromHost(); d->timer->stop(); - d->activePair = 0; + d->activePair = nullptr; } /// Starts ICE connectivity checks. @@ -2047,7 +2047,7 @@ void QXmppIceComponent::connectToHost() bool QXmppIceComponent::isConnected() const { - return d->activePair != 0; + return d->activePair != nullptr; } /// Returns the list of local candidates. @@ -2082,7 +2082,7 @@ void QXmppIceComponent::handleDatagram(const QByteArray &buffer, const QHostAddr } // check if it's STUN - QXmppStunTransaction *stunTransaction = 0; + QXmppStunTransaction *stunTransaction = nullptr; foreach (QXmppStunTransaction *t, d->stunTransactions.keys()) { if (t->request().id() == messageId && d->stunTransactions.value(t) == transport) { @@ -2125,7 +2125,7 @@ void QXmppIceComponent::handleDatagram(const QByteArray &buffer, const QHostAddr } // process message from peer - CandidatePair *pair = 0; + CandidatePair *pair = nullptr; if (message.messageClass() == QXmppStunMessage::Request) { // check for role conflict @@ -2184,7 +2184,7 @@ void QXmppIceComponent::handleDatagram(const QByteArray &buffer, const QHostAddr pair->transport = transport; d->pairs << pair; - qSort(d->pairs.begin(), d->pairs.end(), candidatePairPtrLessThan); + std::sort(d->pairs.begin(), d->pairs.end(), candidatePairPtrLessThan); } switch (pair->state()) { @@ -2238,7 +2238,7 @@ void QXmppIceComponent::handleDatagram(const QByteArray &buffer, const QHostAddr if (!d->activePair || pair->priority() > d->activePair->priority()) { info(QString("ICE pair selected %1 (priority: %2)").arg( pair->toString(), QString::number(pair->priority()))); - const bool wasConnected = (d->activePair != 0); + const bool wasConnected = (d->activePair != nullptr); d->activePair = pair; if (!wasConnected) emit connected(); @@ -2273,7 +2273,7 @@ void QXmppIceComponent::transactionFinished() transaction->response().errorPhrase)); pair->setState(CandidatePair::FailedState); } - pair->transaction = 0; + pair->transaction = nullptr; return; } @@ -2518,7 +2518,7 @@ public: }; QXmppIceConnectionPrivate::QXmppIceConnectionPrivate() - : connectTimer(NULL) + : connectTimer(nullptr) , gatheringState(QXmppIceConnection::NewGatheringState) , turnPort(0) { @@ -2621,7 +2621,7 @@ bool QXmppIceConnection::bind(const QList<QHostAddress> &addresses) // assign sockets QList<int> keys = d->components.keys(); - qSort(keys); + std::sort(keys.begin(), keys.end()); int s = 0; foreach (int k, keys) { d->components[k]->d->setSockets(sockets.mid(s, addresses.size())); diff --git a/src/base/QXmppStun.h b/src/base/QXmppStun.h index 973e1f90..4fa3e34b 100644 --- a/src/base/QXmppStun.h +++ b/src/base/QXmppStun.h @@ -113,7 +113,7 @@ public: void setUsername(const QString &username); QByteArray encode(const QByteArray &key = QByteArray(), bool addFingerprint = true) const; - bool decode(const QByteArray &buffer, const QByteArray &key = QByteArray(), QStringList *errors = 0); + bool decode(const QByteArray &buffer, const QByteArray &key = QByteArray(), QStringList *errors = nullptr); QString toString() const; static quint16 peekType(const QByteArray &buffer, quint32 &cookie, QByteArray &id); @@ -167,14 +167,14 @@ class QXMPP_EXPORT QXmppIceComponent : public QXmppLoggable Q_OBJECT public: - ~QXmppIceComponent(); + ~QXmppIceComponent() override; int component() const; bool isConnected() const; QList<QXmppJingleCandidate> localCandidates() const; static QList<QHostAddress> discoverAddresses(); - static QList<QUdpSocket*> reservePorts(const QList<QHostAddress> &addresses, int count, QObject *parent = 0); + static QList<QUdpSocket*> reservePorts(const QList<QHostAddress> &addresses, int count, QObject *parent = nullptr); public slots: void close(); @@ -203,7 +203,7 @@ signals: void localCandidatesChanged(); private: - QXmppIceComponent(int component, QXmppIcePrivate *config, QObject *parent=0); + QXmppIceComponent(int component, QXmppIcePrivate *config, QObject *parent=nullptr); QXmppIceComponentPrivate *d; friend class QXmppIceComponentPrivate; @@ -252,8 +252,8 @@ public: CompleteGatheringState }; - QXmppIceConnection(QObject *parent = 0); - ~QXmppIceConnection(); + QXmppIceConnection(QObject *parent = nullptr); + ~QXmppIceConnection() override; QXmppIceComponent *component(int component); void addComponent(int component); diff --git a/src/base/QXmppStun_p.h b/src/base/QXmppStun_p.h index ea87234f..c3c8eb1d 100644 --- a/src/base/QXmppStun_p.h +++ b/src/base/QXmppStun_p.h @@ -77,8 +77,8 @@ class QXMPP_EXPORT QXmppIceTransport : public QXmppLoggable Q_OBJECT public: - QXmppIceTransport(QObject *parent = 0); - ~QXmppIceTransport(); + QXmppIceTransport(QObject *parent = nullptr); + ~QXmppIceTransport() override; virtual QXmppJingleCandidate localCandidate(int component) const = 0; virtual qint64 writeDatagram(const QByteArray &data, const QHostAddress &host, quint16 port) = 0; @@ -110,8 +110,8 @@ public: ClosingState }; - QXmppTurnAllocation(QObject *parent = 0); - ~QXmppTurnAllocation(); + QXmppTurnAllocation(QObject *parent = nullptr); + ~QXmppTurnAllocation() override; QHostAddress relayedHost() const; quint16 relayedPort() const; @@ -121,8 +121,8 @@ public: void setUser(const QString &user); void setPassword(const QString &password); - QXmppJingleCandidate localCandidate(int component) const; - qint64 writeDatagram(const QByteArray &data, const QHostAddress &host, quint16 port); + QXmppJingleCandidate localCandidate(int component) const override; + qint64 writeDatagram(const QByteArray &data, const QHostAddress &host, quint16 port) override; signals: /// \brief This signal is emitted once TURN allocation succeeds. @@ -133,7 +133,7 @@ signals: public slots: void connectToHost(); - void disconnectFromHost(); + void disconnectFromHost() override; private slots: void readyRead(); @@ -180,14 +180,14 @@ class QXMPP_EXPORT QXmppUdpTransport : public QXmppIceTransport Q_OBJECT public: - QXmppUdpTransport(QUdpSocket *socket, QObject *parent = 0); - ~QXmppUdpTransport(); + QXmppUdpTransport(QUdpSocket *socket, QObject *parent = nullptr); + ~QXmppUdpTransport() override; - QXmppJingleCandidate localCandidate(int component) const; - qint64 writeDatagram(const QByteArray &data, const QHostAddress &host, quint16 port); + QXmppJingleCandidate localCandidate(int component) const override; + qint64 writeDatagram(const QByteArray &data, const QHostAddress &host, quint16 port) override; public slots: - void disconnectFromHost(); + void disconnectFromHost() override; private slots: void readyRead(); diff --git a/src/base/QXmppVCardIq.h b/src/base/QXmppVCardIq.h index 26456d00..e0b9ec87 100644 --- a/src/base/QXmppVCardIq.h +++ b/src/base/QXmppVCardIq.h @@ -228,7 +228,7 @@ class QXMPP_EXPORT QXmppVCardIq : public QXmppIq public: QXmppVCardIq(const QString& bareJid = QString()); QXmppVCardIq(const QXmppVCardIq &other); - ~QXmppVCardIq(); + ~QXmppVCardIq() override; QXmppVCardIq& operator=(const QXmppVCardIq &other); @@ -283,8 +283,8 @@ public: protected: /// \cond - void parseElementFromChild(const QDomElement&); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement&) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: diff --git a/src/base/QXmppVersionIq.h b/src/base/QXmppVersionIq.h index 2d953a99..3f558165 100644 --- a/src/base/QXmppVersionIq.h +++ b/src/base/QXmppVersionIq.h @@ -49,8 +49,8 @@ public: protected: /// \cond - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: diff --git a/src/client/QXmppArchiveManager.h b/src/client/QXmppArchiveManager.h index 6e365397..03fd1d91 100644 --- a/src/client/QXmppArchiveManager.h +++ b/src/client/QXmppArchiveManager.h @@ -63,8 +63,8 @@ public: void retrieveCollection(const QString &jid, const QDateTime &start, int max); /// \cond - QStringList discoveryFeatures() const; - bool handleStanza(const QDomElement &element); + QStringList discoveryFeatures() const override; + bool handleStanza(const QDomElement &element) override; /// \endcond signals: diff --git a/src/client/QXmppBookmarkManager.cpp b/src/client/QXmppBookmarkManager.cpp index 6fb72d73..0e8fdbf5 100644 --- a/src/client/QXmppBookmarkManager.cpp +++ b/src/client/QXmppBookmarkManager.cpp @@ -44,8 +44,8 @@ public: static bool isPrivateStorageIq(const QDomElement &element); protected: - void parseElementFromChild(const QDomElement &element); - void toXmlElementFromChild(QXmlStreamWriter *writer) const; + void parseElementFromChild(const QDomElement &element) override; + void toXmlElementFromChild(QXmlStreamWriter *writer) const override; private: QXmppBookmarkSet m_bookmarks; diff --git a/src/client/QXmppBookmarkManager.h b/src/client/QXmppBookmarkManager.h index 94d60d74..71e2c1ae 100644 --- a/src/client/QXmppBookmarkManager.h +++ b/src/client/QXmppBookmarkManager.h @@ -41,14 +41,14 @@ class QXMPP_EXPORT QXmppBookmarkManager : public QXmppClientExtension public: QXmppBookmarkManager(); - ~QXmppBookmarkManager(); + ~QXmppBookmarkManager() override; bool areBookmarksReceived() const; QXmppBookmarkSet bookmarks() const; bool setBookmarks(const QXmppBookmarkSet &bookmarks); /// \cond - bool handleStanza(const QDomElement &stanza); + bool handleStanza(const QDomElement &stanza) override; /// \endcond signals: @@ -57,7 +57,7 @@ signals: protected: /// \cond - void setClient(QXmppClient* client); + void setClient(QXmppClient* client) override; /// \endcond private slots: diff --git a/src/client/QXmppCallManager.cpp b/src/client/QXmppCallManager.cpp index 2c35f439..0a1d490f 100644 --- a/src/client/QXmppCallManager.cpp +++ b/src/client/QXmppCallManager.cpp @@ -105,7 +105,7 @@ private: QXmppCallPrivate::QXmppCallPrivate(QXmppCall *qq) : direction(QXmppCall::IncomingDirection), - manager(0), + manager(nullptr), state(QXmppCall::ConnectingState), sendVideo(false), audioMode(QIODevice::NotOpen), @@ -120,7 +120,7 @@ QXmppCallPrivate::Stream *QXmppCallPrivate::findStreamByMedia(const QString &med foreach (Stream *stream, streams) if (stream->media == media) return stream; - return 0; + return nullptr; } QXmppCallPrivate::Stream *QXmppCallPrivate::findStreamByName(const QString &name) @@ -128,7 +128,7 @@ QXmppCallPrivate::Stream *QXmppCallPrivate::findStreamByName(const QString &name foreach (Stream *stream, streams) if (stream->name == name) return stream; - return 0; + return nullptr; } void QXmppCallPrivate::handleAck(const QXmppIq &ack) @@ -297,7 +297,7 @@ QXmppCallPrivate::Stream *QXmppCallPrivate::createStream(const QString &media) stream->media = media; // RTP channel - QObject *channelObject = 0; + QObject *channelObject = nullptr; if (media == AUDIO_MEDIA) { QXmppRtpAudioChannel *audioChannel = new QXmppRtpAudioChannel(q); stream->channel = audioChannel; @@ -309,7 +309,7 @@ QXmppCallPrivate::Stream *QXmppCallPrivate::createStream(const QString &media) } else { q->warning(QString("Unsupported media type %1").arg(media)); delete stream; - return 0; + return nullptr; } // ICE connection @@ -510,7 +510,7 @@ QXmppRtpAudioChannel *QXmppCall::audioChannel() const if (stream) return static_cast<QXmppRtpAudioChannel*>(stream->channel); else - return 0; + return nullptr; } /// Returns the audio mode. @@ -529,7 +529,7 @@ QXmppRtpVideoChannel *QXmppCall::videoChannel() const if (stream) return static_cast<QXmppRtpVideoChannel*>(stream->channel); else - return 0; + return nullptr; } /// Returns the video mode. @@ -574,7 +574,7 @@ void QXmppCall::localCandidatesChanged() { // find the stream QXmppIceConnection *conn = qobject_cast<QXmppIceConnection*>(sender()); - QXmppCallPrivate::Stream *stream = 0; + QXmppCallPrivate::Stream *stream = nullptr; foreach (QXmppCallPrivate::Stream *ptr, d->streams) { if (ptr->connection == conn) { stream = ptr; @@ -704,7 +704,7 @@ QXmppCall *QXmppCallManagerPrivate::findCall(const QString &sid) const foreach (QXmppCall *call, calls) if (call->sid() == sid) return call; - return 0; + return nullptr; } QXmppCall *QXmppCallManagerPrivate::findCall(const QString &sid, QXmppCall::Direction direction) const @@ -712,7 +712,7 @@ QXmppCall *QXmppCallManagerPrivate::findCall(const QString &sid, QXmppCall::Dire foreach (QXmppCall *call, calls) if (call->sid() == sid && call->direction() == direction) return call; - return 0; + return nullptr; } /// Constructs a QXmppCallManager object to handle incoming and outgoing @@ -791,12 +791,12 @@ QXmppCall *QXmppCallManager::call(const QString &jid) if (jid.isEmpty()) { warning("Refusing to call an empty jid"); - return 0; + return nullptr; } if (jid == client()->configuration().jid()) { warning("Refusing to call self"); - return 0; + return nullptr; } QXmppCall *call = new QXmppCall(jid, QXmppCall::OutgoingDirection, this); diff --git a/src/client/QXmppCallManager.h b/src/client/QXmppCallManager.h index 0399ba61..220bd31f 100644 --- a/src/client/QXmppCallManager.h +++ b/src/client/QXmppCallManager.h @@ -78,7 +78,7 @@ public: FinishedState = 3 ///< The call is finished. }; - ~QXmppCall(); + ~QXmppCall() override; QXmppCall::Direction direction() const; QString jid() const; @@ -163,15 +163,15 @@ class QXMPP_EXPORT QXmppCallManager : public QXmppClientExtension public: QXmppCallManager(); - ~QXmppCallManager(); + ~QXmppCallManager() override; void setStunServer(const QHostAddress &host, quint16 port = 3478); void setTurnServer(const QHostAddress &host, quint16 port = 3478); void setTurnUser(const QString &user); void setTurnPassword(const QString &password); /// \cond - QStringList discoveryFeatures() const; - bool handleStanza(const QDomElement &element); + QStringList discoveryFeatures() const override; + bool handleStanza(const QDomElement &element) override; /// \endcond signals: @@ -189,7 +189,7 @@ public slots: protected: /// \cond - void setClient(QXmppClient* client); + void setClient(QXmppClient* client) override; /// \endcond private slots: diff --git a/src/client/QXmppCarbonManager.h b/src/client/QXmppCarbonManager.h index edc00f99..821c56df 100644 --- a/src/client/QXmppCarbonManager.h +++ b/src/client/QXmppCarbonManager.h @@ -43,14 +43,14 @@ class QXMPP_EXPORT QXmppCarbonManager : public QXmppClientExtension public: QXmppCarbonManager(); - ~QXmppCarbonManager(); + ~QXmppCarbonManager() override; bool carbonsEnabled() const; void setCarbonsEnabled(bool enabled); /// \cond - QStringList discoveryFeatures() const; - bool handleStanza(const QDomElement &element); + QStringList discoveryFeatures() const override; + bool handleStanza(const QDomElement &element) override; /// \endcond signals: diff --git a/src/client/QXmppClient.cpp b/src/client/QXmppClient.cpp index fe3ddf68..e5bd5473 100644 --- a/src/client/QXmppClient.cpp +++ b/src/client/QXmppClient.cpp @@ -66,11 +66,11 @@ private: QXmppClientPrivate::QXmppClientPrivate(QXmppClient *qq) : clientPresence(QXmppPresence::Available) - , logger(0) - , stream(0) + , logger(nullptr) + , stream(nullptr) , receivedConflict(false) , reconnectionTries(0) - , reconnectionTimer(0) + , reconnectionTimer(nullptr) , isActive(true) , q(qq) { @@ -344,9 +344,12 @@ void QXmppClient::setActive(bool active) } /// Returns the reference to QXmppRosterManager object of the client. +/// /// \return Reference to the roster object of the connected client. Use this to /// get the list of friends in the roster and their presence information. /// +/// \deprecated This method is deprecated since QXmpp 1.1. Use +/// \c QXmppClient::findExtension<QXmppRosterManager>() instead. QXmppRosterManager& QXmppClient::rosterManager() { @@ -461,6 +464,8 @@ QXmppStanza::Error::Condition QXmppClient::xmppStreamError() /// Returns the reference to QXmppVCardManager, implementation of XEP-0054. /// http://xmpp.org/extensions/xep-0054.html /// +/// \deprecated This method is deprecated since QXmpp 1.1. Use +/// \c QXmppClient::findExtension<QXmppVCardManager>() instead. QXmppVCardManager& QXmppClient::vCardManager() { @@ -470,6 +475,8 @@ QXmppVCardManager& QXmppClient::vCardManager() /// Returns the reference to QXmppVersionManager, implementation of XEP-0092. /// http://xmpp.org/extensions/xep-0092.html /// +/// \deprecated This method is deprecated since QXmpp 1.1. Use +/// \c QXmppClient::findExtension<QXmppVersionManager>() instead. QXmppVersionManager& QXmppClient::versionManager() { diff --git a/src/client/QXmppClient.h b/src/client/QXmppClient.h index a3da7cf6..de4bb3d5 100644 --- a/src/client/QXmppClient.h +++ b/src/client/QXmppClient.h @@ -103,8 +103,8 @@ public: ConnectedState ///< Connected to the server. }; - QXmppClient(QObject *parent = 0); - ~QXmppClient(); + QXmppClient(QObject *parent = nullptr); + ~QXmppClient() override; bool addExtension(QXmppClientExtension* extension); bool insertExtension(int index, QXmppClientExtension* extension); @@ -134,7 +134,7 @@ public: if(extension) return extension; } - return 0; + return nullptr; } bool isAuthenticated() const; @@ -155,9 +155,16 @@ public: State state() const; QXmppStanza::Error::Condition xmppStreamError(); +#if QXMPP_DEPRECATED_SINCE(1, 1) + QT_DEPRECATED_X("Use QXmppClient::findExtension<QXmppRosterManager>() instead") QXmppRosterManager& rosterManager(); + + QT_DEPRECATED_X("Use QXmppClient::findExtension<QXmppVCardManager>() instead") QXmppVCardManager& vCardManager(); + + QT_DEPRECATED_X("Use QXmppClient::findExtension<QXmppVersionManager>() instead") QXmppVersionManager& versionManager(); +#endif signals: diff --git a/src/client/QXmppClientExtension.cpp b/src/client/QXmppClientExtension.cpp index 3c52415d..cd11f2d2 100644 --- a/src/client/QXmppClientExtension.cpp +++ b/src/client/QXmppClientExtension.cpp @@ -37,7 +37,7 @@ public: QXmppClientExtension::QXmppClientExtension() : d(new QXmppClientExtensionPrivate) { - d->client = 0; + d->client = nullptr; } /// Destroys a QXmppClient extension. diff --git a/src/client/QXmppClientExtension.h b/src/client/QXmppClientExtension.h index d2e94af9..9f777194 100644 --- a/src/client/QXmppClientExtension.h +++ b/src/client/QXmppClientExtension.h @@ -50,7 +50,7 @@ class QXMPP_EXPORT QXmppClientExtension : public QXmppLoggable public: QXmppClientExtension(); - virtual ~QXmppClientExtension(); + ~QXmppClientExtension() override; virtual QStringList discoveryFeatures() const; virtual QList<QXmppDiscoveryIq::Identity> discoveryIdentities() const; diff --git a/src/client/QXmppConfiguration.cpp b/src/client/QXmppConfiguration.cpp index 817e8ee6..9b56b7c5 100644 --- a/src/client/QXmppConfiguration.cpp +++ b/src/client/QXmppConfiguration.cpp @@ -90,7 +90,6 @@ QXmppConfigurationPrivate::QXmppConfigurationPrivate() , ignoreSslErrors(false) , streamSecurityMode(QXmppConfiguration::TLSEnabled) , nonSASLAuthMechanism(QXmppConfiguration::NonSASLDigest) - , saslAuthMechanism("DIGEST-MD5") { } @@ -501,8 +500,6 @@ void QXmppConfiguration::setNonSASLAuthMechanism( } /// Returns the preferred SASL authentication mechanism. -/// -/// Default value: "DIGEST-MD5" QString QXmppConfiguration::saslAuthMechanism() const { @@ -511,7 +508,8 @@ QString QXmppConfiguration::saslAuthMechanism() const /// Sets the preferred SASL authentication \a mechanism. /// -/// Valid values: "PLAIN", "DIGEST-MD5", "ANONYMOUS", "X-FACEBOOK-PLATFORM" +/// Valid values: "SCRAM-SHA-256", "SCRAM-SHA-1", "DIGEST-MD5", "PLAIN", "ANONYMOUS", +// "X-FACEBOOK-PLATFORM", "X-MESSENGER-OAUTH2", "X-OAUTH2" void QXmppConfiguration::setSaslAuthMechanism(const QString &mechanism) { diff --git a/src/client/QXmppDiscoveryManager.h b/src/client/QXmppDiscoveryManager.h index 16f3d0ac..33c8a660 100644 --- a/src/client/QXmppDiscoveryManager.h +++ b/src/client/QXmppDiscoveryManager.h @@ -41,7 +41,7 @@ class QXMPP_EXPORT QXmppDiscoveryManager : public QXmppClientExtension public: QXmppDiscoveryManager(); - ~QXmppDiscoveryManager(); + ~QXmppDiscoveryManager() override; QXmppDiscoveryIq capabilities(); @@ -65,8 +65,8 @@ public: void setClientInfoForm(const QXmppDataForm &form); /// \cond - QStringList discoveryFeatures() const; - bool handleStanza(const QDomElement &element); + QStringList discoveryFeatures() const override; + bool handleStanza(const QDomElement &element) override; /// \endcond signals: diff --git a/src/client/QXmppEntityTimeManager.h b/src/client/QXmppEntityTimeManager.h index 7d9e91a1..7f16cb17 100644 --- a/src/client/QXmppEntityTimeManager.h +++ b/src/client/QXmppEntityTimeManager.h @@ -42,8 +42,8 @@ public: QString requestTime(const QString& jid); /// \cond - QStringList discoveryFeatures() const; - bool handleStanza(const QDomElement &element); + QStringList discoveryFeatures() const override; + bool handleStanza(const QDomElement &element) override; /// \endcond signals: diff --git a/src/client/QXmppInvokable.cpp b/src/client/QXmppInvokable.cpp index a70378f4..91c646db 100644 --- a/src/client/QXmppInvokable.cpp +++ b/src/client/QXmppInvokable.cpp @@ -58,7 +58,7 @@ QVariant QXmppInvokable::dispatch( const QByteArray & method, const QList< QVari const char *typeName = metaObject()->method(idx).typeName(); int resultType = QMetaType::type(typeName); - void *result = QMetaType::create(resultType, 0); + void *result = QMetaType::create(resultType, nullptr); QGenericReturnArgument ret( typeName, result ); QList<QGenericArgument> genericArgs; diff --git a/src/client/QXmppInvokable.h b/src/client/QXmppInvokable.h index 6edd511e..fbb52978 100644 --- a/src/client/QXmppInvokable.h +++ b/src/client/QXmppInvokable.h @@ -41,9 +41,9 @@ class QXMPP_EXPORT QXmppInvokable : public QObject { Q_OBJECT public: - QXmppInvokable( QObject *parent = 0 ); + QXmppInvokable( QObject *parent = nullptr ); - ~QXmppInvokable(); + ~QXmppInvokable() override; /** * Execute a method on an object. with a set of arguments. This method is reentrant, and the method diff --git a/src/client/QXmppMamManager.h b/src/client/QXmppMamManager.h index ca9aa816..c686c686 100644 --- a/src/client/QXmppMamManager.h +++ b/src/client/QXmppMamManager.h @@ -57,8 +57,8 @@ public: const QXmppResultSetQuery &resultSetQuery = QXmppResultSetQuery()); /// \cond - QStringList discoveryFeatures() const; - bool handleStanza(const QDomElement &element); + QStringList discoveryFeatures() const override; + bool handleStanza(const QDomElement &element) override; /// \endcond signals: diff --git a/src/client/QXmppMessageReceiptManager.cpp b/src/client/QXmppMessageReceiptManager.cpp index 2abe32a6..c010eadf 100644 --- a/src/client/QXmppMessageReceiptManager.cpp +++ b/src/client/QXmppMessageReceiptManager.cpp @@ -29,6 +29,7 @@ #include "QXmppConstants_p.h" #include "QXmppMessage.h" #include "QXmppClient.h" +#include "QXmppUtils.h" /// Constructs a QXmppMessageReceiptManager to handle incoming and outgoing /// message delivery receipts. @@ -54,7 +55,12 @@ bool QXmppMessageReceiptManager::handleStanza(const QDomElement &stanza) // Handle receipts and cancel any further processing. if (!message.receiptId().isEmpty()) { - emit messageDelivered(message.from(), message.receiptId()); + // Buggy clients also mark carbon messages as received; to avoid this + // we check whether sender and receiver have the same bare JID. + if (QXmppUtils::jidToBareJid(message.from()) + != QXmppUtils::jidToBareJid(message.to())) { + emit messageDelivered(message.from(), message.receiptId()); + } return true; } diff --git a/src/client/QXmppMessageReceiptManager.h b/src/client/QXmppMessageReceiptManager.h index a7e5c36d..cef7ca27 100644 --- a/src/client/QXmppMessageReceiptManager.h +++ b/src/client/QXmppMessageReceiptManager.h @@ -40,8 +40,8 @@ public: QXmppMessageReceiptManager(); /// \cond - virtual QStringList discoveryFeatures() const; - virtual bool handleStanza(const QDomElement &stanza); + QStringList discoveryFeatures() const override; + bool handleStanza(const QDomElement &stanza) override; /// \endcond signals: diff --git a/src/client/QXmppMucManager.h b/src/client/QXmppMucManager.h index 58864d01..ca9ea1cb 100644 --- a/src/client/QXmppMucManager.h +++ b/src/client/QXmppMucManager.h @@ -63,14 +63,14 @@ class QXMPP_EXPORT QXmppMucManager : public QXmppClientExtension public: QXmppMucManager(); - ~QXmppMucManager(); + ~QXmppMucManager() override; QXmppMucRoom *addRoom(const QString &roomJid); QList<QXmppMucRoom*> rooms() const; /// \cond - QStringList discoveryFeatures() const; - bool handleStanza(const QDomElement &element); + QStringList discoveryFeatures() const override; + bool handleStanza(const QDomElement &element) override; /// \endcond signals: @@ -82,7 +82,7 @@ signals: protected: /// \cond - void setClient(QXmppClient* client); + void setClient(QXmppClient* client) override; /// \endcond private slots: @@ -123,7 +123,7 @@ public: }; Q_DECLARE_FLAGS(Actions, Action) - ~QXmppMucRoom(); + ~QXmppMucRoom() override; Actions allowedActions() const; bool isJoined() const; diff --git a/src/client/QXmppOutgoingClient.cpp b/src/client/QXmppOutgoingClient.cpp index b9f01188..3e8b4034 100644 --- a/src/client/QXmppOutgoingClient.cpp +++ b/src/client/QXmppOutgoingClient.cpp @@ -25,6 +25,7 @@ #include <QCryptographicHash> #include <QNetworkProxy> #include <QSslSocket> +#include <QSslConfiguration> #include <QUrl> #include <QDnsLookup> @@ -124,14 +125,14 @@ QXmppOutgoingClientPrivate::QXmppOutgoingClientPrivate(QXmppOutgoingClient *qq) , sessionAvailable(false) , sessionStarted(false) , isAuthenticated(false) - , saslClient(0) + , saslClient(nullptr) , streamManagementAvailable(false) , canResume(false) , isResuming(false) , resumePort(0) , clientStateIndicationEnabled(false) - , pingTimer(0) - , timeoutTimer(0) + , pingTimer(nullptr) + , timeoutTimer(nullptr) , q(qq) { } @@ -141,8 +142,11 @@ void QXmppOutgoingClientPrivate::connectToHost(const QString &host, quint16 port q->info(QString("Connecting to %1:%2").arg(host, QString::number(port))); // override CA certificates if requested - if (!config.caCertificates().isEmpty()) - q->socket()->setCaCertificates(config.caCertificates()); + if (!config.caCertificates().isEmpty()) { + QSslConfiguration newSslConfig; + newSslConfig.setCaCertificates(config.caCertificates()); + q->socket()->setSslConfiguration(newSslConfig); + } // respect proxy q->socket()->setProxy(config.networkProxy()); @@ -358,7 +362,7 @@ void QXmppOutgoingClient::handleStart() // reset authentication step if (d->saslClient) { delete d->saslClient; - d->saslClient = 0; + d->saslClient = nullptr; } // reset session information @@ -450,8 +454,12 @@ void QXmppOutgoingClient::handleStanza(const QDomElement &nodeRecv) if (saslAvailable && configuration().useSASLAuthentication()) { // supported and preferred SASL auth mechanisms - QStringList supportedMechanisms = QXmppSaslClient::availableMechanisms(); const QString preferredMechanism = configuration().saslAuthMechanism(); + QStringList supportedMechanisms = QXmppSaslClient::availableMechanisms(); + if (supportedMechanisms.contains(preferredMechanism)) { + supportedMechanisms.removeAll(preferredMechanism); + supportedMechanisms.prepend(preferredMechanism); + } if (configuration().facebookAppId().isEmpty() || configuration().facebookAccessToken().isEmpty()) supportedMechanisms.removeAll("X-FACEBOOK-PLATFORM"); if (configuration().windowsLiveAccessToken().isEmpty()) @@ -462,19 +470,16 @@ void QXmppOutgoingClient::handleStanza(const QDomElement &nodeRecv) // determine SASL Authentication mechanism to use QStringList commonMechanisms; QString usedMechanism; - foreach (const QString &mechanism, features.authMechanisms()) { - if (supportedMechanisms.contains(mechanism)) + foreach (const QString &mechanism, supportedMechanisms) { + if (features.authMechanisms().contains(mechanism)) commonMechanisms << mechanism; } if (commonMechanisms.isEmpty()) { warning("No supported SASL Authentication mechanism available"); disconnectFromHost(); return; - } else if (!commonMechanisms.contains(preferredMechanism)) { - info(QString("Desired SASL Auth mechanism '%1' is not available, selecting first available one").arg(preferredMechanism)); - usedMechanism = commonMechanisms.first(); } else { - usedMechanism = preferredMechanism; + usedMechanism = commonMechanisms.first(); } d->saslClient = QXmppSaslClient::create(usedMechanism, this); diff --git a/src/client/QXmppOutgoingClient.h b/src/client/QXmppOutgoingClient.h index 961dee73..52dae4a1 100644 --- a/src/client/QXmppOutgoingClient.h +++ b/src/client/QXmppOutgoingClient.h @@ -50,11 +50,11 @@ class QXMPP_EXPORT QXmppOutgoingClient : public QXmppStream public: QXmppOutgoingClient(QObject *parent); - ~QXmppOutgoingClient(); + ~QXmppOutgoingClient() override; void connectToHost(); bool isAuthenticated() const; - bool isConnected() const; + bool isConnected() const override; bool isClientStateIndicationEnabled() const; QSslSocket *socket() const { return QXmppStream::socket(); }; @@ -84,13 +84,13 @@ signals: protected: /// \cond // Overridable methods - virtual void handleStart(); - virtual void handleStanza(const QDomElement &element); - virtual void handleStream(const QDomElement &element); + void handleStart() override; + void handleStanza(const QDomElement &element) override; + void handleStream(const QDomElement &element) override; /// \endcond public slots: - virtual void disconnectFromHost(); + void disconnectFromHost() override; private slots: void _q_dnsLookupFinished(); diff --git a/src/client/QXmppRosterManager.h b/src/client/QXmppRosterManager.h index 94750c1a..869b6b42 100644 --- a/src/client/QXmppRosterManager.h +++ b/src/client/QXmppRosterManager.h @@ -67,7 +67,7 @@ class QXMPP_EXPORT QXmppRosterManager : public QXmppClientExtension public: QXmppRosterManager(QXmppClient* stream); - ~QXmppRosterManager(); + ~QXmppRosterManager() override; bool isRosterReceived() const; QStringList getRosterBareJids() const; @@ -80,7 +80,7 @@ public: const QString& resource) const; /// \cond - bool handleStanza(const QDomElement &element); + bool handleStanza(const QDomElement &element) override; /// \endcond public slots: diff --git a/src/client/QXmppRpcManager.h b/src/client/QXmppRpcManager.h index 864dbc1f..0d2afdf0 100644 --- a/src/client/QXmppRpcManager.h +++ b/src/client/QXmppRpcManager.h @@ -73,9 +73,9 @@ public: const QVariant &arg10 = QVariant() ); /// \cond - QStringList discoveryFeatures() const; - virtual QList<QXmppDiscoveryIq::Identity> discoveryIdentities() const; - bool handleStanza(const QDomElement &element); + QStringList discoveryFeatures() const override; + QList<QXmppDiscoveryIq::Identity> discoveryIdentities() const override; + bool handleStanza(const QDomElement &element) override; /// \endcond signals: diff --git a/src/client/QXmppTransferManager.cpp b/src/client/QXmppTransferManager.cpp index 7251b53c..309fe4bf 100644 --- a/src/client/QXmppTransferManager.cpp +++ b/src/client/QXmppTransferManager.cpp @@ -219,17 +219,17 @@ public: QXmppTransferJobPrivate::QXmppTransferJobPrivate() : blockSize(16384), - client(0), + client(nullptr), direction(QXmppTransferJob::IncomingDirection), done(0), error(QXmppTransferJob::NoError), hash(QCryptographicHash::Md5), - iodevice(0), + iodevice(nullptr), method(QXmppTransferJob::NoMethod), state(QXmppTransferJob::OfferState), deviceIsOwn(false), ibbSequence(0), - socksSocket(0) + socksSocket(nullptr) { } @@ -446,8 +446,8 @@ void QXmppTransferJob::terminate(QXmppTransferJob::Error cause) /// \cond QXmppTransferIncomingJob::QXmppTransferIncomingJob(const QString& jid, QXmppClient* client, QObject* parent) : QXmppTransferJob(jid, IncomingDirection, client, parent) - , m_candidateClient(0) - , m_candidateTimer(0) + , m_candidateClient(nullptr) + , m_candidateTimer(nullptr) { } @@ -551,9 +551,9 @@ void QXmppTransferIncomingJob::_q_candidateReady() setState(QXmppTransferJob::TransferState); d->socksSocket = m_candidateClient; - m_candidateClient = 0; + m_candidateClient = nullptr; m_candidateTimer->deleteLater(); - m_candidateTimer = 0; + m_candidateTimer = nullptr; check = connect(d->socksSocket, SIGNAL(readyRead()), this, SLOT(_q_receiveData())); @@ -583,9 +583,9 @@ void QXmppTransferIncomingJob::_q_candidateDisconnected() QString::number(m_candidateHost.port()))); m_candidateClient->deleteLater(); - m_candidateClient = 0; + m_candidateClient = nullptr; m_candidateTimer->deleteLater(); - m_candidateTimer = 0; + m_candidateTimer = nullptr; // try next host connectToNextHost(); @@ -749,7 +749,7 @@ private: QXmppTransferManagerPrivate::QXmppTransferManagerPrivate(QXmppTransferManager *qq) : ibbBlockSize(4096) , proxyOnly(false) - , socksServer(0) + , socksServer(nullptr) , supportedMethods(QXmppTransferJob::AnyMethod) , q(qq) { @@ -762,7 +762,7 @@ QXmppTransferJob* QXmppTransferManagerPrivate::getJobByRequestId(QXmppTransferJo job->d->jid == jid && job->d->requestId == id) return job; - return 0; + return nullptr; } QXmppTransferIncomingJob *QXmppTransferManagerPrivate::getIncomingJobByRequestId(const QString &jid, const QString &id) @@ -777,7 +777,7 @@ QXmppTransferIncomingJob* QXmppTransferManagerPrivate::getIncomingJobBySid(const job->d->jid == jid && job->d->sid == sid) return static_cast<QXmppTransferIncomingJob*>(job); - return 0; + return nullptr; } QXmppTransferOutgoingJob *QXmppTransferManagerPrivate::getOutgoingJobByRequestId(const QString &jid, const QString &id) @@ -1298,7 +1298,7 @@ QXmppTransferJob *QXmppTransferManager::sendFile(const QString &jid, const QStri { if (QXmppUtils::jidToResource(jid).isEmpty()) { warning("The file recipient's JID must be a full JID"); - return 0; + return nullptr; } QFileInfo info(filePath); @@ -1315,7 +1315,7 @@ QXmppTransferJob *QXmppTransferManager::sendFile(const QString &jid, const QStri { warning(QString("Could not read from %1").arg(filePath)); delete device; - device = 0; + device = nullptr; } // hash file @@ -1356,7 +1356,7 @@ QXmppTransferJob *QXmppTransferManager::sendFile(const QString &jid, QIODevice * if (QXmppUtils::jidToResource(jid).isEmpty()) { warning("The file recipient's JID must be a full JID"); - return 0; + return nullptr; } QXmppTransferOutgoingJob *job = new QXmppTransferOutgoingJob(jid, client(), this); diff --git a/src/client/QXmppTransferManager.h b/src/client/QXmppTransferManager.h index 3b571b18..8202ea91 100644 --- a/src/client/QXmppTransferManager.h +++ b/src/client/QXmppTransferManager.h @@ -134,7 +134,7 @@ public: FinishedState = 3 ///< The transfer is finished. }; - ~QXmppTransferJob(); + ~QXmppTransferJob() override; QXmppTransferJob::Direction direction() const; QXmppTransferJob::Error error() const; @@ -225,7 +225,7 @@ class QXMPP_EXPORT QXmppTransferManager : public QXmppClientExtension public: QXmppTransferManager(); - ~QXmppTransferManager(); + ~QXmppTransferManager() override; QString proxy() const; void setProxy(const QString &proxyJid); @@ -237,8 +237,8 @@ public: void setSupportedMethods(QXmppTransferJob::Methods methods); /// \cond - QStringList discoveryFeatures() const; - bool handleStanza(const QDomElement &element); + QStringList discoveryFeatures() const override; + bool handleStanza(const QDomElement &element) override; /// \endcond signals: @@ -262,7 +262,7 @@ public slots: protected: /// \cond - void setClient(QXmppClient* client); + void setClient(QXmppClient* client) override; /// \endcond private slots: diff --git a/src/client/QXmppVCardManager.h b/src/client/QXmppVCardManager.h index fcb16a7f..328fe4a0 100644 --- a/src/client/QXmppVCardManager.h +++ b/src/client/QXmppVCardManager.h @@ -59,7 +59,7 @@ class QXMPP_EXPORT QXmppVCardManager : public QXmppClientExtension public: QXmppVCardManager(); - ~QXmppVCardManager(); + ~QXmppVCardManager() override; QString requestVCard(const QString& bareJid = QString()); @@ -70,8 +70,8 @@ public: bool isClientVCardReceived() const; /// \cond - QStringList discoveryFeatures() const; - bool handleStanza(const QDomElement &element); + QStringList discoveryFeatures() const override; + bool handleStanza(const QDomElement &element) override; /// \endcond signals: diff --git a/src/client/QXmppVersionManager.h b/src/client/QXmppVersionManager.h index effaed17..eb992611 100644 --- a/src/client/QXmppVersionManager.h +++ b/src/client/QXmppVersionManager.h @@ -40,7 +40,7 @@ class QXMPP_EXPORT QXmppVersionManager : public QXmppClientExtension public: QXmppVersionManager(); - ~QXmppVersionManager(); + ~QXmppVersionManager() override; QString requestVersion(const QString& jid); @@ -53,8 +53,8 @@ public: QString clientOs() const; /// \cond - QStringList discoveryFeatures() const; - bool handleStanza(const QDomElement &element); + QStringList discoveryFeatures() const override; + bool handleStanza(const QDomElement &element) override; /// \endcond signals: diff --git a/src/server/QXmppDialback.h b/src/server/QXmppDialback.h index f8da6aeb..5166df51 100644 --- a/src/server/QXmppDialback.h +++ b/src/server/QXmppDialback.h @@ -54,8 +54,8 @@ public: void setType(const QString &type); /// \cond - void parse(const QDomElement &element); - void toXml(QXmlStreamWriter *writer) const; + void parse(const QDomElement &element) override; + void toXml(QXmlStreamWriter *writer) const override; static bool isDialback(const QDomElement &element); /// \endcond diff --git a/src/server/QXmppIncomingClient.cpp b/src/server/QXmppIncomingClient.cpp index b1b95ec5..e563ef1c 100644 --- a/src/server/QXmppIncomingClient.cpp +++ b/src/server/QXmppIncomingClient.cpp @@ -58,9 +58,9 @@ private: }; QXmppIncomingClientPrivate::QXmppIncomingClientPrivate(QXmppIncomingClient *qq) - : idleTimer(0) - , passwordChecker(0) - , saslServer(0) + : idleTimer(nullptr) + , passwordChecker(nullptr) + , saslServer(nullptr) , q(qq) { } @@ -184,9 +184,9 @@ void QXmppIncomingClient::handleStream(const QDomElement &streamElement) { if (d->idleTimer->interval()) d->idleTimer->start(); - if (d->saslServer != 0) { + if (d->saslServer != nullptr) { delete d->saslServer; - d->saslServer = 0; + d->saslServer = nullptr; } // start stream diff --git a/src/server/QXmppIncomingClient.h b/src/server/QXmppIncomingClient.h index 9c1e4afd..374fabd4 100644 --- a/src/server/QXmppIncomingClient.h +++ b/src/server/QXmppIncomingClient.h @@ -41,10 +41,10 @@ class QXMPP_EXPORT QXmppIncomingClient : public QXmppStream Q_OBJECT public: - QXmppIncomingClient(QSslSocket *socket, const QString &domain, QObject *parent = 0); - ~QXmppIncomingClient(); + QXmppIncomingClient(QSslSocket *socket, const QString &domain, QObject *parent = nullptr); + ~QXmppIncomingClient() override; - bool isConnected() const; + bool isConnected() const override; QString jid() const; void setInactivityTimeout(int secs); @@ -56,8 +56,8 @@ signals: protected: /// \cond - void handleStream(const QDomElement &element); - void handleStanza(const QDomElement &element); + void handleStream(const QDomElement &element) override; + void handleStanza(const QDomElement &element) override; /// \endcond private slots: diff --git a/src/server/QXmppIncomingServer.h b/src/server/QXmppIncomingServer.h index f0d1a54a..883d9f01 100644 --- a/src/server/QXmppIncomingServer.h +++ b/src/server/QXmppIncomingServer.h @@ -40,9 +40,9 @@ class QXMPP_EXPORT QXmppIncomingServer : public QXmppStream public: QXmppIncomingServer(QSslSocket *socket, const QString &domain, QObject *parent); - ~QXmppIncomingServer(); + ~QXmppIncomingServer() override; - bool isConnected() const; + bool isConnected() const override; QString localStreamId() const; signals: @@ -54,8 +54,8 @@ signals: protected: /// \cond - void handleStanza(const QDomElement &stanzaElement); - void handleStream(const QDomElement &streamElement); + void handleStanza(const QDomElement &stanzaElement) override; + void handleStream(const QDomElement &streamElement) override; /// \endcond private slots: diff --git a/src/server/QXmppOutgoingServer.h b/src/server/QXmppOutgoingServer.h index 690dff1a..139a2f22 100644 --- a/src/server/QXmppOutgoingServer.h +++ b/src/server/QXmppOutgoingServer.h @@ -43,9 +43,9 @@ class QXMPP_EXPORT QXmppOutgoingServer : public QXmppStream public: QXmppOutgoingServer(const QString &domain, QObject *parent); - ~QXmppOutgoingServer(); + ~QXmppOutgoingServer() override; - bool isConnected() const; + bool isConnected() const override; QString localStreamKey() const; void setLocalStreamKey(const QString &key); @@ -59,9 +59,9 @@ signals: protected: /// \cond - void handleStart(); - void handleStream(const QDomElement &streamElement); - void handleStanza(const QDomElement &stanzaElement); + void handleStart() override; + void handleStream(const QDomElement &streamElement) override; + void handleStanza(const QDomElement &stanzaElement) override; /// \endcond public slots: diff --git a/src/server/QXmppPasswordChecker.h b/src/server/QXmppPasswordChecker.h index f8cf16ef..2bdbc9c5 100644 --- a/src/server/QXmppPasswordChecker.h +++ b/src/server/QXmppPasswordChecker.h @@ -67,7 +67,7 @@ public: TemporaryError }; - QXmppPasswordReply(QObject *parent = 0); + QXmppPasswordReply(QObject *parent = nullptr); QByteArray digest() const; void setDigest(const QByteArray &digest); diff --git a/src/server/QXmppServer.cpp b/src/server/QXmppServer.cpp index 511b32a4..82bc5d37 100644 --- a/src/server/QXmppServer.cpp +++ b/src/server/QXmppServer.cpp @@ -111,8 +111,8 @@ private: }; QXmppServerPrivate::QXmppServerPrivate(QXmppServer *qq) - : logger(0), - passwordChecker(0), + : logger(nullptr), + passwordChecker(nullptr), loaded(false), started(false), q(qq) @@ -166,7 +166,7 @@ bool QXmppServerPrivate::routeData(const QString &to, const QByteArray &data) // if we did not find an outgoing server, // we need to establish the S2S connection - QXmppOutgoingServer *conn = new QXmppOutgoingServer(domain, 0); + QXmppOutgoingServer *conn = new QXmppOutgoingServer(domain, nullptr); conn->setLocalStreamKey(QXmppUtils::generateStanzaHash().toLatin1()); conn->moveToThread(q->thread()); conn->setParent(q); diff --git a/src/server/QXmppServer.h b/src/server/QXmppServer.h index ef4f5a6e..2f9bca8e 100644 --- a/src/server/QXmppServer.h +++ b/src/server/QXmppServer.h @@ -62,8 +62,8 @@ class QXMPP_EXPORT QXmppServer : public QXmppLoggable Q_PROPERTY(QXmppLogger* logger READ logger WRITE setLogger NOTIFY loggerChanged) public: - QXmppServer(QObject *parent = 0); - ~QXmppServer(); + QXmppServer(QObject *parent = nullptr); + ~QXmppServer() override; void addExtension(QXmppServerExtension *extension); QList<QXmppServerExtension*> extensions(); @@ -131,8 +131,8 @@ class QXMPP_EXPORT QXmppSslServer : public QTcpServer Q_OBJECT public: - QXmppSslServer(QObject *parent = 0); - ~QXmppSslServer(); + QXmppSslServer(QObject *parent = nullptr); + ~QXmppSslServer() override; void addCaCertificates(const QList<QSslCertificate> &certificates); void setLocalCertificate(const QSslCertificate &certificate); diff --git a/src/server/QXmppServerExtension.cpp b/src/server/QXmppServerExtension.cpp index 3629f590..81a23b12 100644 --- a/src/server/QXmppServerExtension.cpp +++ b/src/server/QXmppServerExtension.cpp @@ -38,7 +38,7 @@ public: QXmppServerExtension::QXmppServerExtension() : d(new QXmppServerExtensionPrivate) { - d->server = 0; + d->server = nullptr; } QXmppServerExtension::~QXmppServerExtension() diff --git a/src/server/QXmppServerExtension.h b/src/server/QXmppServerExtension.h index ec9edb2a..bfdde5d8 100644 --- a/src/server/QXmppServerExtension.h +++ b/src/server/QXmppServerExtension.h @@ -51,7 +51,7 @@ class QXMPP_EXPORT QXmppServerExtension : public QXmppLoggable public: QXmppServerExtension(); - ~QXmppServerExtension(); + ~QXmppServerExtension() override; virtual QString extensionName() const; virtual int extensionPriority() const; diff --git a/src/server/QXmppServerPlugin.h b/src/server/QXmppServerPlugin.h index a07e7ca8..73b10631 100644 --- a/src/server/QXmppServerPlugin.h +++ b/src/server/QXmppServerPlugin.h @@ -55,11 +55,11 @@ public: /// Creates and returns the specified QXmppServerExtension. /// /// \param key The key for the QXmppServerExtension. - virtual QXmppServerExtension *create(const QString &key) = 0; + QXmppServerExtension *create(const QString &key) override = 0; /// Returns the list of keys supported by this plugin. /// - virtual QStringList keys() const = 0; + QStringList keys() const override = 0; }; #endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index df125219..67ad4966 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -21,11 +21,15 @@ add_simple_test(qxmppcarbonmanager) add_simple_test(qxmppdataform) add_simple_test(qxmppdiscoveryiq) add_simple_test(qxmppentitytimeiq) +add_simple_test(qxmpphttpuploadiq) add_simple_test(qxmppiceconnection) add_simple_test(qxmppiq) add_simple_test(qxmppjingleiq) add_simple_test(qxmppmammanager) +add_simple_test(qxmppmixitem) add_simple_test(qxmppmessage) +add_simple_test(qxmppmessagereceiptmanager) +add_simple_test(qxmppmixiq) add_simple_test(qxmppnonsaslauthiq) add_simple_test(qxmpppresence) add_simple_test(qxmpppubsubiq) diff --git a/tests/qxmppcallmanager/tst_qxmppcallmanager.cpp b/tests/qxmppcallmanager/tst_qxmppcallmanager.cpp index 66a9e431..d5542c8e 100644 --- a/tests/qxmppcallmanager/tst_qxmppcallmanager.cpp +++ b/tests/qxmppcallmanager/tst_qxmppcallmanager.cpp @@ -47,7 +47,7 @@ private: void tst_QXmppCallManager::init() { - receiverCall = 0; + receiverCall = nullptr; } void tst_QXmppCallManager::acceptCall(QXmppCall *call) diff --git a/tests/qxmpphttpuploadiq/tst_qxmpphttpuploadiq.cpp b/tests/qxmpphttpuploadiq/tst_qxmpphttpuploadiq.cpp new file mode 100644 index 00000000..d88a113c --- /dev/null +++ b/tests/qxmpphttpuploadiq/tst_qxmpphttpuploadiq.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2008-2019 The QXmpp developers + * + * Author: + * Linus Jahn <lnj@kaidan.im> + * + * Source: + * https://github.com/qxmpp-project/qxmpp + * + * This file is a part of QXmpp library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + */ + +#include <QObject> +#include "QXmppHttpUploadIq.h" +#include "util.h" + +class tst_QXmppHttpUploadIq : public QObject +{ + Q_OBJECT + +private slots: + void testRequest(); + void testIsRequest_data(); + void testIsRequest(); + void testSlot(); + void testIsSlot_data(); + void testIsSlot(); +}; + +void tst_QXmppHttpUploadIq::testRequest() +{ + const QByteArray xml( + "<iq id=\"step_03\" " + "to=\"upload.montague.tld\" " + "from=\"romeo@montague.tld/garden\" " + "type=\"get\">" + "<request xmlns=\"urn:xmpp:http:upload:0\" " + "filename=\"très cool.jpg\" " + "size=\"23456\" " + "content-type=\"image/jpeg\"/>" + "</iq>" + ); + + QXmppHttpUploadRequestIq iq; + parsePacket(iq, xml); + QCOMPARE(iq.fileName(), QString("très cool.jpg")); + QCOMPARE(iq.size(), 23456); + QCOMPARE(iq.contentType().name(), QString("image/jpeg")); + serializePacket(iq, xml); + + // test setters + iq.setFileName("icon.png"); + QCOMPARE(iq.fileName(), QString("icon.png")); + iq.setSize(23421337); + QCOMPARE(iq.size(), 23421337); + iq.setContentType(QMimeDatabase().mimeTypeForName("image/png")); + QCOMPARE(iq.contentType().name(), QString("image/png")); +} + +void tst_QXmppHttpUploadIq::testIsRequest_data() +{ + QTest::addColumn<QByteArray>("xml"); + QTest::addColumn<bool>("isRequest"); + + QTest::newRow("wrong-stanza") + << QByteArray("<message><request xmlns=\"urn:xmpp:http:upload:0\"/></message>") + << false; + QTest::newRow("empty-iq") + << QByteArray("<iq/>") + << false; + QTest::newRow("wrong-ns") + << QByteArray("<iq><request xmlns=\"some:other:request\"/></iq>") + << false; + QTest::newRow("correct") + << QByteArray("<iq><request xmlns=\"urn:xmpp:http:upload:0\"/></iq>") + << true; +} + +void tst_QXmppHttpUploadIq::testIsRequest() +{ + QFETCH(QByteArray, xml); + QFETCH(bool, isRequest); + + QDomDocument doc; + QCOMPARE(doc.setContent(xml, true), true); + QCOMPARE(QXmppHttpUploadRequestIq::isHttpUploadRequestIq(doc.documentElement()), isRequest); +} + +void tst_QXmppHttpUploadIq::testSlot() +{ + const QByteArray xml( + "<iq id=\"step_03\" " + "to=\"romeo@montague.tld/garden\" " + "from=\"upload.montague.tld\" " + "type=\"result\">" + "<slot xmlns=\"urn:xmpp:http:upload:0\">" + "<put url=\"https://upload.montague.tld/4a771ac1-f0b2-4a4a-970" + "0-f2a26fa2bb67/tr%C3%A8s%20cool.jpg\">" + "<header name=\"Authorization\">Basic Base64String==</header>" + "<header name=\"Cookie\">foo=bar; user=romeo</header>" + "</put>" + "<get url=\"https://download.montague.tld/4a771ac1-f0b2-4a4a-9" + "700-f2a26fa2bb67/tr%C3%A8s%20cool.jpg\"/>" + "</slot>" + "</iq>" + ); + + QXmppHttpUploadSlotIq iq; + parsePacket(iq, xml); + QCOMPARE(iq.putUrl(), QUrl("https://upload.montague.tld/4a771ac1-f0b2-4a4a" + "-9700-f2a26fa2bb67/tr%C3%A8s%20cool.jpg")); + QCOMPARE(iq.getUrl(), QUrl("https://download.montague.tld/4a771ac1-f0b2-4a" + "4a-9700-f2a26fa2bb67/tr%C3%A8s%20cool.jpg")); + QMap<QString, QString> headers; + headers["Authorization"] = "Basic Base64String=="; + headers["Cookie"] = "foo=bar; user=romeo"; + QCOMPARE(iq.putHeaders(), headers); + serializePacket(iq, xml); + + // test setters + iq.setGetUrl(QUrl("https://dl.example.org/user/file")); + QCOMPARE(iq.getUrl(), QUrl("https://dl.example.org/user/file")); + iq.setPutUrl(QUrl("https://ul.example.org/user/file")); + QCOMPARE(iq.putUrl(), QUrl("https://ul.example.org/user/file")); + QMap<QString, QString> emptyMap; + iq.setPutHeaders(emptyMap); + QCOMPARE(iq.putHeaders(), emptyMap); + +} + +void tst_QXmppHttpUploadIq::testIsSlot_data() +{ + QTest::addColumn<QByteArray>("xml"); + QTest::addColumn<bool>("isSlot"); + + QTest::newRow("wrong-stanza") + << QByteArray("<message><slot xmlns=\"urn:xmpp:http:upload:0\"/></message>") + << false; + QTest::newRow("empty-iq") + << QByteArray("<iq/>") + << false; + QTest::newRow("wrong-ns") + << QByteArray("<iq><slot xmlns=\"some:other:slot\"/></iq>") + << false; + QTest::newRow("correct") + << QByteArray("<iq><slot xmlns=\"urn:xmpp:http:upload:0\"/></iq>") + << true; +} + +void tst_QXmppHttpUploadIq::testIsSlot() +{ + QFETCH(QByteArray, xml); + QFETCH(bool, isSlot); + + QDomDocument doc; + QCOMPARE(doc.setContent(xml, true), true); + QCOMPARE(QXmppHttpUploadSlotIq::isHttpUploadSlotIq(doc.documentElement()), isSlot); +} + +QTEST_MAIN(tst_QXmppHttpUploadIq) +#include "tst_qxmpphttpuploadiq.moc" diff --git a/tests/qxmppmessage/tst_qxmppmessage.cpp b/tests/qxmppmessage/tst_qxmppmessage.cpp index 0218bb93..a57ffab2 100644 --- a/tests/qxmppmessage/tst_qxmppmessage.cpp +++ b/tests/qxmppmessage/tst_qxmppmessage.cpp @@ -48,6 +48,11 @@ private slots: void testPrivateMessage(); void testOutOfBandUrl(); void testMessageCorrect(); + void testMessageAttaching(); + void testMix(); + void testEme(); + void testSpoiler(); + void testProcessingHints(); }; void tst_QXmppMessage::testBasic_data() @@ -111,6 +116,12 @@ void tst_QXmppMessage::testBasic() QCOMPARE(message.isReceiptRequested(), false); QCOMPARE(message.receiptId(), QString()); QCOMPARE(message.xhtml(), QString()); + QCOMPARE(message.encryptionMethod(), QXmppMessage::NoEncryption); + QVERIFY(!message.isSpoiler()); + QVERIFY(!message.hasHint(QXmppMessage::NoPermanentStore)); + QVERIFY(!message.hasHint(QXmppMessage::NoStore)); + QVERIFY(!message.hasHint(QXmppMessage::NoCopy)); + QVERIFY(!message.hasHint(QXmppMessage::Store)); serializePacket(message, xml); } @@ -615,5 +626,178 @@ void tst_QXmppMessage::testMessageCorrect() QCOMPARE(message.replaceId(), QString("someotherid")); } +void tst_QXmppMessage::testMessageAttaching() +{ + const QByteArray xml( + "<message to=\"foo@example.com/QXmpp\" from=\"bar@example.com/QXmpp\" type=\"normal\">" + "<body>This is the corrected version.</body>" + "<attach-to xmlns=\"urn:xmpp:message-attaching:1\" id=\"SD24VCzSYQ\"/>" + "</message>"); + + QXmppMessage message; + parsePacket(message, xml); + QCOMPARE(message.attachId(), QString("SD24VCzSYQ")); + serializePacket(message, xml); + + message.setAttachId("someotherid"); + QCOMPARE(message.attachId(), QString("someotherid")); +} + +void tst_QXmppMessage::testMix() +{ + const QByteArray xml( + "<message to=\"hag66@shakespeare.example\" " + "from=\"coven@mix.shakespeare.example/123456\" " + "type=\"groupchat\">" + "<body>Harpier cries: 'tis time, 'tis time.</body>" + "<mix xmlns=\"urn:xmpp:mix:core:1\">" + "<jid>hag66@shakespeare.example</jid>" + "<nick>thirdwitch</nick>" + "</mix>" + "</message>" + ); + + QXmppMessage message; + parsePacket(message, xml); + serializePacket(message, xml); + + QCOMPARE(message.mixUserJid(), QString("hag66@shakespeare.example")); + QCOMPARE(message.mixUserNick(), QString("thirdwitch")); + + message.setMixUserJid("alexander@example.org"); + QCOMPARE(message.mixUserJid(), QString("alexander@example.org")); + message.setMixUserNick("erik"); + QCOMPARE(message.mixUserNick(), QString("erik")); +} + +void tst_QXmppMessage::testEme() +{ + // test standard encryption: OMEMO + const QByteArray xmlOmemo( + "<message to=\"foo@example.com/QXmpp\" from=\"bar@example.com/QXmpp\" type=\"normal\">" + "<body>This message is encrypted with OMEMO, but your client doesn't seem to support that.</body>" + "<encryption xmlns=\"urn:xmpp:eme:0\" namespace=\"eu.siacs.conversations.axolotl\"/>" + "</message>"); + + QXmppMessage messageOmemo; + parsePacket(messageOmemo, xmlOmemo); + QCOMPARE(messageOmemo.encryptionMethodNs(), QString("eu.siacs.conversations.axolotl")); + QCOMPARE(messageOmemo.encryptionMethod(), QXmppMessage::OMEMO); + QCOMPARE(messageOmemo.encryptionName(), QString("OMEMO")); + serializePacket(messageOmemo, xmlOmemo); + + // test custom encryption + const QByteArray xmlCustom( + "<message to=\"foo@example.com/QXmpp\" from=\"bar@example.com/QXmpp\" type=\"normal\">" + "<body>This message is encrypted with CustomCrypt, but your client doesn't seem to support that.</body>" + "<encryption xmlns=\"urn:xmpp:eme:0\" namespace=\"im:example:customcrypt:1\" name=\"CustomCrypt\"/>" + "</message>"); + + QXmppMessage messageCustom; + parsePacket(messageCustom, xmlCustom); + QCOMPARE(messageCustom.encryptionMethodNs(), QString("im:example:customcrypt:1")); + QCOMPARE(messageCustom.encryptionMethod(), QXmppMessage::UnknownEncryption); + QCOMPARE(messageCustom.encryptionName(), QString("CustomCrypt")); + serializePacket(messageCustom, xmlCustom); + + // test setters/getters + QXmppMessage message; + message.setEncryptionMethod(QXmppMessage::LegacyOpenPGP); + QCOMPARE(message.encryptionMethod(), QXmppMessage::LegacyOpenPGP); + QCOMPARE(message.encryptionMethodNs(), QString("jabber:x:encrypted")); + QCOMPARE(message.encryptionName(), QString("Legacy OpenPGP")); + + message.setEncryptionMethodNs("fancyorg:encryption:fancycrypt:0"); + message.setEncryptionName("FancyCrypt"); + QCOMPARE(message.encryptionMethod(), QXmppMessage::UnknownEncryption); + QCOMPARE(message.encryptionMethodNs(), QString("fancyorg:encryption:fancycrypt:0")); + QCOMPARE(message.encryptionName(), QString("FancyCrypt")); +} + +void tst_QXmppMessage::testSpoiler() +{ + // test parsing with hint + const QByteArray xmlWithHint( + "<message to=\"foo@example.com/QXmpp\" from=\"bar@example.com/QXmpp\" type=\"normal\">" + "<body>And at the end of the story, both of them die! It is so tragic!</body>" + "<spoiler xmlns=\"urn:xmpp:spoiler:0\">Love story end</spoiler>" + "</message>"); + + QXmppMessage messageWithHint; + parsePacket(messageWithHint, xmlWithHint); + QVERIFY(messageWithHint.isSpoiler()); + QCOMPARE(messageWithHint.spoilerHint(), QString("Love story end")); + serializePacket(messageWithHint, xmlWithHint); + + // test parsing without hint + const QByteArray xmlWithoutHint( + "<message to=\"foo@example.com/QXmpp\" from=\"bar@example.com/QXmpp\" type=\"normal\">" + "<body>And at the end of the story, both of them die! It is so tragic!</body>" + "<spoiler xmlns=\"urn:xmpp:spoiler:0\"></spoiler>" + "</message>"); + + QXmppMessage messageWithoutHint; + parsePacket(messageWithoutHint, xmlWithoutHint); + QVERIFY(messageWithoutHint.isSpoiler()); + QCOMPARE(messageWithoutHint.spoilerHint(), QString("")); + serializePacket(messageWithoutHint, xmlWithoutHint); + + // test setters + QXmppMessage message; + message.setIsSpoiler(true); + QVERIFY(message.isSpoiler()); + + message.setIsSpoiler(false); + message.setSpoilerHint("test hint"); + QCOMPARE(message.spoilerHint(), QString("test hint")); + QVERIFY(message.isSpoiler()); +} + +void tst_QXmppMessage::testProcessingHints() +{ + const QByteArray xml( + "<message to=\"juliet@capulet.lit/laptop\" " + "from=\"romeo@montague.lit/laptop\" " + "type=\"chat\">" + "<body>V unir avtug'f pybnx gb uvqr zr sebz gurve fvtug</body>" + "<no-permanent-store xmlns=\"urn:xmpp:hints\"/>" + "<no-store xmlns=\"urn:xmpp:hints\"/>" + "<no-copy xmlns=\"urn:xmpp:hints\"/>" + "<store xmlns=\"urn:xmpp:hints\"/>" + "</message>" + ); + + // test parsing + QXmppMessage message; + parsePacket(message, xml); + QVERIFY(message.hasHint(QXmppMessage::NoPermanentStore)); + QVERIFY(message.hasHint(QXmppMessage::NoStore)); + QVERIFY(message.hasHint(QXmppMessage::NoCopy)); + QVERIFY(message.hasHint(QXmppMessage::Store)); + + // test serialization + QXmppMessage message2; + message2.setType(QXmppMessage::Chat); + message2.setFrom(QString("romeo@montague.lit/laptop")); + message2.setTo(QString("juliet@capulet.lit/laptop")); + message2.setBody(QString("V unir avtug'f pybnx gb uvqr zr sebz gurve fvtug")); + message2.addHint(QXmppMessage::NoPermanentStore); + message2.addHint(QXmppMessage::NoStore); + message2.addHint(QXmppMessage::NoCopy); + message2.addHint(QXmppMessage::Store); + serializePacket(message2, xml); + + // test remove hint + message2.removeHint(QXmppMessage::NoCopy); + QVERIFY(!message2.hasHint(QXmppMessage::NoCopy)); + + // test remove all hints + message2.removeAllHints(); + QVERIFY(!message2.hasHint(QXmppMessage::NoPermanentStore)); + QVERIFY(!message2.hasHint(QXmppMessage::NoStore)); + QVERIFY(!message2.hasHint(QXmppMessage::NoCopy)); + QVERIFY(!message2.hasHint(QXmppMessage::Store)); +} + QTEST_MAIN(tst_QXmppMessage) #include "tst_qxmppmessage.moc" diff --git a/tests/qxmppmessagereceiptmanager/tst_qxmppmessagereceiptmanager.cpp b/tests/qxmppmessagereceiptmanager/tst_qxmppmessagereceiptmanager.cpp new file mode 100644 index 00000000..ebcfd18c --- /dev/null +++ b/tests/qxmppmessagereceiptmanager/tst_qxmppmessagereceiptmanager.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2008-2019 The QXmpp developers + * + * Authors: + * Linus Jahn <lnj@kaidan.im> + * + * Source: + * https://github.com/qxmpp-project/qxmpp + * + * This file is a part of QXmpp library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + */ + +#include <QObject> +#include "QXmppClient.h" +#include "QXmppMessageReceiptManager.h" +#include "util.h" + +class tst_QXmppMessageReceiptManager : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + + void testReceipt_data(); + void testReceipt(); + + void handleMessageDelivered(const QString&, const QString&) + { + m_messageDelivered = true; + } + +private: + QXmppMessageReceiptManager m_manager; + bool m_messageDelivered = false; +}; + +void tst_QXmppMessageReceiptManager::initTestCase() +{ + connect(&m_manager, &QXmppMessageReceiptManager::messageDelivered, + this, &tst_QXmppMessageReceiptManager::handleMessageDelivered); +} + +void tst_QXmppMessageReceiptManager::testReceipt_data() +{ + QTest::addColumn<QByteArray>("xml"); + QTest::addColumn<bool>("accept"); + + QTest::newRow("correct") + << QByteArray( + "<message id=\"bi29sg183b4v\" " + "to=\"northumberland@shakespeare.lit/westminster\" " + "from=\"kingrichard@royalty.england.lit/throne\" " + "type=\"normal\">" + "<received xmlns=\"urn:xmpp:receipts\" id=\"richard2-4.1.247\"/>" + "</message>" + ) + << true; + QTest::newRow("from-to-equal") + << QByteArray( + "<message id=\"bi29sg183b4v\" " + "to=\"kingrichard@royalty.england.lit/westminster\" " + "from=\"kingrichard@royalty.england.lit/throne\" " + "type=\"normal\">" + "<received xmlns=\"urn:xmpp:receipts\" id=\"richard2-4.1.247\"/>" + "</message>" + ) + << false; +} + +void tst_QXmppMessageReceiptManager::testReceipt() +{ + m_messageDelivered = false; + + QFETCH(QByteArray, xml); + QFETCH(bool, accept); + + QDomDocument doc; + QCOMPARE(doc.setContent(xml, true), true); + QDomElement element = doc.documentElement(); + + QVERIFY(m_manager.handleStanza(element)); + QCOMPARE(m_messageDelivered, accept); +} + +QTEST_MAIN(tst_QXmppMessageReceiptManager) +#include "tst_qxmppmessagereceiptmanager.moc" diff --git a/tests/qxmppmixiq/tst_qxmppmixiq.cpp b/tests/qxmppmixiq/tst_qxmppmixiq.cpp new file mode 100644 index 00000000..0eb52576 --- /dev/null +++ b/tests/qxmppmixiq/tst_qxmppmixiq.cpp @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2008-2019 The QXmpp developers + * + * Author: + * Linus Jahn <lnj@kaidan.im> + * + * Source: + * https://github.com/qxmpp-project/qxmpp + * + * This file is a part of QXmpp library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + */ + +#include <QObject> + +#include "QXmppMixIq.h" +#include "util.h" + +Q_DECLARE_METATYPE(QXmppIq::Type); +Q_DECLARE_METATYPE(QXmppMixIq::Type); + +class tst_QXmppMixIq : public QObject +{ + Q_OBJECT + +private slots: + void testBase_data(); + void testBase(); + void testDefaults(); + void testSetters(); + void testIsMixIq(); +}; + +void tst_QXmppMixIq::testBase_data() +{ + QByteArray joinC2sSetXml( + "<iq id=\"E6E10350-76CF-40C6-B91B-1EA08C332FC7\" " + "to=\"hag66@shakespeare.example\" " + "from=\"hag66@shakespeare.example/UUID-a1j/7533\" " + "type=\"set\">" + "<client-join xmlns=\"urn:xmpp:mix:pam:1\" channel=\"coven@mix.shakespeare.example\">" + "<join xmlns=\"urn:xmpp:mix:core:1\">" + "<subscribe node=\"urn:xmpp:mix:nodes:messages\"/>" + "<subscribe node=\"urn:xmpp:mix:nodes:presence\"/>" + "<subscribe node=\"urn:xmpp:mix:nodes:participants\"/>" + "<subscribe node=\"urn:xmpp:mix:nodes:info\"/>" + "<nick>third witch</nick>" + "</join>" + "</client-join>" + "</iq>" + ); + QByteArray joinS2sSetXml( + "<iq id=\"E6E10350-76CF-40C6-B91B-1EA08C332FC7\" " + "to=\"coven@mix.shakespeare.example\" " + "from=\"hag66@shakespeare.example\" " + "type=\"set\">" + "<join xmlns=\"urn:xmpp:mix:core:1\">" + "<subscribe node=\"urn:xmpp:mix:nodes:messages\"/>" + "<subscribe node=\"urn:xmpp:mix:nodes:presence\"/>" + "<subscribe node=\"urn:xmpp:mix:nodes:participants\"/>" + "<subscribe node=\"urn:xmpp:mix:nodes:info\"/>" + "<nick>stpeter</nick>" + "</join>" + "</iq>" + ); + QByteArray joinS2sResultXml( + "<iq id=\"E6E10350-76CF-40C6-B91B-1EA08C332FC7\" " + "to=\"hag66@shakespeare.example\" " + "from=\"coven@mix.shakespeare.example\" " + "type=\"result\">" + "<join xmlns=\"urn:xmpp:mix:core:1\" jid=\"123456#coven@mix.shakespeare.example\">" + "<subscribe node=\"urn:xmpp:mix:nodes:messages\"/>" + "<subscribe node=\"urn:xmpp:mix:nodes:presence\"/>" + "<subscribe node=\"urn:xmpp:mix:nodes:participants\"/>" + "<subscribe node=\"urn:xmpp:mix:nodes:info\"/>" + "<nick>third witch</nick>" + "</join>" + "</iq>" + ); + QByteArray joinC2sResultXml( + "<iq id=\"E6E10350-76CF-40C6-B91B-1EA08C332FC7\" " + "to=\"hag66@shakespeare.example/UUID-a1j/7533\" " + "from=\"hag66@shakespeare.example\" " + "type=\"result\">" + "<client-join xmlns=\"urn:xmpp:mix:pam:1\">" + "<join xmlns=\"urn:xmpp:mix:core:1\" " + "jid=\"123456#coven@mix.shakespeare.example\">" + "<subscribe node=\"urn:xmpp:mix:nodes:messages\"/>" + "<subscribe node=\"urn:xmpp:mix:nodes:presence\"/>" + "<subscribe node=\"urn:xmpp:mix:nodes:participants\"/>" + "<subscribe node=\"urn:xmpp:mix:nodes:info\"/>" + "</join>" + "</client-join>" + "</iq>" + ); + QByteArray leaveC2sSetXml( + "<iq id=\"E6E10350-76CF-40C6-B91B-1EA08C332FC7\" " + "to=\"hag66@shakespeare.example\" " + "from=\"hag66@shakespeare.example/UUID-a1j/7533\" " + "type=\"set\">" + "<client-leave xmlns=\"urn:xmpp:mix:pam:1\" channel=\"coven@mix.shakespeare.example\">" + "<leave xmlns=\"urn:xmpp:mix:core:1\"/>" + "</client-leave>" + "</iq>" + ); + QByteArray leaveS2sSetXml( + "<iq id=\"E6E10350-76CF-40C6-B91B-1EA08C332FC7\" " + "to=\"coven@mix.shakespeare.example\" " + "from=\"hag66@shakespeare.example\" " + "type=\"set\">" + "<leave xmlns=\"urn:xmpp:mix:core:1\"/>" + "</iq>" + ); + QByteArray leaveS2sResultXml( + "<iq id=\"E6E10350-76CF-40C6-B91B-1EA08C332FC7\" " + "to=\"hag66@shakespeare.example\" " + "from=\"coven@mix.shakespeare.example\" " + "type=\"result\">" + "<leave xmlns=\"urn:xmpp:mix:core:1\"/>" + "</iq>" + ); + QByteArray leaveC2sResultXml( + "<iq id=\"E6E10350-76CF-40C6-B91B-1EA08C332FC7\" " + "to=\"hag66@shakespeare.example/UUID-a1j/7533\" " + "from=\"hag66@shakespeare.example\" " + "type=\"result\">" + "<client-leave xmlns=\"urn:xmpp:mix:pam:1\">" + "<leave xmlns=\"urn:xmpp:mix:core:1\"/>" + "</client-leave>" + "</iq>" + ); + QByteArray updateSubscriptionSetXml( + "<iq id=\"E6E10350-76CF-40C6-B91B-1EA08C332FC7\" " + "to=\"hag66@shakespeare.example\" " + "from=\"hag66@shakespeare.example/UUID-a1j/7533\" " + "type=\"set\">" + "<update-subscription xmlns=\"urn:xmpp:mix:core:1\">" + "<subscribe node=\"urn:xmpp:mix:nodes:messages\"/>" + "</update-subscription>" + "</iq>" + ); + QByteArray updateSubscriptionResultXml( + "<iq id=\"E6E10350-76CF-40C6-B91B-1EA08C332FC7\" " + "to=\"hag66@shakespeare.example/UUID-a1j/7533\" " + "from=\"hag66@shakespeare.example\" " + "type=\"result\">" + "<update-subscription xmlns=\"urn:xmpp:mix:core:1\" jid=\"hag66@shakespeare.example\">" + "<subscribe node=\"urn:xmpp:mix:nodes:messages\"/>" + "</update-subscription>" + "</iq>" + ); + QByteArray setNickSetXml( + "<iq id=\"E6E10350-76CF-40C6-B91B-1EA08C332FC7\" " + "to=\"hag66@shakespeare.example\" " + "from=\"hag66@shakespeare.example/UUID-a1j/7533\" " + "type=\"set\">" + "<setnick xmlns=\"urn:xmpp:mix:core:1\">" + "<nick>thirdwitch</nick>" + "</setnick>" + "</iq>" + ); + QByteArray setNickResultXml( + "<iq id=\"E6E10350-76CF-40C6-B91B-1EA08C332FC7\" " + "to=\"hag66@shakespeare.example/UUID-a1j/7533\" " + "from=\"hag66@shakespeare.example\" " + "type=\"result\">" + "<setnick xmlns=\"urn:xmpp:mix:core:1\">" + "<nick>thirdwitch</nick>" + "</setnick>" + "</iq>" + ); + QByteArray createXml( + "<iq id=\"E6E10350-76CF-40C6-B91B-1EA08C332FC7\" " + "to=\"hag66@shakespeare.example\" " + "from=\"hag66@shakespeare.example/UUID-a1j/7533\" " + "type=\"set\">" + "<create xmlns=\"urn:xmpp:mix:core:1\" channel=\"coven\"/>" + "</iq>" + ); + QByteArray createWithoutNameXml( + "<iq id=\"E6E10350-76CF-40C6-B91B-1EA08C332FC7\" " + "to=\"hag66@shakespeare.example\" " + "from=\"hag66@shakespeare.example/UUID-a1j/7533\" " + "type=\"set\">" + "<create xmlns=\"urn:xmpp:mix:core:1\"/>" + "</iq>" + ); + QByteArray destroyXml( + "<iq id=\"E6E10350-76CF-40C6-B91B-1EA08C332FC7\" " + "to=\"hag66@shakespeare.example\" " + "from=\"hag66@shakespeare.example/UUID-a1j/7533\" " + "type=\"set\">" + "<destroy xmlns=\"urn:xmpp:mix:core:1\" channel=\"coven\"/>" + "</iq>" + ); + QByteArray emptyXml( + "<iq id=\"E6E10350-76CF-40C6-B91B-1EA08C332FC7\" " + "to=\"hag66@shakespeare.example\" " + "from=\"hag66@shakespeare.example/UUID-a1j/7533\" " + "type=\"set\"/>" + ); + + QStringList emptyNodes; + QStringList defaultNodes; + defaultNodes << "urn:xmpp:mix:nodes:messages" << "urn:xmpp:mix:nodes:presence" + << "urn:xmpp:mix:nodes:participants" << "urn:xmpp:mix:nodes:info"; + + QTest::addColumn<QByteArray>("xml"); + QTest::addColumn<QXmppIq::Type>("type"); + QTest::addColumn<QXmppMixIq::Type>("actionType"); + QTest::addColumn<QString>("jid"); + QTest::addColumn<QString>("channelName"); + QTest::addColumn<QStringList>("nodes"); + QTest::addColumn<QString>("nick"); + + QTest::newRow("join-c2s-set") + << joinC2sSetXml + << QXmppIq::Set + << QXmppMixIq::ClientJoin + << "coven@mix.shakespeare.example" + << "" + << defaultNodes + << "third witch"; + QTest::newRow("join-s2s-set") + << joinS2sSetXml + << QXmppIq::Set + << QXmppMixIq::Join + << "" + << "" + << defaultNodes + << "stpeter"; + QTest::newRow("join-s2s-result") + << joinS2sResultXml + << QXmppIq::Result + << QXmppMixIq::Join + << "123456#coven@mix.shakespeare.example" + << "" + << defaultNodes + << "third witch"; + QTest::newRow("join-c2s-result") + << joinC2sResultXml + << QXmppIq::Result + << QXmppMixIq::ClientJoin + << "123456#coven@mix.shakespeare.example" + << "" + << defaultNodes + << ""; + QTest::newRow("leave-c2s-set") + << leaveC2sSetXml + << QXmppIq::Set + << QXmppMixIq::ClientLeave + << "coven@mix.shakespeare.example" + << "" << emptyNodes << ""; + QTest::newRow("leave-s2s-set") + << leaveS2sSetXml + << QXmppIq::Set + << QXmppMixIq::Leave + << "" << "" << emptyNodes << ""; + QTest::newRow("leave-s2s-result") + << leaveS2sResultXml + << QXmppIq::Result + << QXmppMixIq::Leave + << "" << "" << emptyNodes << ""; + QTest::newRow("leave-c2s-result") + << leaveC2sResultXml + << QXmppIq::Result + << QXmppMixIq::ClientLeave + << "" << "" << emptyNodes << ""; + QTest::newRow("update-subscription-set") + << updateSubscriptionSetXml + << QXmppIq::Set + << QXmppMixIq::UpdateSubscription + << "" + << "" + << (QStringList() << "urn:xmpp:mix:nodes:messages") + << ""; + QTest::newRow("update-subscription-result") + << updateSubscriptionResultXml + << QXmppIq::Result + << QXmppMixIq::UpdateSubscription + << "hag66@shakespeare.example" + << "" + << (QStringList() << "urn:xmpp:mix:nodes:messages") + << ""; + QTest::newRow("setnick-set") + << setNickSetXml + << QXmppIq::Set + << QXmppMixIq::SetNick + << "" << "" << emptyNodes + << "thirdwitch"; + QTest::newRow("setnick-result") + << setNickResultXml + << QXmppIq::Result + << QXmppMixIq::SetNick + << "" << "" << emptyNodes + << "thirdwitch"; + QTest::newRow("create") + << createXml + << QXmppIq::Set + << QXmppMixIq::Create + << "" << "coven" << emptyNodes << ""; + QTest::newRow("create-without-name") + << createWithoutNameXml + << QXmppIq::Set + << QXmppMixIq::Create + << "" << "" << emptyNodes << ""; + QTest::newRow("destroy") + << destroyXml + << QXmppIq::Set + << QXmppMixIq::Destroy + << "" << "coven" << emptyNodes << ""; + QTest::newRow("empty") + << emptyXml + << QXmppIq::Set + << QXmppMixIq::None + << "" << "" << emptyNodes << ""; +} + +void tst_QXmppMixIq::testBase() +{ + QFETCH(QByteArray, xml); + QFETCH(QXmppIq::Type, type); + QFETCH(QXmppMixIq::Type, actionType); + QFETCH(QString, jid); + QFETCH(QString, channelName); + QFETCH(QStringList, nodes); + QFETCH(QString, nick); + + QXmppMixIq iq; + parsePacket(iq, xml); + QCOMPARE(iq.type(), type); + QCOMPARE(iq.actionType(), actionType); + QCOMPARE(iq.jid(), jid); + QCOMPARE(iq.channelName(), channelName); + QCOMPARE(iq.nodes(), nodes); + QCOMPARE(iq.nick(), nick); + serializePacket(iq, xml); +} + +void tst_QXmppMixIq::testDefaults() +{ + QXmppMixIq iq; + QCOMPARE(iq.actionType(), QXmppMixIq::None); + QCOMPARE(iq.jid(), QString()); + QCOMPARE(iq.channelName(), QString()); + QCOMPARE(iq.nodes(), QStringList()); + QCOMPARE(iq.nick(), QString()); +} + +void tst_QXmppMixIq::testSetters() +{ + QXmppMixIq iq; + iq.setActionType(QXmppMixIq::Join); + QCOMPARE(iq.actionType(), QXmppMixIq::Join); + iq.setJid("interestingnews@mix.example.com"); + QCOMPARE(iq.jid(), QString("interestingnews@mix.example.com")); + iq.setChannelName("interestingnews"); + QCOMPARE(iq.channelName(), QString("interestingnews")); + iq.setNodes(QStringList() << "com:example:mix:node:custom"); + QCOMPARE(iq.nodes(), QStringList() << "com:example:mix:node:custom"); + iq.setNick("SMUDO"); + QCOMPARE(iq.nick(), QString("SMUDO")); +} + +void tst_QXmppMixIq::testIsMixIq() +{ + const QByteArray trueXml( + "<iq id=\"E6E10350-76CF-40C6-B91B-1EA08C332FC7\" " + "to=\"hag66@shakespeare.example\" " + "from=\"hag66@shakespeare.example/UUID-a1j/7533\" " + "type=\"set\">" + "<destroy xmlns=\"urn:xmpp:mix:core:1\" channel=\"coven\"/>" + "</iq>" + ); + const QByteArray truePamXml( + "<iq id=\"E6E10350-76CF-40C6-B91B-1EA08C332FC7\" " + "to=\"hag66@shakespeare.example\" " + "from=\"hag66@shakespeare.example/UUID-a1j/7533\" " + "type=\"set\">" + "<client-leave xmlns=\"urn:xmpp:mix:pam:1\" channel=\"coven@mix.shakespeare.example\">" + "<leave xmlns=\"urn:xmpp:mix:core:1\"/>" + "</client-leave>" + "</iq>" + ); + const QByteArray falseXml( + "<iq id=\"E6E10350-76CF-40C6-B91B-1EA08C332FC7\" " + "to=\"hag66@shakespeare.example\" " + "from=\"hag66@shakespeare.example/UUID-a1j/7533\" " + "type=\"set\">" + "<destroy xmlns=\"something:else\" channel=\"coven\"/>" + "</iq>" + ); + + QDomDocument doc; + doc.setContent(trueXml, true); + QDomElement trueElement = doc.documentElement(); + QVERIFY(QXmppMixIq::isMixIq(trueElement)); + + doc.setContent(truePamXml, true); + QDomElement truePamElement = doc.documentElement(); + QVERIFY(QXmppMixIq::isMixIq(truePamElement)); + + doc.setContent(falseXml, true); + QDomElement falseElement = doc.documentElement(); + QVERIFY(!QXmppMixIq::isMixIq(falseElement)); +} + +QTEST_MAIN(tst_QXmppMixIq) +#include "tst_qxmppmixiq.moc" diff --git a/tests/qxmppmixitem/tst_qxmppmixitem.cpp b/tests/qxmppmixitem/tst_qxmppmixitem.cpp new file mode 100644 index 00000000..e1d73744 --- /dev/null +++ b/tests/qxmppmixitem/tst_qxmppmixitem.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2008-2019 The QXmpp developers + * + * Author: + * Linus Jahn <lnj@kaidan.im> + * + * Source: + * https://github.com/qxmpp-project/qxmpp + * + * This file is a part of QXmpp library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + */ + +#include <QObject> +#include <QDomDocument> +#include "QXmppMixItem.h" +#include "QXmppPubSubIq.h" +#include "util.h" + +class tst_QXmppMixItem : public QObject +{ + Q_OBJECT + +private slots: + void testInfo(); + void testIsInfoItem(); + void testParticipant(); + void testIsParticipantItem(); +}; + +void tst_QXmppMixItem::testInfo() +{ + const QByteArray xml( + "<x xmlns=\"jabber:x:data\" type=\"result\">" + "<field type=\"hidden\" var=\"FORM_TYPE\">" + "<value>urn:xmpp:mix:core:1</value>" + "</field>" + "<field type=\"text-single\" var=\"Name\">" + "<value>Witches Coven</value>" + "</field>" + "<field type=\"text-single\" var=\"Description\">" + "<value>A location not far from the blasted heath where the " + "three witches meet</value>" + "</field>" + "<field type=\"jid-multi\" var=\"Contact\">" + "<value>greymalkin@shakespeare.example</value>" + "<value>joan@shakespeare.example</value>" + "</field>" + "</x>" + ); + + QXmppMixInfoItem item; + parsePacket(item, xml); + + QCOMPARE(item.name(), QString("Witches Coven")); + QCOMPARE(item.description(), QString("A location not far from the blasted " + "heath where the three witches meet")); + QCOMPARE(item.contactJids(), QStringList() << "greymalkin@shakespeare.example" + << "joan@shakespeare.example"); + + QXmppElement element = item.toElement(); + serializePacket(element, xml); + + // test setters + item.setName("Skynet Development"); + QCOMPARE(item.name(), QString("Skynet Development")); + item.setDescription("Very cool development group."); + QCOMPARE(item.description(), QString("Very cool development group.")); + item.setContactJids(QStringList() << "somebody@example.org"); + QCOMPARE(item.contactJids(), QStringList() << "somebody@example.org"); +} + +void tst_QXmppMixItem::testIsInfoItem() +{ + QDomDocument doc; + QDomElement element; + + const QByteArray xmlCorrect( + "<x xmlns=\"jabber:x:data\" type=\"result\">" + "<field type=\"hidden\" var=\"FORM_TYPE\">" + "<value>urn:xmpp:mix:core:1</value>" + "</field>" + "</x>" + ); + QCOMPARE(doc.setContent(xmlCorrect, true), true); + element = doc.documentElement(); + QVERIFY(QXmppMixInfoItem::isMixChannelInfo(element)); + + const QByteArray xmlWrong( + "<x xmlns=\"jabber:x:data\" type=\"result\">" + "<field type=\"hidden\" var=\"FORM_TYPE\">" + "<value>other:namespace</value>" + "</field>" + "</x>" + ); + QCOMPARE(doc.setContent(xmlWrong, true), true); + element = doc.documentElement(); + QVERIFY(!QXmppMixInfoItem::isMixChannelInfo(element)); +} + +void tst_QXmppMixItem::testParticipant() +{ + const QByteArray xml( + "<participant xmlns=\"urn:xmpp:mix:core:1\">" + "<jid>hag66@shakespeare.example</jid>" + "<nick>thirdwitch</nick>" + "</participant>" + ); + + QXmppMixParticipantItem item; + parsePacket(item, xml); + + QCOMPARE(item.nick(), QString("thirdwitch")); + QCOMPARE(item.jid(), QString("hag66@shakespeare.example")); + + QXmppElement element = item.toElement(); + serializePacket(element, xml); + + // test setters + item.setNick("thomasd"); + QCOMPARE(item.nick(), QString("thomasd")); + item.setJid("thomas@d.example"); + QCOMPARE(item.jid(), QString("thomas@d.example")); +} + +void tst_QXmppMixItem::testIsParticipantItem() +{ + QDomDocument doc; + QDomElement element; + + const QByteArray xmlCorrect( + "<participant xmlns=\"urn:xmpp:mix:core:1\">" + "</participant>" + ); + QCOMPARE(doc.setContent(xmlCorrect, true), true); + element = doc.documentElement(); + QVERIFY(QXmppMixParticipantItem::isMixParticipantItem(element)); + + const QByteArray xmlWrong( + "<participant xmlns=\"other:namespace:1\">" + "</participant>" + ); + QCOMPARE(doc.setContent(xmlWrong, true), true); + element = doc.documentElement(); + QVERIFY(!QXmppMixParticipantItem::isMixParticipantItem(element)); +} + +QTEST_MAIN(tst_QXmppMixItem) +#include "tst_qxmppmixitem.moc" diff --git a/tests/qxmpppresence/tst_qxmpppresence.cpp b/tests/qxmpppresence/tst_qxmpppresence.cpp index af144f26..4144467a 100644 --- a/tests/qxmpppresence/tst_qxmpppresence.cpp +++ b/tests/qxmpppresence/tst_qxmpppresence.cpp @@ -42,6 +42,7 @@ private slots: void testPresenceWithMucPassword(); void testPresenceWithMucSupport(); void testPresenceWithLastUserInteraction(); + void testPresenceWithMix(); }; void tst_QXmppPresence::testPresence_data() @@ -254,5 +255,32 @@ void tst_QXmppPresence::testPresenceWithLastUserInteraction() QCOMPARE(presence.lastUserInteraction(), another); } +void tst_QXmppPresence::testPresenceWithMix() +{ + const QByteArray xml( + "<presence to=\"hag99@shakespeare.example\" " + "from=\"123435#coven@mix.shakespeare.example/UUID-a1j/7533\">" + "<show>dnd</show>" + "<status>Making a Brew</status>" + "<mix xmlns=\"urn:xmpp:presence:0\">" + "<jid>hecate@shakespeare.example/UUID-x4r/2491</jid>" + "<nick>thirdwitch</nick>" + "</mix>" + "</presence>" + ); + + QXmppPresence presence; + parsePacket(presence, xml); + + QCOMPARE(presence.mixUserJid(), QString("hecate@shakespeare.example/UUID-x4r/2491")); + QCOMPARE(presence.mixUserNick(), QString("thirdwitch")); + serializePacket(presence, xml); + + presence.setMixUserJid("alexander@example.org"); + QCOMPARE(presence.mixUserJid(), QString("alexander@example.org")); + presence.setMixUserNick("erik"); + QCOMPARE(presence.mixUserNick(), QString("erik")); +} + QTEST_MAIN(tst_QXmppPresence) #include "tst_qxmpppresence.moc" diff --git a/tests/qxmpprosteriq/tst_qxmpprosteriq.cpp b/tests/qxmpprosteriq/tst_qxmpprosteriq.cpp index f8db1dea..33d511b5 100644 --- a/tests/qxmpprosteriq/tst_qxmpprosteriq.cpp +++ b/tests/qxmpprosteriq/tst_qxmpprosteriq.cpp @@ -34,6 +34,8 @@ private slots: void testItem(); void testVersion_data(); void testVersion(); + void testMixAnnotate(); + void testMixChannel(); }; void tst_QXmppRosterIq::testItem_data() @@ -114,5 +116,45 @@ void tst_QXmppRosterIq::testVersion() serializePacket(iq, xml); } +void tst_QXmppRosterIq::testMixAnnotate() +{ + const QByteArray xml( + "<iq from=\"juliet@example.com/balcony\" " + "type=\"get\">" + "<query xmlns=\"jabber:iq:roster\">" + "<annotate xmlns=\"urn:xmpp:mix:roster:0\"/>" + "</query>" + "</iq>" + ); + + QXmppRosterIq iq; + parsePacket(iq, xml); + QCOMPARE(iq.mixAnnotate(), true); + serializePacket(iq, xml); + + iq.setMixAnnotate(false); + QCOMPARE(iq.mixAnnotate(), false); +} + +void tst_QXmppRosterIq::testMixChannel() +{ + const QByteArray xml( + "<item jid=\"balcony@example.net\">" + "<channel xmlns=\"urn:xmpp:mix:roster:0\" participant-id=\"123456\"/>" + "</item>" + ); + + QXmppRosterIq::Item item; + parsePacket(item, xml); + QCOMPARE(item.isMixChannel(), true); + QCOMPARE(item.mixParticipantId(), QString("123456")); + serializePacket(item, xml); + + item.setIsMixChannel(false); + QCOMPARE(item.isMixChannel(), false); + item.setMixParticipantId("23a7n"); + QCOMPARE(item.mixParticipantId(), QString("23a7n")); +} + QTEST_MAIN(tst_QXmppRosterIq) #include "tst_qxmpprosteriq.moc" diff --git a/tests/qxmppsasl/tst_qxmppsasl.cpp b/tests/qxmppsasl/tst_qxmppsasl.cpp index 3beabf00..1626c59f 100644 --- a/tests/qxmppsasl/tst_qxmppsasl.cpp +++ b/tests/qxmppsasl/tst_qxmppsasl.cpp @@ -49,6 +49,9 @@ private slots: void testClientFacebook(); void testClientGoogle(); void testClientPlain(); + void testClientScramSha1(); + void testClientScramSha1_bad(); + void testClientScramSha256(); void testClientWindowsLive(); // server @@ -186,7 +189,7 @@ void tst_QXmppSasl::testSuccess() void tst_QXmppSasl::testClientAvailableMechanisms() { - QCOMPARE(QXmppSaslClient::availableMechanisms(), QStringList() << "PLAIN" << "DIGEST-MD5" << "ANONYMOUS" << "X-FACEBOOK-PLATFORM" << "X-MESSENGER-OAUTH2" << "X-OAUTH2"); + QCOMPARE(QXmppSaslClient::availableMechanisms(), QStringList() << "SCRAM-SHA-256" << "SCRAM-SHA-1" << "DIGEST-MD5" << "PLAIN" << "ANONYMOUS" << "X-FACEBOOK-PLATFORM" << "X-MESSENGER-OAUTH2" << "X-OAUTH2"); } void tst_QXmppSasl::testClientBadMechanism() @@ -316,6 +319,94 @@ void tst_QXmppSasl::testClientPlain() delete client; } +void tst_QXmppSasl::testClientScramSha1() +{ + QXmppSaslDigestMd5::setNonce("fyko+d2lbbFgONRv9qkxdawL"); + + QXmppSaslClient *client = QXmppSaslClient::create("SCRAM-SHA-1"); + QVERIFY(client != 0); + QCOMPARE(client->mechanism(), QLatin1String("SCRAM-SHA-1")); + + client->setUsername("user"); + client->setPassword("pencil"); + + // first step + QByteArray response; + QVERIFY(client->respond(QByteArray(), response)); + QCOMPARE(response, QByteArray("n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL")); + + // second step + QVERIFY(client->respond(QByteArray("r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096"), response)); + QCOMPARE(response, QByteArray("c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts=")); + + // third step + QVERIFY(client->respond(QByteArray("v=rmF9pqV8S7suAoZWja4dJRkFsKQ"), response)); + QCOMPARE(response, QByteArray()); + + // any further step is an error + QVERIFY(!client->respond(QByteArray(), response)); + + delete client; +} + +void tst_QXmppSasl::testClientScramSha1_bad() +{ + QXmppSaslDigestMd5::setNonce("fyko+d2lbbFgONRv9qkxdawL"); + + QXmppSaslClient *client = QXmppSaslClient::create("SCRAM-SHA-1"); + QVERIFY(client != 0); + QCOMPARE(client->mechanism(), QLatin1String("SCRAM-SHA-1")); + + client->setUsername("user"); + client->setPassword("pencil"); + + // first step + QByteArray response; + QVERIFY(client->respond(QByteArray(), response)); + QCOMPARE(response, QByteArray("n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL")); + + // no nonce + QVERIFY(!client->respond(QByteArray("s=QSXCR+Q6sek8bf92,i=4096"), response)); + + // no salt + QVERIFY(!client->respond(QByteArray("r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,i=4096"), response)); + + // no iterations + QVERIFY(!client->respond(QByteArray("r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92"), response)); + + delete client; +} + +void tst_QXmppSasl::testClientScramSha256() +{ + QXmppSaslDigestMd5::setNonce("rOprNGfwEbeRWgbNEkqO"); + + QXmppSaslClient *client = QXmppSaslClient::create("SCRAM-SHA-256"); + QVERIFY(client != 0); + QCOMPARE(client->mechanism(), QLatin1String("SCRAM-SHA-256")); + + client->setUsername("user"); + client->setPassword("pencil"); + + // first step + QByteArray response; + QVERIFY(client->respond(QByteArray(), response)); + QCOMPARE(response, QByteArray("n,,n=user,r=rOprNGfwEbeRWgbNEkqO")); + + // second step + QVERIFY(client->respond(QByteArray("r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,s=W22ZaJ0SNY7soEsUEjb6gQ==,i=4096"), response)); + QCOMPARE(response, QByteArray("c=biws,r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,p=dHzbZapWIk4jUhN+Ute9ytag9zjfMHgsqmmiz7AndVQ=")); + + // third step + QVERIFY(client->respond(QByteArray("v=6rriTRBi23WpRR/wtup+mMhUZUn/dB5nLTJRsjl95G4="), response)); + QCOMPARE(response, QByteArray()); + + // any further step is an error + QVERIFY(!client->respond(QByteArray(), response)); + + delete client; +} + void tst_QXmppSasl::testClientWindowsLive() { QXmppSaslClient *client = QXmppSaslClient::create("X-MESSENGER-OAUTH2"); diff --git a/tests/qxmppsocks/tst_qxmppsocks.cpp b/tests/qxmppsocks/tst_qxmppsocks.cpp index 76bffbd4..2632c4c3 100644 --- a/tests/qxmppsocks/tst_qxmppsocks.cpp +++ b/tests/qxmppsocks/tst_qxmppsocks.cpp @@ -48,7 +48,7 @@ private: void tst_QXmppSocks::init() { - m_connectionSocket = 0; + m_connectionSocket = nullptr; m_connectionHostName = QString(); m_connectionPort = 0; } diff --git a/tests/qxmppstreamfeatures/tst_qxmppstreamfeatures.cpp b/tests/qxmppstreamfeatures/tst_qxmppstreamfeatures.cpp index 4646d354..57932e89 100644 --- a/tests/qxmppstreamfeatures/tst_qxmppstreamfeatures.cpp +++ b/tests/qxmppstreamfeatures/tst_qxmppstreamfeatures.cpp @@ -30,7 +30,9 @@ class tst_QXmppStreamFeatures : public QObject private slots: void testEmpty(); + void testRequired(); void testFull(); + void testSetters(); }; void tst_QXmppStreamFeatures::testEmpty() @@ -44,11 +46,27 @@ void tst_QXmppStreamFeatures::testEmpty() QCOMPARE(features.nonSaslAuthMode(), QXmppStreamFeatures::Disabled); QCOMPARE(features.tlsMode(), QXmppStreamFeatures::Disabled); QCOMPARE(features.clientStateIndicationMode(), QXmppStreamFeatures::Disabled); + QCOMPARE(features.registerMode(), QXmppStreamFeatures::Disabled); QCOMPARE(features.authMechanisms(), QStringList()); QCOMPARE(features.compressionMethods(), QStringList()); serializePacket(features, xml); } +void tst_QXmppStreamFeatures::testRequired() +{ + const QByteArray xml( + "<stream:features>" + "<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\">" + "<required/>" + "</starttls>" + "</stream:features>"); + + QXmppStreamFeatures features; + parsePacket(features, xml); + QCOMPARE(features.tlsMode(), QXmppStreamFeatures::Required); + serializePacket(features, xml); +} + void tst_QXmppStreamFeatures::testFull() { const QByteArray xml("<stream:features>" @@ -57,6 +75,7 @@ void tst_QXmppStreamFeatures::testFull() "<auth xmlns=\"http://jabber.org/features/iq-auth\"/>" "<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>" "<csi xmlns=\"urn:xmpp:csi:0\"/>" + "<register xmlns=\"http://jabber.org/features/iq-register\"/>" "<compression xmlns=\"http://jabber.org/features/compress\"><method>zlib</method></compression>" "<mechanisms xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\"><mechanism>PLAIN</mechanism></mechanisms>" "</stream:features>"); @@ -68,10 +87,35 @@ void tst_QXmppStreamFeatures::testFull() QCOMPARE(features.nonSaslAuthMode(), QXmppStreamFeatures::Enabled); QCOMPARE(features.tlsMode(), QXmppStreamFeatures::Enabled); QCOMPARE(features.clientStateIndicationMode(), QXmppStreamFeatures::Enabled); + QCOMPARE(features.registerMode(), QXmppStreamFeatures::Enabled); QCOMPARE(features.authMechanisms(), QStringList() << "PLAIN"); QCOMPARE(features.compressionMethods(), QStringList() << "zlib"); serializePacket(features, xml); } +void tst_QXmppStreamFeatures::testSetters() +{ + QXmppStreamFeatures features; + features.setBindMode(QXmppStreamFeatures::Enabled); + QCOMPARE(features.bindMode(), QXmppStreamFeatures::Enabled); + features.setSessionMode(QXmppStreamFeatures::Enabled); + QCOMPARE(features.sessionMode(), QXmppStreamFeatures::Enabled); + features.setNonSaslAuthMode(QXmppStreamFeatures::Enabled); + QCOMPARE(features.nonSaslAuthMode(), QXmppStreamFeatures::Enabled); + features.setTlsMode(QXmppStreamFeatures::Enabled); + QCOMPARE(features.tlsMode(), QXmppStreamFeatures::Enabled); + features.setClientStateIndicationMode(QXmppStreamFeatures::Enabled); + QCOMPARE(features.clientStateIndicationMode(), QXmppStreamFeatures::Enabled); + features.setClientStateIndicationMode(QXmppStreamFeatures::Enabled); + QCOMPARE(features.clientStateIndicationMode(), QXmppStreamFeatures::Enabled); + features.setRegisterMode(QXmppStreamFeatures::Enabled); + QCOMPARE(features.registerMode(), QXmppStreamFeatures::Enabled); + + features.setAuthMechanisms(QStringList() << "custom-mechanism"); + QCOMPARE(features.authMechanisms(), QStringList() << "custom-mechanism"); + features.setCompressionMethods(QStringList() << "compression-methods"); + QCOMPARE(features.compressionMethods(), QStringList() << "compression-methods"); +} + QTEST_MAIN(tst_QXmppStreamFeatures) #include "tst_qxmppstreamfeatures.moc" diff --git a/tests/qxmpptransfermanager/tst_qxmpptransfermanager.cpp b/tests/qxmpptransfermanager/tst_qxmpptransfermanager.cpp index b34a73a1..3d1d6f00 100644 --- a/tests/qxmpptransfermanager/tst_qxmpptransfermanager.cpp +++ b/tests/qxmpptransfermanager/tst_qxmpptransfermanager.cpp @@ -51,7 +51,7 @@ void tst_QXmppTransferManager::init() { receiverBuffer.close(); receiverBuffer.setData(QByteArray()); - receiverJob = 0; + receiverJob = nullptr; } void tst_QXmppTransferManager::acceptFile(QXmppTransferJob *job) diff --git a/tests/travis/build-and-test b/tests/travis/build-and-test index f677f4a8..65972231 100755 --- a/tests/travis/build-and-test +++ b/tests/travis/build-and-test @@ -1,6 +1,7 @@ #!/bin/sh set -e +HOST_SYSTEM=$(uname -s) CMAKE_ARGS="-DBUILD_TESTS:BOOL=True" case "$CONFIG" in @@ -15,7 +16,7 @@ case "$CONFIG" in ;; esac -if [ "$(uname -s)" = "Darwin" ]; then +if [ $HOST_SYSTEM = "Darwin" ]; then # pick up cmake files from homebrew CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_PREFIX_PATH=/usr/local/opt/qt5" else @@ -33,7 +34,12 @@ fi mkdir build cd build cmake .. $CMAKE_ARGS -make + +if [ $HOST_SYSTEM = "Darwin" ]; then + make -k -j$(sysctl -n hw.logicalcpu) +else + make -k -j$(nproc) +fi # run tests make test diff --git a/tests/util.h b/tests/util.h index d0c55e89..91f59c61 100644 --- a/tests/util.h +++ b/tests/util.h @@ -57,7 +57,7 @@ public: }; /// Retrieves the password for the given username. - QXmppPasswordReply::Error getPassword(const QXmppPasswordRequest &request, QString &password) + QXmppPasswordReply::Error getPassword(const QXmppPasswordRequest &request, QString &password) override { if (m_credentials.contains(request.username())) { @@ -69,7 +69,7 @@ public: }; /// Returns whether getPassword() is enabled. - bool hasGetPassword() const + bool hasGetPassword() const override { return true; }; |
