pax_global_header00006660000000000000000000000064140237056210014512gustar00rootroot0000000000000052 comment=5cf21d869945f6d548805db6d685012cf61e4008 qxmpp-1.4.0/000077500000000000000000000000001402370562100126615ustar00rootroot00000000000000qxmpp-1.4.0/.appveyor.yml000066400000000000000000000005021402370562100153240ustar00rootroot00000000000000platform: x64 environment: matrix: - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 GENERATOR: Visual Studio 15 2017 Win64 QTDIR: C:\Qt\5.11\msvc2017_64 build: parallel: true build_script: - mkdir build - cd build - cmake -G "%GENERATOR%" .. "-DCMAKE_PREFIX_PATH=%QTDIR%" - cmake --build . qxmpp-1.4.0/.clang-format000066400000000000000000000010411402370562100152300ustar00rootroot00000000000000AlignAfterOpenBracket: Align AlignTrailingComments: true BasedOnStyle: WebKit BraceWrapping: AfterClass: true AfterFunction: true BreakBeforeBinaryOperators: None BreakBeforeBraces: Custom BreakConstructorInitializers: BeforeColon DerivePointerAlignment: true DisableFormat: false IncludeBlocks: Regroup IncludeCategories: - Regex: "^.QXmpp.*" Priority: 1 - Regex: "^ * Initial author of QXmpp. Jeremy Lainé * Co-author of QXmpp. Linus Jahn * Current lead developer of QXmpp. Ian Reinhart Geiser * Initial author of Jabber-RPC support. Georg Rudoy <0xd34df00d@gmail.com> * Author of receipts manager, extended stanza addressing. Niels Ole Salscheider * Build system, cleanups. Oliver Goffart * Author of QXmppResultSet. qxmpp-1.4.0/CHANGELOG.md000066400000000000000000000573601402370562100145050ustar00rootroot00000000000000QXmpp 1.4.0 (Mar 15, 2021) -------------------------- QXmpp now requires a C++17 compliant compiler. Qt 6 support has been added. The new compliance suites for 2021 are fulfilled without any changes being required. New features: - Qt 6 port, including many undeprecations and some refactoring (@lnjX) - Add XEP-0224: Attention manager (@lnjX) - Implement MIX-MISC XEP-0407: Invitation message extension (#329, @melvo) - New SCRAM-SHA3-512 and SCRAM-SHA-512 SASL algorithms (#320, @lnjX) - Client: Advertise stream management state (#309, @lnjX) - RosterManager: Cache roster if stream can be resumed (#309, @lnjX) - RosterManager: Enable MIX annotation for roster items (#331, @melvo) Fixes: - Bind IQ errors are now handled properly (@Bonnie080) - Fix cached stream management packets are resent with the wrong account (#335, @lnjX) QXmpp 1.3.2 (Jan 09, 2021) -------------------------- This release only contains some little bug-fixes. Fixes: - TransferManager: Fix undefined behaviour when parsing features (#322, @lichtzeichner) - OutgoingServer: Add missing 'to' and 'from' stream attributes (#324, @Kaffeine) QXmpp 1.3.1 (Jul 20, 2020) -------------------------- The most important change of this release is the fix of CVE-2017-5603. QXmpp is not vulnerable to roster push attacks (CVE-2016-9928). Fixes: - QXmppRosterIq: Set subscriptionType to NotSet correctly (#293, @melvo) - Fix `QXMPP_EXPORT` define when linking statically (#299, @leobasilio) - QXmppMessageReceiptManager: Ignore all error messages (#300, @lnjX) - QXmppCarbonManager: Fix CVE-2017-5603 (missing sender check) (#304, @lnjX) QXmpp 1.3.0 (Apr 06, 2020) -------------------------- QXmpp complys with the XMPP Compliance Suites 2020 (XEP-0423) for client development in the Core, IM and Advanced Mobile suites now. For this prupose it has been updated to RFC6120 and RFC6121. ABI compatibility was kept with this release (apart from classes marked as 'NOT FINALIZED'). New features: - Port QXmppCallManager to GStreamer (#207, @olesalscheider) - Add XEP-0245: The /me Command (#276, @lnjX) - Add XEP-0357: Push Notifications: Enable/disable IQ (#271, @jbbgameich, @zatroxde) - Add XEP-0359: Unique and Stable Stanza IDs (#256, @lnjX) - Add XEP-0428: Fallback Indication (#253, @lnjX) - Update from RFC3920 to RFC6120: * Deprecate PaymentRequired stanza error condition as it was not adopted in RFC6120 (#277, @lnjX) * Add PolicyViolation stanza error condition added in RFC6120 (#279, @lnjX) * Add redirection URI from RFC6120 for Gone and Redirect conditions (#280, @lnjX) * Add 'by' attribute to QXmppStanza::Error from RFC6120 (#283, @lnjX) - Update from RFC3921 to RFC6121: * Add pre-approved presence subscriptions (#285, @lnjX): - Add 'approved' attribute to QXmppRosterIq - Add stream feature for pre-approved presence subscriptions * Add stream feature for roster versioning (#286, @lnjX) - Use QUuid by default to generate unique stanza IDs (#255, @lnjX) - Add roster extension for MIX-PAM (XEP-0405) (#175, @lnjX) - Update MAM to v0.6 (including namespace bump to `urn:xmpp:mam:2`) (#254, #257, @lnjX) - Add not-authorized stream error condition in QXmppOutgoingClient (#191, @henry61024) - Add missing static service discovery features for supported message extensions (#287, @lnjX) - Add utility constructor to QXmppDataForm and QXmppDataForm::Field to make creation of forms easier (#273, @lnjX) Misc: - Make QXmpp work with projects using `QT_NO_KEYWORDS` (#258, @tomcucinotta) - Add hyperlinks to XEP references in the documentation (@lnjX) - Move from Travis-CI to GitHub Actions (#265, @jlaine) - Replace deprecated `qsrand()` and `qrand()` by QRandomGenerator (#267, @melvo) - Add tests for QXmppStanza::Error parsing (#279, @lnjX) Deprecations: - `QXmppStanza::Error::Condition::PaymentRequired`: The error condition was unused and not adopted in RFC6120 QXmpp 1.2.1 (Apr 01, 2020) -------------------------- This release contains some bug fixes that have been found in the last two months. Also, the coverage has slightly improved due to new unit tests for the bug fixes. Fixes: - QXmppRegistrationManager: Fix failed and succeeded signals are both emitted on success (#260, @melvo) - QXmppMessageReceiptManager: Fix receipts are sent on error messages (#269, @TheBluestBird) - QXmppVCardManager: Fix clientVCardReceived() not emitted when IQ is from the bare JID of the user (#281, @melvo, @lnjX) - QXmppRosterManager: Fix 'ask' attribute is included when renaming item (#262, @melvo, @lnjX) - QXmppRosterIq: Add missing implementation of the copy constructor (@lnjX) QXmpp 1.2.0 (Feb 06, 2020) -------------------------- QXmpp now requires Qt 5.7 or later. Unfortunately ABI compatibility was not kept this release again. Code coverage has been improved from 68.93% to 69.55%. New features: - Implement XEP-0077: In-band registration: - Add registration manager with full unit tests (#248, @lnjX) - Add `registered` and `remove` to the IQ (#240, @lnjX) - Implement XEP-0231: Bits of Binary (#230, @lnjX) - Add `QXmppClient::indexOfExtension()` (#241, @lnjX) - Add QXmppStartTlsPacket to replace fixed XML data (#236, @lnjX) - Move TLS code to private QXmppTlsManager (#236, @lnjX) - Add private QXmppInternalClientExtensions to access private part of the client (#243, @lnjX) - Add utility methods to QXmppRegisterIq to create common requests (#247, @lnjX) Fixes: - QXmppMucManager: Make it possible to handle stanzas not handled by the manager (#226, @kollix) - Only send Client State Indication (CSI) states when connected (#232, @lnjX) - Fix no documentation is generated for QXmppStanza::Error and QXmppStreamFeatures (@lnjX) - Fix some doxygen warnings and undocumented Q_PROPERTYs (@lnjX) Misc: - Replace deprecated Q_FOREACH (#210, @lnjX) - Replace deprecated Q_ENUMS with Q_ENUM (#227, @lnjX) - Replace deprecated signal/slots syntax (#237, @jbbgameich) - Switch to Ubuntu Bionic for Travis-CI builds (#210, @lnjX) - Use QSharedDataPointers for QXmppRegisterIq, QXmppPubSubIq, QXmppDiscoveryIq, QXmppMam{Query,Result}Iq, QXmppStreamFeatures (#230, #235, #252, @lnjX) - Refactor QXmppPubSubIq and add missing tests (#235, @lnjX) - Refactor QXmppPresence and add missing tests (#231, @lnjX) - Replace manual xmlns writing by writeDefaultNamespace() (#244, @lnjX) - Use QT_VERSION_CHECK to generate QXMPP_VERSION (#238, @lnjX) - Add clang-format file (#239, @0xd34df00d) QXmpp 1.1.0 (Oct 23, 2019) -------------------------- All new classes and methods in this release are marked in the documentation with *since QXmpp 1.1*. New features: - Add support for SCRAM-SHA-1 and SCRAM-SHA-256 (#183, @jlaine) - Order SASL mechanisms to prefer the most secure (#187, @jlaine) - Add XEP-0334: Message Processing Hints (v0.3.0) (#212, @lnjX, @jaragont, @sam-truscott) - Add XEP-0363: HTTP File Upload (v0.9.0) (#188, @lnjX) - Add XEP-0367: Message Attaching (v0.3.0) (#196, @lnjX) - Add XEP-0369: Mediated Information eXchange (MIX) (v0.14.2) (partially): * Add QXmppMixIq to manage/join channels (#174, @lnjX) * Add QXmppMessage and QXmppPresence extensions for MIX (#175, @lnjX) * Add channel info and participant PubSub/PEP items (#179, @lnjX) - Add XEP-0380: Explicit Message Encryption (v0.3.0) (#199, @lnjX) - Add XEP-0382: Spoiler messages (v0.2.0) (#195, @lnjX) Fixes: - Do not accept receipts from other resources of the used account (#192, lnjX) - cmake: Set minimum version before creating project() and bump to 3.3 (#205, @jbbgameich) Deprecations: - Deprecate QXmppClient extension getters (#214, @lnjX): * `QXmppClient::rosterManager()`: Use `QXmppClient::findExtension()` instead * `QXmppClient::vCardManager()`: Use `QXmppClient::findExtension()` instead * `QXmppClient::versionManager()`: Use `QXmppClient::findExtension()` instead - Refactor data form media element, deprecate `QXmppDataForm::Media` (#222, @lnjX): * `QXmppDataForm::Media`: Use a list of the new `QXmppDataForm::MediaSource` in combination with a `QSize` * `QXmppDataForm::Field::media()` / `QXmppDataForm::Field::setMedia()`: Use `QXmppDataForm::Field::mediaSources()` and `QXmppDataForm::Field::mediaSize()` Misc: - Replace deprecated `qSort()` by `std::sort()` (#206, @jbbgameich) - Do not use deprecated `QSslSocket::setCaCertificates()` (#206, @jbbgameich) - Modernize code by using `nullptr`, `override`, etc. (#204, @jbbgameich) - Move attributes into private d-pointer for future ABI compatibility: * QXmppRosterIq (#175, @lnjX) * QXmppStanza::Error (#203, @lnjX) - Use raw literals, range based loops and `auto` (#224, @jbbgameich) QXmpp 1.0.1 (Oct 14, 2019) -------------------------- - Fix potential SEGFAULT on connection error (#216, @0xd34df00d) - Fix `SO_VERSION` to 1: ABI has changed since last minor release (#185, @tehnick) - Add CMake option for internal tests (`BUILD_INTERNAL_TESTS`) (#184, @jlaine) QXmpp 1.0.0 (Jan 8, 2019) ------------------------- New features: - Add XEP-0066: Out of Band Data (partially) (#167, @lnjX) - Add XEP-0198: Stream Management (#99, @olesalscheider) - Add XEP-0237: Roster Versioning (#142, @LightZam) - Add XEP-0280: Message Carbons (#88, @fbeutel) - Add XEP-0308: Last Message Correction (#170, @lnjX) - Add XEP-0313: Message Archive Management (#120, @olesalscheider) - Add XEP-0319: Last User Interaction in Presence (#171, @lnjX) - Add XEP-0352: Client State Indication (#159, @fbeutel, @lnjX) - Auto-connect to next DNS-SRV record server on connection failure (#105, @kollix) - QXmppVersionManager: Use QSysInfo to determine default OS (#168, @lnjX) - QXmppDiscoveryManager: Default to `phone` type on mobile platforms (#168, @lnjX) - CMake based build system (#131, @olesalscheider) - Add BUILD_SHARED option (#160, @LightZam) - Use C++11 compiler standard (@jlaine) Fixes: - Do not ignore SSL errors by default (#113), if you need to deal with broken SSL configurations, set QXmppConfiguration::ignoreSslErrors to true. (@jlaine) - Disable tests that require QXMPP_AUTOTEST_EXPORT (fixes #149) (@jlaine) - Fix QXmppSslServer::incomingConnection signature (#131, @olesalscheider) - Add missed variables initialization in constructors of few classes (#122, @tehnick) Tests: - travis: Test builds with clang (@0xd34df00d) - travis: Switch to Ubuntu Xenial (#151, @tehnick) - tests: Generate coverage repot (@jlaine) - Build examples by default Deprecations: - Drop Qt4 support (#131, @olesalscheider) - Remove example_4 / GuiClient (#131, @olesalscheider) QXmpp 0.9.3 (Dec 3, 2015) ------------------------- - Add QXmppIceConnection::gatheringState property. - Improve QXmppTransferManager::sendFile's handling of QIODevice ownership. - Fix QXmppTransferManagerFix convering filename to a QUrl. QXmpp 0.9.2 (Sep 2, 2015) ------------------------- - Fix build error for debug builds. - Allow QXmppJingleIq to have multiple contents. QXmpp 0.9.1 (Aug 30, 2015) -------------------------- - Fix build error when VPX support is enabled (issue 71). QXmpp 0.9.0 (Aug 28, 2015) -------------------------- - Fix phone numbers incorrectly read from / written to vCard as "PHONE" element instead of "TEL" (issue 65). - Make QXmppClient::connectToServer(QXmppConfiguration, QXmppPresence) a slot (issue 63). - Correctly receive data immediately following a SOCKS5 message (issue 64). - Make QXmppStream handle end of incoming stream (issue 70). - Add unit tests for QXmppCallManager and QXmppTransferManager. - Improve ICE implementation to follow RFC 5245 more closely and hide implementation details from public API. QXmpp 0.8.3 (Mar 13, 2015) -------------------------- - Add a QXmppClient::sslErrors signal to report SSL errors. - Handle broken servers which send "bad-auth" instead of "not-authorized". - Fix a compilation issue with Qt 5.5 due to a missing header include. - Do not install test cases. - Remove trailing comma after last item in enums. QXmpp 0.8.2 (Jan 7, 2015) ------------------------- - The previous release was missing an update to the VERSION definition, resulting in stale pkg-config files. This release fixes this issue. - Refactor HTML documentation so that "make docs" works in out-of-source builds. - Add support for Opus audio codec. - Enable error concealment for VPX video codec. QXmpp 0.8.1 (Dec 19, 2014) -------------------------- - Use QString() instead of "" for default methods arguments, to enable building project which use QT_NO_CAST_FROM_ASCII. - Add support for legacy SSL. - Add XEP-0333: Chat Markers attributes to QXmppMessage. - Add QXmppClient::socketErrorString to retrieve socket error string. - Add equality/inequality operators for QXmppVCardIq. - Add "make check" command to run tests. QXmpp 0.8.0 (Mar 26, 2014) -------------------------- - Fix QXmppServer incoming connections with Qt5 (issue 175). - Support for QXmppMessage extensions having tag names other than 'x'. - Support for retrieving the source QDomElement that has been used to initialize a QXmppElement. - Add organizations info interface to QXmppVCardIq. - Remove deprecated QXmppPresence::Status type. QXmpp 0.7.6 (Mar 9, 2013) ------------------------- - Add QXmppClient::insertExtension to insert an extension at a given index. - Disable Facebook / Google / Facebook specific mechanisms if we do not have the corresponding credentials. QXmpp 0.7.5 (Jan 11, 2013) -------------------------- - Replace toAscii/fromAscii with toLatin1/fromLatin1 for Qt 5 compatibility. - Fix build using clang in pedantic mode. QXmpp 0.7.4 (Oct 1, 2012) ------------------------- - Add XEP-0249: Direct MUC Invitations attributes to QXmppMessage. - Add XEP-0045: Multi-User Chat attributes to QXmppPresence. - Improve GuiClient, stop using deprecated APIs. - Improve QXmppServer: * Move statistics to a counter / gauge system. * Make it possible to call listenForClients and listenForServers multiple times to supported multiple IP address / ports. - Improve QXmppTransferManager: * Change third argument of QXmppTransferManager::sendFile to a description. * Enable file transfer using IPv6. * Allow StreamHost::host to contain a host name. QXmpp 0.7.3 (Sep 7, 2012) ------------------------- - Fix QXmppMucRoom::name(), only consider discovery IQs from the room. QXmpp 0.7.2 (Sep 6, 2012) ------------------------- - Handle Error replies in QXmppDiscoveryManager so that library users can know about errors. - If building with Qt 5, use Qt's QDnsLookup instead of our backport. - Improve MUC scriptability: * Add QXmppMucRoom::ban() to ban users. * Add QXmppMucRoom::name() to get the room's human-readable name. * Add QXmppMucRoom::participantFullJid() to lookup an occupant full JID. - With Qt >= 4.8, verify peer SSL certificate against domain name as specified by RFC 3920. - Add support for X-OAUTH2 authentication for Google Talk. - Add links to RFCs in generated HTML documentation. QXmpp 0.7.1 (Sep 3, 2012) ------------------------- - Fix export of QXmppVCardPhone class. QXmpp 0.7.0 (Sep 3, 2012) ------------------------- - New XEPs: * XEP-0033: Extended Stanza Addressing - Remove deprecated APIs: * QXmppRosterManager::rosterChanged() * QXmppConfiguration::sASLAuthMechanism() - Improve vCard support: * Add support for free-form descriptive text. * Make it possible to have several addresses. * Make it possible to have several e-mail addresses. * Make it possible to have several phone numbers. - Make it possible to set the client's extended information form (XEP-0128). - Make sure QXmppDiscoveryManager only emits results. - Fix XEP-0115 verification strings (remove duplicate features, sort form values) - Fix issues: * Issue 144: QXmppBookmarkConference autojoin parsing - Add support for see-other-host server change. - Add support for X-MESSENGER-OAUTH2 authentication for Windows Live Messenger. - Make it possible to disable non-SASL authentication. - Add QXmppClient::isAuthenticated() to query whether authentication has been performed. QXmpp 0.6.3 (Jul 24, 2012) -------------------------- - Fix regression in X-FACEBOOK-PLATFORM authentication. QXmpp 0.6.2 (Jul 22, 2012) -------------------------- - New XEPs * XEP-0071: XHTML-IM - Improve SASL code test coverage. - Improve QXmppMessage test coverage. - Add a "reason" argument to QXmppRosterManager's subscription methods. - Refactor QXmppPresence: * add availableStatusType(), priority(), statusText() * deprecate QXmppPresence::Status - Remove deprecated QXmppRosterManager::removeRosterEntry(). QXmpp 0.6.1 (Jul 20, 2012) -------------------------- - New XEPs * XEP-0221: Data Forms Media Element - Fix data form title/instructions XML serialization. - Remove confusing QXmppPresence::Status::Offline status type. - Deprecate QXmppConfiguration::setSASLAuthMechanism(), replaced by the string-based QXmppConfiguration::setSaslAuthMechanism(). - Fix issues: * Issue 111: QXmppPresence::Status::getTypeStr() gives warning if type is invisible * Issue 126: Modularize SASL mechanisms QXmpp 0.5.0 (Jul 18, 2012) -------------------------- - New XEPs * XEP-0059: Result Set Management - Build a shared library by default. - Advertise support for XEP-0249: Direct MUC Invitations - Make QXmppTransferManager fully asynchronous. - Remove QXmppPacket class. - Move utility methods to a QXmppUtils class. - Remove QXmppReconnectionManager, QXmppClient handles reconnections. - Improve QXmppArchiveManager to allow paginated navigation (Olivier Goffart). - Only emit QXmppVersionManager::versionReceived() for results. - Remove deprecated QXmppClient::discoveryIqReceived() signal. - Fix issues: * Issue 64: Compile qxmpp as shared library by default * Issue 79: Export classes for Visual C++ Compiler * Issue 140: Proper XEP-0115 ver string generation with dataforms * Issue 142: qxmpp does not build in Qt5 QXmpp 0.4.0 (Apr 12, 2012) -------------------------- - New XEPs * XEP-0048: Bookmarks * XEP-0184: Message Delivery Receipts * XEP-0224: Attention - Remove deprecated "get*" getter accessors from: QXmppClient QXmppConfiguration QXmppMessage QXmppPresence QXmppIq QXmppStanza QXmppVCardIq QXmppRosterIq - Remove deprecated headers: * QXmppRoster.h * QXmppVCard.h - Add TURN support for VoIP calls to use a relay in double-NAT network topologies. - Overhaul Multi-User Chat support to make it easier and more fully featured. - Improve QXmppServer packet routing performance. - Add support for X-FACEBOOK-PLATFORM SASL method. - Improve XEP-0136 support to enable archive deletion. - Set default keep-alive timeout to 20 seconds, enables detection of broken connections. - Make install path configurable using the PREFIX variable instead of Qt's installation path. - Make it possible to build a shared library by invoking "qmake QXMPP_LIBRARY_TYPE=lib". - Fix issues: * Issue 95: Patch for several utility methods in RosterManager * Issue 103: Does not compile for Symbian^3 with NokiaQtSDK 1.1 Beta * Issue 105: Initial presence is set before the roster request * Issue 106: QXmppClient can't override Qt's set of trusted SSL CAs * Issue 109: Patch for XEP-0224 (Attention) * Issue 113: qxmpp.pc sets incorrect include path * Issue 116: sessionStarted not set for non-SASL connections * Issue 119: ICE negotiation time out after successful ICE check * Issue 120: QXmppIceComponent doesn't accept interfaces with 255.255.255.255 netmask as a local candidate * Issue 132: [FreeBSD]: build error * Issue 135: qxmpp won't reconnect when disconnected QXmpp 0.3.0 (Mar 05, 2011) ------------------------ - New XEPs * XEP-0153: vCard-Based Avatars * XEP-0202: Entity Time - New Classes * QXmppClientExtension: base class for QXmppClient extensions (managers) * QXmppServer: base class for building XMPP servers * QXmppServerExtension: base class for QXmppServer extensions * QXmppDiscoveryManager: manager class for XEP-0030: Service Discovery * QXmppVersionManager: manager class for XEP-0092: Software Version * QXmppIceConnection: class representing an Interactive Connectivity Establishment (ICE) over UDP "connection" * QXmppRtpChannel: class representing an RTP audio channel for VoIP calls - Refactor QXmppVCardManager to use QXmppClientExtension - New Examples * example_9_vCard: vCard handling example * GuiClient: Graphical chat client, test bench for QXmpp functionalities - Deprecation * QXmppVCard class name changed to QXmppVCardIq * Signal QXmppClient::discoveryIqReceived in favour of QXmppDiscoveryManager::infoReceived and QXmppDiscoveryManager::itemsReceived - Removal Extensions QXmppArchiveManager, QXmppMucManager, QXmppCallManager, QXmppTransferManager will not load by default. Therefore following functions to provide the reference have been removed. QXmppClient::transferManager() QXmppClient::archiveManager() QXmppClient::callManager() QXmppClient::mucManager() Note: Once should use QXmppClient::addExtension() and QXmppClient::findExtension() to load or enable these extensions. - Add support for DNS SRV lookups, meaning you can connect to nearly all servers using only a JID and a password. - Improve support for SASL authentication, with a verification of the second challenge message sent by the server. - Add support for the birthday and URL attributes in vCards. - Improve STUN support for VoIP calls by detecting server-reflexive address. - Add QXMPP_VERSION and QXmppVersion() for compile and run time version checks. - Improve code documentation coverage and quality. - Remove dependency on QtGui, making it easier to write console applications. - Fix MSVC 2005 and 2008 build issues. - Fix Symbian build issues, add DNS SRV support for Symbian devices. QXmpp 0.2.0 (Aug 22, 2010) -------------------------- - New XEPs * XEP-0030: Service Discovery * XEP-0045: Multi-User Chat * XEP-0047: In-Band Bytestreams * XEP-0054: vcard-temp * XEP-0065: SOCKS5 Bytestreams * XEP-0078: Non-SASL Authentication * XEP-0082: XMPP Date and Time Profiles * XEP-0085: Chat State Notifications * XEP-0091: Legacy Delayed Delivery * XEP-0092: Software Version * XEP-0095: Stream Initiation * XEP-0096: SI File Transfer * XEP-0115: Entity Capabilities * XEP-0128: Service Discovery Extensions * XEP-0166: Jingle * XEP-0167: Jingle RTP Sessions * XEP-0199: XMPP Ping * XEP-0203: Delayed Delivery * XEP-0009: Jabber-RPC * XEP-0004: Data Forms - New XEPs (Initial Support) * XEP-0136: Message Archiving * XEP-0176: Jingle ICE-UDP Transport Method [Experimental] - New authentication schemes * DIGEST-MD5 * SASL * NonSASL * Anonymous - Add doxygen documentation - Add targets in *.pro file for packaging, installing and generating documentation - Use QXmlStreamWriter while creating stanzas to be sent to the server - Clean up getter accessors from "getFoo" to "foo" - Add proper file transfer management - Add support for keep-alive pings - Report authentication errors - Automatic reconnection mechanism - Test suite for stanza parsing/serialisation - Refactor the logging code - Add proxy support - Fixed compile time warning messages - New examples - Support for attaching an extension element to messages and presences (QXmppElement) - Move parsing to the stanzas itself QXmppStanza::parse() - QXMPP_NO_GUI define to remove dependency on QtGui - Change QXmppRoster to QXmppRosterManager to have a consistent API QXmpp 0.1 (Jun 14, 2009) ------------------------ - First public release qxmpp-1.4.0/CMakeLists.txt000066400000000000000000000036401402370562100154240ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.3) project(qxmpp VERSION 1.4.0) set(SO_VERSION 3) # C++ standard settings: set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/modules") find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Network Xml) find_package(Qt${QT_VERSION_MAJOR} 5.7.0 REQUIRED COMPONENTS Core Network Xml) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) include(GNUInstallDirs) option(BUILD_TESTS "Build tests." ON) option(BUILD_INTERNAL_TESTS "Build internal tests." OFF) option(BUILD_DOCUMENTATION "Build API documentation." OFF) option(BUILD_EXAMPLES "Build examples." ON) option(WITH_GSTREAMER "Build with GStreamer support for Jingle" OFF) add_definitions( -DQT_DISABLE_DEPRECATED_BEFORE=0x050F00 -DQURL_NO_CAST_FROM_STRING -DQT_NO_CAST_TO_ASCII -DQT_NO_FOREACH ) add_subdirectory(src) if(BUILD_TESTS) enable_testing() add_subdirectory(tests) endif() if(BUILD_DOCUMENTATION) add_subdirectory(doc) endif() if(BUILD_EXAMPLES) add_subdirectory(examples) endif() include(CMakePackageConfigHelpers) configure_package_config_file( QXmppConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/QXmppConfig.cmake INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/qxmpp" ) write_basic_package_version_file( ${CMAKE_CURRENT_BINARY_DIR}/QXmppConfigVersion.cmake VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/QXmppConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/QXmppConfigVersion.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/qxmpp" COMPONENT Devel ) # Generate qxmpp.pc configure_file(${CMAKE_CURRENT_SOURCE_DIR}/qxmpp.pc.in ${CMAKE_CURRENT_BINARY_DIR}/qxmpp.pc @ONLY) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/qxmpp.pc DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" COMPONENT Devel ) qxmpp-1.4.0/LICENSE.LGPL000066400000000000000000000635041402370562100144330ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! qxmpp-1.4.0/QXmppConfig.cmake.in000066400000000000000000000001421402370562100164600ustar00rootroot00000000000000@PACKAGE_INIT@ include("${CMAKE_CURRENT_LIST_DIR}/QXmpp.cmake") check_required_components(QXmpp) qxmpp-1.4.0/README.md000066400000000000000000000073251402370562100141470ustar00rootroot00000000000000# QXmpp [![XMPP:2021 Client IM Mobile+ Compliance Badge](https://img.shields.io/badge/XMPP%3A2021%20Client-Core%20IM%20Mobile%2B-green)][xmpp-compliance] [![Build Status](https://github.com/qxmpp-project/qxmpp/workflows/tests/badge.svg)](https://github.com/qxmpp-project/qxmpp/actions) [![Code Coverage](https://img.shields.io/codecov/c/github/qxmpp-project/qxmpp.svg)](https://codecov.io/gh/qxmpp-project/qxmpp) [![Latest release](https://img.shields.io/github/v/release/qxmpp-project/qxmpp)](https://github.com/qxmpp-project/qxmpp/releases/latest) [![Website](https://img.shields.io/website?down_message=offline&label=documentation&up_message=online&url=https%3A%2F%2Fdoc.qxmpp.org%2F)][qxmpp-doc] QXmpp is a cross-platform C++ XMPP client and server library. It is written in C++ and uses Qt framework. QXmpp strives to be as easy to use as possible, the underlying TCP socket, the core XMPP RFCs (RFC6120 and RFC6121) and XMPP extensions have been nicely encapsulated into classes. QXmpp is ready to build XMPP clients complying with the [XMPP Compliance Suites 2021][xmpp-compliance] for IM and Advanced Mobile. It comes with full API documentation, automatic tests and some examples. QXmpp uses Qt extensively, and as such users need to a have working knowledge of C++ and Qt basics (Signals and Slots and Qt data types). Qt is the only third party library which is required to build QXmpp, but 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 ============== QXmpp requires Qt 5.7 or higher with SSL enabled. It uses CMake as build system. Build from command line: mkdir build cd build cmake .. cmake --build . You can pass the following arguments to CMake: BUILD_SHARED to build with shared type library, otherwise static (default: true) BUILD_DOCUMENTATION to build the documentation (default: false) BUILD_EXAMPLES to build the examples (default: true) BUILD_TESTS to build the unit tests (default: true) WITH_GSTREAMER to enable audio/video over jingle (default: false) Installing QXmpp ================ After building QXmpp, you can install the Headers, Libraries and Documentation using the following command: Install from command line: cmake --build . --target install Examples ======== Look at the example directory for various examples. Here is a description of a few. * *example_0_connected* This example just connects to the xmpp server and start receiving presences (updates) from the server. After running this example, you can see this user online, if it's added in your roster (friends list). * *example_1_echoClient* 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. Documentation ============= You can find the API documentation for the latest stable QXmpp version here: https://doc.qxmpp.org/ The API documentation of the master branch is also available: https://doc.qxmpp.org/qxmpp-dev/ Supported Platforms =================== It should work on all the platforms supported by Qt. For a complete list of platforms support by Qt, see: https://doc.qt.io/qt-5/supported-platforms.html How to report a bug =================== If you think you have found a bug in QXmpp, we would like to hear about it so that we can fix it. Before reporting a bug, please check if the issue is already know at: https://github.com/qxmpp-project/qxmpp/issues [xmpp-compliance]: https://xmpp.org/extensions/xep-0443.html [qxmpp-doc]: https://doc.qxmpp.org/ qxmpp-1.4.0/cmake/000077500000000000000000000000001402370562100137415ustar00rootroot00000000000000qxmpp-1.4.0/cmake/modules/000077500000000000000000000000001402370562100154115ustar00rootroot00000000000000qxmpp-1.4.0/cmake/modules/FindGLIB2.cmake000066400000000000000000000067721402370562100200270ustar00rootroot00000000000000# - Try to find the GLIB2 libraries # Once done this will define # # GLIB2_FOUND - system has glib2 # GLIB2_INCLUDE_DIR - the glib2 include directory # GLIB2_LIBRARIES - glib2 library # Copyright (c) 2008 Laurent Montel, # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if(GLIB2_INCLUDE_DIR AND GLIB2_LIBRARIES) # Already in cache, be silent set(GLIB2_FIND_QUIETLY TRUE) endif(GLIB2_INCLUDE_DIR AND GLIB2_LIBRARIES) if (NOT WIN32) find_package(PkgConfig QUIET) if(PKG_CONFIG_FOUND) pkg_check_modules(PKG_GLIB QUIET glib-2.0) endif() endif(NOT WIN32) find_path(GLIB2_MAIN_INCLUDE_DIR glib.h PATH_SUFFIXES glib-2.0 HINTS ${PKG_GLIB_INCLUDE_DIRS} ${PKG_GLIB_INCLUDEDIR}) # search the glibconfig.h include dir under the same root where the library is found find_library(GLIB2_LIBRARIES NAMES glib-2.0 HINTS ${PKG_GLIB_LIBRARY_DIRS} ${PKG_GLIB_LIBDIR}) find_path(GLIB2_INTERNAL_INCLUDE_DIR glibconfig.h PATH_SUFFIXES glib-2.0/include ../lib/glib-2.0/include HINTS ${PKG_GLIB_INCLUDE_DIRS} ${PKG_GLIB_LIBRARIES} ${CMAKE_SYSTEM_LIBRARY_PATH}) set(GLIB2_INCLUDE_DIR ${GLIB2_MAIN_INCLUDE_DIR}) # not sure if this include dir is optional or required # for now it is optional if(GLIB2_INTERNAL_INCLUDE_DIR) set(GLIB2_INCLUDE_DIR ${GLIB2_INCLUDE_DIR} ${GLIB2_INTERNAL_INCLUDE_DIR}) endif(GLIB2_INTERNAL_INCLUDE_DIR) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GLIB2 DEFAULT_MSG GLIB2_LIBRARIES GLIB2_MAIN_INCLUDE_DIR) mark_as_advanced(GLIB2_INCLUDE_DIR GLIB2_LIBRARIES) find_program(GLIB2_GENMARSHAL_UTIL glib-genmarshal) macro(glib2_genmarshal output_name) file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/genmarshal_tmp) foreach(_declaration ${ARGN}) file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/genmarshal_tmp "${_declaration}\n") endforeach() add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${output_name}.h ${CMAKE_CURRENT_BINARY_DIR}/${output_name}.c COMMAND ${GLIB2_GENMARSHAL_UTIL} --header genmarshal_tmp > ${output_name}.h COMMAND ${GLIB2_GENMARSHAL_UTIL} --body genmarshal_tmp > ${output_name}.c WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) endmacro() qxmpp-1.4.0/cmake/modules/FindGObject.cmake000066400000000000000000000062041402370562100205330ustar00rootroot00000000000000# - Try to find GObject # Once done this will define # # GOBJECT_FOUND - system has GObject # GOBJECT_INCLUDE_DIR - the GObject include directory # GOBJECT_LIBRARIES - the libraries needed to use GObject # GOBJECT_DEFINITIONS - Compiler switches required for using GObject # Copyright (c) 2006, Tim Beaulen # Copyright (c) 2008 Helio Chissini de Castro, # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. IF (GOBJECT_INCLUDE_DIR AND GOBJECT_LIBRARIES) # in cache already SET(GObject_FIND_QUIETLY TRUE) ELSE (GOBJECT_INCLUDE_DIR AND GOBJECT_LIBRARIES) SET(GObject_FIND_QUIETLY FALSE) ENDIF (GOBJECT_INCLUDE_DIR AND GOBJECT_LIBRARIES) IF (NOT WIN32) FIND_PACKAGE(PkgConfig REQUIRED) # use pkg-config to get the directories and then use these values # in the FIND_PATH() and FIND_LIBRARY() calls PKG_CHECK_MODULES(PKG_GOBJECT2 REQUIRED gobject-2.0) SET(GOBJECT_DEFINITIONS ${PKG_GOBJECT2_CFLAGS}) ENDIF (NOT WIN32) FIND_PATH(GOBJECT_INCLUDE_DIR gobject/gobject.h HINTS ${PKG_GOBJECT2_INCLUDE_DIRS} ${PKG_GOBJECT2_INCLUDEDIR} PATHS /usr/include/glib-2.0/ PATH_SUFFIXES glib-2.0 ) FIND_LIBRARY(_GObjectLibs NAMES gobject-2.0 HINTS ${PKG_GOBJECT2_LIBRARY_DIRS} ${PKG_GOBJECT2_LIBDIR} ) FIND_LIBRARY(_GModuleLibs NAMES gmodule-2.0 HINTS ${PKG_GOBJECT2_LIBRARY_DIRS} ${PKG_GOBJECT2_LIBDIR} ) FIND_LIBRARY(_GThreadLibs NAMES gthread-2.0 HINTS ${PKG_GOBJECT2_LIBRARY_DIRS} ${PKG_GOBJECT2_LIBDIR} ) FIND_LIBRARY(_GLibs NAMES glib-2.0 HINTS ${PKG_GOBJECT2_LIBRARY_DIRS} ${PKG_GOBJECT2_LIBDIR} ) SET (GOBJECT_LIBRARIES ${_GObjectLibs} ${_GModuleLibs} ${_GThreadLibs} ${_GLibs}) MARK_AS_ADVANCED(GOBJECT_INCLUDE_DIR GOBJECT_LIBRARIES) INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(GObject DEFAULT_MSG GOBJECT_INCLUDE_DIR GOBJECT_LIBRARIES) qxmpp-1.4.0/cmake/modules/FindGStreamer.cmake000066400000000000000000000151321402370562100211070ustar00rootroot00000000000000# - Try to find GStreamer # Once done this will define # # GSTREAMER_FOUND - system has GStreamer # GSTREAMER_INCLUDE_DIR - the GStreamer main include directory # GSTREAMER_INCLUDE_DIRS - the GStreamer include directories # GSTREAMER_LIBRARY - the main GStreamer library # GSTREAMER_PLUGIN_DIR - the GStreamer plugin directory # # And for all the plugin libraries specified in the COMPONENTS # of find_package, this module will define: # # GSTREAMER__LIBRARY_FOUND - system has # GSTREAMER__LIBRARY - the library # GSTREAMER__INCLUDE_DIR - the include directory # # Copyright (c) 2010, Collabora Ltd. # @author George Kiagiadakis # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if (GSTREAMER_INCLUDE_DIR AND GSTREAMER_LIBRARY) set(GStreamer_FIND_QUIETLY TRUE) else() set(GStreamer_FIND_QUIETLY FALSE) endif() set(GSTREAMER_ABI_VERSION "1.0") # Find the main library find_package(PkgConfig) if (PKG_CONFIG_FOUND) pkg_check_modules(PKG_GSTREAMER QUIET gstreamer-${GSTREAMER_ABI_VERSION}) if(PKG_GSTREAMER_FOUND) exec_program(${PKG_CONFIG_EXECUTABLE} ARGS --variable pluginsdir gstreamer-${GSTREAMER_ABI_VERSION} OUTPUT_VARIABLE PKG_GSTREAMER_PLUGIN_DIR) endif() set(GSTREAMER_DEFINITIONS ${PKG_GSTREAMER_CFLAGS}) endif() find_library(GSTREAMER_LIBRARY NAMES gstreamer-${GSTREAMER_ABI_VERSION} HINTS ${PKG_GSTREAMER_LIBRARY_DIRS} ${PKG_GSTREAMER_LIBDIR}) find_path(GSTREAMER_INCLUDE_DIR gst/gst.h HINTS ${PKG_GSTREAMER_INCLUDE_DIRS} ${PKG_GSTREAMER_INCLUDEDIR} PATH_SUFFIXES gstreamer-${GSTREAMER_ABI_VERSION}) find_path(GSTREAMER_gstconfig_INCLUDE_DIR gst/gstconfig.h HINTS ${PKG_GSTREAMER_INCLUDE_DIRS} ${PKG_GSTREAMER_INCLUDEDIR} PATH_SUFFIXES gstreamer-${GSTREAMER_ABI_VERSION}) set(GSTREAMER_INCLUDE_DIRS ${GSTREAMER_INCLUDE_DIR} ${GSTREAMER_gstconfig_INCLUDE_DIR}) list(REMOVE_DUPLICATES GSTREAMER_INCLUDE_DIRS) if (PKG_GSTREAMER_PLUGIN_DIR) set(_GSTREAMER_PLUGIN_DIR ${PKG_GSTREAMER_PLUGIN_DIR}) else() get_filename_component(_GSTREAMER_LIB_DIR ${GSTREAMER_LIBRARY} PATH) set(_GSTREAMER_PLUGIN_DIR ${_GSTREAMER_LIB_DIR}/gstreamer-${GSTREAMER_ABI_VERSION}) endif() set(GSTREAMER_PLUGIN_DIR ${_GSTREAMER_PLUGIN_DIR} CACHE PATH "The path to the gstreamer plugins installation directory") mark_as_advanced(GSTREAMER_LIBRARY GSTREAMER_INCLUDE_DIR GSTREAMER_gstconfig_INCLUDE_DIR GSTREAMER_PLUGIN_DIR) # Find additional libraries include(MacroFindGStreamerLibrary) macro(_find_gst_component _name _header) find_gstreamer_library(${_name} ${_header} ${GSTREAMER_ABI_VERSION} ${GStreamer_FIND_QUIETLY}) set(_GSTREAMER_EXTRA_VARIABLES ${_GSTREAMER_EXTRA_VARIABLES} GSTREAMER_${_name}_LIBRARY GSTREAMER_${_name}_INCLUDE_DIR) endmacro() foreach(_component ${GStreamer_FIND_COMPONENTS}) if (${_component} STREQUAL "base") _find_gst_component(BASE gstbasesink.h) elseif (${_component} STREQUAL "check") _find_gst_component(CHECK gstcheck.h) elseif (${_component} STREQUAL "controller") _find_gst_component(CONTROLLER gstargbcontrolbinding.h) elseif (${_component} STREQUAL "net") _find_gst_component(NET gstnet.h) else() message (AUTHOR_WARNING "FindGStreamerPluginsBase.cmake: Invalid component \"${_component}\" was specified") endif() endforeach() # Version check if (GStreamer_FIND_VERSION) if (PKG_GSTREAMER_FOUND) if("${PKG_GSTREAMER_VERSION}" VERSION_LESS "${GStreamer_FIND_VERSION}") if(NOT GStreamer_FIND_QUIETLY) message(STATUS "Found GStreamer version ${PKG_GSTREAMER_VERSION}, but at least version ${GStreamer_FIND_VERSION} is required") endif() set(GSTREAMER_VERSION_COMPATIBLE FALSE) else() set(GSTREAMER_VERSION_COMPATIBLE TRUE) endif() elseif(GSTREAMER_INCLUDE_DIR) include(CheckCXXSourceCompiles) set(CMAKE_REQUIRED_INCLUDES ${GSTREAMER_INCLUDE_DIR}) string(REPLACE "." "," _comma_version ${GStreamer_FIND_VERSION}) # Hack to invalidate the cached value set(GSTREAMER_VERSION_COMPATIBLE GSTREAMER_VERSION_COMPATIBLE) check_cxx_source_compiles(" #define G_BEGIN_DECLS #define G_END_DECLS #include #if GST_CHECK_VERSION(${_comma_version}) int main() { return 0; } #else # error \"GStreamer version incompatible\" #endif " GSTREAMER_VERSION_COMPATIBLE) if (NOT GSTREAMER_VERSION_COMPATIBLE AND NOT GStreamer_FIND_QUIETLY) message(STATUS "GStreamer ${GStreamer_FIND_VERSION} is required, but the version found is older") endif() else() # We didn't find gstreamer at all set(GSTREAMER_VERSION_COMPATIBLE FALSE) endif() else() # No version constrain was specified, thus we consider the version compatible set(GSTREAMER_VERSION_COMPATIBLE TRUE) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GStreamer DEFAULT_MSG GSTREAMER_LIBRARY GSTREAMER_INCLUDE_DIRS GSTREAMER_VERSION_COMPATIBLE ${_GSTREAMER_EXTRA_VARIABLES}) qxmpp-1.4.0/cmake/modules/MacroFindGStreamerLibrary.cmake000066400000000000000000000066211402370562100234210ustar00rootroot00000000000000# - macro find_gstreamer_library # # Copyright (c) 2010, Collabora Ltd. # @author George Kiagiadakis # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. macro(find_gstreamer_library _name _header _abi_version _quiet) string(TOLOWER ${_name} _lower_name) string(TOUPPER ${_name} _upper_name) if (GSTREAMER_${_upper_name}_LIBRARY AND GSTREAMER_${_upper_name}_INCLUDE_DIR) set(_GSTREAMER_${_upper_name}_QUIET TRUE) else() set(_GSTREAMER_${_upper_name}_QUIET FALSE) endif() if (PKG_CONFIG_FOUND) pkg_check_modules(PKG_GSTREAMER_${_upper_name} QUIET gstreamer-${_lower_name}-${_abi_version}) endif() find_library(GSTREAMER_${_upper_name}_LIBRARY NAMES gst${_lower_name}-${_abi_version} HINTS ${PKG_GSTREAMER_${_upper_name}_LIBRARY_DIRS} ${PKG_GSTREAMER_${_upper_name}_LIBDIR} ) find_path(GSTREAMER_${_upper_name}_INCLUDE_DIR gst/${_lower_name}/${_header} HINTS ${PKG_GSTREAMER_${_upper_name}_INCLUDE_DIRS} ${PKG_GSTREAMER_${_upper_name}_INCLUDEDIR} PATH_SUFFIXES gstreamer-${_abi_version} ) if (GSTREAMER_${_upper_name}_LIBRARY AND GSTREAMER_${_upper_name}_INCLUDE_DIR) set(GSTREAMER_${_upper_name}_LIBRARY_FOUND TRUE) else() set(GSTREAMER_${_upper_name}_LIBRARY_FOUND FALSE) endif() if (NOT _GSTREAMER_${_upper_name}_QUIET AND NOT _quiet) if (GSTREAMER_${_upper_name}_LIBRARY) message(STATUS "Found GSTREAMER_${_upper_name}_LIBRARY: ${GSTREAMER_${_upper_name}_LIBRARY}") else() message(STATUS "Could NOT find GSTREAMER_${_upper_name}_LIBRARY") endif() if (GSTREAMER_${_upper_name}_INCLUDE_DIR) message(STATUS "Found GSTREAMER_${_upper_name}_INCLUDE_DIR: ${GSTREAMER_${_upper_name}_INCLUDE_DIR}") else() message(STATUS "Could NOT find GSTREAMER_${_upper_name}_INCLUDE_DIR") endif() endif() mark_as_advanced(GSTREAMER_${_upper_name}_LIBRARY GSTREAMER_${_upper_name}_INCLUDE_DIR) endmacro() qxmpp-1.4.0/doc/000077500000000000000000000000001402370562100134265ustar00rootroot00000000000000qxmpp-1.4.0/doc/CMakeLists.txt000066400000000000000000000010611402370562100161640ustar00rootroot00000000000000find_package(Doxygen REQUIRED) if(NOT DOXYGEN_FOUND) message(FATAL_ERROR "Doxygen is needed to build the documentation.") endif() configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY ) add_custom_target( doc ALL COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Generate API documentation with Doxygen" VERBATIM ) install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION ${CMAKE_INSTALL_DOCDIR}) qxmpp-1.4.0/doc/Doxyfile.in000066400000000000000000000017241402370562100155450ustar00rootroot00000000000000PROJECT_NAME = "QXmpp" PROJECT_NUMBER = "Version: @PROJECT_VERSION@" INPUT = @PROJECT_SOURCE_DIR@/doc/index.doc \ @PROJECT_SOURCE_DIR@/doc/using.doc \ @PROJECT_SOURCE_DIR@/doc/xep.doc \ @PROJECT_SOURCE_DIR@/src ALIASES = "xep{1}=XEP-\1" \ "xep{2}=XEP-\1: \2" ALPHABETICAL_INDEX = NO EXCLUDE_PATTERNS = */moc_* */mod_* */*_p.h */QXmppCodec.cpp */QXmppSasl.cpp FULL_PATH_NAMES = NO HIDE_UNDOC_CLASSES = YES 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)=1" "QT_DEPRECATED_X(msg)=" qxmpp-1.4.0/doc/index.doc000066400000000000000000000022441402370562100152260ustar00rootroot00000000000000/*! \mainpage QXmpp is a cross-platform C++ XMPP client library based on the Qt framework. It tries to use Qt's programming conventions in order to ease the learning curve for new programmers. QXmpp based clients are built using QXmppClient instances which handle the establishment of the XMPP connection and provide a number of high-level "managers" to perform specific tasks. You can write your own managers to extend QXmpp by subclassing QXmppClientExtension. Main Class: - QXmppClient Managers to perform specific tasks: - QXmppRosterManager - QXmppVCardManager - QXmppTransferManager - QXmppMamManager - QXmppMucManager - QXmppCallManager - QXmppArchiveManager - QXmppVersionManager - QXmppDiscoveryManager - QXmppEntityTimeManager - QXmppUploadRequestManager - QXmppRegistrationManager - QXmppAttentionManager XMPP stanzas: If you are interested in a more low-level API, you can refer to these classes. - QXmppIq - QXmppMessage - QXmppPresence Project Details: - Project Page: https://github.com/qxmpp-project/qxmpp - Report Issues: https://github.com/qxmpp-project/qxmpp/issues - New Releases: https://github.com/qxmpp-project/qxmpp/releases */ qxmpp-1.4.0/doc/using.doc000066400000000000000000000056311402370562100152470ustar00rootroot00000000000000/*! \page using Using QXmpp

Example: example_0_connected

This example just connects to the xmpp server. And starts receiving presences (updates) from the server. After running this example, you can see this user online, if it's added in your roster (friends list). Logging type has been set to stdout. You can see the progress on the command line. This example is also available with the source code in the example directory. \code #include #include "QXmppClient.h" #include "QXmppLogger.h" int main(int argc, char *argv[]) { // create a Qt application QCoreApplication a(argc, argv); // setting the logging type to stdout QXmppLogger::getLogger()->setLoggingType(QXmppLogger::StdoutLogging); // creating the object of the client class QXmppClient // and then calling the connectToServer function to connect to gtalk server QXmppClient client; client.connectToServer("qxmpp.test1@gmail.com", "qxmpp123"); // run the application main loop return a.exec(); } \endcode

Example: example_1_echoClient

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. You will receive the message back. This example shows how to receive and send messages. This example is also available with the source code in the example directory. \code // subclass the QXmppClient and create a new class echoClient // in the constructor the signal QXmppClient::messageReceived(const QXmppMessage&) // is connected to the slot echoClient::messageReceived(const QXmppMessage&) // in the slot one can process the message received #include "QXmppClient.h" class echoClient : public QXmppClient { Q_OBJECT public: echoClient(QObject *parent = 0); ~echoClient(); public slots: void messageReceived(const QXmppMessage&); }; }}} \endcode \code #include "echoClient.h" #include "QXmppMessage.h" echoClient::echoClient(QObject *parent) : QXmppClient(parent) { bool check = connect(this, SIGNAL(messageReceived(const QXmppMessage&)), SLOT(messageReceived(const QXmppMessage&))); Q_ASSERT(check); } echoClient::~echoClient() { } // slot where message sent to this client is received // here getFrom() gives the sender and getBody() gives the message // using the function sendPacket message is sent back to the sender void echoClient::messageReceived(const QXmppMessage& message) { QString from = message.getFrom(); QString msg = message.getBody(); sendPacket(QXmppMessage("", from, "Your message: " + msg)); } }}} \endcode \code #include #include "echoClient.h" #include "QXmppLogger.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QXmppLogger::getLogger()->setLoggingType(QXmppLogger::StdoutLogging); echoClient client; client.connectToServer("qxmpp.test1@gmail.com", "qxmpp123"); return a.exec(); } \endcode */ qxmpp-1.4.0/doc/xep.doc000066400000000000000000000044731402370562100147210ustar00rootroot00000000000000/*! \page xep XMPP Extensions This document lists the XMPP Extensions (XEP) available in QXmpp. Complete: - \xep{0004, Data Forms} - \xep{0030, Service Discovery} - \xep{0033, Extended Stanza Addressing} - \xep{0045, Multi-User Chat} - \xep{0047, In-Band Bytestreams} - \xep{0048, Bookmarks} - \xep{0054, vcard-temp} - \xep{0059, Result Set Management} - \xep{0065, SOCKS5 Bytestreams} - \xep{0066, Out of Band Data} (partially) - \xep{0071, XHTML-IM} - \xep{0077, In-Band Registration} (v2.4) - \xep{0078, Non-SASL Authentication} - \xep{0082, XMPP Date and Time Profiles} - \xep{0085, Chat State Notifications} - \xep{0091, Legacy Delayed Delivery} - \xep{0092, Software Version} - \xep{0095, Stream Initiation} - \xep{0096, SI File Transfer} - \xep{0115, Entity Capabilities} - \xep{0128, Service Discovery Extensions} - \xep{0136, Message Archiving} - \xep{0153, vCard-Based Avatars} - \xep{0166, Jingle} - \xep{0167, Jingle RTP Sessions} - \xep{0176, Jingle ICE-UDP Transport Method} - \xep{0184, Message Delivery Receipts} - \xep{0198, Stream Management} - \xep{0199, XMPP Ping} - \xep{0202, Entity Time} - \xep{0203, Delayed Delivery} - \xep{0221, Data Forms Media Element} - \xep{0224, Attention} - \xep{0231, Bits of Binary} (v1.0) - \xep{0237, Roster Versioning} (partially) - \xep{0245, The /me Command} (v1.0) - \xep{0249, Direct MUC Invitations} (v1.2) - \xep{0280, Message Carbons} - \xep{0308, Last Message Correction} - \xep{0313, Message Archive Management} (v0.6) - \xep{0319, Last User Interaction in Presence} - \xep{0334, Message Processing Hints} (v0.3) - \xep{0352, Client State Indication} - \xep{0357, Push Notifications} (v0.4) (partially) - \xep{0359, Unique and Stable Stanza IDs} (v0.6) - \xep{0363, HTTP File Upload} (v1.0) - \xep{0367, Message Attaching} (v0.3) - \xep{0380, Explicit Message Encryption} (v0.3) - \xep{0382, Spoiler messages} (v0.2) - \xep{0428, Fallback Indication} (v0.1) Ongoing: - \xep{0009, Jabber-RPC} (API is not finalized yet) - \xep{0060, Publish-Subscribe} (Only basic IQ implemented) - \xep{0369, Mediated Information eXchange (MIX)} (Only IQ queries implemented) (v0.14) - \xep{0405, Mediated Information eXchange (MIX): Participant Server Requirements} (Only IQ queries implemented) (v0.4) - \xep{0407, Mediated Information eXchange (MIX): Miscellaneous Capabilities} (QXmppMixInvitation) (v0.1) */ qxmpp-1.4.0/examples/000077500000000000000000000000001402370562100144775ustar00rootroot00000000000000qxmpp-1.4.0/examples/CMakeLists.txt000066400000000000000000000013241402370562100172370ustar00rootroot00000000000000macro(add_simple_example EXAMPLE_NAME) add_executable(${EXAMPLE_NAME} example_${EXAMPLE_NAME}/example_${EXAMPLE_NAME}.cpp) target_link_libraries(${EXAMPLE_NAME} qxmpp) endmacro() include_directories(${PROJECT_SOURCE_DIR}/src/base) include_directories(${PROJECT_SOURCE_DIR}/src/client) include_directories(${PROJECT_SOURCE_DIR}/src/server) include_directories(${PROJECT_BINARY_DIR}/src/base) add_simple_example(0_connected) add_simple_example(1_echoClient) add_simple_example(2_rosterHandling) add_simple_example(3_transferHandling) add_simple_example(7_archiveHandling) add_simple_example(8_server) add_subdirectory(example_5_rpcInterface) add_subdirectory(example_6_rpcClient) add_subdirectory(example_9_vCard) qxmpp-1.4.0/examples/example_0_connected/000077500000000000000000000000001402370562100203735ustar00rootroot00000000000000qxmpp-1.4.0/examples/example_0_connected/README000066400000000000000000000004441402370562100212550ustar00rootroot00000000000000This example just connects to the xmpp server. And start receiving presences (updates) from the server. After running this example, you can see this user online, if it's added in your roster (friends list). Logging type has been set to stdout. You can see the progress on the command line. qxmpp-1.4.0/examples/example_0_connected/example_0_connected.cpp000066400000000000000000000023001402370562100247660ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 "QXmppClient.h" #include "QXmppLogger.h" #include int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); QXmppClient client; client.logger()->setLoggingType(QXmppLogger::StdoutLogging); // For jabber // client.connectToServer("username@jabber.org", "passwd"); // For google talk // client.connectToServer("username@gmail.com", "passwd"); client.connectToServer("qxmpp.test1@qxmpp.org", "qxmpp123"); return app.exec(); } qxmpp-1.4.0/examples/example_1_echoClient/000077500000000000000000000000001402370562100205075ustar00rootroot00000000000000qxmpp-1.4.0/examples/example_1_echoClient/README000066400000000000000000000003301402370562100213630ustar00rootroot00000000000000This 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. You will receive the message back. This example shows how to receive and send messages. qxmpp-1.4.0/examples/example_1_echoClient/example_1_echoClient.cpp000066400000000000000000000026751402370562100252350ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 "example_1_echoClient.h" #include "QXmppLogger.h" #include "QXmppMessage.h" #include echoClient::echoClient(QObject *parent) : QXmppClient(parent) { connect(this, &QXmppClient::messageReceived, this, &echoClient::messageReceived); } echoClient::~echoClient() { } void echoClient::messageReceived(const QXmppMessage &message) { QString from = message.from(); QString msg = message.body(); sendPacket(QXmppMessage("", from, "Your message: " + msg)); } int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); echoClient client; client.logger()->setLoggingType(QXmppLogger::StdoutLogging); client.connectToServer("qxmpp.test1@qxmpp.org", "qxmpp123"); return app.exec(); } qxmpp-1.4.0/examples/example_1_echoClient/example_1_echoClient.h000066400000000000000000000017541402370562100246770ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 ECHOCLIENT_H #define ECHOCLIENT_H #include "QXmppClient.h" class echoClient : public QXmppClient { Q_OBJECT public: echoClient(QObject *parent = nullptr); ~echoClient() override; public slots: void messageReceived(const QXmppMessage &); }; #endif // ECHOCLIENT_H qxmpp-1.4.0/examples/example_2_rosterHandling/000077500000000000000000000000001402370562100214165ustar00rootroot00000000000000qxmpp-1.4.0/examples/example_2_rosterHandling/README000066400000000000000000000001061402370562100222730ustar00rootroot00000000000000This example demonstrates how to get the roster and presence updates. qxmpp-1.4.0/examples/example_2_rosterHandling/example_2_rosterHandling.cpp000066400000000000000000000045331402370562100270460ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 "example_2_rosterHandling.h" #include "QXmppMessage.h" #include "QXmppRosterManager.h" #include xmppClient::xmppClient(QObject *parent) : QXmppClient(parent), m_rosterManager(findExtension()) { connect(this, &QXmppClient::connected, this, &xmppClient::clientConnected); connect(m_rosterManager, &QXmppRosterManager::rosterReceived, this, &xmppClient::rosterReceived); /// Then QXmppRoster::presenceChanged() is emitted whenever presence of /// someone in roster changes connect(m_rosterManager, &QXmppRosterManager::presenceChanged, this, &xmppClient::presenceChanged); } xmppClient::~xmppClient() = default; void xmppClient::clientConnected() { qDebug("example_2_rosterHandling:: CONNECTED"); } void xmppClient::rosterReceived() { qDebug("example_2_rosterHandling:: Roster received"); const QStringList jids = m_rosterManager->getRosterBareJids(); for (const QString &bareJid : jids) { QString name = m_rosterManager->getRosterEntry(bareJid).name(); if (name.isEmpty()) name = "-"; qDebug("example_2_rosterHandling:: Roster received: %s [%s]", qPrintable(bareJid), qPrintable(name)); } } void xmppClient::presenceChanged(const QString &bareJid, const QString &resource) { qDebug("example_2_rosterHandling:: Presence changed %s/%s", qPrintable(bareJid), qPrintable(resource)); } int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); xmppClient client; client.connectToServer("qxmpp.test1@qxmpp.org", "qxmpp123"); return app.exec(); } qxmpp-1.4.0/examples/example_2_rosterHandling/example_2_rosterHandling.h000066400000000000000000000022141402370562100265050ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 XMPPCLIENT_H #define XMPPCLIENT_H #include "QXmppClient.h" class QXmppRosterManager; class xmppClient : public QXmppClient { Q_OBJECT public: xmppClient(QObject* parent = nullptr); ~xmppClient() override; public slots: void clientConnected(); void rosterReceived(); void presenceChanged(const QString& bareJid, const QString& resource); private: QXmppRosterManager* m_rosterManager; }; #endif // XMPPCLIENT_H qxmpp-1.4.0/examples/example_3_transferHandling/000077500000000000000000000000001402370562100217255ustar00rootroot00000000000000qxmpp-1.4.0/examples/example_3_transferHandling/example_3_transferHandling.cpp000066400000000000000000000103311402370562100276550ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Ian Reinhart Geiser * Jeremy Lainé * * 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 "example_3_transferHandling.h" #include "QXmppMessage.h" #include "QXmppUtils.h" #include #include #include #include #include xmppClient::xmppClient(QObject *parent) : QXmppClient(parent), transferManager(nullptr) { // add transfer manager transferManager = new QXmppTransferManager; transferManager->setProxy("proxy.qxmpp.org"); addExtension(transferManager); // uncomment one of the following if you only want to use a specific transfer method: // // transferManager->setSupportedMethods(QXmppTransferJob::InBandMethod); // transferManager->setSupportedMethods(QXmppTransferJob::SocksMethod); connect(this, &QXmppClient::presenceReceived, this, &xmppClient::slotPresenceReceived); connect(transferManager, &QXmppTransferManager::fileReceived, this, &xmppClient::slotFileReceived); } void xmppClient::setRecipient(const QString &recipient) { m_recipient = recipient; } /// A file transfer failed. void xmppClient::slotError(QXmppTransferJob::Error error) { qDebug() << "Transmission failed:" << error; } /// A file transfer request was received. void xmppClient::slotFileReceived(QXmppTransferJob *job) { qDebug() << "Got transfer request from:" << job->jid(); connect(job, SIGNAL(error(QXmppTransferJob::Error)), this, SLOT(slotError(QXmppTransferJob::Error))); connect(job, &QXmppTransferJob::finished, this, &xmppClient::slotFinished); connect(job, &QXmppTransferJob::progress, this, &xmppClient::slotProgress); // allocate a buffer to receive the file auto *buffer = new QBuffer(this); buffer->open(QIODevice::WriteOnly); job->accept(buffer); } /// A file transfer finished. void xmppClient::slotFinished() { qDebug() << "Transmission finished"; } /// A presence was received void xmppClient::slotPresenceReceived(const QXmppPresence &presence) { // if we don't have a recipient, or if the presence is not from the recipient, // do nothing if (m_recipient.isEmpty() || QXmppUtils::jidToBareJid(presence.from()) != m_recipient || presence.type() != QXmppPresence::Available) return; // send the file and connect to the job's signals QXmppTransferJob *job = transferManager->sendFile(presence.from(), ":/example_3_transferHandling.cpp", "example source code"); connect(job, SIGNAL(error(QXmppTransferJob::Error)), this, SLOT(slotError(QXmppTransferJob::Error))); connect(job, &QXmppTransferJob::finished, this, &xmppClient::slotFinished); connect(job, &QXmppTransferJob::progress, this, &xmppClient::slotProgress); } /// A file transfer has made progress. void xmppClient::slotProgress(qint64 done, qint64 total) { qDebug() << "Transmission progress:" << done << "/" << total; } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // we want one argument : "send" or "receive" if (argc != 2 || (strcmp(argv[1], "send") && strcmp(argv[1], "receive"))) { fprintf(stderr, "Usage: %s send|receive\n", argv[0]); return EXIT_FAILURE; } xmppClient client; client.logger()->setLoggingType(QXmppLogger::StdoutLogging); if (!strcmp(argv[1], "send")) { client.setRecipient("qxmpp.test2@qxmpp.org"); client.connectToServer("qxmpp.test1@qxmpp.org", "qxmpp123"); } else { client.connectToServer("qxmpp.test2@qxmpp.org", "qxmpp456"); } return a.exec(); } qxmpp-1.4.0/examples/example_3_transferHandling/example_3_transferHandling.h000066400000000000000000000024621402370562100273300ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Ian Reinhart Geiser * * 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 IBBCLIENT_H #define IBBCLIENT_H #include "QXmppClient.h" #include "QXmppTransferManager.h" class xmppClient : public QXmppClient { Q_OBJECT public: xmppClient(QObject *parent = nullptr); void setRecipient(const QString &recipient); private slots: void slotError(QXmppTransferJob::Error error); void slotFileReceived(QXmppTransferJob *job); void slotFinished(); void slotPresenceReceived(const QXmppPresence &presence); void slotProgress(qint64 done, qint64 total); private: QString m_recipient; QXmppTransferManager *transferManager; }; #endif // IBBCLIENT_H qxmpp-1.4.0/examples/example_3_transferHandling/example_3_transferHandling.qrc000066400000000000000000000001621402370562100276610ustar00rootroot00000000000000 example_3_transferHandling.cpp qxmpp-1.4.0/examples/example_5_rpcInterface/000077500000000000000000000000001402370562100210435ustar00rootroot00000000000000qxmpp-1.4.0/examples/example_5_rpcInterface/CMakeLists.txt000066400000000000000000000001501402370562100235770ustar00rootroot00000000000000add_executable(5_rpcInterface main.cpp remoteinterface.cpp) target_link_libraries(5_rpcInterface qxmpp) qxmpp-1.4.0/examples/example_5_rpcInterface/main.cpp000066400000000000000000000024451402370562100225000ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Manjeet Dahiya * Jeremy Lainé * * 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 "QXmppClient.h" #include "QXmppLogger.h" #include "QXmppRpcManager.h" #include "remoteinterface.h" #include int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QXmppLogger::getLogger()->setLoggingType(QXmppLogger::StdoutLogging); QXmppClient client; // add RPC extension and register interface auto *manager = new QXmppRpcManager; client.addExtension(manager); manager->addInvokableInterface(new RemoteInterface(&client)); client.connectToServer("qxmpp.test1@qxmpp.org", "qxmpp123"); return a.exec(); } qxmpp-1.4.0/examples/example_5_rpcInterface/remoteinterface.cpp000066400000000000000000000004631402370562100247260ustar00rootroot00000000000000#include "remoteinterface.h" RemoteInterface::RemoteInterface(QObject *parent) : QXmppInvokable(parent) { } bool RemoteInterface::isAuthorized(const QString &jid) const { Q_UNUSED(jid); return true; } QString RemoteInterface::echoString(const QString &message) { return "Echo: " + message; } qxmpp-1.4.0/examples/example_5_rpcInterface/remoteinterface.h000066400000000000000000000005701402370562100243720ustar00rootroot00000000000000#ifndef REMOTEINTERFACE_H #define REMOTEINTERFACE_H #include "QXmppRpcManager.h" class RemoteInterface : public QXmppInvokable { Q_OBJECT public: RemoteInterface(QObject *parent = nullptr); bool isAuthorized(const QString &jid) const override; // RPC Interface public slots: QString echoString(const QString &message); }; #endif // REMOTEINTERFACE_H qxmpp-1.4.0/examples/example_5_rpcInterface/xmlrpctest.txt000066400000000000000000000025561402370562100240210ustar00rootroot00000000000000 client telnet Passw0rd RemoteInterface.echoString Test string RemoteInterface.badMethod Test string BadInterface.echoString Test string qxmpp-1.4.0/examples/example_6_rpcClient/000077500000000000000000000000001402370562100203625ustar00rootroot00000000000000qxmpp-1.4.0/examples/example_6_rpcClient/CMakeLists.txt000066400000000000000000000001341402370562100231200ustar00rootroot00000000000000add_executable(6_rpcClient main.cpp rpcClient.cpp) target_link_libraries(6_rpcClient qxmpp) qxmpp-1.4.0/examples/example_6_rpcClient/main.cpp000066400000000000000000000020421402370562100220100ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Ian Reinhart Geiser * * 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 "QXmppLogger.h" #include "rpcClient.h" #include int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QXmppLogger::getLogger()->setLoggingType(QXmppLogger::StdoutLogging); rpcClient client; client.connectToServer("qxmpp.test2@qxmpp.org", "qxmpp456"); return a.exec(); } qxmpp-1.4.0/examples/example_6_rpcClient/rpcClient.cpp000066400000000000000000000041631402370562100230150ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Ian Reinhart Geiser * Jeremy Lainé * * 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 "rpcClient.h" #include "QXmppRpcManager.h" #include "QXmppUtils.h" #include #include rpcClient::rpcClient(QObject *parent) : QXmppClient(parent) { // add RPC manager m_rpcManager = new QXmppRpcManager; addExtension(m_rpcManager); // observe incoming presences connect(this, &QXmppClient::presenceReceived, this, &rpcClient::slotPresenceReceived); } rpcClient::~rpcClient() { } void rpcClient::slotInvokeRemoteMethod() { QXmppRemoteMethodResult methodResult = m_rpcManager->callRemoteMethod( m_remoteJid, "RemoteInterface.echoString", "This is a test"); if (methodResult.hasError) qDebug() << "Error:" << methodResult.code << methodResult.errorMessage; else qDebug() << "Result:" << methodResult.result; } /// A presence was received. void rpcClient::slotPresenceReceived(const QXmppPresence &presence) { const QLatin1String recipient("qxmpp.test1@qxmpp.org"); // if we are the recipient, or if the presence is not from the recipient, // do nothing if (QXmppUtils::jidToBareJid(configuration().jid()) == recipient || QXmppUtils::jidToBareJid(presence.from()) != recipient || presence.type() != QXmppPresence::Available) return; // invoke the remote method in 1 second m_remoteJid = presence.from(); QTimer::singleShot(1000, this, &rpcClient::slotInvokeRemoteMethod); } qxmpp-1.4.0/examples/example_6_rpcClient/rpcClient.h000066400000000000000000000022151402370562100224560ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Ian Reinhart Geiser * Jeremy Lainé * * 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 RPCCLIENT_H #define RPCCLIENT_H #include "QXmppClient.h" class QXmppRpcManager; class rpcClient : public QXmppClient { Q_OBJECT public: rpcClient(QObject *parent = nullptr); ~rpcClient() override; private slots: void slotInvokeRemoteMethod(); void slotPresenceReceived(const QXmppPresence &presence); private: QString m_remoteJid; QXmppRpcManager *m_rpcManager; }; #endif // RPCCLIENT_H qxmpp-1.4.0/examples/example_7_archiveHandling/000077500000000000000000000000001402370562100215265ustar00rootroot00000000000000qxmpp-1.4.0/examples/example_7_archiveHandling/example_7_archiveHandling.cpp000066400000000000000000000110121402370562100272540ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "example_7_archiveHandling.h" #include "QXmppArchiveIq.h" #include "QXmppArchiveManager.h" #include #include static void logStart(const QString &msg) { qDebug("example_7_archiveHandling : %s", qPrintable(msg)); } static void logEnd(const QString &msg) { qDebug(" => %s", qPrintable(msg)); } xmppClient::xmppClient(QObject *parent) : QXmppClient(parent), m_collectionCount(-1), m_pageDirection(PageForwards), m_pageSize(10) { // add archive manager archiveManager = new QXmppArchiveManager; addExtension(archiveManager); // connect signals connect(this, &QXmppClient::connected, this, &xmppClient::clientConnected); connect(archiveManager, &QXmppArchiveManager::archiveChatReceived, this, &xmppClient::archiveChatReceived); connect(archiveManager, &QXmppArchiveManager::archiveListReceived, this, &xmppClient::archiveListReceived); // set limits m_startDate = QDateTime::currentDateTime().addDays(-21); m_endDate = QDateTime::currentDateTime(); } xmppClient::~xmppClient() { } void xmppClient::setPageDirection(PageDirection direction) { m_pageDirection = direction; } void xmppClient::setPageSize(int size) { m_pageSize = size; } void xmppClient::clientConnected() { logEnd("connected"); // we want 0 results, i.e. only result-set management information (count) logStart("fetching collection count"); QXmppResultSetQuery rsmQuery; rsmQuery.setMax(0); archiveManager->listCollections("", m_startDate, m_endDate, rsmQuery); } void xmppClient::archiveListReceived(const QList &chats, const QXmppResultSetReply &rsmReply) { if (m_collectionCount < 0) { logEnd(QString::number(rsmReply.count()) + " items"); m_collectionCount = rsmReply.count(); // fetch first page logStart("fetching collection first page"); QXmppResultSetQuery rsmQuery; rsmQuery.setMax(m_pageSize); if (m_pageDirection == PageBackwards) rsmQuery.setBefore(""); archiveManager->listCollections("", m_startDate, m_endDate, rsmQuery); } else if (!chats.size()) { logEnd("no items"); } else { logEnd(QString("items %1 to %2 of %3").arg(QString::number(rsmReply.index()), QString::number(rsmReply.index() + chats.size() - 1), QString::number(rsmReply.count()))); for (const auto &chat : chats) { qDebug("chat start %s", qPrintable(chat.start().toString())); // NOTE: to actually retrieve conversations, uncomment this //archiveManager->retrieveCollection(chat.with(), chat.start()); } if (!rsmReply.isNull()) { // fetch next page QXmppResultSetQuery rsmQuery; rsmQuery.setMax(m_pageSize); if (m_pageDirection == PageBackwards) { logStart("fetching collection previous page"); rsmQuery.setBefore(rsmReply.first()); } else { logStart("fetching collection next page"); rsmQuery.setAfter(rsmReply.last()); } archiveManager->listCollections("", m_startDate, m_endDate, rsmQuery); } } } void xmppClient::archiveChatReceived(const QXmppArchiveChat &chat, const QXmppResultSetReply &rsmReply) { logEnd(QString("chat received, RSM count %1") .arg(QString::number(rsmReply.count()))); const auto messages = chat.messages(); for (const auto &msg : messages) { qDebug("example_7_archiveHandling : %s", qPrintable(msg.body())); } } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); xmppClient client; client.setPageSize(15); client.setPageDirection(xmppClient::PageBackwards); client.connectToServer("qxmpp.test1@qxmpp.org", "qxmpp123"); return a.exec(); } qxmpp-1.4.0/examples/example_7_archiveHandling/example_7_archiveHandling.h000066400000000000000000000031511402370562100267260ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 XMPPCLIENT_H #define XMPPCLIENT_H #include "QXmppArchiveIq.h" #include "QXmppClient.h" #include class QXmppArchiveManager; class QXmppResultSetReply; class xmppClient : public QXmppClient { Q_OBJECT public: enum PageDirection { PageForwards = 0, PageBackwards }; xmppClient(QObject *parent = nullptr); ~xmppClient() override; void setPageDirection(PageDirection direction); void setPageSize(int size); public slots: void clientConnected(); void archiveListReceived(const QList &chats, const QXmppResultSetReply &rsmReply); void archiveChatReceived(const QXmppArchiveChat &chat, const QXmppResultSetReply &rsmReply); private: QXmppArchiveManager *archiveManager; int m_collectionCount; QDateTime m_startDate; QDateTime m_endDate; PageDirection m_pageDirection; int m_pageSize; }; #endif // XMPPCLIENT_H qxmpp-1.4.0/examples/example_8_server/000077500000000000000000000000001402370562100177475ustar00rootroot00000000000000qxmpp-1.4.0/examples/example_8_server/example_8_server.cpp000066400000000000000000000040441402370562100237250ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppLogger.h" #include "QXmppPasswordChecker.h" #include "QXmppServer.h" #include #define USERNAME "qxmpp.test1" #define PASSWORD "qxmpp123" class passwordChecker : public QXmppPasswordChecker { /// Retrieves the password for the given username. QXmppPasswordReply::Error getPassword(const QXmppPasswordRequest &request, QString &password) override { if (request.username() == USERNAME) { password = PASSWORD; return QXmppPasswordReply::NoError; } else { return QXmppPasswordReply::AuthorizationError; } }; /// Returns true as we implemented getPassword(). bool hasGetPassword() const override { return true; }; }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // we want one argument : the domain to serve if (argc != 2) { fprintf(stderr, "Usage: xmppServer \n"); return EXIT_FAILURE; } const QString domain = QString::fromLocal8Bit(argv[1]); QXmppLogger logger; logger.setLoggingType(QXmppLogger::StdoutLogging); passwordChecker checker; QXmppServer server; server.setDomain(domain); server.setLogger(&logger); server.setPasswordChecker(&checker); server.listenForClients(); server.listenForServers(); return a.exec(); } qxmpp-1.4.0/examples/example_9_vCard/000077500000000000000000000000001402370562100175015ustar00rootroot00000000000000qxmpp-1.4.0/examples/example_9_vCard/CMakeLists.txt000066400000000000000000000002511402370562100222370ustar00rootroot00000000000000find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui) add_executable(9_vCard example_9_vCard.cpp) target_link_libraries(9_vCard Qt${QT_VERSION_MAJOR}::Gui qxmpp) qxmpp-1.4.0/examples/example_9_vCard/README000066400000000000000000000001321402370562100203550ustar00rootroot00000000000000This example demonstrates how to get the vCards and handle Avatars of the roster entities.qxmpp-1.4.0/examples/example_9_vCard/example_9_vCard.cpp000066400000000000000000000060231402370562100232100ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 "example_9_vCard.h" #include "QXmppMessage.h" #include "QXmppRosterManager.h" #include "QXmppVCardIq.h" #include "QXmppVCardManager.h" #include #include #include #include #include #include #include #include xmppClient::xmppClient(QObject *parent) : QXmppClient(parent), m_rosterManager(findExtension()), m_vCardManager(findExtension()) { connect(this, &QXmppClient::connected, this, &xmppClient::clientConnected); connect(m_rosterManager, &QXmppRosterManager::rosterReceived, this, &xmppClient::rosterReceived); } xmppClient::~xmppClient() = default; void xmppClient::clientConnected() { qDebug() << "example_9_vCard: CONNECTED"; } void xmppClient::rosterReceived() { qDebug() << "example_9_vCard: Roster Received"; connect(m_vCardManager, &QXmppVCardManager::vCardReceived, this, &xmppClient::vCardReceived); // request vCard of all the bareJids in roster const QStringList jids = m_rosterManager->getRosterBareJids(); for (const auto &jid : jids) m_vCardManager->requestVCard(jid); } void xmppClient::vCardReceived(const QXmppVCardIq &vCard) { QString bareJid = vCard.from(); qDebug() << "example_9_vCard: vCard Received:" << bareJid; qDebug() << "FullName:" << vCard.fullName(); qDebug() << "Nickname:" << vCard.nickName(); QString vCardsDir("vCards/"); QDir dir; if (!dir.exists(vCardsDir)) dir.mkdir(vCardsDir); QFile file("vCards/" + bareJid + ".xml"); if (file.open(QIODevice::ReadWrite)) { QXmlStreamWriter stream(&file); vCard.toXml(&stream); file.close(); qDebug() << "example_9_vCard: vCard written to the file:" << bareJid; } QString name("vCards/" + bareJid + ".png"); QByteArray photo = vCard.photo(); QBuffer buffer; buffer.setData(photo); buffer.open(QIODevice::ReadOnly); QImageReader imageReader(&buffer); QImage image = imageReader.read(); if (image.save(name)) qDebug() << "example_9_vCard: Avatar saved to file"; } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); xmppClient client; client.connectToServer("qxmpp.test1@qxmpp.org", "qxmpp123"); return a.exec(); } qxmpp-1.4.0/examples/example_9_vCard/example_9_vCard.h000066400000000000000000000023031402370562100226520ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 XMPPCLIENT_H #define XMPPCLIENT_H #include "QXmppClient.h" class QXmppRosterManager; class QXmppVCardIq; class QXmppVCardManager; class xmppClient : public QXmppClient { Q_OBJECT public: xmppClient(QObject *parent = nullptr); ~xmppClient() override; public slots: void clientConnected(); void rosterReceived(); void vCardReceived(const QXmppVCardIq &); private: QXmppRosterManager *m_rosterManager; QXmppVCardManager *m_vCardManager; }; #endif // XMPPCLIENT_H qxmpp-1.4.0/qxmpp.pc.in000066400000000000000000000004411402370562100147560ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=${prefix} libdir=@CMAKE_INSTALL_FULL_LIBDIR@ includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@/qxmpp Name: Qxmpp Description: QXmpp Library Version: @PROJECT_VERSION@ Libs: -lqxmpp Libs.private: -lQt5Network -lQt5Xml -lQt5Core Cflags: -I${includedir} qxmpp-1.4.0/src/000077500000000000000000000000001402370562100134505ustar00rootroot00000000000000qxmpp-1.4.0/src/CMakeLists.txt000066400000000000000000000156521402370562100162210ustar00rootroot00000000000000option(BUILD_SHARED "Build SHARED library" ON) add_definitions(-DQXMPP_BUILD) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/base) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/client) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/server) # Configure QXmppGlobal.h.in if(BUILD_SHARED) set(QXMPP_BUILD_SHARED true) else() set(QXMPP_BUILD_SHARED false) endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/base/QXmppGlobal.h.in ${CMAKE_CURRENT_BINARY_DIR}/base/QXmppGlobal.h @ONLY) include_directories(${CMAKE_CURRENT_BINARY_DIR}/base) set(INSTALL_HEADER_FILES ${CMAKE_CURRENT_BINARY_DIR}/base/QXmppGlobal.h # Base base/QXmppArchiveIq.h base/QXmppBindIq.h base/QXmppBitsOfBinaryContentId.h base/QXmppBitsOfBinaryData.h base/QXmppBitsOfBinaryDataList.h base/QXmppBitsOfBinaryIq.h base/QXmppBookmarkSet.h base/QXmppByteStreamIq.h base/QXmppDataForm.h 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/QXmppMixInvitation.h base/QXmppMixIq.h base/QXmppMixItem.h base/QXmppMucIq.h base/QXmppNonSASLAuth.h base/QXmppPingIq.h base/QXmppPresence.h base/QXmppPubSubIq.h base/QXmppPubSubItem.h base/QXmppPushEnableIq.h base/QXmppRegisterIq.h base/QXmppResultSet.h base/QXmppRosterIq.h base/QXmppRpcIq.h base/QXmppSessionIq.h base/QXmppSocks.h base/QXmppStanza.h base/QXmppStartTlsPacket.h base/QXmppStream.h base/QXmppStreamFeatures.h base/QXmppStun.h base/QXmppUtils.h base/QXmppVCardIq.h base/QXmppVersionIq.h # Client client/QXmppArchiveManager.h client/QXmppAttentionManager.h client/QXmppBookmarkManager.h client/QXmppCarbonManager.h client/QXmppClient.h client/QXmppClientExtension.h client/QXmppConfiguration.h client/QXmppDiscoveryManager.h client/QXmppEntityTimeManager.h client/QXmppInvokable.h client/QXmppMamManager.h client/QXmppMessageReceiptManager.h client/QXmppMucManager.h client/QXmppOutgoingClient.h client/QXmppRegistrationManager.h client/QXmppRemoteMethod.h client/QXmppRosterManager.h client/QXmppRpcManager.h client/QXmppTransferManager.h client/QXmppTransferManager_p.h client/QXmppUploadRequestManager.h client/QXmppVCardManager.h client/QXmppVersionManager.h # Server server/QXmppDialback.h server/QXmppIncomingClient.h server/QXmppIncomingServer.h server/QXmppOutgoingServer.h server/QXmppPasswordChecker.h server/QXmppServer.h server/QXmppServerExtension.h server/QXmppServerPlugin.h ) set(SOURCE_FILES # Base base/QXmppArchiveIq.cpp base/QXmppBindIq.cpp base/QXmppBitsOfBinaryContentId.cpp base/QXmppBitsOfBinaryData.cpp base/QXmppBitsOfBinaryDataList.cpp base/QXmppBitsOfBinaryIq.cpp base/QXmppBookmarkSet.cpp base/QXmppByteStreamIq.cpp base/QXmppConstants.cpp base/QXmppDataForm.cpp 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/QXmppMixInvitation.cpp base/QXmppMixIq.cpp base/QXmppMixItem.cpp base/QXmppMucIq.cpp base/QXmppNonSASLAuth.cpp base/QXmppPingIq.cpp base/QXmppPresence.cpp base/QXmppPubSubIq.cpp base/QXmppPubSubItem.cpp base/QXmppPushEnableIq.cpp base/QXmppRegisterIq.cpp base/QXmppResultSet.cpp base/QXmppRosterIq.cpp base/QXmppRpcIq.cpp base/QXmppSasl.cpp base/QXmppSessionIq.cpp base/QXmppSocks.cpp base/QXmppStanza.cpp base/QXmppStartTlsPacket.cpp base/QXmppStream.cpp base/QXmppStreamFeatures.cpp base/QXmppStreamInitiationIq.cpp base/QXmppStreamManagement.cpp base/QXmppStun.cpp base/QXmppUtils.cpp base/QXmppVCardIq.cpp base/QXmppVersionIq.cpp # Client client/QXmppArchiveManager.cpp client/QXmppAttentionManager.cpp client/QXmppBookmarkManager.cpp client/QXmppCarbonManager.cpp client/QXmppClient.cpp client/QXmppClientExtension.cpp client/QXmppConfiguration.cpp client/QXmppDiscoveryManager.cpp client/QXmppEntityTimeManager.cpp client/QXmppInternalClientExtension.cpp client/QXmppInvokable.cpp client/QXmppMamManager.cpp client/QXmppMessageReceiptManager.cpp client/QXmppMucManager.cpp client/QXmppOutgoingClient.cpp client/QXmppRosterManager.cpp client/QXmppRegistrationManager.cpp client/QXmppRemoteMethod.cpp client/QXmppRpcManager.cpp client/QXmppTlsManager.cpp client/QXmppTransferManager.cpp client/QXmppUploadRequestManager.cpp client/QXmppVCardManager.cpp client/QXmppVersionManager.cpp # Server server/QXmppDialback.cpp server/QXmppIncomingClient.cpp server/QXmppIncomingServer.cpp server/QXmppOutgoingServer.cpp server/QXmppPasswordChecker.cpp server/QXmppServer.cpp server/QXmppServerExtension.cpp server/QXmppServerPlugin.cpp ) if(WITH_GSTREAMER) find_package(GStreamer REQUIRED) find_package(GLIB2 REQUIRED) find_package(GObject REQUIRED) set(INSTALL_HEADER_FILES ${INSTALL_HEADER_FILES} client/QXmppCall.h client/QXmppCallManager.h client/QXmppCallStream.h ) set(SOURCE_FILES ${SOURCE_FILES} client/QXmppCall.cpp client/QXmppCallManager.cpp client/QXmppCallStream.cpp ) endif() if(BUILD_SHARED) add_library(qxmpp SHARED ${SOURCE_FILES}) else() add_library(qxmpp STATIC ${SOURCE_FILES}) endif() set_target_properties(qxmpp PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${SO_VERSION} EXPORT_NAME QXmpp ) target_include_directories(qxmpp PUBLIC $ $ $ $ $ ${GLIB2_INCLUDE_DIR} ${GOBJECT_INCLUDE_DIR} ${GSTREAMER_INCLUDE_DIRS} ) target_link_libraries(qxmpp PUBLIC Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Xml ) if(WITH_GSTREAMER) target_link_libraries(qxmpp PRIVATE ${GLIB2_LIBRARIES} ${GOBJECT_LIBRARIES} ${GSTREAMER_LIBRARY} ) endif() install( TARGETS qxmpp DESTINATION "${CMAKE_INSTALL_LIBDIR}" EXPORT QXmppTarget ) install( EXPORT QXmppTarget DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/qxmpp" FILE QXmpp.cmake NAMESPACE QXmpp:: COMPONENT Devel ) export( TARGETS qxmpp FILE QXmpp.cmake ) install( FILES ${INSTALL_HEADER_FILES} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/qxmpp" ) qxmpp-1.4.0/src/base/000077500000000000000000000000001402370562100143625ustar00rootroot00000000000000qxmpp-1.4.0/src/base/QXmppArchiveIq.cpp000066400000000000000000000362621402370562100177400ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppArchiveIq.h" #include "QXmppConstants_p.h" #include "QXmppUtils.h" #include QXmppArchiveMessage::QXmppArchiveMessage() : m_received(false) { } /// Returns the archived message's body. QString QXmppArchiveMessage::body() const { return m_body; } /// Sets the archived message's body. /// /// \param body void QXmppArchiveMessage::setBody(const QString &body) { m_body = body; } /// Returns the archived message's date. QDateTime QXmppArchiveMessage::date() const { return m_date; } //// Sets the archived message's date. /// /// \param date void QXmppArchiveMessage::setDate(const QDateTime &date) { m_date = date; } /// Returns true if the archived message was received, false if it was sent. bool QXmppArchiveMessage::isReceived() const { return m_received; } /// Set to true if the archived message was received, false if it was sent. /// /// \param isReceived void QXmppArchiveMessage::setReceived(bool isReceived) { m_received = isReceived; } QXmppArchiveChat::QXmppArchiveChat() : m_version(0) { } /// \cond void QXmppArchiveChat::parse(const QDomElement &element) { m_with = element.attribute(QStringLiteral("with")); m_start = QXmppUtils::datetimeFromString(element.attribute(QStringLiteral("start"))); m_subject = element.attribute(QStringLiteral("subject")); m_thread = element.attribute(QStringLiteral("thread")); m_version = element.attribute(QStringLiteral("version")).toInt(); QDateTime timeAccu = m_start; QDomElement child = element.firstChildElement(); while (!child.isNull()) { if ((child.tagName() == QStringLiteral("from")) || (child.tagName() == QStringLiteral("to"))) { QXmppArchiveMessage message; message.setBody(child.firstChildElement(QStringLiteral("body")).text()); timeAccu = timeAccu.addSecs(child.attribute(QStringLiteral("secs")).toInt()); message.setDate(timeAccu); message.setReceived(child.tagName() == QStringLiteral("from")); m_messages << message; } child = child.nextSiblingElement(); } } void QXmppArchiveChat::toXml(QXmlStreamWriter *writer, const QXmppResultSetReply &rsm) const { writer->writeStartElement(QStringLiteral("chat")); writer->writeDefaultNamespace(ns_archive); helperToXmlAddAttribute(writer, QStringLiteral("with"), m_with); if (m_start.isValid()) helperToXmlAddAttribute(writer, QStringLiteral("start"), QXmppUtils::datetimeToString(m_start)); helperToXmlAddAttribute(writer, QStringLiteral("subject"), m_subject); helperToXmlAddAttribute(writer, QStringLiteral("thread"), m_thread); if (m_version) helperToXmlAddAttribute(writer, QStringLiteral("version"), QString::number(m_version)); QDateTime prevTime = m_start; for (const QXmppArchiveMessage &message : m_messages) { writer->writeStartElement(message.isReceived() ? QStringLiteral("from") : QStringLiteral("to")); helperToXmlAddAttribute(writer, QStringLiteral("secs"), QString::number(prevTime.secsTo(message.date()))); writer->writeTextElement(QStringLiteral("body"), message.body()); writer->writeEndElement(); prevTime = message.date(); } if (!rsm.isNull()) rsm.toXml(writer); writer->writeEndElement(); } /// \endcond /// Returns the conversation's messages. QList QXmppArchiveChat::messages() const { return m_messages; } /// Sets the conversation's messages. void QXmppArchiveChat::setMessages(const QList &messages) { m_messages = messages; } /// Returns the start of this conversation. QDateTime QXmppArchiveChat::start() const { return m_start; } /// Sets the start of this conversation. void QXmppArchiveChat::setStart(const QDateTime &start) { m_start = start; } /// Returns the conversation's subject. QString QXmppArchiveChat::subject() const { return m_subject; } /// Sets the conversation's subject. void QXmppArchiveChat::setSubject(const QString &subject) { m_subject = subject; } /// Returns the conversation's thread. QString QXmppArchiveChat::thread() const { return m_thread; } /// Sets the conversation's thread. void QXmppArchiveChat::setThread(const QString &thread) { m_thread = thread; } /// Returns the conversation's version. int QXmppArchiveChat::version() const { return m_version; } /// Sets the conversation's version. void QXmppArchiveChat::setVersion(int version) { m_version = version; } /// Returns the JID of the remote party. QString QXmppArchiveChat::with() const { return m_with; } /// Sets the JID of the remote party. void QXmppArchiveChat::setWith(const QString &with) { m_with = with; } /// Returns the chat conversation carried by this IQ. QXmppArchiveChat QXmppArchiveChatIq::chat() const { return m_chat; } /// Sets the chat conversation carried by this IQ. void QXmppArchiveChatIq::setChat(const QXmppArchiveChat &chat) { m_chat = chat; } /// Returns the result set management reply. /// /// This is used for paging through messages. QXmppResultSetReply QXmppArchiveChatIq::resultSetReply() const { return m_rsmReply; } /// Sets the result set management reply. /// /// This is used for paging through messages. void QXmppArchiveChatIq::setResultSetReply(const QXmppResultSetReply &rsm) { m_rsmReply = rsm; } /// \cond bool QXmppArchiveChatIq::isArchiveChatIq(const QDomElement &element) { QDomElement chatElement = element.firstChildElement(QStringLiteral("chat")); return !chatElement.attribute(QStringLiteral("with")).isEmpty(); //return (chatElement.namespaceURI() == ns_archive); } void QXmppArchiveChatIq::parseElementFromChild(const QDomElement &element) { QDomElement chatElement = element.firstChildElement(QStringLiteral("chat")); m_chat.parse(chatElement); m_rsmReply.parse(chatElement); } void QXmppArchiveChatIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { m_chat.toXml(writer, m_rsmReply); } /// \endcond /// Constructs a QXmppArchiveListIq. QXmppArchiveListIq::QXmppArchiveListIq() : QXmppIq(QXmppIq::Get) { } /// Returns the list of chat conversations. QList QXmppArchiveListIq::chats() const { return m_chats; } /// Sets the list of chat conversations. void QXmppArchiveListIq::setChats(const QList &chats) { m_chats = chats; } /// Returns the JID which archived conversations must match. /// QString QXmppArchiveListIq::with() const { return m_with; } /// Sets the JID which archived conversations must match. /// /// \param with void QXmppArchiveListIq::setWith(const QString &with) { m_with = with; } /// Returns the start date/time for the archived conversations. /// QDateTime QXmppArchiveListIq::start() const { return m_start; } /// Sets the start date/time for the archived conversations. /// /// \param start void QXmppArchiveListIq::setStart(const QDateTime &start) { m_start = start; } /// Returns the end date/time for the archived conversations. /// QDateTime QXmppArchiveListIq::end() const { return m_end; } /// Sets the end date/time for the archived conversations. /// /// \param end void QXmppArchiveListIq::setEnd(const QDateTime &end) { m_end = end; } /// Returns the result set management query. /// /// This is used for paging through conversations. QXmppResultSetQuery QXmppArchiveListIq::resultSetQuery() const { return m_rsmQuery; } /// Sets the result set management query. /// /// This is used for paging through conversations. void QXmppArchiveListIq::setResultSetQuery(const QXmppResultSetQuery &rsm) { m_rsmQuery = rsm; } /// Returns the result set management reply. /// /// This is used for paging through conversations. QXmppResultSetReply QXmppArchiveListIq::resultSetReply() const { return m_rsmReply; } /// Sets the result set management reply. /// /// This is used for paging through conversations. void QXmppArchiveListIq::setResultSetReply(const QXmppResultSetReply &rsm) { m_rsmReply = rsm; } /// \cond bool QXmppArchiveListIq::isArchiveListIq(const QDomElement &element) { QDomElement listElement = element.firstChildElement(QStringLiteral("list")); return (listElement.namespaceURI() == ns_archive); } void QXmppArchiveListIq::parseElementFromChild(const QDomElement &element) { QDomElement listElement = element.firstChildElement(QStringLiteral("list")); m_with = listElement.attribute(QStringLiteral("with")); m_start = QXmppUtils::datetimeFromString(listElement.attribute(QStringLiteral("start"))); m_end = QXmppUtils::datetimeFromString(listElement.attribute(QStringLiteral("end"))); m_rsmQuery.parse(listElement); m_rsmReply.parse(listElement); QDomElement child = listElement.firstChildElement(); while (!child.isNull()) { if (child.tagName() == QStringLiteral("chat")) { QXmppArchiveChat chat; chat.parse(child); m_chats << chat; } child = child.nextSiblingElement(); } } void QXmppArchiveListIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("list")); writer->writeDefaultNamespace(ns_archive); if (!m_with.isEmpty()) helperToXmlAddAttribute(writer, QStringLiteral("with"), m_with); if (m_start.isValid()) helperToXmlAddAttribute(writer, QStringLiteral("start"), QXmppUtils::datetimeToString(m_start)); if (m_end.isValid()) helperToXmlAddAttribute(writer, QStringLiteral("end"), QXmppUtils::datetimeToString(m_end)); if (!m_rsmQuery.isNull()) m_rsmQuery.toXml(writer); else if (!m_rsmReply.isNull()) m_rsmReply.toXml(writer); for (const auto &chat : m_chats) chat.toXml(writer); writer->writeEndElement(); } bool QXmppArchivePrefIq::isArchivePrefIq(const QDomElement &element) { QDomElement prefElement = element.firstChildElement(QStringLiteral("pref")); return (prefElement.namespaceURI() == ns_archive); } void QXmppArchivePrefIq::parseElementFromChild(const QDomElement &element) { QDomElement queryElement = element.firstChildElement(QStringLiteral("pref")); Q_UNUSED(queryElement); } void QXmppArchivePrefIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("pref")); writer->writeDefaultNamespace(ns_archive); writer->writeEndElement(); } /// \endcond /// Returns the JID which archived conversations must match. /// QString QXmppArchiveRemoveIq::with() const { return m_with; } /// Sets the JID which archived conversations must match. /// /// \param with void QXmppArchiveRemoveIq::setWith(const QString &with) { m_with = with; } /// Returns the start date/time for the archived conversations. /// QDateTime QXmppArchiveRemoveIq::start() const { return m_start; } /// Sets the start date/time for the archived conversations. /// /// \param start void QXmppArchiveRemoveIq::setStart(const QDateTime &start) { m_start = start; } /// Returns the end date/time for the archived conversations. /// QDateTime QXmppArchiveRemoveIq::end() const { return m_end; } /// Sets the end date/time for the archived conversations. /// /// \param end void QXmppArchiveRemoveIq::setEnd(const QDateTime &end) { m_end = end; } /// \cond bool QXmppArchiveRemoveIq::isArchiveRemoveIq(const QDomElement &element) { QDomElement retrieveElement = element.firstChildElement(QStringLiteral("remove")); return (retrieveElement.namespaceURI() == ns_archive); } void QXmppArchiveRemoveIq::parseElementFromChild(const QDomElement &element) { QDomElement listElement = element.firstChildElement(QStringLiteral("remove")); m_with = listElement.attribute(QStringLiteral("with")); m_start = QXmppUtils::datetimeFromString(listElement.attribute(QStringLiteral("start"))); m_end = QXmppUtils::datetimeFromString(listElement.attribute(QStringLiteral("end"))); } void QXmppArchiveRemoveIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("remove")); writer->writeDefaultNamespace(ns_archive); if (!m_with.isEmpty()) helperToXmlAddAttribute(writer, QStringLiteral("with"), m_with); if (m_start.isValid()) helperToXmlAddAttribute(writer, QStringLiteral("start"), QXmppUtils::datetimeToString(m_start)); if (m_end.isValid()) helperToXmlAddAttribute(writer, QStringLiteral("end"), QXmppUtils::datetimeToString(m_end)); writer->writeEndElement(); } /// \endcond QXmppArchiveRetrieveIq::QXmppArchiveRetrieveIq() : QXmppIq(QXmppIq::Get) { } /// Returns the start date/time for the archived conversations. /// QDateTime QXmppArchiveRetrieveIq::start() const { return m_start; } /// Sets the start date/time for the archived conversations. /// /// \param start void QXmppArchiveRetrieveIq::setStart(const QDateTime &start) { m_start = start; } /// Returns the JID which archived conversations must match. /// QString QXmppArchiveRetrieveIq::with() const { return m_with; } /// Sets the JID which archived conversations must match. /// /// \param with void QXmppArchiveRetrieveIq::setWith(const QString &with) { m_with = with; } /// Returns the result set management query. /// /// This is used for paging through messages. QXmppResultSetQuery QXmppArchiveRetrieveIq::resultSetQuery() const { return m_rsmQuery; } /// Sets the result set management query. /// /// This is used for paging through messages. void QXmppArchiveRetrieveIq::setResultSetQuery(const QXmppResultSetQuery &rsm) { m_rsmQuery = rsm; } /// \cond bool QXmppArchiveRetrieveIq::isArchiveRetrieveIq(const QDomElement &element) { QDomElement retrieveElement = element.firstChildElement(QStringLiteral("retrieve")); return (retrieveElement.namespaceURI() == ns_archive); } void QXmppArchiveRetrieveIq::parseElementFromChild(const QDomElement &element) { QDomElement retrieveElement = element.firstChildElement(QStringLiteral("retrieve")); m_with = retrieveElement.attribute(QStringLiteral("with")); m_start = QXmppUtils::datetimeFromString(retrieveElement.attribute(QStringLiteral("start"))); m_rsmQuery.parse(retrieveElement); } void QXmppArchiveRetrieveIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("retrieve")); writer->writeDefaultNamespace(ns_archive); helperToXmlAddAttribute(writer, QStringLiteral("with"), m_with); helperToXmlAddAttribute(writer, QStringLiteral("start"), QXmppUtils::datetimeToString(m_start)); if (!m_rsmQuery.isNull()) m_rsmQuery.toXml(writer); writer->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/base/QXmppArchiveIq.h000066400000000000000000000141301402370562100173730ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPARCHIVEIQ_H #define QXMPPARCHIVEIQ_H #include "QXmppIq.h" #include "QXmppResultSet.h" #include /// \brief The QXmppArchiveMessage class represents an archived message /// as defined by \xep{0136}: Message Archiving. class QXMPP_EXPORT QXmppArchiveMessage { public: QXmppArchiveMessage(); QString body() const; void setBody(const QString &body); QDateTime date() const; void setDate(const QDateTime &date); bool isReceived() const; void setReceived(bool isReceived); private: QString m_body; QDateTime m_date; bool m_received; }; /// \brief The QXmppArchiveChat class represents an archived conversation /// as defined by \xep{0136}: Message Archiving. class QXMPP_EXPORT QXmppArchiveChat { public: QXmppArchiveChat(); QList messages() const; void setMessages(const QList &messages); QDateTime start() const; void setStart(const QDateTime &start); QString subject() const; void setSubject(const QString &subject); QString thread() const; void setThread(const QString &thread); int version() const; void setVersion(int version); QString with() const; void setWith(const QString &with); /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *writer, const QXmppResultSetReply &rsm = QXmppResultSetReply()) const; /// \endcond private: QList m_messages; QDateTime m_start; QString m_subject; QString m_thread; int m_version; QString m_with; }; /// \brief Represents an archive chat as defined by \xep{0136}: Message Archiving. /// /// It is used to get chat as a QXmppArchiveChat. /// /// \ingroup Stanzas class QXMPP_EXPORT QXmppArchiveChatIq : public QXmppIq { public: QXmppArchiveChat chat() const; void setChat(const QXmppArchiveChat &chat); QXmppResultSetReply resultSetReply() const; void setResultSetReply(const QXmppResultSetReply &rsm); /// \cond static bool isArchiveChatIq(const QDomElement &element); protected: void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: QXmppArchiveChat m_chat; QXmppResultSetReply m_rsmReply; }; /// \brief Represents an archive list as defined by \xep{0136}: Message Archiving. /// /// \ingroup Stanzas class QXMPP_EXPORT QXmppArchiveListIq : public QXmppIq { public: QXmppArchiveListIq(); QList chats() const; void setChats(const QList &chats); QString with() const; void setWith(const QString &with); QDateTime start() const; void setStart(const QDateTime &start); QDateTime end() const; void setEnd(const QDateTime &end); QXmppResultSetQuery resultSetQuery() const; void setResultSetQuery(const QXmppResultSetQuery &rsm); QXmppResultSetReply resultSetReply() const; void setResultSetReply(const QXmppResultSetReply &rsm); /// \cond static bool isArchiveListIq(const QDomElement &element); /// \endcond protected: /// \cond void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: QString m_with; QDateTime m_start; QDateTime m_end; QList m_chats; QXmppResultSetQuery m_rsmQuery; QXmppResultSetReply m_rsmReply; }; /// \brief Represents an archive remove IQ as defined by \xep{0136}: Message Archiving. /// /// \ingroup Stanzas class QXMPP_EXPORT QXmppArchiveRemoveIq : public QXmppIq { public: QString with() const; void setWith(const QString &with); QDateTime start() const; void setStart(const QDateTime &start); QDateTime end() const; void setEnd(const QDateTime &end); /// \cond static bool isArchiveRemoveIq(const QDomElement &element); protected: void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: QString m_with; QDateTime m_start; QDateTime m_end; }; /// \brief Represents an archive retrieve IQ as defined by \xep{0136}: Message Archiving. /// /// \ingroup Stanzas class QXMPP_EXPORT QXmppArchiveRetrieveIq : public QXmppIq { public: QXmppArchiveRetrieveIq(); QDateTime start() const; void setStart(const QDateTime &start); QString with() const; void setWith(const QString &with); QXmppResultSetQuery resultSetQuery() const; void setResultSetQuery(const QXmppResultSetQuery &rsm); /// \cond static bool isArchiveRetrieveIq(const QDomElement &element); protected: void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: QString m_with; QDateTime m_start; QXmppResultSetQuery m_rsmQuery; }; /// \brief Represents an archive preference IQ as defined by \xep{0136}: Message Archiving. /// /// \ingroup Stanzas class QXMPP_EXPORT QXmppArchivePrefIq : public QXmppIq { public: /// \cond static bool isArchivePrefIq(const QDomElement &element); protected: void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond }; #endif // QXMPPARCHIVEIQ_H qxmpp-1.4.0/src/base/QXmppBindIq.cpp000066400000000000000000000043541402370562100172300ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Manjeet Dahiya * Jeremy Lainé * * 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 "QXmppBindIq.h" #include "QXmppConstants_p.h" #include "QXmppUtils.h" #include #include #include /// Returns the bound JID. /// QString QXmppBindIq::jid() const { return m_jid; } /// Sets the bound JID. /// /// \param jid void QXmppBindIq::setJid(const QString &jid) { m_jid = jid; } /// Returns the requested resource. /// QString QXmppBindIq::resource() const { return m_resource; } /// Sets the requested resource. /// /// \param resource void QXmppBindIq::setResource(const QString &resource) { m_resource = resource; } /// \cond bool QXmppBindIq::isBindIq(const QDomElement &element) { QDomElement bindElement = element.firstChildElement(QStringLiteral("bind")); return (bindElement.namespaceURI() == ns_bind); } void QXmppBindIq::parseElementFromChild(const QDomElement &element) { QDomElement bindElement = element.firstChildElement(QStringLiteral("bind")); m_jid = bindElement.firstChildElement(QStringLiteral("jid")).text(); m_resource = bindElement.firstChildElement(QStringLiteral("resource")).text(); } void QXmppBindIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("bind")); writer->writeDefaultNamespace(ns_bind); if (!m_jid.isEmpty()) helperToXmlAddTextElement(writer, QStringLiteral("jid"), m_jid); if (!m_resource.isEmpty()) helperToXmlAddTextElement(writer, QStringLiteral("resource"), m_resource); writer->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/base/QXmppBindIq.h000066400000000000000000000026731402370562100166770ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Manjeet Dahiya * Jeremy Lainé * * 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 QXMPPBINDIQ_H #define QXMPPBINDIQ_H #include "QXmppIq.h" /// \brief The QXmppBindIq class represents an IQ used for resource /// binding as defined by RFC 3921. /// /// \ingroup Stanzas class QXMPP_EXPORT QXmppBindIq : public QXmppIq { public: QString jid() const; void setJid(const QString &); QString resource() const; void setResource(const QString &); /// \cond static bool isBindIq(const QDomElement &element); /// \endcond protected: /// \cond void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: QString m_jid; QString m_resource; }; #endif // QXMPPBIND_H qxmpp-1.4.0/src/base/QXmppBitsOfBinaryContentId.cpp000066400000000000000000000160421402370562100222220ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Linus Jahn * * 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 "QXmppBitsOfBinaryContentId.h" #include #include #include #define CONTENTID_URL QStringLiteral("cid:") #define CONTENTID_URL_LENGTH 4 #define CONTENTID_POSTFIX QStringLiteral("@bob.xmpp.org") #define CONTENTID_POSTFIX_LENGTH 13 #define CONTENTID_HASH_SEPARATOR QStringLiteral("+") static const QMap HASH_ALGORITHMS = { { QCryptographicHash::Sha1, QStringLiteral("sha1") }, #ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 { QCryptographicHash::Md4, QStringLiteral("md4") }, { QCryptographicHash::Md5, QStringLiteral("md5") }, { QCryptographicHash::Sha224, QStringLiteral("sha224") }, { QCryptographicHash::Sha256, QStringLiteral("sha256") }, { QCryptographicHash::Sha384, QStringLiteral("sha384") }, { QCryptographicHash::Sha512, QStringLiteral("sha512") }, { QCryptographicHash::Sha3_224, QStringLiteral("sha3-224") }, { QCryptographicHash::Sha3_256, QStringLiteral("sha3-256") }, { QCryptographicHash::Sha3_384, QStringLiteral("sha3-384") }, { QCryptographicHash::Sha3_512, QStringLiteral("sha3-512") }, #endif }; class QXmppBitsOfBinaryContentIdPrivate : public QSharedData { public: QXmppBitsOfBinaryContentIdPrivate(); QCryptographicHash::Algorithm algorithm; QByteArray hash; }; QXmppBitsOfBinaryContentIdPrivate::QXmppBitsOfBinaryContentIdPrivate() : algorithm(QCryptographicHash::Sha1) { } /// Parses a \c QXmppBitsOfBinaryContentId from a \xep{0231}: Bits of Binary /// \c cid: URL /// /// In case parsing failed, the returned \c QXmppBitsOfBinaryContentId is /// empty. /// /// \see QXmppBitsOfBinaryContentId::fromContentId QXmppBitsOfBinaryContentId QXmppBitsOfBinaryContentId::fromCidUrl(const QString &input) { if (input.startsWith(CONTENTID_URL)) return fromContentId(input.mid(CONTENTID_URL_LENGTH)); return {}; } /// Parses a \c QXmppBitsOfBinaryContentId from a \xep{0231}: Bits of Binary /// content id /// /// In case parsing failed, the returned \c QXmppBitsOfBinaryContentId is /// empty. /// /// \note This does not allow \c cid: URLs to be passed. Use /// \c QXmppBitsOfBinaryContentId::fromCidUrl for that purpose. /// /// \see QXmppBitsOfBinaryContentId::fromCidUrl QXmppBitsOfBinaryContentId QXmppBitsOfBinaryContentId::fromContentId(const QString &input) { if (input.startsWith(CONTENTID_URL) || !input.endsWith(CONTENTID_POSTFIX)) return {}; // remove '@bob.xmpp.org' QString hashAndAlgoStr = input.left(input.size() - CONTENTID_POSTFIX_LENGTH); // get size of hash algo id QStringList algoAndHash = hashAndAlgoStr.split(CONTENTID_HASH_SEPARATOR); if (algoAndHash.size() != 2) return {}; QCryptographicHash::Algorithm algo = HASH_ALGORITHMS.key(algoAndHash.first(), QCryptographicHash::Algorithm(-1)); if (int(algo) == -1) return {}; QXmppBitsOfBinaryContentId cid; cid.setAlgorithm(algo); cid.setHash(QByteArray::fromHex(algoAndHash.last().toUtf8())); return cid; } /// Default contructor QXmppBitsOfBinaryContentId::QXmppBitsOfBinaryContentId() : d(new QXmppBitsOfBinaryContentIdPrivate) { } /// Returns true, if two \c QXmppBitsOfBinaryContentId equal bool QXmppBitsOfBinaryContentId::operator==(const QXmppBitsOfBinaryContentId &other) const { return d->algorithm == other.algorithm() && d->hash == other.hash(); } QXmppBitsOfBinaryContentId::~QXmppBitsOfBinaryContentId() = default; QXmppBitsOfBinaryContentId::QXmppBitsOfBinaryContentId(const QXmppBitsOfBinaryContentId &cid) = default; QXmppBitsOfBinaryContentId &QXmppBitsOfBinaryContentId::operator=(const QXmppBitsOfBinaryContentId &other) = default; /// Returns a \xep{0231}: Bits of Binary content id QString QXmppBitsOfBinaryContentId::toContentId() const { if (!isValid()) return {}; return HASH_ALGORITHMS.value(d->algorithm) + CONTENTID_HASH_SEPARATOR + d->hash.toHex() + CONTENTID_POSTFIX; } /// Returns a \xep{0231}: Bits of Binary \c cid: URL QString QXmppBitsOfBinaryContentId::toCidUrl() const { if (!isValid()) return {}; return toContentId().prepend(CONTENTID_URL); } /// Returns the hash value in binary form QByteArray QXmppBitsOfBinaryContentId::hash() const { return d->hash; } /// Sets the hash value in binary form void QXmppBitsOfBinaryContentId::setHash(const QByteArray &hash) { d->hash = hash; } /// Returns the hash algorithm used to calculate the \c hash value /// /// The default value is \c QCryptographicHash::Sha1. /// /// This currently supports MD4, MD5, SHA-1, SHA-2 (SHA224 - SHA512) and SHA-3 /// (SHA3-224 - SHA3-512). QCryptographicHash::Algorithm QXmppBitsOfBinaryContentId::algorithm() const { return d->algorithm; } /// Sets the hash algorithm used to calculate the \c hash value /// /// The default value is \c QCryptographicHash::Sha1. /// /// This currently supports MD4, MD5, SHA-1, SHA-2 (SHA224 - SHA512) and SHA-3 /// (SHA3-224 - SHA3-512). /// /// \note Only change this, if you know what you do. The XEP allows other /// hashing algorithms than SHA-1 to be used, but not all clients support this. /// Since in most cases the content id is not security relevant it is not a /// problem to continue using SHA-1. void QXmppBitsOfBinaryContentId::setAlgorithm(QCryptographicHash::Algorithm algo) { d->algorithm = algo; } /// Checks whether the content id is valid and can be serialized into a string. /// /// \note Checking the hash length requires QXmpp to be built with Qt 5.12.0 or /// later. /// /// \returns True, if the set hashing algorithm is supported, a hash value is /// set and its length is correct, false otherwise. bool QXmppBitsOfBinaryContentId::isValid() const { #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) return !d->hash.isEmpty() && HASH_ALGORITHMS.contains(d->algorithm) && d->hash.length() == QCryptographicHash::hashLength(d->algorithm); #else return !d->hash.isEmpty() && HASH_ALGORITHMS.contains(d->algorithm); #endif } /// Checks whether \c input is a Bits of Binary content id or \c cid: URL /// /// \param checkIsCidUrl If true, it only accepts \c cid: URLs. /// /// \returns True, if \c input is valid. bool QXmppBitsOfBinaryContentId::isBitsOfBinaryContentId(const QString &input, bool checkIsCidUrl) { return input.endsWith(CONTENTID_POSTFIX) && input.contains(CONTENTID_HASH_SEPARATOR) && (!checkIsCidUrl || input.startsWith(CONTENTID_URL)); } qxmpp-1.4.0/src/base/QXmppBitsOfBinaryContentId.h000066400000000000000000000037621402370562100216740ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Linus Jahn * * 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 QXMPPBITSOFBINARYCONTENTID_H #define QXMPPBITSOFBINARYCONTENTID_H #include "QXmppGlobal.h" #include #include class QXmppBitsOfBinaryContentIdPrivate; /// \class QXmppBitsOfBinaryContentId represents a link to or an identifier of /// \xep{0231}: Bits of Binary data. /// /// \since QXmpp 1.2 class QXMPP_EXPORT QXmppBitsOfBinaryContentId { public: static QXmppBitsOfBinaryContentId fromCidUrl(const QString &input); static QXmppBitsOfBinaryContentId fromContentId(const QString &input); QXmppBitsOfBinaryContentId(); QXmppBitsOfBinaryContentId(const QXmppBitsOfBinaryContentId &cid); ~QXmppBitsOfBinaryContentId(); QXmppBitsOfBinaryContentId &operator=(const QXmppBitsOfBinaryContentId &other); QString toContentId() const; QString toCidUrl() const; QByteArray hash() const; void setHash(const QByteArray &hash); QCryptographicHash::Algorithm algorithm() const; void setAlgorithm(QCryptographicHash::Algorithm algo); bool isValid() const; static bool isBitsOfBinaryContentId(const QString &uri, bool checkIsCidUrl = false); bool operator==(const QXmppBitsOfBinaryContentId &other) const; private: QSharedDataPointer d; }; #endif // QXMPPBITSOFBINARYCONTENTID_H qxmpp-1.4.0/src/base/QXmppBitsOfBinaryData.cpp000066400000000000000000000107141402370562100212040ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Linus Jahn * * 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 "QXmppBitsOfBinaryData.h" #include "QXmppBitsOfBinaryContentId.h" #include "QXmppConstants_p.h" #include "QXmppUtils.h" #include #include #include #include #include class QXmppBitsOfBinaryDataPrivate : public QSharedData { public: QXmppBitsOfBinaryDataPrivate(); QXmppBitsOfBinaryContentId cid; int maxAge; QMimeType contentType; QByteArray data; }; QXmppBitsOfBinaryDataPrivate::QXmppBitsOfBinaryDataPrivate() : maxAge(-1) { } QXmppBitsOfBinaryData::QXmppBitsOfBinaryData() : d(new QXmppBitsOfBinaryDataPrivate) { } QXmppBitsOfBinaryData::QXmppBitsOfBinaryData(const QXmppBitsOfBinaryData &) = default; QXmppBitsOfBinaryData::~QXmppBitsOfBinaryData() = default; QXmppBitsOfBinaryData &QXmppBitsOfBinaryData::operator=(const QXmppBitsOfBinaryData &) = default; /// Returns the content id of the data QXmppBitsOfBinaryContentId QXmppBitsOfBinaryData::cid() const { return d->cid; } /// Sets the content id of the data void QXmppBitsOfBinaryData::setCid(const QXmppBitsOfBinaryContentId &cid) { d->cid = cid; } /// Returns the time in seconds the data should be cached /// /// A value of 0 means that the data should not be cached, while a value of -1 /// means that nothing was set. /// /// The default value is -1. int QXmppBitsOfBinaryData::maxAge() const { return d->maxAge; } /// Sets the time in seconds the data should be cached /// /// A value of 0 means that the data should not be cached, while a value of -1 /// means that nothing was set. /// /// The default value is -1. void QXmppBitsOfBinaryData::setMaxAge(int maxAge) { d->maxAge = maxAge; } /// Returns the content type of the data /// /// \note This is the advertised content type and may differ from the actual /// content type of the data. QMimeType QXmppBitsOfBinaryData::contentType() const { return d->contentType; } /// Sets the content type of the data void QXmppBitsOfBinaryData::setContentType(const QMimeType &contentType) { d->contentType = contentType; } /// Returns the included data in binary form QByteArray QXmppBitsOfBinaryData::data() const { return d->data; } /// Sets the data in binary form void QXmppBitsOfBinaryData::setData(const QByteArray &data) { d->data = data; } /// Returns true, if \c element is a \xep{0231}: Bits of Binary data element bool QXmppBitsOfBinaryData::isBitsOfBinaryData(const QDomElement &element) { return element.tagName() == QStringLiteral("data") && element.namespaceURI() == ns_bob; } /// \cond void QXmppBitsOfBinaryData::parseElementFromChild(const QDomElement &dataElement) { d->cid = QXmppBitsOfBinaryContentId::fromContentId(dataElement.attribute(QStringLiteral("cid"))); d->maxAge = dataElement.attribute(QStringLiteral("max-age"), QStringLiteral("-1")).toInt(); d->contentType = QMimeDatabase().mimeTypeForName(dataElement.attribute(QStringLiteral("type"))); d->data = QByteArray::fromBase64(dataElement.text().toUtf8()); } void QXmppBitsOfBinaryData::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("data")); writer->writeDefaultNamespace(ns_bob); helperToXmlAddAttribute(writer, QStringLiteral("cid"), d->cid.toContentId()); if (d->maxAge > -1) helperToXmlAddAttribute(writer, QStringLiteral("max-age"), QString::number(d->maxAge)); helperToXmlAddAttribute(writer, QStringLiteral("type"), d->contentType.name()); writer->writeCharacters(d->data.toBase64()); writer->writeEndElement(); } /// \endcond bool QXmppBitsOfBinaryData::operator==(const QXmppBitsOfBinaryData &other) const { return d->cid == other.cid() && d->maxAge == other.maxAge() && d->contentType == other.contentType() && d->data == other.data(); } qxmpp-1.4.0/src/base/QXmppBitsOfBinaryData.h000066400000000000000000000041411402370562100206460ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Linus Jahn * * 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 QXMPPBITSOFBINARYDATA_H #define QXMPPBITSOFBINARYDATA_H #include "QXmppGlobal.h" #include class QDomElement; class QMimeType; class QXmlStreamWriter; class QXmppBitsOfBinaryDataPrivate; class QXmppBitsOfBinaryContentId; /// \class QXmppBitsOfBinaryData represents a data element for \xep{0231}: Bits /// of Binary. It can be used as an extension in other stanzas. /// /// \see QXmppBitsOfBinaryIq, QXmppBitsOfBinaryDataList /// /// \since QXmpp 1.2 class QXMPP_EXPORT QXmppBitsOfBinaryData { public: QXmppBitsOfBinaryData(); QXmppBitsOfBinaryData(const QXmppBitsOfBinaryData &); ~QXmppBitsOfBinaryData(); QXmppBitsOfBinaryData &operator=(const QXmppBitsOfBinaryData &); QXmppBitsOfBinaryContentId cid() const; void setCid(const QXmppBitsOfBinaryContentId &cid); int maxAge() const; void setMaxAge(int maxAge); QMimeType contentType() const; void setContentType(const QMimeType &contentType); QByteArray data() const; void setData(const QByteArray &data); bool static isBitsOfBinaryData(const QDomElement &element); /// \cond void parseElementFromChild(const QDomElement &dataElement); void toXmlElementFromChild(QXmlStreamWriter *writer) const; /// \endcond bool operator==(const QXmppBitsOfBinaryData &other) const; private: QSharedDataPointer d; }; #endif // QXMPPBITSOFBINARYDATA_H qxmpp-1.4.0/src/base/QXmppBitsOfBinaryDataList.cpp000066400000000000000000000031611402370562100220360ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Linus Jahn * * 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 "QXmppBitsOfBinaryDataList.h" #include "QXmppBitsOfBinaryData.h" #include "QXmppConstants_p.h" #include QXmppBitsOfBinaryDataList::QXmppBitsOfBinaryDataList() = default; QXmppBitsOfBinaryDataList::~QXmppBitsOfBinaryDataList() = default; /// \cond void QXmppBitsOfBinaryDataList::parse(const QDomElement &element) { // clear previous data elements clear(); // parse all elements QDomElement child = element.firstChildElement(); while (!child.isNull()) { if (QXmppBitsOfBinaryData::isBitsOfBinaryData(child)) { QXmppBitsOfBinaryData data; data.parseElementFromChild(child); append(data); } child = child.nextSiblingElement(); } } void QXmppBitsOfBinaryDataList::toXml(QXmlStreamWriter *writer) const { for (const auto &bitsOfBinaryData : *this) bitsOfBinaryData.toXmlElementFromChild(writer); } /// \endcond qxmpp-1.4.0/src/base/QXmppBitsOfBinaryDataList.h000066400000000000000000000025211402370562100215020ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Linus Jahn * * 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 QXMPPBITSOFBINARYDATACONTAINER_H #define QXMPPBITSOFBINARYDATACONTAINER_H #include "QXmppBitsOfBinaryData.h" #include class QDomElement; class QXmlStreamWriter; /// \class QXmppBitsOfBinaryDataList represents a list of data elements from /// \xep{0231}: Bits of Binary. /// /// \since QXmpp 1.2 class QXMPP_EXPORT QXmppBitsOfBinaryDataList : public QVector { public: QXmppBitsOfBinaryDataList(); ~QXmppBitsOfBinaryDataList(); /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *writer) const; /// \endcond }; #endif // QXMPPBITSOFBINARYDATACONTAINER_H qxmpp-1.4.0/src/base/QXmppBitsOfBinaryIq.cpp000066400000000000000000000037161402370562100207100ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Linus Jahn * * 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 "QXmppBitsOfBinaryIq.h" #include "QXmppConstants_p.h" #include #include QXmppBitsOfBinaryIq::QXmppBitsOfBinaryIq() = default; QXmppBitsOfBinaryIq::~QXmppBitsOfBinaryIq() = default; /// Returns true, if \c element is a \xep{0231}: Bits of Binary IQ /// /// \note This may also return true, if the IQ is not a Bits of Binary IQ in /// first place, but only contains a Bits of Binary data element. bool QXmppBitsOfBinaryIq::isBitsOfBinaryIq(const QDomElement &element) { QDomElement child = element.firstChildElement(); while (!child.isNull()) { if (QXmppBitsOfBinaryData::isBitsOfBinaryData(child)) return true; child = child.nextSiblingElement(); } return false; } /// \cond void QXmppBitsOfBinaryIq::parseElementFromChild(const QDomElement &element) { QDomElement child = element.firstChildElement(); while (!child.isNull()) { if (QXmppBitsOfBinaryData::isBitsOfBinaryData(child)) { QXmppBitsOfBinaryData::parseElementFromChild(child); break; } child = child.nextSiblingElement(); } } void QXmppBitsOfBinaryIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { QXmppBitsOfBinaryData::toXmlElementFromChild(writer); } /// \endcond qxmpp-1.4.0/src/base/QXmppBitsOfBinaryIq.h000066400000000000000000000026151402370562100203520ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Linus Jahn * * 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 QXMPPBITSOFBINARYIQ_H #define QXMPPBITSOFBINARYIQ_H #include "QXmppBitsOfBinaryData.h" #include "QXmppIq.h" /// \class QXmppBitsOfBinaryIq represents a \xep{0231}: Bits of Binary IQ to /// request and transmit Bits of Binary data elements. /// /// \since QXmpp 1.2 class QXMPP_EXPORT QXmppBitsOfBinaryIq : public QXmppIq, public QXmppBitsOfBinaryData { public: QXmppBitsOfBinaryIq(); ~QXmppBitsOfBinaryIq(); static bool isBitsOfBinaryIq(const QDomElement &element); protected: /// \cond void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond }; #endif // QXMPPBITSOFBINARYIQ_H qxmpp-1.4.0/src/base/QXmppBookmarkSet.cpp000066400000000000000000000132141402370562100202760ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppBookmarkSet.h" #include "QXmppUtils.h" #include static const char *ns_bookmarks = "storage:bookmarks"; /// Constructs a new conference room bookmark. /// QXmppBookmarkConference::QXmppBookmarkConference() : m_autoJoin(false) { } /// Returns whether the client should automatically join the conference room /// on login. /// bool QXmppBookmarkConference::autoJoin() const { return m_autoJoin; } /// Sets whether the client should automatically join the conference room /// on login. /// /// \param autoJoin void QXmppBookmarkConference::setAutoJoin(bool autoJoin) { m_autoJoin = autoJoin; } /// Returns the JID of the conference room. /// QString QXmppBookmarkConference::jid() const { return m_jid; } /// Sets the JID of the conference room. /// /// \param jid void QXmppBookmarkConference::setJid(const QString &jid) { m_jid = jid; } /// Returns the friendly name for the bookmark. /// QString QXmppBookmarkConference::name() const { return m_name; } /// Sets the friendly name for the bookmark. /// /// \param name void QXmppBookmarkConference::setName(const QString &name) { m_name = name; } /// Returns the preferred nickname for the conference room. /// QString QXmppBookmarkConference::nickName() const { return m_nickName; } /// Sets the preferred nickname for the conference room. /// /// \param nickName void QXmppBookmarkConference::setNickName(const QString &nickName) { m_nickName = nickName; } /// Returns the friendly name for the bookmark. /// QString QXmppBookmarkUrl::name() const { return m_name; } /// Sets the friendly name for the bookmark. /// /// \param name void QXmppBookmarkUrl::setName(const QString &name) { m_name = name; } /// Returns the URL for the web page. /// QUrl QXmppBookmarkUrl::url() const { return m_url; } /// Sets the URL for the web page. /// /// \param url void QXmppBookmarkUrl::setUrl(const QUrl &url) { m_url = url; } /// Returns the conference rooms bookmarks in this bookmark set. /// QList QXmppBookmarkSet::conferences() const { return m_conferences; } /// Sets the conference rooms bookmarks in this bookmark set. /// /// \param conferences void QXmppBookmarkSet::setConferences(const QList &conferences) { m_conferences = conferences; } /// Returns the web page bookmarks in this bookmark set. /// QList QXmppBookmarkSet::urls() const { return m_urls; } /// Sets the web page bookmarks in this bookmark set. /// /// \param urls void QXmppBookmarkSet::setUrls(const QList &urls) { m_urls = urls; } /// \cond bool QXmppBookmarkSet::isBookmarkSet(const QDomElement &element) { return element.tagName() == QStringLiteral("storage") && element.namespaceURI() == ns_bookmarks; } void QXmppBookmarkSet::parse(const QDomElement &element) { QDomElement childElement = element.firstChildElement(); while (!childElement.isNull()) { if (childElement.tagName() == QStringLiteral("conference")) { QXmppBookmarkConference conference; conference.setAutoJoin(childElement.attribute(QStringLiteral("autojoin")) == QStringLiteral("true") || childElement.attribute("autojoin") == "1"); conference.setJid(childElement.attribute(QStringLiteral("jid"))); conference.setName(childElement.attribute(QStringLiteral("name"))); conference.setNickName(childElement.firstChildElement(QStringLiteral("nick")).text()); m_conferences << conference; } else if (childElement.tagName() == QStringLiteral("url")) { QXmppBookmarkUrl url; url.setName(childElement.attribute(QStringLiteral("name"))); url.setUrl(QUrl(childElement.attribute(QStringLiteral("url")))); m_urls << url; } childElement = childElement.nextSiblingElement(); } } void QXmppBookmarkSet::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("storage")); writer->writeDefaultNamespace(ns_bookmarks); for (const auto &conference : m_conferences) { writer->writeStartElement(QStringLiteral("conference")); if (conference.autoJoin()) helperToXmlAddAttribute(writer, QStringLiteral("autojoin"), QStringLiteral("true")); helperToXmlAddAttribute(writer, QStringLiteral("jid"), conference.jid()); helperToXmlAddAttribute(writer, QStringLiteral("name"), conference.name()); if (!conference.nickName().isEmpty()) helperToXmlAddTextElement(writer, QStringLiteral("nick"), conference.nickName()); writer->writeEndElement(); } for (const auto &url : m_urls) { writer->writeStartElement(QStringLiteral("url")); helperToXmlAddAttribute(writer, QStringLiteral("name"), url.name()); helperToXmlAddAttribute(writer, QStringLiteral("url"), url.url().toString()); writer->writeEndElement(); } writer->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/base/QXmppBookmarkSet.h000066400000000000000000000046041402370562100177460ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPBOOKMARKSET_H #define QXMPPBOOKMARKSET_H #include "QXmppStanza.h" #include #include /// \brief The QXmppBookmarkConference class represents a bookmark for a conference room, /// as defined by \xep{0048}: Bookmarks. /// class QXMPP_EXPORT QXmppBookmarkConference { public: QXmppBookmarkConference(); bool autoJoin() const; void setAutoJoin(bool autoJoin); QString jid() const; void setJid(const QString &jid); QString name() const; void setName(const QString &name); QString nickName() const; void setNickName(const QString &nickName); private: bool m_autoJoin; QString m_jid; QString m_name; QString m_nickName; }; /// \brief The QXmppBookmarkUrl class represents a bookmark for a web page, /// as defined by \xep{0048}: Bookmarks. /// class QXMPP_EXPORT QXmppBookmarkUrl { public: QString name() const; void setName(const QString &name); QUrl url() const; void setUrl(const QUrl &url); private: QString m_name; QUrl m_url; }; /// \brief The QXmppbookmarkSets class represents a set of bookmarks, as defined /// by \xep{0048}: Bookmarks. /// class QXMPP_EXPORT QXmppBookmarkSet { public: QList conferences() const; void setConferences(const QList &conferences); QList urls() const; void setUrls(const QList &urls); /// \cond static bool isBookmarkSet(const QDomElement &element); void parse(const QDomElement &element); void toXml(QXmlStreamWriter *writer) const; /// \endcond private: QList m_conferences; QList m_urls; }; #endif qxmpp-1.4.0/src/base/QXmppByteStreamIq.cpp000066400000000000000000000121451402370562100204300ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppByteStreamIq.h" #include "QXmppConstants_p.h" #include "QXmppUtils.h" #include QString QXmppByteStreamIq::StreamHost::host() const { return m_host; } void QXmppByteStreamIq::StreamHost::setHost(const QString &host) { m_host = host; } QString QXmppByteStreamIq::StreamHost::jid() const { return m_jid; } void QXmppByteStreamIq::StreamHost::setJid(const QString &jid) { m_jid = jid; } quint16 QXmppByteStreamIq::StreamHost::port() const { return m_port; } void QXmppByteStreamIq::StreamHost::setPort(quint16 port) { m_port = port; } QString QXmppByteStreamIq::StreamHost::zeroconf() const { return m_zeroconf; } void QXmppByteStreamIq::StreamHost::setZeroconf(const QString &zeroconf) { m_zeroconf = zeroconf; } QXmppByteStreamIq::Mode QXmppByteStreamIq::mode() const { return m_mode; } void QXmppByteStreamIq::setMode(QXmppByteStreamIq::Mode mode) { m_mode = mode; } QString QXmppByteStreamIq::sid() const { return m_sid; } void QXmppByteStreamIq::setSid(const QString &sid) { m_sid = sid; } QString QXmppByteStreamIq::activate() const { return m_activate; } void QXmppByteStreamIq::setActivate(const QString &activate) { m_activate = activate; } QList QXmppByteStreamIq::streamHosts() const { return m_streamHosts; } void QXmppByteStreamIq::setStreamHosts(const QList &streamHosts) { m_streamHosts = streamHosts; } QString QXmppByteStreamIq::streamHostUsed() const { return m_streamHostUsed; } void QXmppByteStreamIq::setStreamHostUsed(const QString &jid) { m_streamHostUsed = jid; } /// \cond bool QXmppByteStreamIq::isByteStreamIq(const QDomElement &element) { return element.firstChildElement(QStringLiteral("query")).namespaceURI() == ns_bytestreams; } void QXmppByteStreamIq::parseElementFromChild(const QDomElement &element) { QDomElement queryElement = element.firstChildElement(QStringLiteral("query")); m_sid = queryElement.attribute(QStringLiteral("sid")); const QString modeStr = queryElement.attribute(QStringLiteral("mode")); if (modeStr == QStringLiteral("tcp")) m_mode = Tcp; else if (modeStr == QStringLiteral("udp")) m_mode = Udp; else m_mode = None; QDomElement hostElement = queryElement.firstChildElement(QStringLiteral("streamhost")); while (!hostElement.isNull()) { StreamHost streamHost; streamHost.setHost(hostElement.attribute(QStringLiteral("host"))); streamHost.setJid(hostElement.attribute(QStringLiteral("jid"))); streamHost.setPort(hostElement.attribute(QStringLiteral("port")).toInt()); streamHost.setZeroconf(hostElement.attribute(QStringLiteral("zeroconf"))); m_streamHosts.append(streamHost); hostElement = hostElement.nextSiblingElement(QStringLiteral("streamhost")); } m_activate = queryElement.firstChildElement(QStringLiteral("activate")).text(); m_streamHostUsed = queryElement.firstChildElement(QStringLiteral("streamhost-used")).attribute(QStringLiteral("jid")); } void QXmppByteStreamIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("query")); writer->writeDefaultNamespace(ns_bytestreams); helperToXmlAddAttribute(writer, QStringLiteral("sid"), m_sid); QString modeStr; if (m_mode == Tcp) modeStr = QStringLiteral("tcp"); else if (m_mode == Udp) modeStr = QStringLiteral("udp"); helperToXmlAddAttribute(writer, QStringLiteral("mode"), modeStr); for (const auto &streamHost : m_streamHosts) { writer->writeStartElement(QStringLiteral("streamhost")); helperToXmlAddAttribute(writer, QStringLiteral("host"), streamHost.host()); helperToXmlAddAttribute(writer, QStringLiteral("jid"), streamHost.jid()); helperToXmlAddAttribute(writer, QStringLiteral("port"), QString::number(streamHost.port())); helperToXmlAddAttribute(writer, QStringLiteral("zeroconf"), streamHost.zeroconf()); writer->writeEndElement(); } if (!m_activate.isEmpty()) helperToXmlAddTextElement(writer, QStringLiteral("activate"), m_activate); if (!m_streamHostUsed.isEmpty()) { writer->writeStartElement(QStringLiteral("streamhost-used")); helperToXmlAddAttribute(writer, QStringLiteral("jid"), m_streamHostUsed); writer->writeEndElement(); } writer->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/base/QXmppByteStreamIq.h000066400000000000000000000047331402370562100201010ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPBYTESTREAMIQ_H #define QXMPPBYTESTREAMIQ_H #include "QXmppIq.h" #include /// /// \brief QXmppByteStreamIq represents a SOCKS5 bytestreams negoatiation IQ as /// defined by \xep{0065}: SOCKS5 Bytestreams. /// class QXMPP_EXPORT QXmppByteStreamIq : public QXmppIq { public: enum Mode { None = 0, Tcp, Udp }; /// /// \brief StreamHost represents information about a specific SOCKS5 /// bytestreams host. /// class QXMPP_EXPORT StreamHost { public: QString jid() const; void setJid(const QString &jid); QString host() const; void setHost(const QString &host); quint16 port() const; void setPort(quint16 port); QString zeroconf() const; void setZeroconf(const QString &zeroconf); private: QString m_host; QString m_jid; quint16 m_port; QString m_zeroconf; }; QXmppByteStreamIq::Mode mode() const; void setMode(QXmppByteStreamIq::Mode mode); QString sid() const; void setSid(const QString &sid); QString activate() const; void setActivate(const QString &activate); QList streamHosts() const; void setStreamHosts(const QList &streamHosts); QString streamHostUsed() const; void setStreamHostUsed(const QString &jid); static bool isByteStreamIq(const QDomElement &element); protected: /// \cond void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: Mode m_mode; QString m_sid; QString m_activate; QList m_streamHosts; QString m_streamHostUsed; }; #endif qxmpp-1.4.0/src/base/QXmppConstants.cpp000066400000000000000000000175471402370562100200460ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 "QXmppConstants_p.h" const char* ns_stream = "http://etherx.jabber.org/streams"; const char* ns_client = "jabber:client"; const char* ns_server = "jabber:server"; const char* ns_roster = "jabber:iq:roster"; const char* ns_tls = "urn:ietf:params:xml:ns:xmpp-tls"; const char* ns_sasl = "urn:ietf:params:xml:ns:xmpp-sasl"; const char* ns_bind = "urn:ietf:params:xml:ns:xmpp-bind"; const char* ns_session = "urn:ietf:params:xml:ns:xmpp-session"; const char* ns_stanza = "urn:ietf:params:xml:ns:xmpp-stanzas"; const char* ns_pre_approval = "urn:xmpp:features:pre-approval"; const char* ns_rosterver = "urn:xmpp:features:rosterver"; // XEP-0009: Jabber-RPC 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"; // XEP-0033: Extended Stanza Addressing const char* ns_extended_addressing = "http://jabber.org/protocol/address"; // XEP-0045: Multi-User Chat const char* ns_muc = "http://jabber.org/protocol/muc"; const char* ns_muc_admin = "http://jabber.org/protocol/muc#admin"; const char* ns_muc_owner = "http://jabber.org/protocol/muc#owner"; const char* ns_muc_user = "http://jabber.org/protocol/muc#user"; // XEP-0047: In-Band Bytestreams const char* ns_ibb = "http://jabber.org/protocol/ibb"; // XEP-0049: Private XML Storage const char* ns_private = "jabber:iq:private"; // XEP-0054: vcard-temp const char* ns_vcard = "vcard-temp"; // XEP-0059: Result Set Management const char* ns_rsm = "http://jabber.org/protocol/rsm"; // XEP-0060: Publish-Subscribe const char* ns_pubsub = "http://jabber.org/protocol/pubsub"; // XEP-0065: SOCKS5 Bytestreams const char* ns_bytestreams = "http://jabber.org/protocol/bytestreams"; // XEP-0066: Out of Band Data const char* ns_oob = "jabber:x:oob"; // XEP-0071: XHTML-IM const char* ns_xhtml = "http://www.w3.org/1999/xhtml"; 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"; // XEP-0085: Chat State Notifications const char* ns_chat_states = "http://jabber.org/protocol/chatstates"; // XEP-0091: Legacy Delayed Delivery const char* ns_legacy_delayed_delivery = "jabber:x:delay"; // XEP-0092: Software Version const char* ns_version = "jabber:iq:version"; const char* ns_data = "jabber:x:data"; // XEP-0095: Stream Initiation const char* ns_stream_initiation = "http://jabber.org/protocol/si"; const char* ns_stream_initiation_file_transfer = "http://jabber.org/protocol/si/profile/file-transfer"; // XEP-0108: User Activity const char* ns_activity = "http://jabber.org/protocol/activity"; // XEP-0115: Entity Capabilities const char* ns_capabilities = "http://jabber.org/protocol/caps"; // XEP-0136: Message Archiving const char* ns_archive = "urn:xmpp:archive"; // XEP-0138: Stream Compression const char* ns_compress = "http://jabber.org/protocol/compress"; const char* ns_compressFeature = "http://jabber.org/features/compress"; // XEP-0145: Annotations const char* ns_rosternotes = "storage:rosternotes"; // XEP-0153: vCard-Based Avatars const char* ns_vcard_update = "vcard-temp:x:update"; // XEP-0158: CAPTCHA Forms const char* ns_captcha = "urn:xmpp:captcha"; // XEP-0166: Jingle const char* ns_jingle = "urn:xmpp:jingle:1"; const char* ns_jingle_raw_udp = "urn:xmpp:jingle:transports:raw-udp:1"; const char* ns_jingle_ice_udp = "urn:xmpp:jingle:transports:ice-udp:1"; const char* ns_jingle_rtp = "urn:xmpp:jingle:apps:rtp:1"; const char* ns_jingle_rtp_audio = "urn:xmpp:jingle:apps:rtp:audio"; const char* ns_jingle_rtp_video = "urn:xmpp:jingle:apps:rtp:video"; // XEP-0184: Message Receipts const char* ns_message_receipts = "urn:xmpp:receipts"; // XEP-0198: Stream Management const char* ns_stream_management = "urn:xmpp:sm:3"; // XEP-0199: XMPP Ping const char* ns_ping = "urn:xmpp:ping"; // XEP-0202: Entity Time const char* ns_entity_time = "urn:xmpp:time"; // XEP-0203: Delayed Delivery const char* ns_delayed_delivery = "urn:xmpp:delay"; // XEP-0220: Server Dialback const char* ns_server_dialback = "jabber:server:dialback"; // XEP-0221: Data Forms Media Element const char* ns_media_element = "urn:xmpp:media-element"; // XEP-0224: Attention const char* ns_attention = "urn:xmpp:attention:0"; // XEP-0231: Bits of Binary const char* ns_bob = "urn:xmpp:bob"; // XEP-0249: Direct MUC Invitations const char* ns_conference = "jabber:x:conference"; // XEP-0280: Message Carbons const char* ns_carbons = "urn:xmpp:carbons:2"; // XEP-0297: Stanza Forwarding const char* ns_forwarding = "urn:xmpp:forward:0"; // XEP-0308: Last Message Correction const char* ns_message_correct = "urn:xmpp:message-correct:0"; // XEP-0313: Message Archive Management const char* ns_mam = "urn:xmpp:mam:2"; // XEP-0319: Last User Interaction in Presence 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-0357: Push Notifications const char* ns_push = "urn:xmpp:push:0"; // XEP-0359: Unique and Stable Stanza IDs const char* ns_sid = "urn:xmpp:sid: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"; // XEP-0407: Mediated Information eXchange (MIX): Miscellaneous Capabilities const char* ns_mix_misc = "urn:xmpp:mix:misc:0"; // XEP-0428: Fallback Indication const char* ns_fallback_indication = "urn:xmpp:fallback:0"; qxmpp-1.4.0/src/base/QXmppConstants_p.h000066400000000000000000000141541402370562100200210ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 QXMPPCONSTANTS_H #define QXMPPCONSTANTS_H // // W A R N I N G // ------------- // // This file is not part of the QXmpp API. It exists for the convenience // of QXmpp's own classes. This header file may change from version to // version without notice, or even be removed. // // We mean it. // extern const char* ns_stream; extern const char* ns_client; extern const char* ns_server; extern const char* ns_roster; extern const char* ns_tls; extern const char* ns_sasl; extern const char* ns_bind; extern const char* ns_session; extern const char* ns_stanza; extern const char* ns_pre_approval; extern const char* ns_rosterver; // XEP-0009: Jabber-RPC 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; // XEP-0033: Extended Stanza Addressing extern const char* ns_extended_addressing; // XEP-0045: Multi-User Chat extern const char* ns_muc; extern const char* ns_muc_admin; extern const char* ns_muc_owner; extern const char* ns_muc_user; // XEP-0047: In-Band Bytestreams extern const char* ns_ibb; // XEP-0049: Private XML Storage extern const char* ns_private; // XEP-0054: vcard-temp extern const char* ns_vcard; // XEP-0059: Result Set Management extern const char* ns_rsm; // XEP-0060: Publish-Subscribe extern const char* ns_pubsub; // XEP-0065: SOCKS5 Bytestreams extern const char* ns_bytestreams; // XEP-0066: Out of Band Data extern const char* ns_oob; // XEP-0071: XHTML-IM extern const char* ns_xhtml; 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; // XEP-0085: Chat State Notifications extern const char* ns_chat_states; // XEP-0091: Legacy Delayed Delivery extern const char* ns_legacy_delayed_delivery; // XEP-0092: Software Version extern const char* ns_version; extern const char* ns_data; // XEP-0095: Stream Initiation extern const char* ns_stream_initiation; extern const char* ns_stream_initiation_file_transfer; // XEP-0108: User Activity extern const char* ns_activity; // XEP-0115: Entity Capabilities extern const char* ns_capabilities; // XEP-0136: Message Archiving extern const char* ns_archive; // XEP-0138: Stream Compression extern const char* ns_compress; extern const char* ns_compressFeature; // XEP-0145: Annotations extern const char* ns_rosternotes; // XEP-0153: vCard-Based Avatars extern const char* ns_vcard_update; // XEP-0158: CAPTCHA Forms extern const char* ns_captcha; // XEP-0166: Jingle extern const char* ns_jingle; extern const char* ns_jingle_ice_udp; extern const char* ns_jingle_raw_udp; extern const char* ns_jingle_rtp; extern const char* ns_jingle_rtp_audio; extern const char* ns_jingle_rtp_video; // XEP-0184: Message Receipts extern const char* ns_message_receipts; // XEP-0198: Stream Management extern const char* ns_stream_management; // XEP-0199: XMPP Ping extern const char* ns_ping; // XEP-0202: Entity Time extern const char* ns_entity_time; // XEP-0203: Delayed Delivery extern const char* ns_delayed_delivery; // XEP-0220: Server Dialback extern const char* ns_server_dialback; // XEP-0221: Data Forms Media Element extern const char* ns_media_element; // XEP-0224: Attention extern const char* ns_attention; // XEP-0231: Bits of Binary extern const char* ns_bob; // XEP-0249: Direct MUC Invitations extern const char* ns_conference; // XEP-0280: Message Carbons extern const char* ns_carbons; // XEP-0297: Stanza Forwarding extern const char* ns_forwarding; // XEP-0308: Last Message Correction extern const char* ns_message_correct; // XEP-0313: Message Archive Management extern const char* ns_mam; // XEP-0319: Last User Interaction in Presence 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-0357: Push Notifications extern const char* ns_push; // XEP-0359: Unique and Stable Stanza IDs extern const char* ns_sid; // 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; // XEP-0407: Mediated Information eXchange (MIX): Miscellaneous Capabilities extern const char* ns_mix_misc; // XEP-0428: Fallback Indication extern const char* ns_fallback_indication; #endif // QXMPPCONSTANTS_H qxmpp-1.4.0/src/base/QXmppDataForm.cpp000066400000000000000000000553071402370562100175630ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * Linus Jahn * * 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 "QXmppDataForm.h" #include "QXmppConstants_p.h" #include "QXmppUtils.h" #include #include #include #include #include #include #include struct field_type { QXmppDataForm::Field::Type type; const char *str; }; static field_type field_types[] = { { QXmppDataForm::Field::BooleanField, "boolean" }, { QXmppDataForm::Field::FixedField, "fixed" }, { QXmppDataForm::Field::HiddenField, "hidden" }, { QXmppDataForm::Field::JidMultiField, "jid-multi" }, { QXmppDataForm::Field::JidSingleField, "jid-single" }, { QXmppDataForm::Field::ListMultiField, "list-multi" }, { QXmppDataForm::Field::ListSingleField, "list-single" }, { QXmppDataForm::Field::TextMultiField, "text-multi" }, { QXmppDataForm::Field::TextPrivateField, "text-private" }, { QXmppDataForm::Field::TextSingleField, "text-single" }, { static_cast(-1), nullptr }, }; class QXmppDataFormMediaSourcePrivate : public QSharedData { public: QUrl uri; QMimeType contentType; }; QXmppDataForm::MediaSource::MediaSource() : d(new QXmppDataFormMediaSourcePrivate) { } QXmppDataForm::MediaSource::MediaSource(const QUrl &uri, const QMimeType &contentType) : d(new QXmppDataFormMediaSourcePrivate) { d->uri = uri; d->contentType = contentType; } QXmppDataForm::MediaSource::MediaSource(const QXmppDataForm::MediaSource &) = default; QXmppDataForm::MediaSource::~MediaSource() = default; QXmppDataForm::MediaSource &QXmppDataForm::MediaSource::operator=(const QXmppDataForm::MediaSource &) = default; /// Returns the media URI as QUrl. This can be i.e. a \c http:// URL or a /// \c cid: Bits of Binary URI. QUrl QXmppDataForm::MediaSource::uri() const { return d->uri; } /// Sets the URI. void QXmppDataForm::MediaSource::setUri(const QUrl &uri) { d->uri = uri; } /// Returns the content type of the source QMimeType QXmppDataForm::MediaSource::contentType() const { return d->contentType; } /// Sets the content type of the media source. void QXmppDataForm::MediaSource::setContentType(const QMimeType &contentType) { d->contentType = contentType; } /// Returns true if two media sources are identical. bool QXmppDataForm::MediaSource::operator==(const QXmppDataForm::MediaSource &other) const { return d->uri == other.uri() && d->contentType == other.contentType(); } class QXmppDataFormMediaPrivate : public QSharedData { public: QSize size; QList> uris; }; /// Constructs an empty QXmppDataForm::Media. /// /// \deprecated This class is deprecated since QXmpp 1.1. QXmppDataForm::Media::Media() : d(new QXmppDataFormMediaPrivate) { } /// Constructs a copy of \a other. /// /// \deprecated This class is deprecated since QXmpp 1.1. QXmppDataForm::Media::Media(const QXmppDataForm::Media &other) = default; /// Destroys the media. /// /// \deprecated This class is deprecated since QXmpp 1.1. QXmppDataForm::Media::~Media() = default; /// Assigns \a other to this media. /// /// \deprecated This class is deprecated since QXmpp 1.1. QXmppDataForm::Media &QXmppDataForm::Media::operator=(const QXmppDataForm::Media &other) = default; /// Returns media's height. /// /// \deprecated This method is deprecated since QXmpp 1.1. Use /// \c QXmppDataForm::Field::mediaSize().height() instead. int QXmppDataForm::Media::height() const { return d->size.height(); } /// Sets media's \a height. /// /// \deprecated This method is deprecated since QXmpp 1.1. Use /// \c QXmppDataForm::Field::mediaSize().setHeight() instead. void QXmppDataForm::Media::setHeight(int height) { d->size.setHeight(height); } /// Returns media's width. /// /// \deprecated This method is deprecated since QXmpp 1.1. Use /// \c QXmppDataForm::Field::mediaSize().width() instead. int QXmppDataForm::Media::width() const { return d->size.width(); } /// Sets media's \a width. /// /// \deprecated This method is deprecated since QXmpp 1.1. Use /// \c QXmppDataForm::Field::mediaSize().setWidth() instead. void QXmppDataForm::Media::setWidth(int width) { d->size.setWidth(width); } /// Returns media's uris. /// /// \deprecated This method is deprecated since QXmpp 1.1. Use /// \c QXmppDataForm::Field::mediaSources() instead. QList> QXmppDataForm::Media::uris() const { return d->uris; } /// Sets media's \a uris. /// /// \deprecated This method is deprecated since QXmpp 1.1. Use /// \c QXmppDataForm::Media::setMediaSources() instead. void QXmppDataForm::Media::setUris(const QList> &uris) { d->uris = uris; } /// Returns true if no media tag present. bool QXmppDataForm::Media::isNull() const { return d->uris.isEmpty(); } class QXmppDataFormFieldPrivate : public QSharedData { public: QXmppDataFormFieldPrivate(); QString description; QString key; QString label; QList> options; bool required; QXmppDataForm::Field::Type type; QVariant value; QSize mediaSize; QVector mediaSources; }; QXmppDataFormFieldPrivate::QXmppDataFormFieldPrivate() : required(false), type(QXmppDataForm::Field::TextSingleField) { } /// Constructs a QXmppDataForm::Field of the specified \a type. QXmppDataForm::Field::Field(QXmppDataForm::Field::Type type) : d(new QXmppDataFormFieldPrivate) { d->type = type; } /// /// Constructs a QXmppDataForm::Field with the specified attributes. /// /// \since QXmpp 1.3 /// QXmppDataForm::Field::Field(QXmppDataForm::Field::Type type, const QString &key, const QVariant &value, bool isRequired, const QString &label, const QString &description, const QList> &options) : d(new QXmppDataFormFieldPrivate) { d->type = type; d->key = key; d->value = value; d->required = isRequired; d->label = label; d->description = description; d->options = options; } /// Constructs a copy of \a other. QXmppDataForm::Field::Field(const QXmppDataForm::Field &other) : d(other.d) { } /// Destroys the form field. QXmppDataForm::Field::~Field() { } /// Assigns \a other to this field. QXmppDataForm::Field &QXmppDataForm::Field::operator=(const QXmppDataForm::Field &other) { d = other.d; return *this; } /// Returns the field's description. QString QXmppDataForm::Field::description() const { return d->description; } /// Sets the field's description. /// /// \param description void QXmppDataForm::Field::setDescription(const QString &description) { d->description = description; } /// Returns the field's key. QString QXmppDataForm::Field::key() const { return d->key; } /// Sets the field's key. /// /// \param key void QXmppDataForm::Field::setKey(const QString &key) { d->key = key; } /// Returns the field's label. QString QXmppDataForm::Field::label() const { return d->label; } /// Sets the field's label. /// /// \param label void QXmppDataForm::Field::setLabel(const QString &label) { d->label = label; } /// /// Returns the field's media. /// /// \deprecated This method is deprecated since QXmpp 1.1. Use /// \c QXmppDataForm::Field::mediaSources() or /// \c QXmppDataForm::Field::mediaSize() instead. /// QXmppDataForm::Media QXmppDataForm::Field::media() const { QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED Media media; QList> pairUris; pairUris.reserve(d->mediaSources.size()); for (const auto &source : qAsConst(d->mediaSources)) { pairUris << qMakePair( source.contentType().name(), source.uri().toString()); } media.setHeight(d->mediaSize.height()); media.setWidth(d->mediaSize.width()); media.setUris(pairUris); return media; QT_WARNING_POP } /// Sets the field's \a media. /// /// \deprecated This method is deprecated since QXmpp 1.1. Use /// \c QXmppDataForm::Field::setMediaSources() or /// \c QXmppDataForm::Field::setMediaSize() instead. void QXmppDataForm::Field::setMedia(const QXmppDataForm::Media &media) { QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED const QList> &uris = media.uris(); QVector sources; sources.reserve(uris.size()); for (const auto &pairUri : uris) { sources << QXmppDataForm::MediaSource( QUrl(pairUri.second), QMimeDatabase().mimeTypeForName(pairUri.first)); } d->mediaSources = sources; d->mediaSize = QSize(media.width(), media.height()); QT_WARNING_POP } /// Returns the field's options. QList> QXmppDataForm::Field::options() const { return d->options; } /// Sets the field's options. /// /// \param options void QXmppDataForm::Field::setOptions(const QList> &options) { d->options = options; } /// Returns true if the field is required, false otherwise. bool QXmppDataForm::Field::isRequired() const { return d->required; } /// Set to true if the field is required, false otherwise. /// /// \param required void QXmppDataForm::Field::setRequired(bool required) { d->required = required; } /// Returns the field's type. QXmppDataForm::Field::Type QXmppDataForm::Field::type() const { return d->type; } /// Sets the field's type. /// /// \param type void QXmppDataForm::Field::setType(QXmppDataForm::Field::Type type) { d->type = type; } /// Returns the field's value. QVariant QXmppDataForm::Field::value() const { return d->value; } /// Sets the field's value. /// /// \param value void QXmppDataForm::Field::setValue(const QVariant &value) { d->value = value; } /// Returns the size of the attached media according to \xep{0221}: Data Forms /// Media Element. /// /// \since QXmpp 1.1 QSize QXmppDataForm::Field::mediaSize() const { return d->mediaSize; } /// Returns the size of the attached media according to \xep{0221}: Data Forms /// Media Element. /// /// \since QXmpp 1.1 QSize &QXmppDataForm::Field::mediaSize() { return d->mediaSize; } /// Sets the size of the attached media according to \xep{0221}: Data Forms Media /// Element. /// /// \since QXmpp 1.1 void QXmppDataForm::Field::setMediaSize(const QSize &size) { d->mediaSize = size; } /// Returns the sources for the attached media according to \xep{0221}: Data /// Forms Media Element. /// /// \since QXmpp 1.1 QVector QXmppDataForm::Field::mediaSources() const { return d->mediaSources; } /// Returns the sources for the attached media according to \xep{0221}: Data /// Forms Media Element. /// /// \since QXmpp 1.1 QVector &QXmppDataForm::Field::mediaSources() { return d->mediaSources; } /// Sets the sources to the attached media of the field according to \xep{0221}: /// Data Forms Media Element. /// /// \since QXmpp 1.1 void QXmppDataForm::Field::setMediaSources(const QVector &mediaSources) { d->mediaSources = mediaSources; } /// Returns true if the other field is identical to this one. /// /// \since QXmpp 1.1 bool QXmppDataForm::Field::operator==(const QXmppDataForm::Field &other) const { return d->description == other.description() && d->key == other.key() && d->label == other.label() && d->options == other.options() && d->required == other.isRequired() && d->type == other.type() && d->value == other.value() && d->mediaSources == other.mediaSources() && d->mediaSize == other.mediaSize(); } class QXmppDataFormPrivate : public QSharedData { public: QXmppDataFormPrivate(); QString instructions; QList fields; QString title; QXmppDataForm::Type type; }; QXmppDataFormPrivate::QXmppDataFormPrivate() : type(QXmppDataForm::None) { } /// Constructs a QXmppDataForm of the specified \a type. QXmppDataForm::QXmppDataForm(QXmppDataForm::Type type) : d(new QXmppDataFormPrivate) { d->type = type; } /// /// Constructs a QXmppDataForm with the specified attributes. /// /// \since QXmpp 1.3 /// QXmppDataForm::QXmppDataForm(QXmppDataForm::Type type, const QList &fields, const QString &title, const QString &instructions) : d(new QXmppDataFormPrivate) { d->type = type; d->fields = fields; d->title = title; d->instructions = instructions; } /// Constructs a copy of \a other. QXmppDataForm::QXmppDataForm(const QXmppDataForm &other) : d(other.d) { } /// Destroys the form. QXmppDataForm::~QXmppDataForm() { } /// Assigns \a other to this form. QXmppDataForm &QXmppDataForm::operator=(const QXmppDataForm &other) { d = other.d; return *this; } /// Returns the form's fields. QList QXmppDataForm::fields() const { return d->fields; } /// Returns the form's fields by reference. QList &QXmppDataForm::fields() { return d->fields; } /// Sets the form's fields. /// /// \param fields void QXmppDataForm::setFields(const QList &fields) { d->fields = fields; } /// Returns the form's instructions. QString QXmppDataForm::instructions() const { return d->instructions; } /// Sets the form's instructions. /// /// \param instructions void QXmppDataForm::setInstructions(const QString &instructions) { d->instructions = instructions; } /// Returns the form's title. QString QXmppDataForm::title() const { return d->title; } /// Sets the form's title. /// /// \param title void QXmppDataForm::setTitle(const QString &title) { d->title = title; } /// Returns the form's type. QXmppDataForm::Type QXmppDataForm::type() const { return d->type; } /// Sets the form's type. /// /// \param type void QXmppDataForm::setType(QXmppDataForm::Type type) { d->type = type; } /// Returns true if the form has an unknown type. bool QXmppDataForm::isNull() const { return d->type == QXmppDataForm::None; } /// \cond void QXmppDataForm::parse(const QDomElement &element) { if (element.isNull()) return; /* form type */ const QString typeStr = element.attribute("type"); if (typeStr == "form") d->type = QXmppDataForm::Form; else if (typeStr == "submit") d->type = QXmppDataForm::Submit; else if (typeStr == "cancel") d->type = QXmppDataForm::Cancel; else if (typeStr == "result") d->type = QXmppDataForm::Result; else { qWarning() << "Unknown form type" << typeStr; return; } /* form properties */ d->title = element.firstChildElement("title").text(); d->instructions = element.firstChildElement("instructions").text(); QDomElement fieldElement = element.firstChildElement("field"); while (!fieldElement.isNull()) { QXmppDataForm::Field field; /* field type */ QXmppDataForm::Field::Type type = QXmppDataForm::Field::TextSingleField; const QString typeStr = fieldElement.attribute("type"); struct field_type *ptr; for (ptr = field_types; ptr->str; ptr++) { if (typeStr == ptr->str) { type = ptr->type; break; } } field.setType(type); /* field attributes */ field.setLabel(fieldElement.attribute("label")); field.setKey(fieldElement.attribute("var")); /* field value(s) */ if (type == QXmppDataForm::Field::BooleanField) { const QString valueStr = fieldElement.firstChildElement("value").text(); field.setValue(valueStr == "1" || valueStr == "true"); } else if (type == QXmppDataForm::Field::ListMultiField || type == QXmppDataForm::Field::JidMultiField || type == QXmppDataForm::Field::TextMultiField) { QStringList values; QDomElement valueElement = fieldElement.firstChildElement("value"); while (!valueElement.isNull()) { values.append(valueElement.text()); valueElement = valueElement.nextSiblingElement("value"); } field.setValue(values); } else { field.setValue(fieldElement.firstChildElement("value").text()); } /* field media */ QDomElement mediaElement = fieldElement.firstChildElement("media"); if (!mediaElement.isNull() && mediaElement.namespaceURI() == ns_media_element) { field.mediaSize().setHeight(mediaElement.attribute("height", "-1").toInt()); field.mediaSize().setWidth(mediaElement.attribute("width", "-1").toInt()); QDomElement uriElement = mediaElement.firstChildElement(QStringLiteral("uri")); while (!uriElement.isNull()) { field.mediaSources() << MediaSource( QUrl(uriElement.text()), QMimeDatabase().mimeTypeForName( uriElement.attribute(QStringLiteral("type")))); uriElement = uriElement.nextSiblingElement(QStringLiteral("uri")); } } /* field options */ if (type == QXmppDataForm::Field::ListMultiField || type == QXmppDataForm::Field::ListSingleField) { QList> options; QDomElement optionElement = fieldElement.firstChildElement("option"); while (!optionElement.isNull()) { options.append(QPair(optionElement.attribute("label"), optionElement.firstChildElement("value").text())); optionElement = optionElement.nextSiblingElement("option"); } field.setOptions(options); } /* other properties */ field.setDescription(fieldElement.firstChildElement("description").text()); field.setRequired(!fieldElement.firstChildElement("required").isNull()); d->fields.append(field); fieldElement = fieldElement.nextSiblingElement("field"); } } void QXmppDataForm::toXml(QXmlStreamWriter *writer) const { if (isNull()) return; writer->writeStartElement("x"); writer->writeDefaultNamespace(ns_data); /* form type */ QString typeStr; if (d->type == QXmppDataForm::Form) typeStr = "form"; else if (d->type == QXmppDataForm::Submit) typeStr = "submit"; else if (d->type == QXmppDataForm::Cancel) typeStr = "cancel"; else if (d->type == QXmppDataForm::Result) typeStr = "result"; writer->writeAttribute("type", typeStr); /* form properties */ if (!d->title.isEmpty()) writer->writeTextElement("title", d->title); if (!d->instructions.isEmpty()) writer->writeTextElement("instructions", d->instructions); for (const QXmppDataForm::Field &field : d->fields) { writer->writeStartElement("field"); /* field type */ const QXmppDataForm::Field::Type type = field.type(); QString typeStr; struct field_type *ptr; for (ptr = field_types; ptr->str; ptr++) { if (type == ptr->type) { typeStr = ptr->str; break; } } writer->writeAttribute("type", typeStr); /* field attributes */ helperToXmlAddAttribute(writer, "label", field.label()); helperToXmlAddAttribute(writer, "var", field.key()); /* field value(s) */ if (type == QXmppDataForm::Field::BooleanField) { helperToXmlAddTextElement(writer, "value", field.value().toBool() ? "1" : "0"); } else if (type == QXmppDataForm::Field::ListMultiField || type == QXmppDataForm::Field::JidMultiField || type == QXmppDataForm::Field::TextMultiField) { for (const QString &value : field.value().toStringList()) helperToXmlAddTextElement(writer, "value", value); } else if (!field.value().toString().isEmpty()) { helperToXmlAddTextElement(writer, "value", field.value().toString()); } /* field media */ if (!field.mediaSources().isEmpty()) { writer->writeStartElement("media"); helperToXmlAddAttribute(writer, "xmlns", ns_media_element); // media width and height if (field.mediaSize().width() > 0) helperToXmlAddAttribute( writer, QStringLiteral("width"), QString::number(field.mediaSize().width())); if (field.mediaSize().height() > 0) helperToXmlAddAttribute( writer, QStringLiteral("height"), QString::number(field.mediaSize().height())); const QVector &sources = field.mediaSources(); for (const auto &source : sources) { writer->writeStartElement(QStringLiteral("uri")); helperToXmlAddAttribute(writer, QStringLiteral("type"), source.contentType().name()); writer->writeCharacters(source.uri().toString()); writer->writeEndElement(); } writer->writeEndElement(); } /* field options */ if (type == QXmppDataForm::Field::ListMultiField || type == QXmppDataForm::Field::ListSingleField) { QPair option; for (const auto &option : field.options()) { writer->writeStartElement("option"); helperToXmlAddAttribute(writer, "label", option.first); helperToXmlAddTextElement(writer, "value", option.second); writer->writeEndElement(); } } /* other properties */ if (!field.description().isEmpty()) helperToXmlAddTextElement(writer, "description", field.description()); if (field.isRequired()) helperToXmlAddTextElement(writer, "required", ""); writer->writeEndElement(); } writer->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/base/QXmppDataForm.h000066400000000000000000000172611402370562100172250ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * Linus Jahn * * 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 QXMPPDATAFORM_H #define QXMPPDATAFORM_H #include "QXmppStanza.h" #if QXMPP_DEPRECATED_SINCE(1, 1) #include #endif #include #include class QMimeType; class QUrl; class QXmppDataFormPrivate; class QXmppDataFormFieldPrivate; class QXmppDataFormMediaPrivate; class QXmppDataFormMediaSourcePrivate; /// /// \brief The QXmppDataForm class represents a data form as defined by /// \xep{0004}: Data Forms. /// class QXMPP_EXPORT QXmppDataForm { public: /// /// \brief The \c QXmppDataForm::MediaSource class represents a link to one /// of possibly multiple sources for a media element from \xep{0221}: Data /// Forms Media Element consisting of a MIME type and a QUrl. /// /// \since QXmpp 1.1 /// class QXMPP_EXPORT MediaSource { public: MediaSource(); MediaSource(const QUrl &uri, const QMimeType &contentType); MediaSource(const QXmppDataForm::MediaSource &); ~MediaSource(); MediaSource &operator=(const MediaSource &); QUrl uri() const; void setUri(const QUrl &uri); QMimeType contentType() const; void setContentType(const QMimeType &contentType); bool operator==(const MediaSource &other) const; private: QSharedDataPointer d; }; #if QXMPP_DEPRECATED_SINCE(1, 1) /// /// \brief The QXmppDataForm::Media class represents a media field as /// defined by \xep{0221}: Data Forms Media Element. /// /// \deprecated This class is deprecated since QXmpp 1.1. /// class QXMPP_EXPORT Media { public: QT_DEPRECATED_X("Use QXmppDataForm::Field() instead") Media(); QT_DEPRECATED_X("Use QXmppDataForm::Field() instead") Media(const QXmppDataForm::Media &other); ~Media(); QXmppDataForm::Media &operator=(const QXmppDataForm::Media &other); QT_DEPRECATED_X("Use QXmppDataForm::Field::mediaSize().height() instead") int height() const; QT_DEPRECATED_X("Use QXmppDataForm::Field::mediaSize().setHeight() instead") void setHeight(int height); QT_DEPRECATED_X("Use QXmppDataForm::Field::mediaSize().width() instead") int width() const; QT_DEPRECATED_X("Use QXmppDataForm::Field::mediaSize().setWidth() instead") void setWidth(int width); QT_DEPRECATED_X("Use QXmppDataForm::Field::mediaSources() instead") QList> uris() const; QT_DEPRECATED_X("Use QXmppDataForm::Field::setMediaSources() instead") void setUris(const QList> &uris); QT_DEPRECATED_X("Use QXmppDataForm::Field::mediaSources().isEmpty() instead") bool isNull() const; private: QSharedDataPointer d; }; #endif /// /// \brief The QXmppDataForm::Field class represents a data form field /// as defined by \xep{0004}: Data Forms. /// class QXMPP_EXPORT Field { public: /// This enum is used to describe a field's type. enum Type { BooleanField, FixedField, HiddenField, JidMultiField, JidSingleField, ListMultiField, ListSingleField, TextMultiField, TextPrivateField, TextSingleField }; // ### QXmpp2: merge ctors Field(QXmppDataForm::Field::Type type = QXmppDataForm::Field::TextSingleField); Field(QXmppDataForm::Field::Type type, const QString &key, const QVariant &value = {}, bool isRequired = false, const QString &label = {}, const QString &description = {}, const QList> &options = {}); Field(const QXmppDataForm::Field &other); ~Field(); QXmppDataForm::Field &operator=(const QXmppDataForm::Field &other); QString description() const; void setDescription(const QString &description); QString key() const; void setKey(const QString &key); QString label() const; void setLabel(const QString &label); #if QXMPP_DEPRECATED_SINCE(1, 1) QT_DEPRECATED_X("Use QXmppDataForm::Field::mediaSources() or QXmppDataForm::Field::mediaSize() instead") Media media() const; QT_DEPRECATED_X("Use QXmppDataForm::Field::setMediaSources() or QXmppDataForm::Field::setMediaSize() instead") void setMedia(const Media &media); #endif QList> options() const; void setOptions(const QList> &options); bool isRequired() const; void setRequired(bool required); QXmppDataForm::Field::Type type() const; void setType(QXmppDataForm::Field::Type type); QVariant value() const; void setValue(const QVariant &value); QVector &mediaSources(); QVector mediaSources() const; void setMediaSources(const QVector &mediaSources); QSize mediaSize() const; QSize &mediaSize(); void setMediaSize(const QSize &size); bool operator==(const Field &other) const; private: QSharedDataPointer d; }; /// This enum is used to describe a form's type. enum Type { None, ///< Unknown form type Form, ///< The form-processing entity is asking the form-submitting ///< entity to complete a form. Submit, ///< The form-submitting entity is submitting data to the ///< form-processing entity. Cancel, ///< The form-submitting entity has cancelled submission ///< of data to the form-processing entity. Result ///< The form-processing entity is returning data ///< (e.g., search results) to the form-submitting entity, ///< or the data is a generic data set. }; // ### QXmpp2: merge ctors QXmppDataForm(QXmppDataForm::Type type = QXmppDataForm::None); QXmppDataForm(QXmppDataForm::Type type, const QList &fields, const QString &title = {}, const QString &instructions = {}); QXmppDataForm(const QXmppDataForm &other); ~QXmppDataForm(); QXmppDataForm &operator=(const QXmppDataForm &other); QString instructions() const; void setInstructions(const QString &instructions); QList fields() const; QList &fields(); void setFields(const QList &fields); QString title() const; void setTitle(const QString &title); QXmppDataForm::Type type() const; void setType(QXmppDataForm::Type type); bool isNull() const; /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *writer) const; /// \endcond private: QSharedDataPointer d; }; #endif qxmpp-1.4.0/src/base/QXmppDiscoveryIq.cpp000066400000000000000000000253071402370562100203240ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppDiscoveryIq.h" #include "QXmppConstants_p.h" #include "QXmppUtils.h" #include #include #include static bool identityLessThan(const QXmppDiscoveryIq::Identity &i1, const QXmppDiscoveryIq::Identity &i2) { if (i1.category() < i2.category()) return true; else if (i1.category() > i2.category()) return false; if (i1.type() < i2.type()) return true; else if (i1.type() > i2.type()) return false; if (i1.language() < i2.language()) return true; else if (i1.language() > i2.language()) return false; if (i1.name() < i2.name()) return true; else if (i1.name() > i2.name()) return false; return false; } class QXmppDiscoveryIdentityPrivate : public QSharedData { public: QString category; QString language; QString name; QString type; }; QXmppDiscoveryIq::Identity::Identity() : d(new QXmppDiscoveryIdentityPrivate) { } QXmppDiscoveryIq::Identity::Identity(const QXmppDiscoveryIq::Identity &other) = default; QXmppDiscoveryIq::Identity::~Identity() = default; QXmppDiscoveryIq::Identity &QXmppDiscoveryIq::Identity::operator=(const QXmppDiscoveryIq::Identity &) = default; QString QXmppDiscoveryIq::Identity::category() const { return d->category; } void QXmppDiscoveryIq::Identity::setCategory(const QString &category) { d->category = category; } QString QXmppDiscoveryIq::Identity::language() const { return d->language; } void QXmppDiscoveryIq::Identity::setLanguage(const QString &language) { d->language = language; } QString QXmppDiscoveryIq::Identity::name() const { return d->name; } void QXmppDiscoveryIq::Identity::setName(const QString &name) { d->name = name; } QString QXmppDiscoveryIq::Identity::type() const { return d->type; } void QXmppDiscoveryIq::Identity::setType(const QString &type) { d->type = type; } class QXmppDiscoveryItemPrivate : public QSharedData { public: QString jid; QString name; QString node; }; QXmppDiscoveryIq::Item::Item() : d(new QXmppDiscoveryItemPrivate) { } QXmppDiscoveryIq::Item::Item(const QXmppDiscoveryIq::Item &) = default; QXmppDiscoveryIq::Item::~Item() = default; QXmppDiscoveryIq::Item &QXmppDiscoveryIq::Item::operator=(const QXmppDiscoveryIq::Item &) = default; QString QXmppDiscoveryIq::Item::jid() const { return d->jid; } void QXmppDiscoveryIq::Item::setJid(const QString &jid) { d->jid = jid; } QString QXmppDiscoveryIq::Item::name() const { return d->name; } void QXmppDiscoveryIq::Item::setName(const QString &name) { d->name = name; } QString QXmppDiscoveryIq::Item::node() const { return d->node; } void QXmppDiscoveryIq::Item::setNode(const QString &node) { d->node = node; } class QXmppDiscoveryIqPrivate : public QSharedData { public: QStringList features; QList identities; QList items; QXmppDataForm form; QString queryNode; enum QXmppDiscoveryIq::QueryType queryType; }; QXmppDiscoveryIq::QXmppDiscoveryIq() : d(new QXmppDiscoveryIqPrivate) { } QXmppDiscoveryIq::QXmppDiscoveryIq(const QXmppDiscoveryIq &) = default; QXmppDiscoveryIq::~QXmppDiscoveryIq() = default; QXmppDiscoveryIq &QXmppDiscoveryIq::operator=(const QXmppDiscoveryIq &) = default; QStringList QXmppDiscoveryIq::features() const { return d->features; } void QXmppDiscoveryIq::setFeatures(const QStringList &features) { d->features = features; } QList QXmppDiscoveryIq::identities() const { return d->identities; } void QXmppDiscoveryIq::setIdentities(const QList &identities) { d->identities = identities; } QList QXmppDiscoveryIq::items() const { return d->items; } void QXmppDiscoveryIq::setItems(const QList &items) { d->items = items; } /// Returns the QXmppDataForm for this IQ, as defined by /// \xep{0128}: Service Discovery Extensions. /// QXmppDataForm QXmppDiscoveryIq::form() const { return d->form; } /// Sets the QXmppDataForm for this IQ, as define by /// \xep{0128}: Service Discovery Extensions. /// /// \param form /// void QXmppDiscoveryIq::setForm(const QXmppDataForm &form) { d->form = form; } QString QXmppDiscoveryIq::queryNode() const { return d->queryNode; } void QXmppDiscoveryIq::setQueryNode(const QString &node) { d->queryNode = node; } enum QXmppDiscoveryIq::QueryType QXmppDiscoveryIq::queryType() const { return d->queryType; } void QXmppDiscoveryIq::setQueryType(enum QXmppDiscoveryIq::QueryType type) { d->queryType = type; } /// Calculate the verification string for \xep{0115}: Entity Capabilities QByteArray QXmppDiscoveryIq::verificationString() const { QString S; QList sortedIdentities = d->identities; std::sort(sortedIdentities.begin(), sortedIdentities.end(), identityLessThan); QStringList sortedFeatures = d->features; std::sort(sortedFeatures.begin(), sortedFeatures.end()); sortedFeatures.removeDuplicates(); for (const auto &identity : sortedIdentities) S += QString("%1/%2/%3/%4<").arg(identity.category(), identity.type(), identity.language(), identity.name()); for (const auto &feature : sortedFeatures) S += feature + QLatin1String("<"); if (!d->form.isNull()) { QMap fieldMap; for (const auto &field : d->form.fields()) { fieldMap.insert(field.key(), field); } if (fieldMap.contains("FORM_TYPE")) { const QXmppDataForm::Field field = fieldMap.take("FORM_TYPE"); S += field.value().toString() + QLatin1String("<"); QStringList keys = fieldMap.keys(); std::sort(keys.begin(), keys.end()); for (const auto &key : keys) { const QXmppDataForm::Field field = fieldMap.value(key); S += key + QLatin1String("<"); if (field.value().canConvert()) { QStringList list = field.value().toStringList(); list.sort(); S += list.join(QLatin1String("<")); } else { S += field.value().toString(); } S += QLatin1String("<"); } } else { qWarning("QXmppDiscoveryIq form does not contain FORM_TYPE"); } } QCryptographicHash hasher(QCryptographicHash::Sha1); hasher.addData(S.toUtf8()); return hasher.result(); } /// \cond bool QXmppDiscoveryIq::isDiscoveryIq(const QDomElement &element) { QDomElement queryElement = element.firstChildElement("query"); return (queryElement.namespaceURI() == ns_disco_info || queryElement.namespaceURI() == ns_disco_items); } void QXmppDiscoveryIq::parseElementFromChild(const QDomElement &element) { QDomElement queryElement = element.firstChildElement("query"); d->queryNode = queryElement.attribute("node"); if (queryElement.namespaceURI() == ns_disco_items) d->queryType = ItemsQuery; else d->queryType = InfoQuery; QDomElement itemElement = queryElement.firstChildElement(); while (!itemElement.isNull()) { if (itemElement.tagName() == "feature") { d->features.append(itemElement.attribute("var")); } else if (itemElement.tagName() == "identity") { QXmppDiscoveryIq::Identity identity; identity.setLanguage(itemElement.attribute("xml:lang")); identity.setCategory(itemElement.attribute("category")); identity.setName(itemElement.attribute("name")); identity.setType(itemElement.attribute("type")); // FIXME: for some reason the language does not found, // so we are forced to use QDomNamedNodeMap QDomNamedNodeMap m(itemElement.attributes()); for (int i = 0; i < m.size(); ++i) { if (m.item(i).nodeName() == "xml:lang") { identity.setLanguage(m.item(i).nodeValue()); break; } } d->identities.append(identity); } else if (itemElement.tagName() == "item") { QXmppDiscoveryIq::Item item; item.setJid(itemElement.attribute("jid")); item.setName(itemElement.attribute("name")); item.setNode(itemElement.attribute("node")); d->items.append(item); } else if (itemElement.tagName() == "x" && itemElement.namespaceURI() == ns_data) { d->form.parse(itemElement); } itemElement = itemElement.nextSiblingElement(); } } void QXmppDiscoveryIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement("query"); writer->writeDefaultNamespace( d->queryType == InfoQuery ? ns_disco_info : ns_disco_items); helperToXmlAddAttribute(writer, "node", d->queryNode); if (d->queryType == InfoQuery) { for (const auto &identity : d->identities) { writer->writeStartElement("identity"); helperToXmlAddAttribute(writer, "xml:lang", identity.language()); helperToXmlAddAttribute(writer, "category", identity.category()); helperToXmlAddAttribute(writer, "name", identity.name()); helperToXmlAddAttribute(writer, "type", identity.type()); writer->writeEndElement(); } for (const auto &feature : d->features) { writer->writeStartElement("feature"); helperToXmlAddAttribute(writer, "var", feature); writer->writeEndElement(); } } else { for (const auto &item : d->items) { writer->writeStartElement("item"); helperToXmlAddAttribute(writer, "jid", item.jid()); helperToXmlAddAttribute(writer, "name", item.name()); helperToXmlAddAttribute(writer, "node", item.node()); writer->writeEndElement(); } } d->form.toXml(writer); writer->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/base/QXmppDiscoveryIq.h000066400000000000000000000072661402370562100177750ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPDISCOVERY_H #define QXMPPDISCOVERY_H #include "QXmppDataForm.h" #include "QXmppIq.h" #include class QXmppDiscoveryIdentityPrivate; class QXmppDiscoveryItemPrivate; class QXmppDiscoveryIqPrivate; /// /// \brief QXmppDiscoveryIq represents a discovery IQ request or result /// containing a list of features and other information about an entity as /// defined by \xep{0030}: Service Discovery. /// /// \ingroup Stanzas /// class QXMPP_EXPORT QXmppDiscoveryIq : public QXmppIq { public: /// /// \brief Identity represents one of possibly multiple identities of an /// XMPP entity obtained from a service discovery request as defined in /// \xep{0030}: Service Discovery. /// class QXMPP_EXPORT Identity { public: Identity(); Identity(const Identity &other); ~Identity(); Identity &operator=(const Identity &other); QString category() const; void setCategory(const QString &category); QString language() const; void setLanguage(const QString &language); QString name() const; void setName(const QString &name); QString type() const; void setType(const QString &type); private: QSharedDataPointer d; }; /// /// \brief Item represents a related XMPP entity that can be queried using /// \xep{0030}: Service Discovery. /// class QXMPP_EXPORT Item { public: Item(); Item(const Item &); ~Item(); Item &operator=(const Item &); QString jid() const; void setJid(const QString &jid); QString name() const; void setName(const QString &name); QString node() const; void setNode(const QString &node); private: QSharedDataPointer d; }; QXmppDiscoveryIq(); QXmppDiscoveryIq(const QXmppDiscoveryIq &); ~QXmppDiscoveryIq(); QXmppDiscoveryIq &operator=(const QXmppDiscoveryIq &); enum QueryType { InfoQuery, ItemsQuery }; QStringList features() const; void setFeatures(const QStringList &features); QList identities() const; void setIdentities(const QList &identities); QList items() const; void setItems(const QList &items); QXmppDataForm form() const; void setForm(const QXmppDataForm &form); QString queryNode() const; void setQueryNode(const QString &node); enum QueryType queryType() const; void setQueryType(enum QueryType type); QByteArray verificationString() const; static bool isDiscoveryIq(const QDomElement &element); protected: /// \cond void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: QSharedDataPointer d; }; #endif qxmpp-1.4.0/src/base/QXmppElement.cpp000066400000000000000000000135071402370562100174530ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppElement.h" #include "QXmppUtils.h" #include #include class QXmppElementPrivate { public: QXmppElementPrivate(); QXmppElementPrivate(const QDomElement &element); ~QXmppElementPrivate(); QAtomicInt counter; QXmppElementPrivate *parent; QMap attributes; QList children; QString name; QString value; QByteArray serializedSource; }; QXmppElementPrivate::QXmppElementPrivate() : counter(1), parent(nullptr) { } QXmppElementPrivate::QXmppElementPrivate(const QDomElement &element) : counter(1), parent(nullptr) { if (element.isNull()) return; name = element.tagName(); QString xmlns = element.namespaceURI(); QString parentns = element.parentNode().namespaceURI(); if (!xmlns.isEmpty() && xmlns != parentns) attributes.insert("xmlns", xmlns); QDomNamedNodeMap attrs = element.attributes(); for (int i = 0; i < attrs.size(); i++) { QDomAttr attr = attrs.item(i).toAttr(); attributes.insert(attr.name(), attr.value()); } QDomNode childNode = element.firstChild(); while (!childNode.isNull()) { if (childNode.isElement()) { QXmppElementPrivate *child = new QXmppElementPrivate(childNode.toElement()); child->parent = this; children.append(child); } else if (childNode.isText()) { value += childNode.toText().data(); } childNode = childNode.nextSibling(); } QTextStream stream(&serializedSource); element.save(stream, 0); } QXmppElementPrivate::~QXmppElementPrivate() { for (auto *child : children) if (!child->counter.deref()) delete child; } QXmppElement::QXmppElement() { d = new QXmppElementPrivate(); } QXmppElement::QXmppElement(const QXmppElement &other) { other.d->counter.ref(); d = other.d; } QXmppElement::QXmppElement(QXmppElementPrivate *other) { other->counter.ref(); d = other; } QXmppElement::QXmppElement(const QDomElement &element) { d = new QXmppElementPrivate(element); } QXmppElement::~QXmppElement() { if (!d->counter.deref()) delete d; } QXmppElement &QXmppElement::operator=(const QXmppElement &other) { if (this != &other) // self-assignment check { other.d->counter.ref(); if (!d->counter.deref()) delete d; d = other.d; } return *this; } QDomElement QXmppElement::sourceDomElement() const { if (d->serializedSource.isEmpty()) return QDomElement(); QDomDocument doc; if (!doc.setContent(d->serializedSource, true)) { qWarning("[QXmpp] QXmppElement::sourceDomElement(): cannot parse source element"); return QDomElement(); } return doc.documentElement(); } QStringList QXmppElement::attributeNames() const { return d->attributes.keys(); } QString QXmppElement::attribute(const QString &name) const { return d->attributes.value(name); } void QXmppElement::setAttribute(const QString &name, const QString &value) { d->attributes.insert(name, value); } void QXmppElement::appendChild(const QXmppElement &child) { if (child.d->parent == d) return; if (child.d->parent) child.d->parent->children.removeAll(child.d); else child.d->counter.ref(); child.d->parent = d; d->children.append(child.d); } QXmppElement QXmppElement::firstChildElement(const QString &name) const { for (auto *child_d : d->children) if (name.isEmpty() || child_d->name == name) return QXmppElement(child_d); return QXmppElement(); } QXmppElement QXmppElement::nextSiblingElement(const QString &name) const { if (!d->parent) return QXmppElement(); const QList &siblings_d = d->parent->children; for (int i = siblings_d.indexOf(d) + 1; i < siblings_d.size(); i++) if (name.isEmpty() || siblings_d[i]->name == name) return QXmppElement(siblings_d[i]); return QXmppElement(); } bool QXmppElement::isNull() const { return d->name.isEmpty(); } void QXmppElement::removeChild(const QXmppElement &child) { if (child.d->parent != d) return; d->children.removeAll(child.d); child.d->counter.deref(); child.d->parent = nullptr; } QString QXmppElement::tagName() const { return d->name; } void QXmppElement::setTagName(const QString &tagName) { d->name = tagName; } QString QXmppElement::value() const { return d->value; } void QXmppElement::setValue(const QString &value) { d->value = value; } void QXmppElement::toXml(QXmlStreamWriter *writer) const { if (isNull()) return; writer->writeStartElement(d->name); if (d->attributes.contains("xmlns")) writer->writeDefaultNamespace(d->attributes.value("xmlns")); for (const auto &attr : d->attributes.keys()) if (attr != "xmlns") helperToXmlAddAttribute(writer, attr, d->attributes.value(attr)); if (!d->value.isEmpty()) writer->writeCharacters(d->value); for (auto *childPrivate : d->children) QXmppElement(childPrivate).toXml(writer); writer->writeEndElement(); } qxmpp-1.4.0/src/base/QXmppElement.h000066400000000000000000000037731402370562100171240ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPELEMENT_H #define QXMPPELEMENT_H #include "QXmppGlobal.h" #include #include #include class QDomElement; class QXmppElement; class QXmppElementPrivate; using QXmppElementList = QList; /// /// \brief QXmppElement represents a raw XML element with possible children. /// class QXMPP_EXPORT QXmppElement { public: QXmppElement(); QXmppElement(const QXmppElement &other); QXmppElement(const QDomElement &element); ~QXmppElement(); QDomElement sourceDomElement() const; QStringList attributeNames() const; QString attribute(const QString &name) const; void setAttribute(const QString &name, const QString &value); void appendChild(const QXmppElement &child); QXmppElement firstChildElement(const QString &name = QString()) const; QXmppElement nextSiblingElement(const QString &name = QString()) const; void removeChild(const QXmppElement &child); QString tagName() const; void setTagName(const QString &type); QString value() const; void setValue(const QString &text); bool isNull() const; void toXml(QXmlStreamWriter *writer) const; QXmppElement &operator=(const QXmppElement &other); private: QXmppElement(QXmppElementPrivate *other); QXmppElementPrivate *d; }; #endif qxmpp-1.4.0/src/base/QXmppEntityTimeIq.cpp000066400000000000000000000043771402370562100204540ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 "QXmppEntityTimeIq.h" #include "QXmppConstants_p.h" #include "QXmppUtils.h" #include /// Returns the timezone offset in seconds. /// int QXmppEntityTimeIq::tzo() const { return m_tzo; } /// Sets the timezone offset in seconds. /// /// \param tzo void QXmppEntityTimeIq::setTzo(int tzo) { m_tzo = tzo; } /// Returns the date/time in Coordinated Universal Time (UTC). /// QDateTime QXmppEntityTimeIq::utc() const { return m_utc; } /// Sets the date/time in Coordinated Universal Time (UTC). /// /// \param utc void QXmppEntityTimeIq::setUtc(const QDateTime &utc) { m_utc = utc; } /// \cond bool QXmppEntityTimeIq::isEntityTimeIq(const QDomElement &element) { QDomElement timeElement = element.firstChildElement("time"); return timeElement.namespaceURI() == ns_entity_time; } void QXmppEntityTimeIq::parseElementFromChild(const QDomElement &element) { QDomElement timeElement = element.firstChildElement("time"); m_tzo = QXmppUtils::timezoneOffsetFromString(timeElement.firstChildElement("tzo").text()); m_utc = QXmppUtils::datetimeFromString(timeElement.firstChildElement("utc").text()); } void QXmppEntityTimeIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement("time"); writer->writeDefaultNamespace(ns_entity_time); if (m_utc.isValid()) { helperToXmlAddTextElement(writer, "tzo", QXmppUtils::timezoneOffsetToString(m_tzo)); helperToXmlAddTextElement(writer, "utc", QXmppUtils::datetimeToString(m_utc)); } writer->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/base/QXmppEntityTimeIq.h000066400000000000000000000026721402370562100201150ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 QXMPPENTITYTIMEIQ_H #define QXMPPENTITYTIMEIQ_H #include "QXmppIq.h" #include /// /// \brief QXmppEntityTimeIq represents an entity time request/response as /// defined in \xep{0202}: Entity Time. /// /// \ingroup Stanzas /// class QXMPP_EXPORT QXmppEntityTimeIq : public QXmppIq { public: int tzo() const; void setTzo(int tzo); QDateTime utc() const; void setUtc(const QDateTime &utc); static bool isEntityTimeIq(const QDomElement &element); protected: /// \cond void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: int m_tzo; QDateTime m_utc; }; #endif //QXMPPENTITYTIMEIQ_H qxmpp-1.4.0/src/base/QXmppGlobal.h.in000066400000000000000000000042671402370562100173370ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * Niels Ole Salscheider * Linus Jahn * * 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 QXMPPGLOBAL_H #define QXMPPGLOBAL_H #include #define QXMPP_BUILD_SHARED @QXMPP_BUILD_SHARED@ #if QXMPP_BUILD_SHARED # if defined(QXMPP_BUILD) # define QXMPP_EXPORT Q_DECL_EXPORT # else # define QXMPP_EXPORT Q_DECL_IMPORT # endif #else # define QXMPP_EXPORT #endif #define QXMPP_AUTOTEST_EXPORT /// This macro expands a numeric value of the form 0xMMNNPP (MM = /// major, NN = minor, PP = patch) that specifies QXmpp's version /// number. For example, if you compile your application against /// QXmpp 1.2.3, the QXMPP_VERSION macro will expand to 0x010203. /// /// You can use QXMPP_VERSION to use the latest QXmpp features where /// available. /// #define QXMPP_VERSION QT_VERSION_CHECK(@PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@) inline QLatin1String QXmppVersion() { return QLatin1String("@PROJECT_VERSION@"); } // 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 0x0 #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) // workaround for Qt < 5.12 #ifndef Q_DECL_ENUMERATOR_DEPRECATED_X #define Q_DECL_ENUMERATOR_DEPRECATED_X(msg) #endif #endif // QXMPPGLOBAL_H qxmpp-1.4.0/src/base/QXmppHttpUploadIq.cpp000066400000000000000000000161301402370562100204330ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Linus Jahn * * 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 "QXmppHttpUploadIq.h" #include "QXmppConstants_p.h" #include #include #include #include class QXmppHttpUploadRequestIqPrivate : public QSharedData { public: QString fileName; qint64 size; QMimeType contentType; }; QXmppHttpUploadRequestIq::QXmppHttpUploadRequestIq() : d(new QXmppHttpUploadRequestIqPrivate()) { } QXmppHttpUploadRequestIq::QXmppHttpUploadRequestIq(const QXmppHttpUploadRequestIq &) = default; QXmppHttpUploadRequestIq::~QXmppHttpUploadRequestIq() = default; QXmppHttpUploadRequestIq &QXmppHttpUploadRequestIq::operator=(const QXmppHttpUploadRequestIq &) = default; /// 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->writeDefaultNamespace(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 QSharedData { public: QUrl putUrl; QUrl getUrl; QMap putHeaders; }; QXmppHttpUploadSlotIq::QXmppHttpUploadSlotIq() : d(new QXmppHttpUploadSlotIqPrivate()) { } QXmppHttpUploadSlotIq::QXmppHttpUploadSlotIq(const QXmppHttpUploadSlotIq &) = default; QXmppHttpUploadSlotIq::~QXmppHttpUploadSlotIq() = default; QXmppHttpUploadSlotIq &QXmppHttpUploadSlotIq::operator=(const QXmppHttpUploadSlotIq &) = default; /// 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 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 &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 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->writeDefaultNamespace(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 qxmpp-1.4.0/src/base/QXmppHttpUploadIq.h000066400000000000000000000056331402370562100201060ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Linus Jahn * * 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 "QXmppIq.h" #include class QUrl; class QMimeType; class QXmppHttpUploadRequestIqPrivate; class QXmppHttpUploadSlotIqPrivate; /// \brief Represents an HTTP File Upload IQ for requesting an upload slot as /// defined by \xep{0363}: HTTP File Upload. /// /// \since QXmpp 1.1 /// /// \ingroup Stanzas class QXMPP_EXPORT QXmppHttpUploadRequestIq : public QXmppIq { public: QXmppHttpUploadRequestIq(); QXmppHttpUploadRequestIq(const QXmppHttpUploadRequestIq &); ~QXmppHttpUploadRequestIq() override; QXmppHttpUploadRequestIq &operator=(const QXmppHttpUploadRequestIq &); 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: QSharedDataPointer d; }; /// \brief Represents an HTTP File Upload IQ result for receiving an upload slot as /// defined by \xep{0363}: HTTP File Upload. /// /// \since QXmpp 1.1 /// /// \ingroup Stanzas class QXMPP_EXPORT QXmppHttpUploadSlotIq : public QXmppIq { public: QXmppHttpUploadSlotIq(); QXmppHttpUploadSlotIq(const QXmppHttpUploadSlotIq &); ~QXmppHttpUploadSlotIq() override; QXmppHttpUploadSlotIq &operator=(const QXmppHttpUploadSlotIq &); QUrl putUrl() const; void setPutUrl(const QUrl &putUrl); QUrl getUrl() const; void setGetUrl(const QUrl &getUrl); QMap putHeaders() const; void setPutHeaders(const QMap &putHeaders); static bool isHttpUploadSlotIq(const QDomElement &element); protected: /// \cond void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: QSharedDataPointer d; }; #endif // QXMPPHTTPUPLOADIQ_H qxmpp-1.4.0/src/base/QXmppIbbIq.cpp000066400000000000000000000100371402370562100170430ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Manjeet Dahiya * Jeremy Lainé * * 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 "QXmppIbbIq.h" #include "QXmppConstants_p.h" #include #include QXmppIbbOpenIq::QXmppIbbOpenIq() : QXmppIq(QXmppIq::Set), m_block_size(1024) { } long QXmppIbbOpenIq::blockSize() const { return m_block_size; } void QXmppIbbOpenIq::setBlockSize(long block_size) { m_block_size = block_size; } QString QXmppIbbOpenIq::sid() const { return m_sid; } void QXmppIbbOpenIq::setSid(const QString &sid) { m_sid = sid; } /// \cond bool QXmppIbbOpenIq::isIbbOpenIq(const QDomElement &element) { QDomElement openElement = element.firstChildElement("open"); return openElement.namespaceURI() == ns_ibb; } void QXmppIbbOpenIq::parseElementFromChild(const QDomElement &element) { QDomElement openElement = element.firstChildElement("open"); m_sid = openElement.attribute("sid"); m_block_size = openElement.attribute("block-size").toLong(); } void QXmppIbbOpenIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement("open"); writer->writeDefaultNamespace(ns_ibb); writer->writeAttribute("sid", m_sid); writer->writeAttribute("block-size", QString::number(m_block_size)); writer->writeEndElement(); } /// \endcond QXmppIbbCloseIq::QXmppIbbCloseIq() : QXmppIq(QXmppIq::Set) { } QString QXmppIbbCloseIq::sid() const { return m_sid; } void QXmppIbbCloseIq::setSid(const QString &sid) { m_sid = sid; } /// \cond bool QXmppIbbCloseIq::isIbbCloseIq(const QDomElement &element) { QDomElement openElement = element.firstChildElement("close"); return openElement.namespaceURI() == ns_ibb; } void QXmppIbbCloseIq::parseElementFromChild(const QDomElement &element) { QDomElement openElement = element.firstChildElement("close"); m_sid = openElement.attribute("sid"); } void QXmppIbbCloseIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement("close"); writer->writeDefaultNamespace(ns_ibb); writer->writeAttribute("sid", m_sid); writer->writeEndElement(); } /// \endcond QXmppIbbDataIq::QXmppIbbDataIq() : QXmppIq(QXmppIq::Set), m_seq(0) { } quint16 QXmppIbbDataIq::sequence() const { return m_seq; } void QXmppIbbDataIq::setSequence(quint16 seq) { m_seq = seq; } QString QXmppIbbDataIq::sid() const { return m_sid; } void QXmppIbbDataIq::setSid(const QString &sid) { m_sid = sid; } QByteArray QXmppIbbDataIq::payload() const { return m_payload; } void QXmppIbbDataIq::setPayload(const QByteArray &data) { m_payload = data; } /// \cond bool QXmppIbbDataIq::isIbbDataIq(const QDomElement &element) { QDomElement dataElement = element.firstChildElement("data"); return dataElement.namespaceURI() == ns_ibb; } void QXmppIbbDataIq::parseElementFromChild(const QDomElement &element) { QDomElement dataElement = element.firstChildElement("data"); m_sid = dataElement.attribute("sid"); m_seq = dataElement.attribute("seq").toLong(); m_payload = QByteArray::fromBase64(dataElement.text().toLatin1()); } void QXmppIbbDataIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement("data"); writer->writeDefaultNamespace(ns_ibb); writer->writeAttribute("sid", m_sid); writer->writeAttribute("seq", QString::number(m_seq)); writer->writeCharacters(m_payload.toBase64()); writer->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/base/QXmppIbbIq.h000066400000000000000000000053241402370562100165130ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Manjeet Dahiya * Jeremy Lainé * * 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 QXMPPIBBIQ_H #define QXMPPIBBIQ_H #include "QXmppIq.h" /// /// \brief QXmppIbbOpenIq represents an IBB open request as defined by /// \xep{0047}: In-Band Bytestreams. /// /// \ingroup Stanzas /// class QXmppIbbOpenIq : public QXmppIq { public: QXmppIbbOpenIq(); long blockSize() const; void setBlockSize(long block_size); QString sid() const; void setSid(const QString &sid); static bool isIbbOpenIq(const QDomElement &element); protected: /// \cond void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: long m_block_size; QString m_sid; }; /// /// \brief QXmppIbbCloseIq represents an IBB close request as defined by /// \xep{0047}: In-Band Bytestreams. /// /// \ingroup Stanzas /// class QXmppIbbCloseIq : public QXmppIq { public: QXmppIbbCloseIq(); QString sid() const; void setSid(const QString &sid); static bool isIbbCloseIq(const QDomElement &element); protected: /// \cond void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: QString m_sid; }; /// /// \brief QXmppIbbCloseIq represents an IBB data request as defined by /// \xep{0047}: In-Band Bytestreams. /// /// \ingroup Stanzas /// class QXMPP_EXPORT QXmppIbbDataIq : public QXmppIq { public: QXmppIbbDataIq(); quint16 sequence() const; void setSequence(quint16 seq); QString sid() const; void setSid(const QString &sid); QByteArray payload() const; void setPayload(const QByteArray &data); static bool isIbbDataIq(const QDomElement &element); protected: /// \cond void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: quint16 m_seq; QString m_sid; QByteArray m_payload; }; #endif // QXMPPIBBIQS_H qxmpp-1.4.0/src/base/QXmppIq.cpp000066400000000000000000000061451402370562100164330ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 "QXmppIq.h" #include "QXmppUtils.h" #include #include static const char *iq_types[] = { "error", "get", "set", "result" }; class QXmppIqPrivate : public QSharedData { public: QXmppIq::Type type; }; /// Constructs a QXmppIq with the specified \a type. /// /// \param type QXmppIq::QXmppIq(QXmppIq::Type type) : QXmppStanza(), d(new QXmppIqPrivate) { d->type = type; generateAndSetNextId(); } /// Constructs a copy of \a other. QXmppIq::QXmppIq(const QXmppIq &other) : QXmppStanza(other), d(other.d) { } QXmppIq::~QXmppIq() { } /// Assigns \a other to this IQ. QXmppIq &QXmppIq::operator=(const QXmppIq &other) { QXmppStanza::operator=(other); d = other.d; return *this; } /// Returns the IQ's type. /// QXmppIq::Type QXmppIq::type() const { return d->type; } /// Sets the IQ's type. /// /// \param type void QXmppIq::setType(QXmppIq::Type type) { d->type = type; } /// /// Indicates if the QXmppStanza is a stanza in the XMPP sense (i. e. a message, /// iq or presence) /// /// \since QXmpp 1.0 /// bool QXmppIq::isXmppStanza() const { return true; } /// \cond void QXmppIq::parse(const QDomElement &element) { QXmppStanza::parse(element); const QString type = element.attribute("type"); for (int i = Error; i <= Result; i++) { if (type == iq_types[i]) { d->type = static_cast(i); break; } } parseElementFromChild(element); } void QXmppIq::parseElementFromChild(const QDomElement &element) { QXmppElementList extensions; QDomElement itemElement = element.firstChildElement(); while (!itemElement.isNull()) { extensions.append(QXmppElement(itemElement)); itemElement = itemElement.nextSiblingElement(); } setExtensions(extensions); } void QXmppIq::toXml(QXmlStreamWriter *xmlWriter) const { xmlWriter->writeStartElement("iq"); helperToXmlAddAttribute(xmlWriter, "id", id()); helperToXmlAddAttribute(xmlWriter, "to", to()); helperToXmlAddAttribute(xmlWriter, "from", from()); helperToXmlAddAttribute(xmlWriter, "type", iq_types[d->type]); toXmlElementFromChild(xmlWriter); error().toXml(xmlWriter); xmlWriter->writeEndElement(); } void QXmppIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { for (const QXmppElement &extension : extensions()) extension.toXml(writer); } /// \endcond qxmpp-1.4.0/src/base/QXmppIq.h000066400000000000000000000037541402370562100161030ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 QXMPPIQ_H #define QXMPPIQ_H #include "QXmppStanza.h" // forward declarations of QXmlStream* classes will not work on Mac, we need to // include the whole header. // See http://lists.trolltech.com/qt-interest/2008-07/thread00798-0.html // for an explanation. #include class QXmppIqPrivate; /// \brief The QXmppIq class is the base class for all IQs. /// /// \ingroup Stanzas class QXMPP_EXPORT QXmppIq : public QXmppStanza { public: /// This enum describes the type of IQ. enum Type { Error = 0, ///< Error response. Get, ///< Get request. Set, ///< Set request. Result ///< Result. }; QXmppIq(QXmppIq::Type type = QXmppIq::Get); QXmppIq(const QXmppIq &other); ~QXmppIq() override; QXmppIq &operator=(const QXmppIq &other); QXmppIq::Type type() const; void setType(QXmppIq::Type); bool isXmppStanza() const override; /// \cond void parse(const QDomElement &element) override; void toXml(QXmlStreamWriter *writer) const override; protected: virtual void parseElementFromChild(const QDomElement &element); virtual void toXmlElementFromChild(QXmlStreamWriter *writer) const; /// \endcond private: QSharedDataPointer d; }; #endif // QXMPPIQ_H qxmpp-1.4.0/src/base/QXmppJingleIq.cpp000066400000000000000000001167671402370562100176000ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppJingleIq.h" #include "QXmppConstants_p.h" #include "QXmppUtils.h" #include #include #include #include static const int RTP_COMPONENT = 1; static const char *ns_jingle_rtp_info = "urn:xmpp:jingle:apps:rtp:info:1"; static const char *ns_jingle_dtls = "urn:xmpp:jingle:apps:dtls:0"; static const char *jingle_actions[] = { "content-accept", "content-add", "content-modify", "content-reject", "content-remove", "description-info", "security-info", "session-accept", "session-info", "session-initiate", "session-terminate", "transport-accept", "transport-info", "transport-reject", "transport-replace", }; static const char *jingle_reasons[] = { "", "alternative-session", "busy", "cancel", "connectivity-error", "decline", "expired", "failed-application", "failed-transport", "general-error", "gone", "incompatible-parameters", "media-error", "security-error", "success", "timeout", "unsupported-applications", "unsupported-transports", }; static QString formatFingerprint(const QByteArray &digest) { QString fingerprint; const QString hx = digest.toHex().toUpper(); for (int i = 0; i < hx.size(); i += 2) { if (!fingerprint.isEmpty()) fingerprint += ':'; fingerprint += hx.mid(i, 2); } return fingerprint; } static QByteArray parseFingerprint(const QString &fingerprint) { QString z = fingerprint; z.replace(':', ""); return QByteArray::fromHex(z.toUtf8()); } static QString addressToSdp(const QHostAddress &host) { return QStringLiteral("IN %1 %2").arg(host.protocol() == QAbstractSocket::IPv6Protocol ? QStringLiteral("IP6") : QStringLiteral("IP4"), host.toString()); } static bool candidateParseSdp(QXmppJingleCandidate *candidate, const QString &sdp) { if (!sdp.startsWith(QStringLiteral("candidate:"))) return false; const QStringList bits = sdp.mid(10).split(" "); if (bits.size() < 6) return false; candidate->setFoundation(bits[0]); candidate->setComponent(bits[1].toInt()); candidate->setProtocol(bits[2].toLower()); candidate->setPriority(bits[3].toInt()); candidate->setHost(QHostAddress(bits[4])); candidate->setPort(bits[5].toInt()); for (int i = 6; i < bits.size() - 1; i += 2) { if (bits[i] == QStringLiteral("typ")) { bool ok; candidate->setType(QXmppJingleCandidate::typeFromString(bits[i + 1], &ok)); if (!ok) return false; } else if (bits[i] == QStringLiteral("generation")) { candidate->setGeneration(bits[i + 1].toInt()); } else { qWarning() << "Candidate SDP contains unknown attribute" << bits[i]; return false; } } return true; } static QString candidateToSdp(const QXmppJingleCandidate &candidate) { return QStringLiteral("candidate:%1 %2 %3 %4 %5 %6 typ %7 generation %8").arg(candidate.foundation(), QString::number(candidate.component()), candidate.protocol(), QString::number(candidate.priority()), candidate.host().toString(), QString::number(candidate.port()), QXmppJingleCandidate::typeToString(candidate.type()), QString::number(candidate.generation())); } class QXmppJingleIqContentPrivate : public QSharedData { public: QXmppJingleIqContentPrivate(); QString creator; QString disposition; QString name; QString senders; QString descriptionMedia; quint32 descriptionSsrc; QString descriptionType; QString transportType; QString transportUser; QString transportPassword; QByteArray transportFingerprint; QString transportFingerprintHash; QString transportFingerprintSetup; QList payloadTypes; QList transportCandidates; }; QXmppJingleIqContentPrivate::QXmppJingleIqContentPrivate() : descriptionSsrc(0) { } /// Constructs an empty content. QXmppJingleIq::Content::Content() : d(new QXmppJingleIqContentPrivate()) { } /// Constructs a copy of other. /// /// \param other QXmppJingleIq::Content::Content(const QXmppJingleIq::Content &other) : d(other.d) { } /// Assigns the other content to this one. /// /// \param other QXmppJingleIq::Content &QXmppJingleIq::Content::operator=(const QXmppJingleIq::Content &other) { d = other.d; return *this; } QXmppJingleIq::Content::~Content() { } QString QXmppJingleIq::Content::creator() const { return d->creator; } void QXmppJingleIq::Content::setCreator(const QString &creator) { d->creator = creator; } QString QXmppJingleIq::Content::name() const { return d->name; } void QXmppJingleIq::Content::setName(const QString &name) { d->name = name; } QString QXmppJingleIq::Content::senders() const { return d->senders; } void QXmppJingleIq::Content::setSenders(const QString &senders) { d->senders = senders; } QString QXmppJingleIq::Content::descriptionMedia() const { return d->descriptionMedia; } void QXmppJingleIq::Content::setDescriptionMedia(const QString &media) { d->descriptionMedia = media; } /// /// Returns the description's 32-bit synchronization source for the media /// stream, as defined in RFC 3550. /// /// \since QXmpp 0.9 /// quint32 QXmppJingleIq::Content::descriptionSsrc() const { return d->descriptionSsrc; } /// /// Sets the description's 32-bit synchronization source for the media stream, /// as defined in RFC 3550. /// /// \since QXmpp 0.9 /// void QXmppJingleIq::Content::setDescriptionSsrc(quint32 ssrc) { d->descriptionSsrc = ssrc; } void QXmppJingleIq::Content::addPayloadType(const QXmppJinglePayloadType &payload) { d->descriptionType = ns_jingle_rtp; d->payloadTypes << payload; } QList QXmppJingleIq::Content::payloadTypes() const { return d->payloadTypes; } void QXmppJingleIq::Content::setPayloadTypes(const QList &payloadTypes) { d->descriptionType = payloadTypes.isEmpty() ? QString() : ns_jingle_rtp; d->payloadTypes = payloadTypes; } void QXmppJingleIq::Content::addTransportCandidate(const QXmppJingleCandidate &candidate) { d->transportType = ns_jingle_ice_udp; d->transportCandidates << candidate; } QList QXmppJingleIq::Content::transportCandidates() const { return d->transportCandidates; } /// /// Sets a list of transport candidates. /// /// \since QXmpp 0.9.2 /// void QXmppJingleIq::Content::setTransportCandidates(const QList &candidates) { d->transportType = candidates.isEmpty() ? QString() : ns_jingle_ice_udp; d->transportCandidates = candidates; } QString QXmppJingleIq::Content::transportUser() const { return d->transportUser; } void QXmppJingleIq::Content::setTransportUser(const QString &user) { d->transportUser = user; } QString QXmppJingleIq::Content::transportPassword() const { return d->transportPassword; } void QXmppJingleIq::Content::setTransportPassword(const QString &password) { d->transportPassword = password; } /// /// Returns the fingerprint hash value for the transport key. /// /// This is used for DTLS-SRTP as defined in \xep{0320}. /// /// \since QXmpp 0.9 /// QByteArray QXmppJingleIq::Content::transportFingerprint() const { return d->transportFingerprint; } /// /// Sets the fingerprint hash value for the transport key. /// /// This is used for DTLS-SRTP as defined in \xep{0320}. /// /// \since QXmpp 0.9 /// void QXmppJingleIq::Content::setTransportFingerprint(const QByteArray &fingerprint) { d->transportFingerprint = fingerprint; } /// /// Returns the fingerprint hash algorithm for the transport key. /// /// This is used for DTLS-SRTP as defined in \xep{0320}. /// /// \since QXmpp 0.9 /// QString QXmppJingleIq::Content::transportFingerprintHash() const { return d->transportFingerprintHash; } /// /// Sets the fingerprint hash algorithm for the transport key. /// /// This is used for DTLS-SRTP as defined in \xep{0320}. /// /// \since QXmpp 0.9 /// void QXmppJingleIq::Content::setTransportFingerprintHash(const QString &hash) { d->transportFingerprintHash = hash; } /// /// Returns the setup role for the encrypted transport. /// /// This is used for DTLS-SRTP as defined in \xep{0320}. /// /// \since QXmpp 0.9 /// QString QXmppJingleIq::Content::transportFingerprintSetup() const { return d->transportFingerprintSetup; } /// /// Sets the setup role for the encrypted transport. /// /// This is used for DTLS-SRTP as defined in \xep{0320}. /// /// \since QXmpp 0.9 /// void QXmppJingleIq::Content::setTransportFingerprintSetup(const QString &setup) { d->transportFingerprintSetup = setup; } /// \cond void QXmppJingleIq::Content::parse(const QDomElement &element) { d->creator = element.attribute(QStringLiteral("creator")); d->disposition = element.attribute(QStringLiteral("disposition")); d->name = element.attribute(QStringLiteral("name")); d->senders = element.attribute(QStringLiteral("senders")); // description QDomElement descriptionElement = element.firstChildElement(QStringLiteral("description")); d->descriptionType = descriptionElement.namespaceURI(); d->descriptionMedia = descriptionElement.attribute(QStringLiteral("media")); d->descriptionSsrc = descriptionElement.attribute(QStringLiteral("ssrc")).toULong(); QDomElement child = descriptionElement.firstChildElement(QStringLiteral("payload-type")); while (!child.isNull()) { QXmppJinglePayloadType payload; payload.parse(child); d->payloadTypes << payload; child = child.nextSiblingElement(QStringLiteral("payload-type")); } // transport QDomElement transportElement = element.firstChildElement(QStringLiteral("transport")); d->transportType = transportElement.namespaceURI(); d->transportUser = transportElement.attribute(QStringLiteral("ufrag")); d->transportPassword = transportElement.attribute(QStringLiteral("pwd")); child = transportElement.firstChildElement(QStringLiteral("candidate")); while (!child.isNull()) { QXmppJingleCandidate candidate; candidate.parse(child); d->transportCandidates << candidate; child = child.nextSiblingElement(QStringLiteral("candidate")); } child = transportElement.firstChildElement(QStringLiteral("fingerprint")); /// XEP-0320 if (!child.isNull()) { d->transportFingerprint = parseFingerprint(child.text()); d->transportFingerprintHash = child.attribute(QStringLiteral("hash")); d->transportFingerprintSetup = child.attribute(QStringLiteral("setup")); } } void QXmppJingleIq::Content::toXml(QXmlStreamWriter *writer) const { if (d->creator.isEmpty() || d->name.isEmpty()) return; writer->writeStartElement(QStringLiteral("content")); helperToXmlAddAttribute(writer, QStringLiteral("creator"), d->creator); helperToXmlAddAttribute(writer, QStringLiteral("disposition"), d->disposition); helperToXmlAddAttribute(writer, QStringLiteral("name"), d->name); helperToXmlAddAttribute(writer, QStringLiteral("senders"), d->senders); // description if (!d->descriptionType.isEmpty() || !d->payloadTypes.isEmpty()) { writer->writeStartElement(QStringLiteral("description")); writer->writeDefaultNamespace(d->descriptionType); helperToXmlAddAttribute(writer, QStringLiteral("media"), d->descriptionMedia); if (d->descriptionSsrc) writer->writeAttribute(QStringLiteral("ssrc"), QString::number(d->descriptionSsrc)); for (const auto &payload : d->payloadTypes) payload.toXml(writer); writer->writeEndElement(); } // transport if (!d->transportType.isEmpty() || !d->transportCandidates.isEmpty()) { writer->writeStartElement(QStringLiteral("transport")); writer->writeDefaultNamespace(d->transportType); helperToXmlAddAttribute(writer, QStringLiteral("ufrag"), d->transportUser); helperToXmlAddAttribute(writer, QStringLiteral("pwd"), d->transportPassword); for (const auto &candidate : d->transportCandidates) candidate.toXml(writer); // XEP-0320 if (!d->transportFingerprint.isEmpty() && !d->transportFingerprintHash.isEmpty()) { writer->writeStartElement(QStringLiteral("fingerprint")); writer->writeDefaultNamespace(ns_jingle_dtls); writer->writeAttribute(QStringLiteral("hash"), d->transportFingerprintHash); writer->writeAttribute(QStringLiteral("setup"), d->transportFingerprintSetup); writer->writeCharacters(formatFingerprint(d->transportFingerprint)); writer->writeEndElement(); } writer->writeEndElement(); } writer->writeEndElement(); } bool QXmppJingleIq::Content::parseSdp(const QString &sdp) { QList payloads; QString line; for (auto &line : sdp.split('\n')) { if (line.endsWith('\r')) line.resize(line.size() - 1); if (line.startsWith(QStringLiteral("a="))) { int idx = line.indexOf(':'); const QString attrName = idx != -1 ? line.mid(2, idx - 2) : line.mid(2); const QString attrValue = idx != -1 ? line.mid(idx + 1) : ""; if (attrName == QStringLiteral("candidate")) { QXmppJingleCandidate candidate; if (!candidateParseSdp(&candidate, line.mid(2))) { qWarning() << "Could not parse candidate" << line; return false; } addTransportCandidate(candidate); } else if (attrName == QStringLiteral("fingerprint")) { const QStringList bits = attrValue.split(' '); if (bits.size() > 1) { d->transportFingerprintHash = bits[0]; d->transportFingerprint = parseFingerprint(bits[1]); } } else if (attrName == QStringLiteral("fmtp")) { int spIdx = attrValue.indexOf(' '); if (spIdx == -1) { qWarning() << "Could not parse payload parameters" << line; return false; } const int id = attrValue.left(spIdx).toInt(); const QString paramStr = attrValue.mid(spIdx + 1); for (auto &payload : payloads) { if (payload.id() == id) { QMap params; if (payload.name() == QStringLiteral("telephone-event")) { params.insert(QStringLiteral("events"), paramStr); } else { for (const auto &p : paramStr.split(QRegularExpression(";\\s*"))) { const QStringList bits = p.split('='); if (bits.size() == 2) params.insert(bits.at(0), bits.at(1)); } } payload.setParameters(params); } } } else if (attrName == QStringLiteral("rtpmap")) { // payload type map const QStringList bits = attrValue.split(' '); if (bits.size() != 2) continue; bool ok = false; const int id = bits[0].toInt(&ok); if (!ok) continue; const QStringList args = bits[1].split('/'); for (auto &payload : payloads) { if (payload.id() == id) { payload.setName(args[0]); if (args.size() > 1) payload.setClockrate(args[1].toInt()); if (args.size() > 2) payload.setChannels(args[2].toInt()); } } } else if (attrName == QStringLiteral("ice-ufrag")) { d->transportUser = attrValue; } else if (attrName == QStringLiteral("ice-pwd")) { d->transportPassword = attrValue; } else if (attrName == QStringLiteral("setup")) { d->transportFingerprintSetup = attrValue; } else if (attrName == QStringLiteral("ssrc")) { const QStringList bits = attrValue.split(' '); if (bits.isEmpty()) { qWarning() << "Could not parse ssrc" << line; return false; } d->descriptionSsrc = bits[0].toULong(); } } else if (line.startsWith(QStringLiteral("m="))) { // FIXME: what do we do with the profile (bits[2]) ? QStringList bits = line.mid(2).split(' '); if (bits.size() < 3) { qWarning() << "Could not parse media" << line; return false; } d->descriptionMedia = bits[0]; // parse payload types for (int i = 3; i < bits.size(); ++i) { bool ok = false; int id = bits[i].toInt(&ok); if (!ok) continue; QXmppJinglePayloadType payload; payload.setId(id); payloads << payload; } } } setPayloadTypes(payloads); return true; } static bool candidateLessThan(const QXmppJingleCandidate &c1, const QXmppJingleCandidate &c2) { if (c1.type() == c2.type()) return c1.priority() > c2.priority(); else return c1.type() == QXmppJingleCandidate::ServerReflexiveType; } QString QXmppJingleIq::Content::toSdp() const { // get default candidate QHostAddress localRtpAddress = QHostAddress::Any; quint16 localRtpPort = 0; QList sortedCandidates = d->transportCandidates; std::sort(sortedCandidates.begin(), sortedCandidates.end(), candidateLessThan); for (const auto &candidate : sortedCandidates) { if (candidate.component() == RTP_COMPONENT) { localRtpAddress = candidate.host(); localRtpPort = candidate.port(); break; } } QStringList sdp; // media QString payloads; QStringList attrs; for (const QXmppJinglePayloadType &payload : d->payloadTypes) { payloads += " " + QString::number(payload.id()); QString rtpmap = QString::number(payload.id()) + " " + payload.name() + "/" + QString::number(payload.clockrate()); if (payload.channels() > 1) rtpmap += "/" + QString::number(payload.channels()); attrs << "a=rtpmap:" + rtpmap; // payload parameters QStringList paramList; const QMap params = payload.parameters(); if (payload.name() == QStringLiteral("telephone-event")) { if (params.contains(QStringLiteral("events"))) paramList << params.value(QStringLiteral("events")); } else { QMap::const_iterator i; for (i = params.begin(); i != params.end(); ++i) paramList << i.key() + QStringLiteral("=") + i.value(); } if (!paramList.isEmpty()) attrs << QStringLiteral("a=fmtp:") + QByteArray::number(payload.id()) + QStringLiteral(" ") + paramList.join("; "); } sdp << QStringLiteral("m=%1 %2 RTP/AVP%3").arg(d->descriptionMedia, QString::number(localRtpPort), payloads); sdp << QStringLiteral("c=%1").arg(addressToSdp(localRtpAddress)); sdp += attrs; // transport for (const auto &candidate : d->transportCandidates) sdp << QStringLiteral("a=%1").arg(candidateToSdp(candidate)); if (!d->transportUser.isEmpty()) sdp << QStringLiteral("a=ice-ufrag:%1").arg(d->transportUser); if (!d->transportPassword.isEmpty()) sdp << QStringLiteral("a=ice-pwd:%1").arg(d->transportPassword); if (!d->transportFingerprint.isEmpty() && !d->transportFingerprintHash.isEmpty()) sdp << QStringLiteral("a=fingerprint:%1 %2").arg(d->transportFingerprintHash, formatFingerprint(d->transportFingerprint)); if (!d->transportFingerprintSetup.isEmpty()) sdp << QStringLiteral("a=setup:%1").arg(d->transportFingerprintSetup); return sdp.join("\r\n") + "\r\n"; } /// \endcond QXmppJingleIq::Reason::Reason() : m_type(None) { } /// Returns the reason's textual description. QString QXmppJingleIq::Reason::text() const { return m_text; } /// Sets the reason's textual description. void QXmppJingleIq::Reason::setText(const QString &text) { m_text = text; } /// Gets the reason's type. QXmppJingleIq::Reason::Type QXmppJingleIq::Reason::type() const { return m_type; } /// Sets the reason's type. void QXmppJingleIq::Reason::setType(QXmppJingleIq::Reason::Type type) { m_type = type; } /// \cond void QXmppJingleIq::Reason::parse(const QDomElement &element) { m_text = element.firstChildElement(QStringLiteral("text")).text(); for (int i = AlternativeSession; i <= UnsupportedTransports; i++) { if (!element.firstChildElement(jingle_reasons[i]).isNull()) { m_type = static_cast(i); break; } } } void QXmppJingleIq::Reason::toXml(QXmlStreamWriter *writer) const { if (m_type < AlternativeSession || m_type > UnsupportedTransports) return; writer->writeStartElement(QStringLiteral("reason")); if (!m_text.isEmpty()) helperToXmlAddTextElement(writer, QStringLiteral("text"), m_text); writer->writeEmptyElement(jingle_reasons[m_type]); writer->writeEndElement(); } /// \endcond class QXmppJingleIqPrivate : public QSharedData { public: QXmppJingleIqPrivate(); QXmppJingleIq::Action action; QString initiator; QString responder; QString sid; QList contents; QXmppJingleIq::Reason reason; bool ringing; }; QXmppJingleIqPrivate::QXmppJingleIqPrivate() : action(QXmppJingleIq::ContentAccept), ringing(false) { } /// Constructs a QXmppJingleIq. QXmppJingleIq::QXmppJingleIq() : d(new QXmppJingleIqPrivate()) { } /// Constructs a copy of other. /// /// \param other QXmppJingleIq::QXmppJingleIq(const QXmppJingleIq &other) : QXmppIq(other), d(other.d) { } QXmppJingleIq::~QXmppJingleIq() { } /// Assigns the other Jingle IQ to this one. /// /// \param other QXmppJingleIq &QXmppJingleIq::operator=(const QXmppJingleIq &other) { d = other.d; return *this; } /// Returns the Jingle IQ's action. QXmppJingleIq::Action QXmppJingleIq::action() const { return d->action; } /// Sets the Jingle IQ's action. /// /// \param action void QXmppJingleIq::setAction(QXmppJingleIq::Action action) { d->action = action; } /// /// Adds an element to the IQ's content elements. /// /// \since QXmpp 0.9.2 /// void QXmppJingleIq::addContent(const QXmppJingleIq::Content &content) { d->contents << content; } /// /// Returns the IQ's content elements. /// /// \since QXmpp 0.9.2 /// QList QXmppJingleIq::contents() const { return d->contents; } /// /// Sets the IQ's content elements. /// /// \since QXmpp 0.9.2 /// void QXmppJingleIq::setContents(const QList &contents) { d->contents = contents; } /// Returns the session initiator. QString QXmppJingleIq::initiator() const { return d->initiator; } /// Sets the session initiator. /// /// \param initiator void QXmppJingleIq::setInitiator(const QString &initiator) { d->initiator = initiator; } /// Returns a reference to the IQ's reason element. QXmppJingleIq::Reason &QXmppJingleIq::reason() { return d->reason; } /// Returns a const reference to the IQ's reason element. const QXmppJingleIq::Reason &QXmppJingleIq::reason() const { return d->reason; } /// Returns the session responder. QString QXmppJingleIq::responder() const { return d->responder; } /// Sets the session responder. /// /// \param responder void QXmppJingleIq::setResponder(const QString &responder) { d->responder = responder; } /// Returns true if the call is ringing. bool QXmppJingleIq::ringing() const { return d->ringing; } /// Set to true if the call is ringing. /// /// \param ringing void QXmppJingleIq::setRinging(bool ringing) { d->ringing = ringing; } /// Returns the session ID. QString QXmppJingleIq::sid() const { return d->sid; } /// Sets the session ID. /// /// \param sid void QXmppJingleIq::setSid(const QString &sid) { d->sid = sid; } /// \cond bool QXmppJingleIq::isJingleIq(const QDomElement &element) { QDomElement jingleElement = element.firstChildElement(QStringLiteral("jingle")); return (jingleElement.namespaceURI() == ns_jingle); } void QXmppJingleIq::parseElementFromChild(const QDomElement &element) { QDomElement jingleElement = element.firstChildElement(QStringLiteral("jingle")); const QString action = jingleElement.attribute(QStringLiteral("action")); for (int i = ContentAccept; i <= TransportReplace; i++) { if (action == jingle_actions[i]) { d->action = static_cast(i); break; } } d->initiator = jingleElement.attribute(QStringLiteral("initiator")); d->responder = jingleElement.attribute(QStringLiteral("responder")); d->sid = jingleElement.attribute(QStringLiteral("sid")); // content d->contents.clear(); QDomElement contentElement = jingleElement.firstChildElement(QStringLiteral("content")); while (!contentElement.isNull()) { QXmppJingleIq::Content content; content.parse(contentElement); addContent(content); contentElement = contentElement.nextSiblingElement(QStringLiteral("content")); } QDomElement reasonElement = jingleElement.firstChildElement(QStringLiteral("reason")); d->reason.parse(reasonElement); // ringing QDomElement ringingElement = jingleElement.firstChildElement(QStringLiteral("ringing")); d->ringing = (ringingElement.namespaceURI() == ns_jingle_rtp_info); } void QXmppJingleIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("jingle")); writer->writeDefaultNamespace(ns_jingle); helperToXmlAddAttribute(writer, QStringLiteral("action"), jingle_actions[d->action]); helperToXmlAddAttribute(writer, QStringLiteral("initiator"), d->initiator); helperToXmlAddAttribute(writer, QStringLiteral("responder"), d->responder); helperToXmlAddAttribute(writer, QStringLiteral("sid"), d->sid); for (const auto &content : d->contents) content.toXml(writer); d->reason.toXml(writer); // ringing if (d->ringing) { writer->writeStartElement(QStringLiteral("ringing")); writer->writeDefaultNamespace(ns_jingle_rtp_info); writer->writeEndElement(); } writer->writeEndElement(); } /// \endcond class QXmppJingleCandidatePrivate : public QSharedData { public: QXmppJingleCandidatePrivate(); int component; QString foundation; int generation; QHostAddress host; QString id; int network; quint16 port; QString protocol; int priority; QXmppJingleCandidate::Type type; }; QXmppJingleCandidatePrivate::QXmppJingleCandidatePrivate() : component(0), generation(0), network(0), port(0), priority(0), type(QXmppJingleCandidate::HostType) { } /// Constructs an empty candidate. QXmppJingleCandidate::QXmppJingleCandidate() : d(new QXmppJingleCandidatePrivate()) { } /// Constructs a copy of other. /// /// \param other QXmppJingleCandidate::QXmppJingleCandidate(const QXmppJingleCandidate &other) : d(other.d) { } QXmppJingleCandidate::~QXmppJingleCandidate() { } /// Assigns the other candidate to this one. /// /// \param other QXmppJingleCandidate &QXmppJingleCandidate::operator=(const QXmppJingleCandidate &other) { d = other.d; return *this; } /// Returns the candidate's component ID. int QXmppJingleCandidate::component() const { return d->component; } /// Sets the candidates's component ID. /// /// \param component void QXmppJingleCandidate::setComponent(int component) { d->component = component; } /// /// Returns the candidate's foundation. /// /// \since QXmpp 0.9 /// QString QXmppJingleCandidate::foundation() const { return d->foundation; } /// /// Sets the candidate's foundation. /// /// \param foundation /// /// \since QXmpp 0.9 /// void QXmppJingleCandidate::setFoundation(const QString &foundation) { d->foundation = foundation; } /// /// Returns the candidate's generation. /// /// \since QXmpp 0.9 /// int QXmppJingleCandidate::generation() const { return d->generation; } /// /// Sets the candidate's generation. /// /// \param generation /// /// \since QXmpp 0.9 /// void QXmppJingleCandidate::setGeneration(int generation) { d->generation = generation; } /// Returns the candidate's host address. /// QHostAddress QXmppJingleCandidate::host() const { return d->host; } /// Sets the candidate's host address. /// /// \param host void QXmppJingleCandidate::setHost(const QHostAddress &host) { d->host = host; } /// Returns the candidate's unique identifier. /// QString QXmppJingleCandidate::id() const { return d->id; } /// Sets the candidate's unique identifier. /// /// \param id void QXmppJingleCandidate::setId(const QString &id) { d->id = id; } /// Returns the network index (starting at 0) the candidate is on. /// int QXmppJingleCandidate::network() const { return d->network; } /// Sets the network index (starting at 0) the candidate is on. /// /// \param network void QXmppJingleCandidate::setNetwork(int network) { d->network = network; } /// Returns the candidate's port number. /// quint16 QXmppJingleCandidate::port() const { return d->port; } /// Sets the candidate's port number. /// /// \param port void QXmppJingleCandidate::setPort(quint16 port) { d->port = port; } /// Returns the candidate's priority. /// int QXmppJingleCandidate::priority() const { return d->priority; } /// Sets the candidate's priority. /// /// \param priority void QXmppJingleCandidate::setPriority(int priority) { d->priority = priority; } /// Returns the candidate's protocol (e.g. "udp"). /// QString QXmppJingleCandidate::protocol() const { return d->protocol; } /// Sets the candidate's protocol (e.g. "udp"). /// /// \param protocol void QXmppJingleCandidate::setProtocol(const QString &protocol) { d->protocol = protocol; } /// Returns the candidate type (e.g. "host"). /// QXmppJingleCandidate::Type QXmppJingleCandidate::type() const { return d->type; } /// Sets the candidate type (e.g. "host"). /// /// \param type void QXmppJingleCandidate::setType(QXmppJingleCandidate::Type type) { d->type = type; } /// Returns true if the host address or port are empty. /// bool QXmppJingleCandidate::isNull() const { return d->host.isNull() || !d->port; } /// \cond void QXmppJingleCandidate::parse(const QDomElement &element) { d->component = element.attribute(QStringLiteral("component")).toInt(); d->foundation = element.attribute(QStringLiteral("foundation")); d->generation = element.attribute(QStringLiteral("generation")).toInt(); d->host = QHostAddress(element.attribute(QStringLiteral("ip"))); d->id = element.attribute(QStringLiteral("id")); d->network = element.attribute(QStringLiteral("network")).toInt(); d->port = element.attribute(QStringLiteral("port")).toInt(); d->priority = element.attribute(QStringLiteral("priority")).toInt(); d->protocol = element.attribute(QStringLiteral("protocol")); d->type = typeFromString(element.attribute(QStringLiteral("type"))); } void QXmppJingleCandidate::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("candidate")); helperToXmlAddAttribute(writer, QStringLiteral("component"), QString::number(d->component)); helperToXmlAddAttribute(writer, QStringLiteral("foundation"), d->foundation); helperToXmlAddAttribute(writer, QStringLiteral("generation"), QString::number(d->generation)); helperToXmlAddAttribute(writer, QStringLiteral("id"), d->id); helperToXmlAddAttribute(writer, QStringLiteral("ip"), d->host.toString()); helperToXmlAddAttribute(writer, QStringLiteral("network"), QString::number(d->network)); helperToXmlAddAttribute(writer, QStringLiteral("port"), QString::number(d->port)); helperToXmlAddAttribute(writer, QStringLiteral("priority"), QString::number(d->priority)); helperToXmlAddAttribute(writer, QStringLiteral("protocol"), d->protocol); helperToXmlAddAttribute(writer, QStringLiteral("type"), typeToString(d->type)); writer->writeEndElement(); } QXmppJingleCandidate::Type QXmppJingleCandidate::typeFromString(const QString &typeStr, bool *ok) { QXmppJingleCandidate::Type type; if (typeStr == QStringLiteral("host")) type = HostType; else if (typeStr == QStringLiteral("prflx")) type = PeerReflexiveType; else if (typeStr == QStringLiteral("srflx")) type = ServerReflexiveType; else if (typeStr == QStringLiteral("relay")) type = RelayedType; else { qWarning() << "Unknown candidate type" << typeStr; if (ok) *ok = false; return HostType; } if (ok) *ok = true; return type; } QString QXmppJingleCandidate::typeToString(QXmppJingleCandidate::Type type) { QString typeStr; switch (type) { case HostType: typeStr = QStringLiteral("host"); break; case PeerReflexiveType: typeStr = QStringLiteral("prflx"); break; case ServerReflexiveType: typeStr = QStringLiteral("srflx"); break; case RelayedType: typeStr = QStringLiteral("relay"); break; } return typeStr; } /// \endcond class QXmppJinglePayloadTypePrivate : public QSharedData { public: QXmppJinglePayloadTypePrivate(); unsigned char channels; unsigned int clockrate; unsigned char id; unsigned int maxptime; QString name; QMap parameters; unsigned int ptime; }; QXmppJinglePayloadTypePrivate::QXmppJinglePayloadTypePrivate() : channels(1), clockrate(0), id(0), maxptime(0), ptime(0) { } QXmppJinglePayloadType::QXmppJinglePayloadType() : d(new QXmppJinglePayloadTypePrivate()) { } /// Constructs a copy of other. /// /// \param other QXmppJinglePayloadType::QXmppJinglePayloadType(const QXmppJinglePayloadType &other) : d(other.d) { } QXmppJinglePayloadType::~QXmppJinglePayloadType() { } /// Returns the number of channels (e.g. 1 for mono, 2 for stereo). /// unsigned char QXmppJinglePayloadType::channels() const { return d->channels; } /// Sets the number of channels (e.g. 1 for mono, 2 for stereo). /// /// \param channels void QXmppJinglePayloadType::setChannels(unsigned char channels) { d->channels = channels; } /// Returns the clockrate in Hz, i.e. the number of samples per second. /// unsigned int QXmppJinglePayloadType::clockrate() const { return d->clockrate; } /// Sets the clockrate in Hz, i.e. the number of samples per second. /// /// \param clockrate void QXmppJinglePayloadType::setClockrate(unsigned int clockrate) { d->clockrate = clockrate; } /// Returns the payload type identifier. /// unsigned char QXmppJinglePayloadType::id() const { return d->id; } /// Sets the payload type identifier. /// void QXmppJinglePayloadType::setId(unsigned char id) { Q_ASSERT(id <= 127); d->id = id; } /// Returns the maximum packet time in milliseconds. /// unsigned int QXmppJinglePayloadType::maxptime() const { return d->maxptime; } /// Sets the maximum packet type in milliseconds. /// /// \param maxptime void QXmppJinglePayloadType::setMaxptime(unsigned int maxptime) { d->maxptime = maxptime; } /// Returns the payload type name. /// QString QXmppJinglePayloadType::name() const { return d->name; } /// Sets the payload type name. /// /// \param name void QXmppJinglePayloadType::setName(const QString &name) { d->name = name; } /// Returns the payload parameters. QMap QXmppJinglePayloadType::parameters() const { return d->parameters; } /// Sets the payload parameters. void QXmppJinglePayloadType::setParameters(const QMap ¶meters) { d->parameters = parameters; } /// Returns the packet time in milliseconds (20 by default). /// unsigned int QXmppJinglePayloadType::ptime() const { return d->ptime ? d->ptime : 20; } /// Sets the packet time in milliseconds (20 by default). /// /// \param ptime void QXmppJinglePayloadType::setPtime(unsigned int ptime) { d->ptime = ptime; } /// \cond void QXmppJinglePayloadType::parse(const QDomElement &element) { d->id = element.attribute(QStringLiteral("id")).toInt(); d->name = element.attribute(QStringLiteral("name")); d->channels = element.attribute(QStringLiteral("channels")).toInt(); if (!d->channels) d->channels = 1; d->clockrate = element.attribute(QStringLiteral("clockrate")).toInt(); d->maxptime = element.attribute(QStringLiteral("maxptime")).toInt(); d->ptime = element.attribute(QStringLiteral("ptime")).toInt(); QDomElement child = element.firstChildElement(QStringLiteral("parameter")); while (!child.isNull()) { d->parameters.insert(child.attribute(QStringLiteral("name")), child.attribute(QStringLiteral("value"))); child = child.nextSiblingElement(QStringLiteral("parameter")); } } void QXmppJinglePayloadType::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("payload-type")); helperToXmlAddAttribute(writer, QStringLiteral("id"), QString::number(d->id)); helperToXmlAddAttribute(writer, QStringLiteral("name"), d->name); if (d->channels > 1) helperToXmlAddAttribute(writer, QStringLiteral("channels"), QString::number(d->channels)); if (d->clockrate > 0) helperToXmlAddAttribute(writer, QStringLiteral("clockrate"), QString::number(d->clockrate)); if (d->maxptime > 0) helperToXmlAddAttribute(writer, QStringLiteral("maxptime"), QString::number(d->maxptime)); if (d->ptime > 0) helperToXmlAddAttribute(writer, QStringLiteral("ptime"), QString::number(d->ptime)); for (const auto &key : d->parameters.keys()) { writer->writeStartElement(QStringLiteral("parameter")); writer->writeAttribute(QStringLiteral("name"), key); writer->writeAttribute(QStringLiteral("value"), d->parameters.value(key)); writer->writeEndElement(); } writer->writeEndElement(); } /// \endcond /// Assigns the other payload type to this one. /// /// \param other QXmppJinglePayloadType &QXmppJinglePayloadType::operator=(const QXmppJinglePayloadType &other) { d = other.d; return *this; } /// Returns true if this QXmppJinglePayloadType and \a other refer to the same payload type. /// /// \param other bool QXmppJinglePayloadType::operator==(const QXmppJinglePayloadType &other) const { // FIXME : what to do with m_ptime and m_maxptime? if (d->id <= 95) return other.d->id == d->id && other.d->clockrate == d->clockrate; else return other.d->channels == d->channels && other.d->clockrate == d->clockrate && other.d->name.toLower() == d->name.toLower(); } qxmpp-1.4.0/src/base/QXmppJingleIq.h000066400000000000000000000216611402370562100172310ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPJINGLEIQ_H #define QXMPPJINGLEIQ_H #include "QXmppIq.h" #include class QXmppJingleCandidatePrivate; class QXmppJingleIqContentPrivate; class QXmppJingleIqPrivate; class QXmppJinglePayloadTypePrivate; /// \brief The QXmppJinglePayloadType class represents a payload type /// as specified by \xep{0167}: Jingle RTP Sessions and RFC 5245. /// class QXMPP_EXPORT QXmppJinglePayloadType { public: QXmppJinglePayloadType(); QXmppJinglePayloadType(const QXmppJinglePayloadType &other); ~QXmppJinglePayloadType(); unsigned char channels() const; void setChannels(unsigned char channels); unsigned int clockrate() const; void setClockrate(unsigned int clockrate); unsigned char id() const; void setId(unsigned char id); unsigned int maxptime() const; void setMaxptime(unsigned int maxptime); QString name() const; void setName(const QString &name); QMap parameters() const; void setParameters(const QMap ¶meters); unsigned int ptime() const; void setPtime(unsigned int ptime); /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *writer) const; /// \endcond QXmppJinglePayloadType &operator=(const QXmppJinglePayloadType &other); bool operator==(const QXmppJinglePayloadType &other) const; private: QSharedDataPointer d; }; /// \brief The QXmppJingleCandidate class represents a transport candidate /// as specified by \xep{0176}: Jingle ICE-UDP Transport Method. /// class QXMPP_EXPORT QXmppJingleCandidate { public: /// This enum is used to describe a candidate's type. enum Type { HostType, ///< Host candidate, a local address/port. PeerReflexiveType, ///< Peer-reflexive candidate, ///< the address/port as seen from the peer. ServerReflexiveType, ///< Server-reflexive candidate, ///< the address/port as seen by the STUN server RelayedType ///< Relayed candidate, a candidate from ///< a TURN relay. }; QXmppJingleCandidate(); QXmppJingleCandidate(const QXmppJingleCandidate &other); ~QXmppJingleCandidate(); QXmppJingleCandidate &operator=(const QXmppJingleCandidate &other); int component() const; void setComponent(int component); QString foundation() const; void setFoundation(const QString &foundation); int generation() const; void setGeneration(int generation); QHostAddress host() const; void setHost(const QHostAddress &host); QString id() const; void setId(const QString &id); int network() const; void setNetwork(int network); quint16 port() const; void setPort(quint16 port); int priority() const; void setPriority(int priority); QString protocol() const; void setProtocol(const QString &protocol); QXmppJingleCandidate::Type type() const; void setType(QXmppJingleCandidate::Type); bool isNull() const; /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *writer) const; static QXmppJingleCandidate::Type typeFromString(const QString &typeStr, bool *ok = nullptr); static QString typeToString(QXmppJingleCandidate::Type type); /// \endcond private: QSharedDataPointer d; }; /// \brief The QXmppJingleIq class represents an IQ used for initiating media /// sessions as specified by \xep{0166}: Jingle. /// /// \ingroup Stanzas class QXMPP_EXPORT QXmppJingleIq : public QXmppIq { public: /// This enum is used to describe a Jingle action. enum Action { ContentAccept, ContentAdd, ContentModify, ContentReject, ContentRemove, DescriptionInfo, SecurityInfo, SessionAccept, SessionInfo, SessionInitiate, SessionTerminate, TransportAccept, TransportInfo, TransportReject, TransportReplace }; /// \internal /// /// The QXmppJingleIq::Content class represents the "content" element of a /// QXmppJingleIq. class QXMPP_EXPORT Content { public: Content(); Content(const QXmppJingleIq::Content &other); ~Content(); Content &operator=(const Content &other); QString creator() const; void setCreator(const QString &creator); QString name() const; void setName(const QString &name); QString senders() const; void setSenders(const QString &senders); // XEP-0167: Jingle RTP Sessions QString descriptionMedia() const; void setDescriptionMedia(const QString &media); quint32 descriptionSsrc() const; void setDescriptionSsrc(quint32 ssrc); void addPayloadType(const QXmppJinglePayloadType &payload); QList payloadTypes() const; void setPayloadTypes(const QList &payloadTypes); void addTransportCandidate(const QXmppJingleCandidate &candidate); QList transportCandidates() const; void setTransportCandidates(const QList &candidates); QString transportUser() const; void setTransportUser(const QString &user); QString transportPassword() const; void setTransportPassword(const QString &password); // XEP-0320: Use of DTLS-SRTP in Jingle Sessions QByteArray transportFingerprint() const; void setTransportFingerprint(const QByteArray &fingerprint); QString transportFingerprintHash() const; void setTransportFingerprintHash(const QString &hash); QString transportFingerprintSetup() const; void setTransportFingerprintSetup(const QString &setup); /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *writer) const; bool parseSdp(const QString &sdp); QString toSdp() const; /// \endcond private: QSharedDataPointer d; }; /// \internal /// /// The QXmppJingleIq::Reason class represents the "reason" element of a /// QXmppJingleIq. class QXMPP_EXPORT Reason { public: /// This enum is used to describe a reason's type. enum Type { None, AlternativeSession, Busy, Cancel, ConnectivityError, Decline, Expired, FailedApplication, FailedTransport, GeneralError, Gone, IncompatibleParameters, MediaError, SecurityError, Success, Timeout, UnsupportedApplications, UnsupportedTransports }; Reason(); QString text() const; void setText(const QString &text); Type type() const; void setType(Type type); /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *writer) const; /// \endcond private: QString m_text; Type m_type; }; QXmppJingleIq(); QXmppJingleIq(const QXmppJingleIq &other); ~QXmppJingleIq() override; QXmppJingleIq &operator=(const QXmppJingleIq &other); Action action() const; void setAction(Action action); void addContent(const Content &content); QList contents() const; void setContents(const QList &contents); QString initiator() const; void setInitiator(const QString &initiator); Reason &reason(); const Reason &reason() const; QString responder() const; void setResponder(const QString &responder); // XEP-0167: Jingle RTP Sessions bool ringing() const; void setRinging(bool ringing); QString sid() const; void setSid(const QString &sid); /// \cond static bool isJingleIq(const QDomElement &element); /// \endcond protected: /// \cond void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: QSharedDataPointer d; }; #endif qxmpp-1.4.0/src/base/QXmppLogger.cpp000066400000000000000000000135411402370562100172770ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Manjeet Dahiya * Jeremy Lainé * * 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 "QXmppLogger.h" #include #include #include #include #include #include QXmppLogger *QXmppLogger::m_logger = nullptr; static const char *typeName(QXmppLogger::MessageType type) { switch (type) { case QXmppLogger::DebugMessage: return "DEBUG"; case QXmppLogger::InformationMessage: return "INFO"; case QXmppLogger::WarningMessage: return "WARNING"; case QXmppLogger::ReceivedMessage: return "RECEIVED"; case QXmppLogger::SentMessage: return "SENT"; default: return ""; } } static QString formatted(QXmppLogger::MessageType type, const QString &text) { return QDateTime::currentDateTime().toString() + " " + QString::fromLatin1(typeName(type)) + " " + text; } static void relaySignals(QXmppLoggable *from, QXmppLoggable *to) { QObject::connect(from, &QXmppLoggable::logMessage, to, &QXmppLoggable::logMessage); QObject::connect(from, &QXmppLoggable::setGauge, to, &QXmppLoggable::setGauge); QObject::connect(from, &QXmppLoggable::updateCounter, to, &QXmppLoggable::updateCounter); } /// Constructs a new QXmppLoggable. /// /// \param parent QXmppLoggable::QXmppLoggable(QObject *parent) : QObject(parent) { auto *logParent = qobject_cast(parent); if (logParent) { relaySignals(this, logParent); } } /// \cond void QXmppLoggable::childEvent(QChildEvent *event) { auto *child = qobject_cast(event->child()); if (!child) return; if (event->added()) { relaySignals(child, this); } else if (event->removed()) { disconnect(child, &QXmppLoggable::logMessage, this, &QXmppLoggable::logMessage); disconnect(child, &QXmppLoggable::setGauge, this, &QXmppLoggable::setGauge); disconnect(child, &QXmppLoggable::updateCounter, this, &QXmppLoggable::updateCounter); } } /// \endcond class QXmppLoggerPrivate { public: QXmppLoggerPrivate(); QXmppLogger::LoggingType loggingType; QFile *logFile; QString logFilePath; QXmppLogger::MessageTypes messageTypes; }; QXmppLoggerPrivate::QXmppLoggerPrivate() : loggingType(QXmppLogger::NoLogging), logFile(nullptr), logFilePath("QXmppClientLog.log"), messageTypes(QXmppLogger::AnyMessage) { } /// Constructs a new QXmppLogger. /// /// \param parent QXmppLogger::QXmppLogger(QObject *parent) : QObject(parent), d(new QXmppLoggerPrivate()) { // make it possible to pass QXmppLogger::MessageType between threads qRegisterMetaType("QXmppLogger::MessageType"); } QXmppLogger::~QXmppLogger() { delete d; } /// Returns the default logger. /// QXmppLogger *QXmppLogger::getLogger() { if (!m_logger) m_logger = new QXmppLogger(); return m_logger; } QXmppLogger::LoggingType QXmppLogger::loggingType() { return d->loggingType; } /// Sets the handler for logging messages. /// /// \param type void QXmppLogger::setLoggingType(QXmppLogger::LoggingType type) { if (d->loggingType != type) { d->loggingType = type; reopen(); } } QXmppLogger::MessageTypes QXmppLogger::messageTypes() { return d->messageTypes; } /// Sets the types of messages to log. /// /// \param types void QXmppLogger::setMessageTypes(QXmppLogger::MessageTypes types) { d->messageTypes = types; } /// Add a logging message. /// /// \param type /// \param text void QXmppLogger::log(QXmppLogger::MessageType type, const QString &text) { // filter messages if (!d->messageTypes.testFlag(type)) return; switch (d->loggingType) { case QXmppLogger::FileLogging: if (!d->logFile) { d->logFile = new QFile(d->logFilePath); d->logFile->open(QIODevice::WriteOnly | QIODevice::Append); } QTextStream(d->logFile) << formatted(type, text) << "\n"; break; case QXmppLogger::StdoutLogging: std::cout << qPrintable(formatted(type, text)) << std::endl; break; case QXmppLogger::SignalLogging: emit message(type, text); break; default: break; } } /// Sets the given \a gauge to \a value. /// /// NOTE: the base implementation does nothing. void QXmppLogger::setGauge(const QString &gauge, double value) { Q_UNUSED(gauge); Q_UNUSED(value); } /// Updates the given \a counter by \a amount. /// /// NOTE: the base implementation does nothing. void QXmppLogger::updateCounter(const QString &counter, qint64 amount) { Q_UNUSED(counter); Q_UNUSED(amount); } QString QXmppLogger::logFilePath() { return d->logFilePath; } /// Sets the path to which logging messages should be written. /// /// \param path /// /// \sa setLoggingType() void QXmppLogger::setLogFilePath(const QString &path) { if (d->logFilePath != path) { d->logFilePath = path; reopen(); } } /// If logging to a file, causes the file to be re-opened. /// void QXmppLogger::reopen() { if (d->logFile) { delete d->logFile; d->logFile = nullptr; } } qxmpp-1.4.0/src/base/QXmppLogger.h000066400000000000000000000131011402370562100167340ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * Jeremy Lainé * * 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 QXMPPLOGGER_H #define QXMPPLOGGER_H #include "QXmppGlobal.h" #include #ifdef QXMPP_LOGGABLE_TRACE #define qxmpp_loggable_trace(x) QString("%1(0x%2) %3").arg(metaObject()->className(), QString::number(reinterpret_cast(this), 16), x) #else #define qxmpp_loggable_trace(x) (x) #endif class QXmppLoggerPrivate; /// /// \brief The QXmppLogger class represents a sink for logging messages. /// /// \ingroup Core /// class QXMPP_EXPORT QXmppLogger : public QObject { Q_OBJECT Q_FLAGS(MessageType MessageTypes) /// The path to which logging messages should be written Q_PROPERTY(QString logFilePath READ logFilePath WRITE setLogFilePath) /// The handler for logging messages Q_PROPERTY(LoggingType loggingType READ loggingType WRITE setLoggingType) /// The types of messages to log Q_PROPERTY(MessageTypes messageTypes READ messageTypes WRITE setMessageTypes) public: /// This enum describes how log message are handled. enum LoggingType { NoLogging = 0, ///< Log messages are discarded FileLogging = 1, ///< Log messages are written to a file StdoutLogging = 2, ///< Log messages are written to the standard output SignalLogging = 4 ///< Log messages are emitted as a signal }; Q_ENUM(LoggingType) /// This enum describes a type of log message. enum MessageType { NoMessage = 0, ///< No message type DebugMessage = 1, ///< Debugging message InformationMessage = 2, ///< Informational message WarningMessage = 4, ///< Warning message ReceivedMessage = 8, ///< Message received from server SentMessage = 16, ///< Message sent to server AnyMessage = 31 ///< Any message type }; Q_DECLARE_FLAGS(MessageTypes, MessageType) QXmppLogger(QObject *parent = nullptr); ~QXmppLogger() override; static QXmppLogger *getLogger(); // documentation needs to be here, see https://stackoverflow.com/questions/49192523/ /// Returns the handler for logging messages. QXmppLogger::LoggingType loggingType(); void setLoggingType(QXmppLogger::LoggingType type); // documentation needs to be here, see https://stackoverflow.com/questions/49192523/ /// /// Returns the path to which logging messages should be written. /// /// \sa loggingType() /// QString logFilePath(); void setLogFilePath(const QString &path); // documentation needs to be here, see https://stackoverflow.com/questions/49192523/ /// Returns the types of messages to log. QXmppLogger::MessageTypes messageTypes(); void setMessageTypes(QXmppLogger::MessageTypes types); public Q_SLOTS: virtual void setGauge(const QString &gauge, double value); virtual void updateCounter(const QString &counter, qint64 amount); void log(QXmppLogger::MessageType type, const QString &text); void reopen(); Q_SIGNALS: /// This signal is emitted whenever a log message is received. void message(QXmppLogger::MessageType type, const QString &text); private: static QXmppLogger *m_logger; QXmppLoggerPrivate *d; }; /// \brief The QXmppLoggable class represents a source of logging messages. /// /// \ingroup Core class QXMPP_EXPORT QXmppLoggable : public QObject { Q_OBJECT public: QXmppLoggable(QObject *parent = nullptr); protected: /// \cond void childEvent(QChildEvent *event) override; /// \endcond /// Logs a debugging message. /// /// \param message void debug(const QString &message) { emit logMessage(QXmppLogger::DebugMessage, qxmpp_loggable_trace(message)); } /// Logs an informational message. /// /// \param message void info(const QString &message) { emit logMessage(QXmppLogger::InformationMessage, qxmpp_loggable_trace(message)); } /// Logs a warning message. /// /// \param message void warning(const QString &message) { emit logMessage(QXmppLogger::WarningMessage, qxmpp_loggable_trace(message)); } /// Logs a received packet. /// /// \param message void logReceived(const QString &message) { emit logMessage(QXmppLogger::ReceivedMessage, qxmpp_loggable_trace(message)); } /// Logs a sent packet. /// /// \param message void logSent(const QString &message) { emit logMessage(QXmppLogger::SentMessage, qxmpp_loggable_trace(message)); } Q_SIGNALS: /// Sets the given \a gauge to \a value. void setGauge(const QString &gauge, double value); /// This signal is emitted to send logging messages. void logMessage(QXmppLogger::MessageType type, const QString &msg); /// Updates the given \a counter by \a amount. void updateCounter(const QString &counter, qint64 amount = 1); }; Q_DECLARE_OPERATORS_FOR_FLAGS(QXmppLogger::MessageTypes) #endif // QXMPPLOGGER_H qxmpp-1.4.0/src/base/QXmppMamIq.cpp000066400000000000000000000141501402370562100170610ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Niels Ole Salscheider * * 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 "QXmppMamIq.h" #include "QXmppConstants_p.h" #include class QXmppMamQueryIqPrivate : public QSharedData { public: QXmppDataForm form; QXmppResultSetQuery resultSetQuery; QString node; QString queryId; }; QXmppMamQueryIq::QXmppMamQueryIq() : QXmppIq(QXmppIq::Set), d(new QXmppMamQueryIqPrivate) { } QXmppMamQueryIq::QXmppMamQueryIq(const QXmppMamQueryIq &) = default; QXmppMamQueryIq::~QXmppMamQueryIq() = default; QXmppMamQueryIq &QXmppMamQueryIq::operator=(const QXmppMamQueryIq &) = default; /// Returns the form that specifies the query. QXmppDataForm QXmppMamQueryIq::form() const { return d->form; } /// Sets the data form that specifies the query. /// /// \param form The data form. void QXmppMamQueryIq::setForm(const QXmppDataForm &form) { d->form = form; } /// Returns the result set query for result set management. QXmppResultSetQuery QXmppMamQueryIq::resultSetQuery() const { return d->resultSetQuery; } /// Sets the result set query for result set management. /// /// \param resultSetQuery The result set query. void QXmppMamQueryIq::setResultSetQuery(const QXmppResultSetQuery &resultSetQuery) { d->resultSetQuery = resultSetQuery; } /// Returns the node to query. QString QXmppMamQueryIq::node() const { return d->node; } /// Sets the node to query. /// /// \param node The node to query. void QXmppMamQueryIq::setNode(const QString &node) { d->node = node; } /// Returns the queryid that will be included in the results. QString QXmppMamQueryIq::queryId() const { return d->queryId; } /// Sets the queryid that will be included in the results. /// /// \param id The query id. void QXmppMamQueryIq::setQueryId(const QString &id) { d->queryId = id; } /// \cond bool QXmppMamQueryIq::isMamQueryIq(const QDomElement &element) { if (element.tagName() == QStringLiteral("iq")) { QDomElement queryElement = element.firstChildElement(QStringLiteral("query")); if (!queryElement.isNull() && queryElement.namespaceURI() == ns_mam) { return true; } } return false; } void QXmppMamQueryIq::parseElementFromChild(const QDomElement &element) { QDomElement queryElement = element.firstChildElement(QStringLiteral("query")); d->node = queryElement.attribute(QStringLiteral("node")); d->queryId = queryElement.attribute(QStringLiteral("queryId")); QDomElement resultSetElement = queryElement.firstChildElement(QStringLiteral("set")); if (!resultSetElement.isNull()) { d->resultSetQuery.parse(resultSetElement); } QDomElement formElement = queryElement.firstChildElement(QStringLiteral("x")); if (!formElement.isNull()) { d->form.parse(formElement); } } void QXmppMamQueryIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("query")); writer->writeDefaultNamespace(ns_mam); if (!d->node.isEmpty()) { writer->writeAttribute(QStringLiteral("node"), d->node); } if (!d->queryId.isEmpty()) { writer->writeAttribute(QStringLiteral("queryid"), d->queryId); } d->form.toXml(writer); d->resultSetQuery.toXml(writer); writer->writeEndElement(); } /// \endcond class QXmppMamResultIqPrivate : public QSharedData { public: QXmppResultSetReply resultSetReply; bool complete; }; QXmppMamResultIq::QXmppMamResultIq() : d(new QXmppMamResultIqPrivate) { d->complete = false; } QXmppMamResultIq::QXmppMamResultIq(const QXmppMamResultIq &) = default; QXmppMamResultIq::~QXmppMamResultIq() = default; QXmppMamResultIq &QXmppMamResultIq::operator=(const QXmppMamResultIq &) = default; /// Returns the result set reply for result set management. QXmppResultSetReply QXmppMamResultIq::resultSetReply() const { return d->resultSetReply; } /// Sets the result set reply for result set management void QXmppMamResultIq::setResultSetReply(const QXmppResultSetReply &resultSetReply) { d->resultSetReply = resultSetReply; } /// Returns true if the results returned by the server are complete (not /// limited by the server). bool QXmppMamResultIq::complete() const { return d->complete; } /// Sets if the results returned by the server are complete (not limited by the /// server). void QXmppMamResultIq::setComplete(bool complete) { d->complete = complete; } /// \cond bool QXmppMamResultIq::isMamResultIq(const QDomElement &element) { if (element.tagName() == QStringLiteral("iq")) { QDomElement finElement = element.firstChildElement(QStringLiteral("fin")); if (!finElement.isNull() && finElement.namespaceURI() == ns_mam) { return true; } } return false; } void QXmppMamResultIq::parseElementFromChild(const QDomElement &element) { QDomElement finElement = element.firstChildElement(QStringLiteral("fin")); d->complete = finElement.attribute(QStringLiteral("complete")) == QStringLiteral("true"); QDomElement resultSetElement = finElement.firstChildElement(QStringLiteral("set")); if (!resultSetElement.isNull()) { d->resultSetReply.parse(resultSetElement); } } void QXmppMamResultIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("fin")); writer->writeDefaultNamespace(ns_mam); if (d->complete) { writer->writeAttribute(QStringLiteral("complete"), QStringLiteral("true")); } d->resultSetReply.toXml(writer); writer->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/base/QXmppMamIq.h000066400000000000000000000053011402370562100165240ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Niels Ole Salscheider * * 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 QXMPPMAMIQ_H #define QXMPPMAMIQ_H #include "QXmppDataForm.h" #include "QXmppIq.h" #include "QXmppResultSet.h" #include class QXmppMamQueryIqPrivate; class QXmppMamResultIqPrivate; /// /// \brief The QXmppMamQueryIq class represents the query IQ for /// \xep{0313}: Message Archive Management. /// /// \ingroup Stanzas /// /// \since QXmpp 1.0 /// class QXmppMamQueryIq : public QXmppIq { public: QXmppMamQueryIq(); QXmppMamQueryIq(const QXmppMamQueryIq &); ~QXmppMamQueryIq(); QXmppMamQueryIq &operator=(const QXmppMamQueryIq &); QXmppDataForm form() const; void setForm(const QXmppDataForm &form); QXmppResultSetQuery resultSetQuery() const; void setResultSetQuery(const QXmppResultSetQuery &resultSetQuery); QString node() const; void setNode(const QString &node); QString queryId() const; void setQueryId(const QString &id); static bool isMamQueryIq(const QDomElement &element); protected: void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; private: QSharedDataPointer d; }; /// /// \brief The QXmppMamQueryIq class represents the result IQ for /// \xep{0313}: Message Archive Management. /// /// \ingroup Stanzas /// /// \since QXmpp 1.0 /// class QXmppMamResultIq : public QXmppIq { public: QXmppMamResultIq(); QXmppMamResultIq(const QXmppMamResultIq &); ~QXmppMamResultIq(); QXmppMamResultIq &operator=(const QXmppMamResultIq &); QXmppResultSetReply resultSetReply() const; void setResultSetReply(const QXmppResultSetReply &resultSetReply); bool complete() const; void setComplete(bool complete); static bool isMamResultIq(const QDomElement &element); protected: void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; private: QSharedDataPointer d; }; #endif qxmpp-1.4.0/src/base/QXmppMessage.cpp000066400000000000000000001224041402370562100174430ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Manjeet Dahiya * Jeremy Lainé * Linus Jahn * * 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 "QXmppMessage.h" #include "QXmppBitsOfBinaryDataList.h" #include "QXmppConstants_p.h" #include "QXmppMixInvitation.h" #include "QXmppUtils.h" #include #include #include #include #include static const QStringList CHAT_STATES = { QString(), QStringLiteral("active"), QStringLiteral("inactive"), QStringLiteral("gone"), QStringLiteral("composing"), QStringLiteral("paused") }; static const QStringList MESSAGE_TYPES = { QStringLiteral("error"), QStringLiteral("normal"), QStringLiteral("chat"), QStringLiteral("groupchat"), QStringLiteral("headline") }; static const QStringList MARKER_TYPES = { QString(), QStringLiteral("received"), QStringLiteral("displayed"), QStringLiteral("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 bool checkElement(const QDomElement &element, const QString &tagName, const QString &xmlns) { return element.tagName() == tagName && element.namespaceURI() == xmlns; } enum StampType { LegacyDelayedDelivery, // XEP-0091: Legacy Delayed Delivery DelayedDelivery // XEP-0203: Delayed Delivery }; class QXmppMessagePrivate : public QSharedData { public: QXmppMessagePrivate(); QString body; QString subject; QString thread; QString parentThread; QXmppMessage::Type type; // XEP-0066: Out of Band Data QString outOfBandUrl; // XEP-0071: XHTML-IM QString xhtml; // XEP-0085: Chat State Notifications QXmppMessage::State state; // XEP-0091: Legacy Delayed Delivery | XEP-0203: Delayed Delivery QDateTime stamp; StampType stampType; // XEP-0184: Message Delivery Receipts QString receiptId; bool receiptRequested; // XEP-0224: Attention bool attentionRequested; // XEP-0231: Bits of Binary QXmppBitsOfBinaryDataList bitsOfBinaryData; // XEP-0249: Direct MUC Invitations QString mucInvitationJid; QString mucInvitationPassword; QString mucInvitationReason; // XEP-0280: Message Carbons bool privatemsg; // XEP-0308: Last Message Correction QString replaceId; // XEP-0333: Chat Markers bool markable; QXmppMessage::Marker marker; QString markedId; QString markedThread; // XEP-0334: Message Processing Hints quint8 hints; // XEP-0359: Unique and Stable Stanza IDs QString stanzaId; QString stanzaIdBy; QString originId; // 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; QString spoilerHint; // XEP-0407: Mediated Information eXchange (MIX): Miscellaneous Capabilities std::optional mixInvitation; // XEP-0428: Fallback Indication bool isFallback; }; QXmppMessagePrivate::QXmppMessagePrivate() : type(QXmppMessage::Normal), state(QXmppMessage::None), stampType(DelayedDelivery), receiptRequested(false), attentionRequested(false), privatemsg(false), markable(false), marker(QXmppMessage::NoMarker), hints(0), isSpoiler(false), isFallback(false) { } /// Constructs a QXmppMessage. /// /// \param from /// \param to /// \param body /// \param thread QXmppMessage::QXmppMessage(const QString &from, const QString &to, const QString &body, const QString &thread) : QXmppStanza(from, to), d(new QXmppMessagePrivate) { d->type = Chat; d->body = body; d->thread = thread; } /// Constructs a copy of \a other. QXmppMessage::QXmppMessage(const QXmppMessage &other) = default; QXmppMessage::~QXmppMessage() = default; /// Assigns \a other to this message. QXmppMessage &QXmppMessage::operator=(const QXmppMessage &other) = default; /// /// Indicates if the QXmppStanza is a stanza in the XMPP sense (i. e. a message, /// iq or presence) /// /// \since QXmpp 1.0 /// bool QXmppMessage::isXmppStanza() const { return true; } /// Returns the message's body. QString QXmppMessage::body() const { return d->body; } /// Sets the message's body. /// /// \param body void QXmppMessage::setBody(const QString &body) { d->body = body; } /// Returns the message's type. QXmppMessage::Type QXmppMessage::type() const { return d->type; } /// Sets the message's type. /// /// \param type void QXmppMessage::setType(QXmppMessage::Type type) { d->type = type; } /// Returns the message's subject. QString QXmppMessage::subject() const { return d->subject; } /// Sets the message's subject. /// /// \param subject void QXmppMessage::setSubject(const QString &subject) { d->subject = subject; } /// Returns the message's thread. QString QXmppMessage::thread() const { return d->thread; } /// Sets the message's thread. /// /// \param thread void QXmppMessage::setThread(const QString &thread) { d->thread = thread; } /// /// Returns the optional parent thread of this message. /// /// The possibility to create child threads was added in RFC6121. /// /// \since QXmpp 1.3 /// QString QXmppMessage::parentThread() const { return d->parentThread; } /// /// Sets the optional parent thread of this message. /// /// The possibility to create child threads was added in RFC6121. /// /// \since QXmpp 1.3 /// void QXmppMessage::setParentThread(const QString &parent) { d->parentThread = parent; } /// /// Returns a possibly attached URL from \xep{0066}: Out of Band Data /// /// \since QXmpp 1.0 /// QString QXmppMessage::outOfBandUrl() const { return d->outOfBandUrl; } /// /// Sets the attached URL for \xep{0066}: Out of Band Data /// /// \since QXmpp 1.0 /// void QXmppMessage::setOutOfBandUrl(const QString &url) { d->outOfBandUrl = url; } /// /// Returns the message's XHTML body as defined by \xep{0071}: XHTML-IM. /// /// \since QXmpp 0.6.2 /// QString QXmppMessage::xhtml() const { return d->xhtml; } /// /// Sets the message's XHTML body as defined by \xep{0071}: XHTML-IM. /// /// \since QXmpp 0.6.2 /// void QXmppMessage::setXhtml(const QString &xhtml) { d->xhtml = xhtml; } /// /// Returns the the chat state notification according to \xep{0085}: Chat State /// Notifications. /// /// \since QXmpp 0.2 /// QXmppMessage::State QXmppMessage::state() const { return d->state; } /// /// Sets the the chat state notification according to \xep{0085}: Chat State /// Notifications. /// /// \since QXmpp 0.2 /// void QXmppMessage::setState(QXmppMessage::State state) { d->state = state; } /// /// Returns the optional timestamp of the message specified using \xep{0093}: /// Legacy Delayed Delivery or using \xep{0203}: Delayed Delivery (preferred). /// /// \since QXmpp 0.2 /// QDateTime QXmppMessage::stamp() const { return d->stamp; } /// /// Sets the message's timestamp without modifying the type of the stamp /// (\xep{0093}: Legacy Delayed Delivery or \xep{0203}: Delayed Delivery). /// /// By default messages are constructed with the new delayed delivery XEP, but /// parsed messages keep their type. /// /// \since QXmpp 0.2 /// void QXmppMessage::setStamp(const QDateTime &stamp) { d->stamp = stamp; } /// /// Returns true if a delivery receipt is requested, as defined by \xep{0184}: /// Message Delivery Receipts. /// /// \since QXmpp 0.4 /// bool QXmppMessage::isReceiptRequested() const { return d->receiptRequested; } /// /// Sets whether a delivery receipt is requested, as defined by \xep{0184}: /// Message Delivery Receipts. /// /// \since QXmpp 0.4 /// void QXmppMessage::setReceiptRequested(bool requested) { d->receiptRequested = requested; if (requested && id().isEmpty()) generateAndSetNextId(); } /// /// If this message is a delivery receipt, returns the ID of the original /// message. /// /// \since QXmpp 0.4 /// QString QXmppMessage::receiptId() const { return d->receiptId; } /// /// Make this message a delivery receipt for the message with the given \a id. /// /// \since QXmpp 0.4 /// void QXmppMessage::setReceiptId(const QString &id) { d->receiptId = id; } /// /// Returns true if the user's attention is requested, as defined by \xep{0224}: /// Attention. /// /// \since QXmpp 0.4 /// bool QXmppMessage::isAttentionRequested() const { return d->attentionRequested; } /// /// Sets whether the user's attention is requested, as defined by \xep{0224}: /// Attention. /// /// \param requested Whether to request attention (true) or not (false) /// /// \since QXmpp 0.4 /// void QXmppMessage::setAttentionRequested(bool requested) { d->attentionRequested = requested; } /// /// Returns a list of data packages attached using \xep{0231}: Bits of Binary. /// /// This could be used to resolve \c cid: URIs found in the X-HTML body. /// /// \since QXmpp 1.2 /// QXmppBitsOfBinaryDataList QXmppMessage::bitsOfBinaryData() const { return d->bitsOfBinaryData; } /// /// Returns a list of data attached using \xep{0231}: Bits of Binary. /// /// This could be used to resolve \c cid: URIs found in the X-HTML body. /// /// \since QXmpp 1.2 /// QXmppBitsOfBinaryDataList &QXmppMessage::bitsOfBinaryData() { return d->bitsOfBinaryData; } /// /// Sets a list of \xep{0231}: Bits of Binary attachments to be included. /// /// \since QXmpp 1.2 /// void QXmppMessage::setBitsOfBinaryData(const QXmppBitsOfBinaryDataList &bitsOfBinaryData) { d->bitsOfBinaryData = bitsOfBinaryData; } /// /// Returns whether the given text is a '/me command' as defined in \xep{0245}: /// The /me Command. /// /// \since QXmpp 1.3 /// bool QXmppMessage::isSlashMeCommand(const QString &body) { return body.startsWith(QStringLiteral("/me ")); } /// /// Returns whether the body of the message is a '/me command' as defined in /// \xep{0245}: The /me Command. /// /// \note If you want to check a custom string for the /me command, you can use /// the static version of this method. This can be helpful when checking user /// input before a message was sent. /// /// \since QXmpp 1.3 /// bool QXmppMessage::isSlashMeCommand() const { return isSlashMeCommand(d->body); } /// /// Returns the part of the body after the /me command. /// /// This cuts off '/me ' (with the space) from the body, in case the body /// starts with that. In case the body does not contain a /me command as /// defined in \xep{0245}: The /me Command, a null string is returned. /// /// This is useful when displaying the /me command correctly to the user. /// /// \since QXmpp 1.3 /// QString QXmppMessage::slashMeCommandText(const QString &body) { if (isSlashMeCommand(body)) return body.mid(4); return {}; } /// /// Returns the part of the body after the /me command. /// /// This cuts off '/me ' (with the space) from the body, in case the body /// starts with that. In case the body does not contain a /me command as /// defined in \xep{0245}: The /me Command, a null string is returned. /// /// This is useful when displaying the /me command correctly to the user. /// /// \since QXmpp 1.3 /// QString QXmppMessage::slashMeCommandText() const { return slashMeCommandText(d->body); } /// /// Returns the JID for a multi-user chat direct invitation as defined by /// \xep{0249}: Direct MUC Invitations. /// /// \since QXmpp 0.7.4 /// QString QXmppMessage::mucInvitationJid() const { return d->mucInvitationJid; } /// /// Sets the JID for a multi-user chat direct invitation as defined by /// \xep{0249}: Direct MUC Invitations. /// /// \since QXmpp 0.7.4 /// void QXmppMessage::setMucInvitationJid(const QString &jid) { d->mucInvitationJid = jid; } /// /// Returns the password for a multi-user chat direct invitation as defined by /// \xep{0249}: Direct MUC Invitations. /// /// \since QXmpp 0.7.4 /// QString QXmppMessage::mucInvitationPassword() const { return d->mucInvitationPassword; } /// /// Sets the \a password for a multi-user chat direct invitation as defined by /// \xep{0249}: Direct MUC Invitations. /// /// \since QXmpp 0.7.4 /// void QXmppMessage::setMucInvitationPassword(const QString &password) { d->mucInvitationPassword = password; } /// /// Returns the reason for a multi-user chat direct invitation as defined by /// \xep{0249}: Direct MUC Invitations. /// /// \since QXmpp 0.7.4 /// QString QXmppMessage::mucInvitationReason() const { return d->mucInvitationReason; } /// /// Sets the \a reason for a multi-user chat direct invitation as defined by /// \xep{0249}: Direct MUC Invitations. /// /// \since QXmpp 0.7.4 /// void QXmppMessage::setMucInvitationReason(const QString &reason) { d->mucInvitationReason = reason; } /// /// Returns if the message is marked with a <private/> tag, in which case /// it will not be forwarded to other resources according to \xep{0280}: Message /// Carbons. /// /// \since QXmpp 1.0 /// bool QXmppMessage::isPrivate() const { return d->privatemsg; } /// /// If true is passed, the message is marked with a <private/> tag, in /// which case it will not be forwarded to other resources according to /// \xep{0280}: Message Carbons. /// /// \since QXmpp 1.0 /// void QXmppMessage::setPrivate(const bool priv) { d->privatemsg = priv; } /// /// Returns the message id to replace with this message as used in \xep{0308}: /// Last Message Correction. If the returned string is empty, this message is /// not replacing another. /// /// \since QXmpp 1.0 /// QString QXmppMessage::replaceId() const { return d->replaceId; } /// /// Sets the message id to replace with this message as in \xep{0308}: Last /// Message Correction. /// /// \since QXmpp 1.0 /// void QXmppMessage::setReplaceId(const QString &replaceId) { d->replaceId = replaceId; } /// /// Returns true if a message is markable, as defined by \xep{0333}: Chat /// Markers. /// /// \since QXmpp 0.8.1 /// bool QXmppMessage::isMarkable() const { return d->markable; } /// /// Sets if the message is markable, as defined by \xep{0333}: Chat Markers. /// /// \since QXmpp 0.8.1 /// void QXmppMessage::setMarkable(const bool markable) { d->markable = markable; } /// /// Returns the message's marker id, as defined by \xep{0333}: Chat Markers. /// /// \since QXmpp 0.8.1 /// QString QXmppMessage::markedId() const { return d->markedId; } /// /// Sets the message's marker id, as defined by \xep{0333}: Chat Markers. /// /// \since QXmpp 0.8.1 /// void QXmppMessage::setMarkerId(const QString &markerId) { d->markedId = markerId; } /// /// Returns the message's marker thread, as defined by \xep{0333}: Chat Markers. /// /// \since QXmpp 0.8.1 /// QString QXmppMessage::markedThread() const { return d->markedThread; } /// /// Sets the message's marked thread, as defined by \xep{0333}: Chat Markers. /// /// \since QXmpp 0.8.1 /// void QXmppMessage::setMarkedThread(const QString &markedThread) { d->markedThread = markedThread; } /// /// Returns the message's marker, as defined by \xep{0333}: Chat Markers. /// /// \since QXmpp 0.8.1 /// QXmppMessage::Marker QXmppMessage::marker() const { return d->marker; } /// /// Sets the message's marker, as defined by \xep{0333}: Chat Markers /// /// \since QXmpp 0.8.1 /// void QXmppMessage::setMarker(const Marker marker) { d->marker = marker; } /// /// Returns true if the message contains the hint passed, as defined in /// \xep{0334}: Message Processing Hints /// /// \since QXmpp 1.1 /// 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 /// /// \since QXmpp 1.1 /// void QXmppMessage::addHint(const Hint hint) { d->hints |= hint; } /// /// Removes a hint from the message, as defined in \xep{0334}: Message /// Processing Hints /// /// \since QXmpp 1.1 /// void QXmppMessage::removeHint(const Hint hint) { d->hints &= ~hint; } /// /// Removes all hints from the message, as defined in \xep{0334}: Message /// Processing Hints /// /// \since QXmpp 1.1 /// void QXmppMessage::removeAllHints() { d->hints = 0; } /// /// Returns the stanza ID of the message according to \xep{0359}: Unique and /// Stable Stanza IDs. /// /// \since QXmpp 1.3 /// QString QXmppMessage::stanzaId() const { return d->stanzaId; } /// /// Sets the stanza ID of the message according to \xep{0359}: Unique and /// Stable Stanza IDs. /// /// \since QXmpp 1.3 /// void QXmppMessage::setStanzaId(const QString &id) { d->stanzaId = id; } /// /// Returns the creator of the stanza ID according to \xep{0359}: Unique and /// Stable Stanza IDs. /// /// \since QXmpp 1.3 /// QString QXmppMessage::stanzaIdBy() const { return d->stanzaIdBy; } /// /// Sets the creator of the stanza ID according to \xep{0359}: Unique and /// Stable Stanza IDs. /// /// \since QXmpp 1.3 /// void QXmppMessage::setStanzaIdBy(const QString &by) { d->stanzaIdBy = by; } /// /// Returns the origin ID of the message according to \xep{0359}: Unique and /// Stable Stanza IDs. /// /// \since QXmpp 1.3 /// QString QXmppMessage::originId() const { return d->originId; } /// /// Sets the origin ID of the message according to \xep{0359}: Unique and /// Stable Stanza IDs. /// /// \since QXmpp 1.3 /// void QXmppMessage::setOriginId(const QString &id) { d->originId = id; } /// /// Returns the message id this message is linked/attached to. See \xep{0367}: /// Message Attaching for details. /// /// \since QXmpp 1.1 /// 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. /// /// \since QXmpp 1.1 /// void QXmppMessage::setAttachId(const QString &attachId) { d->attachId = attachId; } /// /// Returns the actual JID of a MIX channel participant. /// /// \since QXmpp 1.1 /// QString QXmppMessage::mixUserJid() const { return d->mixUserJid; } /// /// Sets the actual JID of a MIX channel participant. /// /// \since QXmpp 1.1 /// void QXmppMessage::setMixUserJid(const QString &mixUserJid) { d->mixUserJid = mixUserJid; } /// /// Returns the MIX participant's nickname. /// /// \since QXmpp 1.1 /// QString QXmppMessage::mixUserNick() const { return d->mixUserNick; } /// /// Sets the MIX participant's nickname. /// /// \since QXmpp 1.1 /// 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(). /// /// \since QXmpp 1.1 /// 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(index); } /// /// Advertises that this message is encrypted with the given encryption method. /// See \xep{0380}: Explicit Message Encryption for details. /// /// \since QXmpp 1.1 /// 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. /// /// \since QXmpp 1.1 /// 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. /// /// \since QXmpp 1.1 /// 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. /// /// \since QXmpp 1.1 /// 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. /// /// \since QXmpp 1.1 /// 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. /// /// \since QXmpp 1.1 /// 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. /// /// \since QXmpp 1.1 /// 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. /// /// \since QXmpp 1.1 /// 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. /// /// \since QXmpp 1.1 /// void QXmppMessage::setSpoilerHint(const QString &spoilerHint) { d->spoilerHint = spoilerHint; if (!spoilerHint.isEmpty()) d->isSpoiler = true; } /// /// Returns an included \xep{0369}: Mediated Information eXchange (MIX) /// invitation as defined by \xep{0407}: Mediated Information eXchange (MIX): /// Miscellaneous Capabilities. /// /// \since QXmpp 1.4 /// std::optional QXmppMessage::mixInvitation() const { return d->mixInvitation; } /// /// Sets a \xep{0369}: Mediated Information eXchange (MIX) invitation as defined /// by \xep{0407}: Mediated Information eXchange (MIX): Miscellaneous /// Capabilities. /// /// \since QXmpp 1.4 /// void QXmppMessage::setMixInvitation(const std::optional &mixInvitation) { d->mixInvitation = mixInvitation; } /// /// Sets whether this message is only a fallback according to \xep{0428}: /// Fallback Indication. /// /// This is useful for clients not supporting end-to-end encryption to indicate /// that the message body does not contain the intended text of the author. /// /// \since QXmpp 1.3 /// bool QXmppMessage::isFallback() const { return d->isFallback; } /// /// Sets whether this message is only a fallback according to \xep{0428}: /// Fallback Indication. /// /// This is useful for clients not supporting end-to-end encryption to indicate /// that the message body does not contain the intended text of the author. /// /// \since QXmpp 1.3 /// void QXmppMessage::setIsFallback(bool isFallback) { d->isFallback = isFallback; } /// \cond void QXmppMessage::parse(const QDomElement &element) { QXmppStanza::parse(element); // message type int messageType = MESSAGE_TYPES.indexOf(element.attribute(QStringLiteral("type"))); if (messageType != -1) d->type = static_cast(messageType); else d->type = QXmppMessage::Normal; QXmppElementList extensions; QDomElement childElement = element.firstChildElement(); while (!childElement.isNull()) { if (childElement.tagName() == QStringLiteral("body")) { d->body = childElement.text(); } else if (childElement.tagName() == QStringLiteral("subject")) { d->subject = childElement.text(); } else if (childElement.tagName() == QStringLiteral("thread")) { d->thread = childElement.text(); d->parentThread = childElement.attribute(QStringLiteral("parent")); // parse message extensions // XEP-0033: Extended Stanza Addressing and errors are parsed by QXmppStanza } else if (!checkElement(childElement, QStringLiteral("addresses"), ns_extended_addressing) && childElement.tagName() != QStringLiteral("error")) { parseExtension(childElement, extensions); } childElement = childElement.nextSiblingElement(); } setExtensions(extensions); } void QXmppMessage::toXml(QXmlStreamWriter *xmlWriter) const { xmlWriter->writeStartElement(QStringLiteral("message")); helperToXmlAddAttribute(xmlWriter, QStringLiteral("xml:lang"), lang()); helperToXmlAddAttribute(xmlWriter, QStringLiteral("id"), id()); helperToXmlAddAttribute(xmlWriter, QStringLiteral("to"), to()); helperToXmlAddAttribute(xmlWriter, QStringLiteral("from"), from()); helperToXmlAddAttribute(xmlWriter, QStringLiteral("type"), MESSAGE_TYPES.at(d->type)); if (!d->subject.isEmpty()) helperToXmlAddTextElement(xmlWriter, QStringLiteral("subject"), d->subject); if (!d->body.isEmpty()) helperToXmlAddTextElement(xmlWriter, QStringLiteral("body"), d->body); if (!d->thread.isEmpty()) { xmlWriter->writeStartElement(QStringLiteral("thread")); helperToXmlAddAttribute(xmlWriter, QStringLiteral("parent"), d->parentThread); xmlWriter->writeCharacters(d->thread); xmlWriter->writeEndElement(); } error().toXml(xmlWriter); // XEP-0066: Out of Band Data if (!d->outOfBandUrl.isEmpty()) { xmlWriter->writeStartElement(QStringLiteral("x")); xmlWriter->writeDefaultNamespace(ns_oob); xmlWriter->writeTextElement(QStringLiteral("url"), d->outOfBandUrl); xmlWriter->writeEndElement(); } // XEP-0071: XHTML-IM if (!d->xhtml.isEmpty()) { xmlWriter->writeStartElement(QStringLiteral("html")); xmlWriter->writeDefaultNamespace(ns_xhtml_im); xmlWriter->writeStartElement(QStringLiteral("body")); xmlWriter->writeDefaultNamespace(ns_xhtml); xmlWriter->writeCharacters(QStringLiteral("")); xmlWriter->device()->write(d->xhtml.toUtf8()); xmlWriter->writeEndElement(); xmlWriter->writeEndElement(); } // XEP-0085: Chat State Notifications if (d->state > None && d->state <= Paused) { xmlWriter->writeStartElement(CHAT_STATES.at(d->state)); xmlWriter->writeDefaultNamespace(ns_chat_states); xmlWriter->writeEndElement(); } // XEP-0091: Legacy Delayed Delivery | XEP-0203: Delayed Delivery if (d->stamp.isValid()) { QDateTime utcStamp = d->stamp.toUTC(); if (d->stampType == DelayedDelivery) { // XEP-0203: Delayed Delivery xmlWriter->writeStartElement(QStringLiteral("delay")); xmlWriter->writeDefaultNamespace(ns_delayed_delivery); helperToXmlAddAttribute(xmlWriter, QStringLiteral("stamp"), QXmppUtils::datetimeToString(utcStamp)); xmlWriter->writeEndElement(); } else { // XEP-0091: Legacy Delayed Delivery xmlWriter->writeStartElement(QStringLiteral("x")); xmlWriter->writeDefaultNamespace(ns_legacy_delayed_delivery); helperToXmlAddAttribute(xmlWriter, QStringLiteral("stamp"), utcStamp.toString(QStringLiteral("yyyyMMddThh:mm:ss"))); xmlWriter->writeEndElement(); } } // XEP-0184: Message Delivery Receipts if (!d->receiptId.isEmpty()) { xmlWriter->writeStartElement(QStringLiteral("received")); xmlWriter->writeDefaultNamespace(ns_message_receipts); xmlWriter->writeAttribute(QStringLiteral("id"), d->receiptId); xmlWriter->writeEndElement(); } if (d->receiptRequested) { xmlWriter->writeStartElement(QStringLiteral("request")); xmlWriter->writeDefaultNamespace(ns_message_receipts); xmlWriter->writeEndElement(); } // XEP-0224: Attention if (d->attentionRequested) { xmlWriter->writeStartElement(QStringLiteral("attention")); xmlWriter->writeDefaultNamespace(ns_attention); xmlWriter->writeEndElement(); } // XEP-0249: Direct MUC Invitations if (!d->mucInvitationJid.isEmpty()) { xmlWriter->writeStartElement(QStringLiteral("x")); xmlWriter->writeDefaultNamespace(ns_conference); xmlWriter->writeAttribute(QStringLiteral("jid"), d->mucInvitationJid); if (!d->mucInvitationPassword.isEmpty()) xmlWriter->writeAttribute(QStringLiteral("password"), d->mucInvitationPassword); if (!d->mucInvitationReason.isEmpty()) xmlWriter->writeAttribute(QStringLiteral("reason"), d->mucInvitationReason); xmlWriter->writeEndElement(); } // XEP-0231: Bits of Binary for (const auto &data : qAsConst(d->bitsOfBinaryData)) data.toXmlElementFromChild(xmlWriter); // XEP-0280: Message Carbons if (d->privatemsg) { xmlWriter->writeStartElement(QStringLiteral("private")); xmlWriter->writeDefaultNamespace(ns_carbons); xmlWriter->writeEndElement(); } // XEP-0308: Last Message Correction if (!d->replaceId.isEmpty()) { xmlWriter->writeStartElement(QStringLiteral("replace")); xmlWriter->writeDefaultNamespace(ns_message_correct); xmlWriter->writeAttribute(QStringLiteral("id"), d->replaceId); xmlWriter->writeEndElement(); } // XEP-0333: Chat Markers if (d->markable) { xmlWriter->writeStartElement(QStringLiteral("markable")); xmlWriter->writeDefaultNamespace(ns_chat_markers); xmlWriter->writeEndElement(); } if (d->marker != NoMarker) { xmlWriter->writeStartElement(MARKER_TYPES.at(d->marker)); xmlWriter->writeDefaultNamespace(ns_chat_markers); xmlWriter->writeAttribute(QStringLiteral("id"), d->markedId); if (!d->markedThread.isNull() && !d->markedThread.isEmpty()) { xmlWriter->writeAttribute(QStringLiteral("thread"), d->markedThread); } 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->writeDefaultNamespace(ns_message_processing_hints); xmlWriter->writeEndElement(); } } // XEP-0359: Unique and Stable Stanza IDs if (!d->stanzaId.isNull()) { xmlWriter->writeStartElement(QStringLiteral("stanza-id")); xmlWriter->writeDefaultNamespace(ns_sid); xmlWriter->writeAttribute(QStringLiteral("id"), d->stanzaId); if (!d->stanzaIdBy.isNull()) xmlWriter->writeAttribute(QStringLiteral("by"), d->stanzaIdBy); xmlWriter->writeEndElement(); } if (!d->originId.isNull()) { xmlWriter->writeStartElement(QStringLiteral("origin-id")); xmlWriter->writeDefaultNamespace(ns_sid); xmlWriter->writeAttribute(QStringLiteral("id"), d->originId); xmlWriter->writeEndElement(); } // XEP-0367: Message Attaching if (!d->attachId.isEmpty()) { xmlWriter->writeStartElement(QStringLiteral("attach-to")); xmlWriter->writeDefaultNamespace(ns_message_attaching); xmlWriter->writeAttribute(QStringLiteral("id"), d->attachId); xmlWriter->writeEndElement(); } // XEP-0369: Mediated Information eXchange (MIX) if (!d->mixUserJid.isEmpty() || !d->mixUserNick.isEmpty()) { xmlWriter->writeStartElement(QStringLiteral("mix")); xmlWriter->writeDefaultNamespace(ns_mix); helperToXmlAddTextElement(xmlWriter, QStringLiteral("jid"), d->mixUserJid); helperToXmlAddTextElement(xmlWriter, QStringLiteral("nick"), d->mixUserNick); xmlWriter->writeEndElement(); } // XEP-0380: Explicit Message Encryption if (!d->encryptionMethod.isEmpty()) { xmlWriter->writeStartElement(QStringLiteral("encryption")); xmlWriter->writeDefaultNamespace(ns_eme); xmlWriter->writeAttribute(QStringLiteral("namespace"), d->encryptionMethod); helperToXmlAddAttribute(xmlWriter, QStringLiteral("name"), d->encryptionName); xmlWriter->writeEndElement(); } // XEP-0382: Spoiler messages if (d->isSpoiler) { xmlWriter->writeStartElement(QStringLiteral("spoiler")); xmlWriter->writeDefaultNamespace(ns_spoiler); xmlWriter->writeCharacters(d->spoilerHint); xmlWriter->writeEndElement(); } // XEP-0407: Mediated Information eXchange (MIX): Miscellaneous Capabilities if (d->mixInvitation) { d->mixInvitation->toXml(xmlWriter); } // XEP-0428: Fallback Indication if (d->isFallback) { xmlWriter->writeStartElement(QStringLiteral("fallback")); xmlWriter->writeDefaultNamespace(ns_fallback_indication); xmlWriter->writeEndElement(); } // other extensions QXmppStanza::extensionsToXml(xmlWriter); xmlWriter->writeEndElement(); } /// \endcond /// /// Parses message extensions /// /// \param element child element of the message to be parsed /// \param unknownExtensions extensions not known are added to this list as /// QXmppElement /// void QXmppMessage::parseExtension(const QDomElement &element, QXmppElementList &unknownExtensions) { if (element.tagName() == QStringLiteral("x")) { parseXElement(element, unknownExtensions); } else if (checkElement(element, QStringLiteral("html"), ns_xhtml_im)) { // XEP-0071: XHTML-IM QDomElement bodyElement = element.firstChildElement(QStringLiteral("body")); if (!bodyElement.isNull() && bodyElement.namespaceURI() == ns_xhtml) { QTextStream stream(&d->xhtml, QIODevice::WriteOnly); bodyElement.save(stream, 0); d->xhtml = d->xhtml.mid(d->xhtml.indexOf('>') + 1); d->xhtml.replace( QStringLiteral(" xmlns=\"http://www.w3.org/1999/xhtml\""), QString()); d->xhtml.replace(QStringLiteral(""), QString()); d->xhtml = d->xhtml.trimmed(); } } else if (element.namespaceURI() == ns_chat_states) { // XEP-0085: Chat State Notifications int i = CHAT_STATES.indexOf(element.tagName()); if (i > 0) d->state = static_cast(i); } else if (checkElement(element, QStringLiteral("received"), ns_message_receipts)) { // XEP-0184: Message Delivery Receipts d->receiptId = element.attribute(QStringLiteral("id")); // compatibility with old-style XEP if (d->receiptId.isEmpty()) d->receiptId = id(); } else if (checkElement(element, QStringLiteral("request"), ns_message_receipts)) { d->receiptRequested = true; } else if (checkElement(element, QStringLiteral("delay"), ns_delayed_delivery)) { // XEP-0203: Delayed Delivery d->stamp = QXmppUtils::datetimeFromString( element.attribute(QStringLiteral("stamp"))); d->stampType = DelayedDelivery; } else if (checkElement(element, QStringLiteral("attention"), ns_attention)) { // XEP-0224: Attention d->attentionRequested = true; } else if (QXmppBitsOfBinaryData::isBitsOfBinaryData(element)) { // XEP-0231: Bits of Binary QXmppBitsOfBinaryData data; data.parseElementFromChild(element); d->bitsOfBinaryData << data; } else if (checkElement(element, QStringLiteral("private"), ns_carbons)) { // XEP-0280: Message Carbons d->privatemsg = true; } else if (checkElement(element, QStringLiteral("replace"), ns_message_correct)) { // XEP-0308: Last Message Correction d->replaceId = element.attribute(QStringLiteral("id")); } else if (element.namespaceURI() == ns_chat_markers) { // XEP-0333: Chat Markers if (element.tagName() == QStringLiteral("markable")) { d->markable = true; } else { int marker = MARKER_TYPES.indexOf(element.tagName()); if (marker != -1) { d->marker = static_cast(marker); d->markedId = element.attribute(QStringLiteral("id")); d->markedThread = element.attribute(QStringLiteral("thread")); } } } else if (element.namespaceURI() == ns_message_processing_hints && HINT_TYPES.contains(element.tagName())) { // XEP-0334: Message Processing Hints addHint(Hint(1 << HINT_TYPES.indexOf(element.tagName()))); } else if (checkElement(element, QStringLiteral("stanza-id"), ns_sid)) { // XEP-0359: Unique and Stable Stanza IDs d->stanzaId = element.attribute(QStringLiteral("id")); d->stanzaIdBy = element.attribute(QStringLiteral("by")); } else if (checkElement(element, QStringLiteral("origin-id"), ns_sid)) { d->originId = element.attribute(QStringLiteral("id")); } else if (checkElement(element, QStringLiteral("attach-to"), ns_message_attaching)) { // XEP-0367: Message Attaching d->attachId = element.attribute(QStringLiteral("id")); } else if (checkElement(element, QStringLiteral("mix"), ns_mix)) { // XEP-0369: Mediated Information eXchange (MIX) d->mixUserJid = element.firstChildElement(QStringLiteral("jid")).text(); d->mixUserNick = element.firstChildElement(QStringLiteral("nick")).text(); } else if (checkElement(element, QStringLiteral("encryption"), ns_eme)) { // XEP-0380: Explicit Message Encryption d->encryptionMethod = element.attribute(QStringLiteral("namespace")); d->encryptionName = element.attribute(QStringLiteral("name")); } else if (checkElement(element, QStringLiteral("spoiler"), ns_spoiler)) { // XEP-0382: Spoiler messages d->isSpoiler = true; d->spoilerHint = element.text(); } else if (checkElement(element, QStringLiteral("invitation"), ns_mix_misc)) { // XEP-0407: Mediated Information eXchange (MIX): Miscellaneous Capabilities QXmppMixInvitation mixInvitation; mixInvitation.parse(element); d->mixInvitation = mixInvitation; } else if (checkElement(element, QStringLiteral("fallback"), ns_fallback_indication)) { // XEP-0428: Fallback Indication d->isFallback = true; } else { // other extensions unknownExtensions << QXmppElement(element); } } /// /// Parses <x/> child elements of the message /// /// \param element child element of the message to be parsed /// \param unknownExtensions extensions not known are added to this list as /// QXmppElement /// void QXmppMessage::parseXElement(const QDomElement &element, QXmppElementList &unknownExtensions) { if (element.namespaceURI() == ns_legacy_delayed_delivery) { // if XEP-0203 exists, XEP-0091 has no need to parse because XEP-0091 // is no more standard protocol) if (d->stamp.isNull()) { // XEP-0091: Legacy Delayed Delivery d->stamp = QDateTime::fromString( element.attribute(QStringLiteral("stamp")), QStringLiteral("yyyyMMddThh:mm:ss")); d->stamp.setTimeSpec(Qt::UTC); d->stampType = LegacyDelayedDelivery; } } else if (element.namespaceURI() == ns_conference) { // XEP-0249: Direct MUC Invitations d->mucInvitationJid = element.attribute(QStringLiteral("jid")); d->mucInvitationPassword = element.attribute(QStringLiteral("password")); d->mucInvitationReason = element.attribute(QStringLiteral("reason")); } else if (element.namespaceURI() == ns_oob) { // XEP-0066: Out of Band Data d->outOfBandUrl = element.firstChildElement(QStringLiteral("url")).text(); } else { unknownExtensions << QXmppElement(element); } } qxmpp-1.4.0/src/base/QXmppMessage.h000066400000000000000000000175401402370562100171140ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * Jeremy Lainé * Linus Jahn * * 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 QXMPPMESSAGE_H #define QXMPPMESSAGE_H #include "QXmppStanza.h" // Required for source compatibility #include #include class QXmppMessagePrivate; class QXmppBitsOfBinaryDataList; class QXmppMixInvitation; /// /// \brief The QXmppMessage class represents an XMPP message. /// /// \ingroup Stanzas /// class QXMPP_EXPORT QXmppMessage : public QXmppStanza { public: /// This enum describes a message type. enum Type { Error = 0, Normal, Chat, GroupChat, Headline }; /// /// This enum describes a chat state as defined by \xep{0085}: Chat State /// Notifications. /// /// \since QXmpp 0.2 /// enum State { None = 0, ///< The message does not contain any chat state information. Active, ///< User is actively participating in the chat session. Inactive, ///< User has not been actively participating in the chat session. Gone, ///< User has effectively ended their participation in the chat session. Composing, ///< User is composing a message. Paused ///< User had been composing but now has stopped. }; /// /// This enum describes a chat marker as defined by \xep{0333}: Chat Markers. /// /// \since QXmpp 0.8.1 /// enum Marker { NoMarker = 0, Received, Displayed, Acknowledged }; /// /// \xep{0334}: Message Processing Hints /// /// \since QXmpp 1.1 /// 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. /// /// \since QXmpp 1.1 /// 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() override; QXmppMessage &operator=(const QXmppMessage &other); bool isXmppStanza() const override; QString body() const; void setBody(const QString &); QString subject() const; void setSubject(const QString &); QString thread() const; void setThread(const QString &); QString parentThread() const; void setParentThread(const QString &); QXmppMessage::Type type() const; void setType(QXmppMessage::Type); // XEP-0066: Out of Band Data QString outOfBandUrl() const; void setOutOfBandUrl(const QString &); // XEP-0071: XHTML-IM QString xhtml() const; void setXhtml(const QString &xhtml); // XEP-0085: Chat State Notifications QXmppMessage::State state() const; void setState(QXmppMessage::State); // XEP-0091: Legacy Delayed Delivery | XEP-0203: Delayed Delivery QDateTime stamp() const; void setStamp(const QDateTime &stamp); // XEP-0184: Message Delivery Receipts bool isReceiptRequested() const; void setReceiptRequested(bool requested); QString receiptId() const; void setReceiptId(const QString &id); // XEP-0224: Attention bool isAttentionRequested() const; void setAttentionRequested(bool requested); // XEP-0231: Bits of Binary QXmppBitsOfBinaryDataList bitsOfBinaryData() const; QXmppBitsOfBinaryDataList &bitsOfBinaryData(); void setBitsOfBinaryData(const QXmppBitsOfBinaryDataList &bitsOfBinaryData); // XEP-0245: The /me Command static bool isSlashMeCommand(const QString &body); bool isSlashMeCommand() const; static QString slashMeCommandText(const QString &body); QString slashMeCommandText() const; // XEP-0249: Direct MUC Invitations QString mucInvitationJid() const; void setMucInvitationJid(const QString &jid); QString mucInvitationPassword() const; void setMucInvitationPassword(const QString &password); QString mucInvitationReason() const; void setMucInvitationReason(const QString &reason); // XEP-0280: Message Carbons bool isPrivate() const; void setPrivate(const bool); // XEP-0308: Last Message Correction QString replaceId() const; void setReplaceId(const QString &); // XEP-0333: Chat State Markers bool isMarkable() const; void setMarkable(const bool); QString markedId() const; void setMarkerId(const QString &); QString markedThread() const; void setMarkedThread(const QString &); Marker marker() const; void setMarker(const Marker); // XEP-0334: Message Processing Hints bool hasHint(const Hint hint) const; void addHint(const Hint hint); void removeHint(const Hint hint); void removeAllHints(); // XEP-0359: Unique and Stable Stanza IDs QString stanzaId() const; void setStanzaId(const QString &id); QString stanzaIdBy() const; void setStanzaIdBy(const QString &id); QString originId() const; void setOriginId(const QString &id); // 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 &); // XEP-0407: Mediated Information eXchange (MIX): Miscellaneous Capabilities std::optional mixInvitation() const; void setMixInvitation(const std::optional &mixInvitation); // XEP-0428: Fallback Indication bool isFallback() const; void setIsFallback(bool isFallback); /// \cond void parse(const QDomElement &element) override; void toXml(QXmlStreamWriter *writer) const override; /// \endcond private: void parseExtension(const QDomElement &element, QXmppElementList &unknownExtensions); void parseXElement(const QDomElement &element, QXmppElementList &unknownElements); QSharedDataPointer d; }; Q_DECLARE_METATYPE(QXmppMessage) #endif // QXMPPMESSAGE_H qxmpp-1.4.0/src/base/QXmppMixInvitation.cpp000066400000000000000000000102251402370562100206560ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Melvin Keskin * * 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 "QXmppMixInvitation.h" #include "QXmppConstants_p.h" #include "QXmppUtils.h" #include #include class QXmppMixInvitationPrivate : public QSharedData { public: QString inviterJid; QString inviteeJid; QString channelJid; QString token; }; /// /// Default constructor /// QXmppMixInvitation::QXmppMixInvitation() : d(new QXmppMixInvitationPrivate) { } /// /// Copy constructor /// QXmppMixInvitation::QXmppMixInvitation(const QXmppMixInvitation &other) = default; /// /// Default assignment operator. /// QXmppMixInvitation &QXmppMixInvitation::operator=(const QXmppMixInvitation &other) = default; /// /// Destructor /// QXmppMixInvitation::~QXmppMixInvitation() = default; /// /// Returns the JID of the inviter. /// /// \return the inviter's JID /// QString QXmppMixInvitation::inviterJid() const { return d->inviterJid; } /// /// Sets the JID of the inviter. /// /// \param inviterJid inviter's JID /// void QXmppMixInvitation::setInviterJid(const QString &inviterJid) { d->inviterJid = inviterJid; } /// /// Returns the JID of the invitee. /// /// \return the invitee's JID /// QString QXmppMixInvitation::inviteeJid() const { return d->inviteeJid; } /// /// Sets the JID of the invitee. /// /// \param inviteeJid invitee's JID /// void QXmppMixInvitation::setInviteeJid(const QString &inviteeJid) { d->inviteeJid = inviteeJid; } /// /// Returns the JID of the channel. /// /// \return the channel's JID /// QString QXmppMixInvitation::channelJid() const { return d->channelJid; } /// /// Sets the JID of the channel. /// /// \param channelJid channel JID /// void QXmppMixInvitation::setChannelJid(const QString &channelJid) { d->channelJid = channelJid; } /// /// Returns the token which is generated by the server and used by the invitee /// for authentication. /// /// \return the generated token used for authentication /// QString QXmppMixInvitation::token() const { return d->token; } /// /// Sets the token which is generated by the server and used by the invitee for /// authentication. /// /// \param token authentication token /// void QXmppMixInvitation::setToken(const QString &token) { d->token = token; } /// \cond void QXmppMixInvitation::parse(const QDomElement &element) { d->inviterJid = element.firstChildElement(QStringLiteral("inviter")).text(); d->inviteeJid = element.firstChildElement(QStringLiteral("invitee")).text(); d->channelJid = element.firstChildElement(QStringLiteral("channel")).text(); d->token = element.firstChildElement(QStringLiteral("token")).text(); } void QXmppMixInvitation::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("invitation")); writer->writeDefaultNamespace(ns_mix_misc); helperToXmlAddTextElement(writer, QStringLiteral("inviter"), d->inviterJid); helperToXmlAddTextElement(writer, QStringLiteral("invitee"), d->inviteeJid); helperToXmlAddTextElement(writer, QStringLiteral("channel"), d->channelJid); helperToXmlAddTextElement(writer, QStringLiteral("token"), d->token); writer->writeEndElement(); } /// \endcond /// /// Determines whether the given DOM element is a MIX invitation. /// /// \param element DOM element being checked /// /// \return true if element is a MIX invitation, otherwise false /// bool QXmppMixInvitation::isMixInvitation(const QDomElement &element) { return element.tagName() == QStringLiteral("invitation") && element.namespaceURI() == ns_mix_misc; } qxmpp-1.4.0/src/base/QXmppMixInvitation.h000066400000000000000000000036731402370562100203340ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Melvin Keskin * * 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 QXMPPMIXINVITATION_H #define QXMPPMIXINVITATION_H #include "QXmppElement.h" #include class QXmppMixInvitationPrivate; /// /// \brief The QXmppMixInvitation class is used to invite a user to a /// \xep{0369}: Mediated Information eXchange (MIX) channel as defined by /// \xep{0407}: Mediated Information eXchange (MIX): Miscellaneous Capabilities. /// /// \ingroup Stanzas /// /// \since QXmpp 1.4 /// class QXMPP_EXPORT QXmppMixInvitation { public: QXmppMixInvitation(); QXmppMixInvitation(const QXmppMixInvitation &other); ~QXmppMixInvitation(); QXmppMixInvitation &operator=(const QXmppMixInvitation &other); QString inviterJid() const; void setInviterJid(const QString &inviterJid); QString inviteeJid() const; void setInviteeJid(const QString &inviteeJid); QString channelJid() const; void setChannelJid(const QString &channelJid); QString token() const; void setToken(const QString &token); /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *writer) const; /// \endcond static bool isMixInvitation(const QDomElement &element); private: QSharedDataPointer d; }; #endif // QXMPPMIXINVITATION_H qxmpp-1.4.0/src/base/QXmppMixIq.cpp000066400000000000000000000134101402370562100171020ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Linus Jahn * * 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 "QXmppConstants_p.h" #include "QXmppDataForm.h" #include "QXmppUtils.h" #include #include static const QStringList MIX_ACTION_TYPES = { QString(), QStringLiteral("client-join"), QStringLiteral("client-leave"), QStringLiteral("join"), QStringLiteral("leave"), QStringLiteral("update-subscription"), QStringLiteral("setnick"), QStringLiteral("create"), QStringLiteral("destroy") }; class QXmppMixIqPrivate : public QSharedData { public: QString jid; QString channelName; QStringList nodes; QString nick; QXmppMixIq::Type actionType = QXmppMixIq::None; }; QXmppMixIq::QXmppMixIq() : d(new QXmppMixIqPrivate) { } QXmppMixIq::QXmppMixIq(const QXmppMixIq&) = default; QXmppMixIq::~QXmppMixIq() = default; QXmppMixIq& QXmppMixIq::operator=(const QXmppMixIq&) = default; /// 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(QStringLiteral("channel"))) d->jid = child.attribute(QStringLiteral("channel")); child = child.firstChildElement(); } if (!child.isNull() && child.namespaceURI() == ns_mix) { if (child.hasAttribute(QStringLiteral("jid"))) d->jid = child.attribute(QStringLiteral("jid")); if (child.hasAttribute(QStringLiteral("channel"))) d->channelName = child.attribute(QStringLiteral("channel")); QDomElement subChild = child.firstChildElement(); while (!subChild.isNull()) { if (subChild.tagName() == QStringLiteral("subscribe")) d->nodes << subChild.attribute(QStringLiteral("node")); else if (subChild.tagName() == QStringLiteral("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->writeDefaultNamespace(ns_mix_pam); if (type() == Set) helperToXmlAddAttribute(writer, QStringLiteral("channel"), d->jid); if (d->actionType == ClientJoin) writer->writeStartElement(QStringLiteral("join")); else if (d->actionType == ClientLeave) writer->writeStartElement(QStringLiteral("leave")); } writer->writeDefaultNamespace(ns_mix); helperToXmlAddAttribute(writer, QStringLiteral("channel"), d->channelName); if (type() == Result) helperToXmlAddAttribute(writer, QStringLiteral("jid"), d->jid); for (const auto& node : d->nodes) { writer->writeStartElement(QStringLiteral("subscribe")); writer->writeAttribute(QStringLiteral("node"), node); writer->writeEndElement(); } if (!d->nick.isEmpty()) writer->writeTextElement(QStringLiteral("nick"), d->nick); writer->writeEndElement(); if (d->actionType == ClientJoin || d->actionType == ClientLeave) writer->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/base/QXmppMixIq.h000066400000000000000000000042121402370562100165470ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Linus Jahn * * 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" #include 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. /// /// \since QXmpp 1.1 /// /// \ingroup Stanzas class QXMPP_EXPORT QXmppMixIq : public QXmppIq { public: enum Type { None, ClientJoin, ClientLeave, Join, Leave, UpdateSubscription, SetNick, Create, Destroy }; QXmppMixIq(); QXmppMixIq(const QXmppMixIq &); ~QXmppMixIq() override; QXmppMixIq &operator=(const QXmppMixIq &); 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: QSharedDataPointer d; }; #endif // QXMPPMIXIQ_H qxmpp-1.4.0/src/base/QXmppMixItem.cpp000066400000000000000000000141151402370562100174320ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Linus Jahn * * 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 "QXmppConstants_p.h" #include "QXmppDataForm.h" #include "QXmppUtils.h" #include #include #include class QXmppMixInfoItemPrivate : public QSharedData { public: QString name; QString description; QStringList contactJids; }; QXmppMixInfoItem::QXmppMixInfoItem() : d(new QXmppMixInfoItemPrivate) { } QXmppMixInfoItem::QXmppMixInfoItem(const QXmppMixInfoItem&) = default; QXmppMixInfoItem& QXmppMixInfoItem::operator=(const QXmppMixInfoItem&) = default; QXmppMixInfoItem::~QXmppMixInfoItem() = default; /// 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() == QStringLiteral("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() == QStringLiteral("Name")) d->name = field.value().toString(); else if (field.key() == QStringLiteral("Description")) d->description = field.value().toString(); else if (field.key() == QStringLiteral("Contact")) d->contactJids = field.value().toStringList(); } } QXmppElement QXmppMixInfoItem::toElement() const { QXmppDataForm form; form.setType(QXmppDataForm::Result); QList fields; QXmppDataForm::Field formType; formType.setType(QXmppDataForm::Field::HiddenField); formType.setKey(QStringLiteral("FORM_TYPE")); formType.setValue(ns_mix); fields << formType; QXmppDataForm::Field nameField; nameField.setKey(QStringLiteral("Name")); nameField.setValue(d->name); fields << nameField; QXmppDataForm::Field descriptionField; descriptionField.setKey(QStringLiteral("Description")); descriptionField.setValue(d->description); fields << descriptionField; QXmppDataForm::Field contactsField; contactsField.setKey(QStringLiteral("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 : public QSharedData { public: QString nick; QString jid; }; QXmppMixParticipantItem::QXmppMixParticipantItem() : d(new QXmppMixParticipantItemPrivate) { } QXmppMixParticipantItem::QXmppMixParticipantItem(const QXmppMixParticipantItem&) = default; QXmppMixParticipantItem& QXmppMixParticipantItem::operator=(const QXmppMixParticipantItem&) = default; QXmppMixParticipantItem::~QXmppMixParticipantItem() = default; /// 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(QStringLiteral("nick")).value(); d->jid = itemContent.firstChildElement(QStringLiteral("jid")).value(); } QXmppElement QXmppMixParticipantItem::toElement() const { QXmppElement element; element.setTagName(QStringLiteral("participant")); element.setAttribute(QStringLiteral("xmlns"), ns_mix); QXmppElement jid; jid.setTagName(QStringLiteral("jid")); jid.setValue(d->jid); element.appendChild(jid); QXmppElement nick; nick.setTagName(QStringLiteral("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() == QStringLiteral("participant") && element.namespaceURI() == ns_mix; } qxmpp-1.4.0/src/base/QXmppMixItem.h000066400000000000000000000050341402370562100170770ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Linus Jahn * * 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" #include 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). /// /// \since QXmpp 1.1 /// /// \ingroup Stanzas class QXMPP_EXPORT QXmppMixInfoItem { public: QXmppMixInfoItem(); QXmppMixInfoItem(const QXmppMixInfoItem &); ~QXmppMixInfoItem(); QXmppMixInfoItem &operator=(const 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: QSharedDataPointer d; }; /// \brief The QXmppMixParticipantItem class represents a PubSub item of a MIX /// channel participant as defined by \xep{0369}: Mediated Information eXchange /// (MIX). /// /// \since QXmpp 1.1 /// /// \ingroup Stanzas class QXMPP_EXPORT QXmppMixParticipantItem { public: QXmppMixParticipantItem(); QXmppMixParticipantItem(const QXmppMixParticipantItem &); ~QXmppMixParticipantItem(); QXmppMixParticipantItem &operator=(const 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: QSharedDataPointer d; }; #endif // QXMPPMIXITEM_H qxmpp-1.4.0/src/base/QXmppMucIq.cpp000066400000000000000000000205441402370562100170770ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppMucIq.h" #include "QXmppConstants_p.h" #include "QXmppUtils.h" #include QXmppMucItem::QXmppMucItem() : m_affiliation(QXmppMucItem::UnspecifiedAffiliation), m_role(QXmppMucItem::UnspecifiedRole) { } /// Returns true if the current item is null. bool QXmppMucItem::isNull() const { return m_actor.isEmpty() && m_affiliation == UnspecifiedAffiliation && m_jid.isEmpty() && m_nick.isEmpty() && m_reason.isEmpty() && m_role == UnspecifiedRole; } /// Returns the actor for this item, for instance the admin who kicked /// a user out of a room. QString QXmppMucItem::actor() const { return m_actor; } /// Sets the \a actor for this item, for instance the admin who kicked /// a user out of a room. void QXmppMucItem::setActor(const QString &actor) { m_actor = actor; } /// Returns the user's affiliation, i.e. long-lived permissions. QXmppMucItem::Affiliation QXmppMucItem::affiliation() const { return m_affiliation; } /// \cond QXmppMucItem::Affiliation QXmppMucItem::affiliationFromString(const QString &affiliationStr) { if (affiliationStr == QStringLiteral("owner")) return QXmppMucItem::OwnerAffiliation; else if (affiliationStr == QStringLiteral("admin")) return QXmppMucItem::AdminAffiliation; else if (affiliationStr == QStringLiteral("member")) return QXmppMucItem::MemberAffiliation; else if (affiliationStr == QStringLiteral("outcast")) return QXmppMucItem::OutcastAffiliation; else if (affiliationStr == QStringLiteral("none")) return QXmppMucItem::NoAffiliation; else return QXmppMucItem::UnspecifiedAffiliation; } QString QXmppMucItem::affiliationToString(Affiliation affiliation) { switch (affiliation) { case QXmppMucItem::OwnerAffiliation: return "owner"; case QXmppMucItem::AdminAffiliation: return "admin"; case QXmppMucItem::MemberAffiliation: return "member"; case QXmppMucItem::OutcastAffiliation: return "outcast"; case QXmppMucItem::NoAffiliation: return "none"; default: return QString(); } } /// \endcond /// Sets the user's affiliation, i.e. long-lived permissions. /// /// \param affiliation void QXmppMucItem::setAffiliation(Affiliation affiliation) { m_affiliation = affiliation; } /// Returns the user's real JID. QString QXmppMucItem::jid() const { return m_jid; } /// Sets the user's real JID. /// /// \param jid void QXmppMucItem::setJid(const QString &jid) { m_jid = jid; } /// Returns the user's nickname. QString QXmppMucItem::nick() const { return m_nick; } /// Sets the user's nickname. /// /// \param nick void QXmppMucItem::setNick(const QString &nick) { m_nick = nick; } /// Returns the reason for this item, for example the reason for kicking /// a user out of a room. QString QXmppMucItem::reason() const { return m_reason; } /// Sets the \a reason for this item, for example the reason for kicking /// a user out of a room. void QXmppMucItem::setReason(const QString &reason) { m_reason = reason; } /// Returns the user's role, i.e. short-lived permissions. QXmppMucItem::Role QXmppMucItem::role() const { return m_role; } /// \cond QXmppMucItem::Role QXmppMucItem::roleFromString(const QString &roleStr) { if (roleStr == QStringLiteral("moderator")) return QXmppMucItem::ModeratorRole; else if (roleStr == QStringLiteral("participant")) return QXmppMucItem::ParticipantRole; else if (roleStr == QStringLiteral("visitor")) return QXmppMucItem::VisitorRole; else if (roleStr == QStringLiteral("none")) return QXmppMucItem::NoRole; else return QXmppMucItem::UnspecifiedRole; } QString QXmppMucItem::roleToString(Role role) { switch (role) { case QXmppMucItem::ModeratorRole: return QStringLiteral("moderator"); case QXmppMucItem::ParticipantRole: return QStringLiteral("participant"); case QXmppMucItem::VisitorRole: return QStringLiteral("visitor"); case QXmppMucItem::NoRole: return QStringLiteral("none"); default: return QString(); } } /// \endcond /// Sets the user's role, i.e. short-lived permissions. /// /// \param role void QXmppMucItem::setRole(Role role) { m_role = role; } /// \cond void QXmppMucItem::parse(const QDomElement &element) { m_affiliation = QXmppMucItem::affiliationFromString(element.attribute(QStringLiteral("affiliation")).toLower()); m_jid = element.attribute(QStringLiteral("jid")); m_nick = element.attribute(QStringLiteral("nick")); m_role = QXmppMucItem::roleFromString(element.attribute(QStringLiteral("role")).toLower()); m_actor = element.firstChildElement(QStringLiteral("actor")).attribute("jid"); m_reason = element.firstChildElement(QStringLiteral("reason")).text(); } void QXmppMucItem::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("item")); helperToXmlAddAttribute(writer, QStringLiteral("affiliation"), affiliationToString(m_affiliation)); helperToXmlAddAttribute(writer, QStringLiteral("jid"), m_jid); helperToXmlAddAttribute(writer, QStringLiteral("nick"), m_nick); helperToXmlAddAttribute(writer, QStringLiteral("role"), roleToString(m_role)); if (!m_actor.isEmpty()) { writer->writeStartElement(QStringLiteral("actor")); helperToXmlAddAttribute(writer, QStringLiteral("jid"), m_actor); writer->writeEndElement(); } if (!m_reason.isEmpty()) helperToXmlAddTextElement(writer, QStringLiteral("reason"), m_reason); writer->writeEndElement(); } /// \endcond /// Returns the IQ's items. QList QXmppMucAdminIq::items() const { return m_items; } /// Sets the IQ's items. /// /// \param items void QXmppMucAdminIq::setItems(const QList &items) { m_items = items; } /// \cond bool QXmppMucAdminIq::isMucAdminIq(const QDomElement &element) { QDomElement queryElement = element.firstChildElement(QStringLiteral("query")); return (queryElement.namespaceURI() == ns_muc_admin); } void QXmppMucAdminIq::parseElementFromChild(const QDomElement &element) { QDomElement queryElement = element.firstChildElement(QStringLiteral("query")); QDomElement child = queryElement.firstChildElement(QStringLiteral("item")); while (!child.isNull()) { QXmppMucItem item; item.parse(child); m_items << item; child = child.nextSiblingElement(QStringLiteral("item")); } } void QXmppMucAdminIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("query")); writer->writeDefaultNamespace(ns_muc_admin); for (const QXmppMucItem &item : m_items) item.toXml(writer); writer->writeEndElement(); } /// \endcond /// Returns the IQ's data form. QXmppDataForm QXmppMucOwnerIq::form() const { return m_form; } /// Sets the IQ's data form. /// /// \param form void QXmppMucOwnerIq::setForm(const QXmppDataForm &form) { m_form = form; } /// \cond bool QXmppMucOwnerIq::isMucOwnerIq(const QDomElement &element) { QDomElement queryElement = element.firstChildElement(QStringLiteral("query")); return (queryElement.namespaceURI() == ns_muc_owner); } void QXmppMucOwnerIq::parseElementFromChild(const QDomElement &element) { QDomElement queryElement = element.firstChildElement(QStringLiteral("query")); m_form.parse(queryElement.firstChildElement(QStringLiteral("x"))); } void QXmppMucOwnerIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("query")); writer->writeDefaultNamespace(ns_muc_owner); m_form.toXml(writer); writer->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/base/QXmppMucIq.h000066400000000000000000000073551402370562100165510ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPMUCIQ_H #define QXMPPMUCIQ_H #include "QXmppDataForm.h" #include "QXmppIq.h" /// \brief The QXmppMucItem class represents a chat room "item". /// /// It is used to convey information such as permissions. /// /// \ingroup Stanzas class QXMPP_EXPORT QXmppMucItem { public: /// This enum is used to represent long-lived permissions in a room (affiliations). enum Affiliation { UnspecifiedAffiliation, OutcastAffiliation, NoAffiliation, MemberAffiliation, AdminAffiliation, OwnerAffiliation }; /// This enum is used to represent short-lived permissions in a room (roles). enum Role { UnspecifiedRole, NoRole, VisitorRole, ParticipantRole, ModeratorRole }; QXmppMucItem(); bool isNull() const; QString actor() const; void setActor(const QString &actor); Affiliation affiliation() const; void setAffiliation(Affiliation affiliation); QString jid() const; void setJid(const QString &jid); QString nick() const; void setNick(const QString &nick); QString reason() const; void setReason(const QString &reason); Role role() const; void setRole(Role role); /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *writer) const; static Affiliation affiliationFromString(const QString &affiliationStr); static QString affiliationToString(Affiliation affiliation); static Role roleFromString(const QString &roleStr); static QString roleToString(Role role); /// \endcond private: QString m_actor; Affiliation m_affiliation; QString m_jid; QString m_nick; QString m_reason; Role m_role; }; /// \brief The QXmppMucAdminIq class represents a chat room administration IQ /// as defined by \xep{0045}: Multi-User Chat. /// /// It is used to get or modify room memberships. /// /// \ingroup Stanzas class QXMPP_EXPORT QXmppMucAdminIq : public QXmppIq { public: QList items() const; void setItems(const QList &items); /// \cond static bool isMucAdminIq(const QDomElement &element); /// \endcond protected: /// \cond void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: QList m_items; }; /// \brief The QXmppMucOwnerIq class represents a chat room configuration IQ as /// defined by \xep{0045}: Multi-User Chat. /// /// It is used to get or modify room configuration options. /// /// \sa QXmppDataForm /// class QXMPP_EXPORT QXmppMucOwnerIq : public QXmppIq { public: QXmppDataForm form() const; void setForm(const QXmppDataForm &form); /// \cond static bool isMucOwnerIq(const QDomElement &element); /// \endcond protected: /// \cond void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: QXmppDataForm m_form; }; #endif qxmpp-1.4.0/src/base/QXmppNonSASLAuth.cpp000066400000000000000000000060561402370562100201220ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 "QXmppNonSASLAuth.h" #include "QXmppConstants_p.h" #include "QXmppUtils.h" #include #include #include QXmppNonSASLAuthIq::QXmppNonSASLAuthIq() : QXmppIq(QXmppIq::Set) { } QString QXmppNonSASLAuthIq::username() const { return m_username; } void QXmppNonSASLAuthIq::setUsername(const QString &username) { m_username = username; } QByteArray QXmppNonSASLAuthIq::digest() const { return m_digest; } void QXmppNonSASLAuthIq::setDigest(const QString &streamId, const QString &password) { m_digest = QCryptographicHash::hash(streamId.toUtf8() + password.toUtf8(), QCryptographicHash::Sha1); } QString QXmppNonSASLAuthIq::password() const { return m_password; } void QXmppNonSASLAuthIq::setPassword(const QString &password) { m_password = password; } QString QXmppNonSASLAuthIq::resource() const { return m_resource; } void QXmppNonSASLAuthIq::setResource(const QString &resource) { m_resource = resource; } /// \cond bool QXmppNonSASLAuthIq::isNonSASLAuthIq(const QDomElement &element) { QDomElement queryElement = element.firstChildElement(QStringLiteral("query")); return queryElement.namespaceURI() == ns_auth; } void QXmppNonSASLAuthIq::parseElementFromChild(const QDomElement &element) { QDomElement queryElement = element.firstChildElement(QStringLiteral("query")); m_username = queryElement.firstChildElement(QStringLiteral("username")).text(); m_password = queryElement.firstChildElement(QStringLiteral("password")).text(); m_digest = QByteArray::fromHex(queryElement.firstChildElement(QStringLiteral("digest")).text().toLatin1()); m_resource = queryElement.firstChildElement(QStringLiteral("resource")).text(); } void QXmppNonSASLAuthIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("query")); writer->writeDefaultNamespace(ns_auth); if (!m_username.isEmpty()) writer->writeTextElement(QStringLiteral("username"), m_username); if (!m_digest.isEmpty()) writer->writeTextElement(QStringLiteral("digest"), m_digest.toHex()); if (!m_password.isEmpty()) writer->writeTextElement(QStringLiteral("password"), m_password); if (!m_resource.isEmpty()) writer->writeTextElement(QStringLiteral("resource"), m_resource); writer->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/base/QXmppNonSASLAuth.h000066400000000000000000000033431402370562100175630ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 QXmppNonSASLAuth_H #define QXmppNonSASLAuth_H #include "QXmppIq.h" /// /// \brief QXmppNonSASLAuthIq represents a Non-SASL authentication IQ as /// defined by \xep{0078}: Non-SASL Authentication. /// /// \ingroup Stanzas /// class QXMPP_EXPORT QXmppNonSASLAuthIq : public QXmppIq { public: QXmppNonSASLAuthIq(); QString username() const; void setUsername(const QString &username); QByteArray digest() const; void setDigest(const QString &streamId, const QString &password); QString password() const; void setPassword(const QString &password); QString resource() const; void setResource(const QString &resource); static bool isNonSASLAuthIq(const QDomElement &element); protected: /// \cond void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: QString m_username; QByteArray m_digest; QString m_password; QString m_resource; }; #endif // QXmppNonSASLAuth_H qxmpp-1.4.0/src/base/QXmppPingIq.cpp000066400000000000000000000025121402370562100172430ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppPingIq.h" #include "QXmppConstants_p.h" #include "QXmppUtils.h" #include QXmppPingIq::QXmppPingIq() : QXmppIq(QXmppIq::Get) { } bool QXmppPingIq::isPingIq(const QDomElement &element) { QDomElement pingElement = element.firstChildElement(QStringLiteral("ping")); return (element.attribute(QStringLiteral("type")) == QStringLiteral("get") && pingElement.namespaceURI() == ns_ping); } void QXmppPingIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("ping")); writer->writeDefaultNamespace(ns_ping); writer->writeEndElement(); } qxmpp-1.4.0/src/base/QXmppPingIq.h000066400000000000000000000021301402370562100167040ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPPINGIQ_H #define QXMPPPINGIQ_H #include "QXmppIq.h" /// /// \brief QXmppPingIq represents a Ping IQ as defined by \xep{0199}: XMPP /// Ping. /// /// \ingroup Stanzas /// class QXMPP_EXPORT QXmppPingIq : public QXmppIq { public: QXmppPingIq(); void toXmlElementFromChild(QXmlStreamWriter *writer) const override; static bool isPingIq(const QDomElement &element); }; #endif qxmpp-1.4.0/src/base/QXmppPresence.cpp000066400000000000000000000415061402370562100176260ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 "QXmppPresence.h" #include "QXmppConstants_p.h" #include "QXmppUtils.h" #include #include static const QStringList PRESENCE_TYPES = { QStringLiteral("error"), QString(), QStringLiteral("unavailable"), QStringLiteral("subscribe"), QStringLiteral("subscribed"), QStringLiteral("unsubscribe"), QStringLiteral("unsubscribed"), QStringLiteral("probe") }; static const QStringList AVAILABLE_STATUS_TYPES = { QString(), QStringLiteral("away"), QStringLiteral("xa"), QStringLiteral("dnd"), QStringLiteral("chat"), QStringLiteral("invisible") }; class QXmppPresencePrivate : public QSharedData { public: QXmppPresencePrivate(); QXmppPresence::Type type; QXmppPresence::AvailableStatusType availableStatusType; QString statusText; int priority; // XEP-0045: Multi-User Chat QXmppMucItem mucItem; QString mucPassword; QList mucStatusCodes; bool mucSupported; // XEP-0115: Entity Capabilities QString capabilityHash; QString capabilityNode; QByteArray capabilityVer; // Legacy XEP-0115: Entity Capabilities QStringList capabilityExt; // XEP-0153: vCard-Based Avatars // photoHash: the SHA1 hash of the avatar image data itself (not the base64-encoded version) // in accordance with RFC 3174 QByteArray photoHash; QXmppPresence::VCardUpdateType vCardUpdateType; // XEP-0319: Last User Interaction in Presence QDateTime lastUserInteraction; // XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements QString mixUserJid; QString mixUserNick; }; QXmppPresencePrivate::QXmppPresencePrivate() : type(QXmppPresence::Available), availableStatusType(QXmppPresence::Online), priority(0), mucSupported(false), vCardUpdateType(QXmppPresence::VCardUpdateNone) { } /// Constructs a QXmppPresence. /// /// \param type QXmppPresence::QXmppPresence(QXmppPresence::Type type) : d(new QXmppPresencePrivate) { d->type = type; } /// Constructs a copy of \a other. QXmppPresence::QXmppPresence(const QXmppPresence &other) = default; /// Destroys a QXmppPresence. QXmppPresence::~QXmppPresence() = default; /// Assigns \a other to this presence. QXmppPresence &QXmppPresence::operator=(const QXmppPresence &other) = default; /// /// Indicates if the QXmppStanza is a stanza in the XMPP sence (i. e. a message, /// iq or presence) /// /// \since QXmpp 1.0 /// bool QXmppPresence::isXmppStanza() const { return true; } /// Returns the availability status type, for instance busy or away. /// /// This will not tell you whether a contact is connected, check whether /// type() is QXmppPresence::Available instead. QXmppPresence::AvailableStatusType QXmppPresence::availableStatusType() const { return d->availableStatusType; } /// Sets the availability status type, for instance busy or away. void QXmppPresence::setAvailableStatusType(AvailableStatusType type) { d->availableStatusType = type; } /// Returns the priority level of the resource. int QXmppPresence::priority() const { return d->priority; } /// Sets the \a priority level of the resource. void QXmppPresence::setPriority(int priority) { d->priority = priority; } /// Returns the status text, a textual description of the user's status. QString QXmppPresence::statusText() const { return d->statusText; } /// Sets the status text, a textual description of the user's status. /// /// \param statusText The status text, for example "Gone fishing". void QXmppPresence::setStatusText(const QString &statusText) { d->statusText = statusText; } /// Returns the presence type. /// /// You can use this method to determine the action which needs to be /// taken in response to receiving the presence. For instance, if the type is /// QXmppPresence::Available or QXmppPresence::Unavailable, you could update /// the icon representing a contact's availability. QXmppPresence::Type QXmppPresence::type() const { return d->type; } /// Sets the presence type. /// /// \param type void QXmppPresence::setType(QXmppPresence::Type type) { d->type = type; } /// Returns the photo-hash of the VCardUpdate. /// /// \return QByteArray QByteArray QXmppPresence::photoHash() const { return d->photoHash; } /// Sets the photo-hash of the VCardUpdate. /// /// \param photoHash as QByteArray void QXmppPresence::setPhotoHash(const QByteArray &photoHash) { d->photoHash = photoHash; } /// Returns the type of VCardUpdate /// /// \return VCardUpdateType QXmppPresence::VCardUpdateType QXmppPresence::vCardUpdateType() const { return d->vCardUpdateType; } /// Sets the type of VCardUpdate /// /// \param type VCardUpdateType void QXmppPresence::setVCardUpdateType(VCardUpdateType type) { d->vCardUpdateType = type; } /// \xep{0115}: Entity Capabilities QString QXmppPresence::capabilityHash() const { return d->capabilityHash; } /// \xep{0115}: Entity Capabilities void QXmppPresence::setCapabilityHash(const QString &hash) { d->capabilityHash = hash; } /// \xep{0115}: Entity Capabilities QString QXmppPresence::capabilityNode() const { return d->capabilityNode; } /// \xep{0115}: Entity Capabilities void QXmppPresence::setCapabilityNode(const QString &node) { d->capabilityNode = node; } /// \xep{0115}: Entity Capabilities QByteArray QXmppPresence::capabilityVer() const { return d->capabilityVer; } /// \xep{0115}: Entity Capabilities void QXmppPresence::setCapabilityVer(const QByteArray &ver) { d->capabilityVer = ver; } /// Legacy \xep{0115}: Entity Capabilities QStringList QXmppPresence::capabilityExt() const { return d->capabilityExt; } /// Returns the MUC item. QXmppMucItem QXmppPresence::mucItem() const { return d->mucItem; } /// Sets the MUC item. /// /// \param item void QXmppPresence::setMucItem(const QXmppMucItem &item) { d->mucItem = item; } /// Returns the password used to join a MUC room. QString QXmppPresence::mucPassword() const { return d->mucPassword; } /// Sets the password used to join a MUC room. void QXmppPresence::setMucPassword(const QString &password) { d->mucPassword = password; } /// Returns the MUC status codes. QList QXmppPresence::mucStatusCodes() const { return d->mucStatusCodes; } /// Sets the MUC status codes. /// /// \param codes void QXmppPresence::setMucStatusCodes(const QList &codes) { d->mucStatusCodes = codes; } /// Returns true if the sender has indicated MUC support. bool QXmppPresence::isMucSupported() const { return d->mucSupported; } /// Sets whether MUC is \a supported. void QXmppPresence::setMucSupported(bool supported) { d->mucSupported = supported; } /// /// Returns when the last user interaction with the client took place. See /// \xep{0319}: Last User Interaction in Presence for details. /// /// \since QXmpp 1.0 /// QDateTime QXmppPresence::lastUserInteraction() const { return d->lastUserInteraction; } /// /// Sets the time of the last user interaction as defined in \xep{0319}: Last /// User Interaction in Presence. /// /// \since QXmpp 1.0 /// void QXmppPresence::setLastUserInteraction(const QDateTime &lastUserInteraction) { d->lastUserInteraction = lastUserInteraction; } /// Returns the actual (full) JID of the MIX channel participant. /// /// \since QXmpp 1.1 QString QXmppPresence::mixUserJid() const { return d->mixUserJid; } /// Sets the actual (full) JID of the MIX channel participant. /// /// \since QXmpp 1.1 void QXmppPresence::setMixUserJid(const QString &mixUserJid) { d->mixUserJid = mixUserJid; } /// Returns the MIX participant's nickname. /// /// \since QXmpp 1.1 QString QXmppPresence::mixUserNick() const { return d->mixUserNick; } /// Sets the MIX participant's nickname. /// /// \since QXmpp 1.1 void QXmppPresence::setMixUserNick(const QString &mixUserNick) { d->mixUserNick = mixUserNick; } /// \cond void QXmppPresence::parse(const QDomElement &element) { QXmppStanza::parse(element); // attributes int type = PRESENCE_TYPES.indexOf(element.attribute(QStringLiteral("type"))); if (type > -1) d->type = Type(type); QXmppElementList unknownElements; QDomElement childElement = element.firstChildElement(); while (!childElement.isNull()) { if (childElement.tagName() == QStringLiteral("show")) { int availableStatusType = AVAILABLE_STATUS_TYPES.indexOf(childElement.text()); if (availableStatusType > -1) d->availableStatusType = AvailableStatusType(availableStatusType); } else if (childElement.tagName() == QStringLiteral("status")) { d->statusText = childElement.text(); } else if (childElement.tagName() == QStringLiteral("priority")) { d->priority = childElement.text().toInt(); // parse presence extensions // XEP-0033: Extended Stanza Addressing and errors are parsed by QXmppStanza } else if (!(childElement.tagName() == QStringLiteral("addresses") && childElement.namespaceURI() == ns_extended_addressing) && childElement.tagName() != "error") { parseExtension(childElement, unknownElements); } childElement = childElement.nextSiblingElement(); } setExtensions(unknownElements); } void QXmppPresence::parseExtension(const QDomElement &element, QXmppElementList &unknownElements) { // XEP-0045: Multi-User Chat if (element.tagName() == QStringLiteral("x") && element.namespaceURI() == ns_muc) { d->mucSupported = true; d->mucPassword = element.firstChildElement(QStringLiteral("password")).text(); } else if (element.tagName() == QStringLiteral("x") && element.namespaceURI() == ns_muc_user) { QDomElement itemElement = element.firstChildElement(QStringLiteral("item")); d->mucItem.parse(itemElement); QDomElement statusElement = element.firstChildElement(QStringLiteral("status")); d->mucStatusCodes.clear(); while (!statusElement.isNull()) { d->mucStatusCodes << statusElement.attribute(QStringLiteral("code")).toInt(); statusElement = statusElement.nextSiblingElement(QStringLiteral("status")); } // XEP-0115: Entity Capabilities } else if (element.tagName() == QStringLiteral("c") && element.namespaceURI() == ns_capabilities) { d->capabilityNode = element.attribute(QStringLiteral("node")); d->capabilityVer = QByteArray::fromBase64(element.attribute(QStringLiteral("ver")).toLatin1()); d->capabilityHash = element.attribute(QStringLiteral("hash")); #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) d->capabilityExt = element.attribute(QStringLiteral("ext")).split(' ', Qt::SkipEmptyParts); #else d->capabilityExt = element.attribute(QStringLiteral("ext")).split(' ', QString::SkipEmptyParts); #endif // XEP-0153: vCard-Based Avatars } else if (element.namespaceURI() == ns_vcard_update) { QDomElement photoElement = element.firstChildElement(QStringLiteral("photo")); if (photoElement.isNull()) { d->photoHash = {}; d->vCardUpdateType = VCardUpdateNotReady; } else { d->photoHash = QByteArray::fromHex(photoElement.text().toLatin1()); if (d->photoHash.isEmpty()) d->vCardUpdateType = VCardUpdateNoPhoto; else d->vCardUpdateType = VCardUpdateValidPhoto; } // XEP-0319: Last User Interaction in Presence } else if (element.tagName() == QStringLiteral("idle") && element.namespaceURI() == ns_idle) { if (element.hasAttribute(QStringLiteral("since"))) { const QString since = element.attribute(QStringLiteral("since")); d->lastUserInteraction = QXmppUtils::datetimeFromString(since); } // XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements } else if (element.tagName() == QStringLiteral("mix") && element.namespaceURI() == ns_mix_presence) { d->mixUserJid = element.firstChildElement(QStringLiteral("jid")).text(); d->mixUserNick = element.firstChildElement(QStringLiteral("nick")).text(); } else { unknownElements << element; } } void QXmppPresence::toXml(QXmlStreamWriter *xmlWriter) const { xmlWriter->writeStartElement(QStringLiteral("presence")); helperToXmlAddAttribute(xmlWriter, QStringLiteral("xml:lang"), lang()); helperToXmlAddAttribute(xmlWriter, QStringLiteral("id"), id()); helperToXmlAddAttribute(xmlWriter, QStringLiteral("to"), to()); helperToXmlAddAttribute(xmlWriter, QStringLiteral("from"), from()); helperToXmlAddAttribute(xmlWriter, QStringLiteral("type"), PRESENCE_TYPES.at(d->type)); const QString show = AVAILABLE_STATUS_TYPES.at(d->availableStatusType); if (!show.isEmpty()) helperToXmlAddTextElement(xmlWriter, QStringLiteral("show"), show); if (!d->statusText.isEmpty()) helperToXmlAddTextElement(xmlWriter, QStringLiteral("status"), d->statusText); if (d->priority != 0) helperToXmlAddTextElement(xmlWriter, QStringLiteral("priority"), QString::number(d->priority)); error().toXml(xmlWriter); // XEP-0045: Multi-User Chat if (d->mucSupported) { xmlWriter->writeStartElement(QStringLiteral("x")); xmlWriter->writeDefaultNamespace(ns_muc); if (!d->mucPassword.isEmpty()) xmlWriter->writeTextElement(QStringLiteral("password"), d->mucPassword); xmlWriter->writeEndElement(); } if (!d->mucItem.isNull() || !d->mucStatusCodes.isEmpty()) { xmlWriter->writeStartElement(QStringLiteral("x")); xmlWriter->writeDefaultNamespace(ns_muc_user); if (!d->mucItem.isNull()) d->mucItem.toXml(xmlWriter); for (const auto code : d->mucStatusCodes) { xmlWriter->writeStartElement(QStringLiteral("status")); xmlWriter->writeAttribute(QStringLiteral("code"), QString::number(code)); xmlWriter->writeEndElement(); } xmlWriter->writeEndElement(); } // XEP-0115: Entity Capabilities if (!d->capabilityNode.isEmpty() && !d->capabilityVer.isEmpty() && !d->capabilityHash.isEmpty()) { xmlWriter->writeStartElement(QStringLiteral("c")); xmlWriter->writeDefaultNamespace(ns_capabilities); helperToXmlAddAttribute(xmlWriter, QStringLiteral("hash"), d->capabilityHash); helperToXmlAddAttribute(xmlWriter, QStringLiteral("node"), d->capabilityNode); helperToXmlAddAttribute(xmlWriter, QStringLiteral("ver"), d->capabilityVer.toBase64()); xmlWriter->writeEndElement(); } // XEP-0153: vCard-Based Avatars if (d->vCardUpdateType != VCardUpdateNone) { xmlWriter->writeStartElement(QStringLiteral("x")); xmlWriter->writeDefaultNamespace(ns_vcard_update); switch (d->vCardUpdateType) { case VCardUpdateNoPhoto: xmlWriter->writeEmptyElement(QStringLiteral("photo")); break; case VCardUpdateValidPhoto: helperToXmlAddTextElement(xmlWriter, QStringLiteral("photo"), d->photoHash.toHex()); break; default: break; } xmlWriter->writeEndElement(); } // XEP-0319: Last User Interaction in Presence if (!d->lastUserInteraction.isNull() && d->lastUserInteraction.isValid()) { xmlWriter->writeStartElement(QStringLiteral("idle")); xmlWriter->writeDefaultNamespace(ns_idle); helperToXmlAddAttribute(xmlWriter, QStringLiteral("since"), QXmppUtils::datetimeToString(d->lastUserInteraction)); xmlWriter->writeEndElement(); } // XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements if (!d->mixUserJid.isEmpty() || !d->mixUserNick.isEmpty()) { xmlWriter->writeStartElement(QStringLiteral("mix")); xmlWriter->writeDefaultNamespace(ns_mix_presence); if (!d->mixUserJid.isEmpty()) helperToXmlAddTextElement(xmlWriter, QStringLiteral("jid"), d->mixUserJid); if (!d->mixUserNick.isEmpty()) helperToXmlAddTextElement(xmlWriter, QStringLiteral("nick"), d->mixUserNick); xmlWriter->writeEndElement(); } // unknown extensions QXmppStanza::extensionsToXml(xmlWriter); xmlWriter->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/base/QXmppPresence.h000066400000000000000000000122341402370562100172670ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 QXMPPPRESENCE_H #define QXMPPPRESENCE_H #include "QXmppMucIq.h" #include "QXmppStanza.h" class QXmppPresencePrivate; /// \brief The QXmppPresence class represents an XMPP presence stanza. /// /// \ingroup Stanzas class QXMPP_EXPORT QXmppPresence : public QXmppStanza { public: /// This enum is used to describe a presence type. enum Type { Error = 0, ///< An error has occurred regarding processing or delivery of a previously-sent presence stanza. Available, ///< Signals that the sender is online and available for communication. Unavailable, ///< Signals that the sender is no longer available for communication. Subscribe, ///< The sender wishes to subscribe to the recipient's presence. Subscribed, ///< The sender has allowed the recipient to receive their presence. Unsubscribe, ///< The sender is unsubscribing from another entity's presence. Unsubscribed, ///< The subscription request has been denied or a previously-granted subscription has been cancelled. Probe ///< A request for an entity's current presence; SHOULD be generated only by a server on behalf of a user. }; /// This enum is used to describe an availability status. enum AvailableStatusType { Online = 0, ///< The entity or resource is online. Away, ///< The entity or resource is temporarily away. XA, ///< The entity or resource is away for an extended period. DND, ///< The entity or resource is busy ("Do Not Disturb"). Chat, ///< The entity or resource is actively interested in chatting. Invisible ///< obsolete \xep{0018}: Invisible Presence }; /// This enum is used to describe vCard updates as defined by /// \xep{0153}: vCard-Based Avatars enum VCardUpdateType { VCardUpdateNone = 0, ///< Protocol is not supported VCardUpdateNoPhoto, ///< User is not using any image VCardUpdateValidPhoto, ///< User is advertising an image VCardUpdateNotReady ///< User is not ready to advertise an image /// \note This enables recipients to distinguish between the absence of an image /// (empty photo element) and mere support for the protocol (empty update child). }; QXmppPresence(QXmppPresence::Type type = QXmppPresence::Available); QXmppPresence(const QXmppPresence &other); ~QXmppPresence() override; QXmppPresence &operator=(const QXmppPresence &other); bool isXmppStanza() const override; AvailableStatusType availableStatusType() const; void setAvailableStatusType(AvailableStatusType type); int priority() const; void setPriority(int priority); QXmppPresence::Type type() const; void setType(QXmppPresence::Type); QString statusText() const; void setStatusText(const QString &statusText); // XEP-0045: Multi-User Chat QXmppMucItem mucItem() const; void setMucItem(const QXmppMucItem &item); QString mucPassword() const; void setMucPassword(const QString &password); QList mucStatusCodes() const; void setMucStatusCodes(const QList &codes); bool isMucSupported() const; void setMucSupported(bool supported); // XEP-0153: vCard-Based Avatars QByteArray photoHash() const; void setPhotoHash(const QByteArray &); VCardUpdateType vCardUpdateType() const; void setVCardUpdateType(VCardUpdateType type); // XEP-0115: Entity Capabilities QString capabilityHash() const; void setCapabilityHash(const QString &); QString capabilityNode() const; void setCapabilityNode(const QString &); QByteArray capabilityVer() const; void setCapabilityVer(const QByteArray &); QStringList capabilityExt() const; // XEP-0319: Last User Interaction in Presence QDateTime lastUserInteraction() const; void setLastUserInteraction(const QDateTime &); // XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements QString mixUserJid() const; void setMixUserJid(const QString &); QString mixUserNick() const; void setMixUserNick(const QString &); /// \cond void parse(const QDomElement &element) override; void toXml(QXmlStreamWriter *writer) const override; /// \endcond private: /// \cond void parseExtension(const QDomElement &element, QXmppElementList &unknownElements); /// \endcond QSharedDataPointer d; }; #endif // QXMPPPRESENCE_H qxmpp-1.4.0/src/base/QXmppPubSubIq.cpp000066400000000000000000000131241402370562100175470ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppPubSubIq.h" #include "QXmppConstants_p.h" #include "QXmppUtils.h" #include #include static const QStringList PUBSUB_QUERIES = { QStringLiteral("affiliations"), QStringLiteral("default"), QStringLiteral("items"), QStringLiteral("publish"), QStringLiteral("retract"), QStringLiteral("subscribe"), QStringLiteral("subscription"), QStringLiteral("subscriptions"), QStringLiteral("unsubscribe"), }; class QXmppPubSubIqPrivate : public QSharedData { public: QXmppPubSubIqPrivate(); QXmppPubSubIq::QueryType queryType; QString queryJid; QString queryNode; QList items; QString subscriptionId; QString subscriptionType; }; QXmppPubSubIqPrivate::QXmppPubSubIqPrivate() : queryType(QXmppPubSubIq::ItemsQuery) { } QXmppPubSubIq::QXmppPubSubIq() : d(new QXmppPubSubIqPrivate) { } QXmppPubSubIq::QXmppPubSubIq(const QXmppPubSubIq &iq) = default; QXmppPubSubIq::~QXmppPubSubIq() = default; QXmppPubSubIq &QXmppPubSubIq::operator=(const QXmppPubSubIq &iq) = default; /// Returns the PubSub queryType for this IQ. QXmppPubSubIq::QueryType QXmppPubSubIq::queryType() const { return d->queryType; } /// Sets the PubSub queryType for this IQ. /// /// \param queryType void QXmppPubSubIq::setQueryType(QXmppPubSubIq::QueryType queryType) { d->queryType = queryType; } /// Returns the JID being queried. QString QXmppPubSubIq::queryJid() const { return d->queryJid; } /// Sets the JID being queried. /// /// \param queryJid void QXmppPubSubIq::setQueryJid(const QString &queryJid) { d->queryJid = queryJid; } /// Returns the node being queried. QString QXmppPubSubIq::queryNode() const { return d->queryNode; } /// Sets the node being queried. /// /// \param queryNode void QXmppPubSubIq::setQueryNode(const QString &queryNode) { d->queryNode = queryNode; } /// Returns the subscription ID. QString QXmppPubSubIq::subscriptionId() const { return d->subscriptionId; } /// Sets the subscription ID. /// /// \param subscriptionId void QXmppPubSubIq::setSubscriptionId(const QString &subscriptionId) { d->subscriptionId = subscriptionId; } /// Returns the IQ's items. QList QXmppPubSubIq::items() const { return d->items; } /// Sets the IQ's items. /// /// \param items void QXmppPubSubIq::setItems(const QList &items) { d->items = items; } /// \cond bool QXmppPubSubIq::isPubSubIq(const QDomElement &element) { return element.firstChildElement(QStringLiteral("pubsub")).namespaceURI() == ns_pubsub; } void QXmppPubSubIq::parseElementFromChild(const QDomElement &element) { const QDomElement pubSubElement = element.firstChildElement(QStringLiteral("pubsub")); const QDomElement queryElement = pubSubElement.firstChildElement(); // determine query type const QString tagName = queryElement.tagName(); int queryType = PUBSUB_QUERIES.indexOf(queryElement.tagName()); if (queryType > -1) d->queryType = QueryType(queryType); d->queryJid = queryElement.attribute(QStringLiteral("jid")); d->queryNode = queryElement.attribute(QStringLiteral("node")); // parse contents QDomElement childElement; switch (d->queryType) { case QXmppPubSubIq::ItemsQuery: case QXmppPubSubIq::PublishQuery: case QXmppPubSubIq::RetractQuery: childElement = queryElement.firstChildElement(QStringLiteral("item")); while (!childElement.isNull()) { QXmppPubSubItem item; item.parse(childElement); d->items << item; childElement = childElement.nextSiblingElement(QStringLiteral("item")); } break; case QXmppPubSubIq::SubscriptionQuery: d->subscriptionId = queryElement.attribute(QStringLiteral("subid")); d->subscriptionType = queryElement.attribute(QStringLiteral("subscription")); break; default: break; } } void QXmppPubSubIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("pubsub")); writer->writeDefaultNamespace(ns_pubsub); // write query type writer->writeStartElement(PUBSUB_QUERIES.at(d->queryType)); helperToXmlAddAttribute(writer, QStringLiteral("jid"), d->queryJid); helperToXmlAddAttribute(writer, QStringLiteral("node"), d->queryNode); // write contents switch (d->queryType) { case QXmppPubSubIq::ItemsQuery: case QXmppPubSubIq::PublishQuery: case QXmppPubSubIq::RetractQuery: for (const auto &item : d->items) item.toXml(writer); break; case QXmppPubSubIq::SubscriptionQuery: helperToXmlAddAttribute(writer, QStringLiteral("subid"), d->subscriptionId); helperToXmlAddAttribute(writer, QStringLiteral("subscription"), d->subscriptionType); break; default: break; } writer->writeEndElement(); writer->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/base/QXmppPubSubIq.h000066400000000000000000000045311402370562100172160ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPPUBSUBIQ_H #define QXMPPPUBSUBIQ_H #include "QXmppIq.h" #include #if QXMPP_DEPRECATED_SINCE(1, 2) #include "QXmppPubSubItem.h" #endif class QXmppPubSubIqPrivate; /// \brief The QXmppPubSubIq class represents an IQ used for the /// publish-subscribe mechanisms defined by \xep{0060}: Publish-Subscribe. /// /// \ingroup Stanzas class QXMPP_EXPORT QXmppPubSubIq : public QXmppIq { public: /// This enum is used to describe a publish-subscribe query type. enum QueryType { AffiliationsQuery, DefaultQuery, ItemsQuery, PublishQuery, RetractQuery, SubscribeQuery, SubscriptionQuery, SubscriptionsQuery, UnsubscribeQuery }; QXmppPubSubIq(); QXmppPubSubIq(const QXmppPubSubIq &iq); ~QXmppPubSubIq(); QXmppPubSubIq &operator=(const QXmppPubSubIq &iq); QXmppPubSubIq::QueryType queryType() const; void setQueryType(QXmppPubSubIq::QueryType queryType); QString queryJid() const; void setQueryJid(const QString &jid); QString queryNode() const; void setQueryNode(const QString &node); QList items() const; void setItems(const QList &items); QString subscriptionId() const; void setSubscriptionId(const QString &id); /// \cond static bool isPubSubIq(const QDomElement &element); /// \endcond protected: /// \cond void parseElementFromChild(const QDomElement &) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: QSharedDataPointer d; }; #endif // QXMPPPUBSUBIQ_H qxmpp-1.4.0/src/base/QXmppPubSubItem.cpp000066400000000000000000000041541402370562100200770ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppPubSubItem.h" #include "QXmppElement.h" #include "QXmppUtils.h" #include class QXmppPubSubItemPrivate : public QSharedData { public: QString id; QXmppElement contents; }; QXmppPubSubItem::QXmppPubSubItem() : d(new QXmppPubSubItemPrivate) { } QXmppPubSubItem::QXmppPubSubItem(const QXmppPubSubItem &iq) = default; QXmppPubSubItem::~QXmppPubSubItem() = default; QXmppPubSubItem &QXmppPubSubItem::operator=(const QXmppPubSubItem &iq) = default; /// Returns the ID of the PubSub item. QString QXmppPubSubItem::id() const { return d->id; } /// Sets the ID of the PubSub item. /// /// \param id void QXmppPubSubItem::setId(const QString &id) { d->id = id; } /// Returns the contents of the PubSub item. QXmppElement QXmppPubSubItem::contents() const { return d->contents; } /// Sets the contents of the PubSub item. /// /// \param contents void QXmppPubSubItem::setContents(const QXmppElement &contents) { d->contents = contents; } /// \cond void QXmppPubSubItem::parse(const QDomElement &element) { d->id = element.attribute(QStringLiteral("id")); d->contents = QXmppElement(element.firstChildElement()); } void QXmppPubSubItem::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("item")); helperToXmlAddAttribute(writer, QStringLiteral("id"), d->id); d->contents.toXml(writer); writer->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/base/QXmppPubSubItem.h000066400000000000000000000031051402370562100175370ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPPUBSUBITEM_H #define QXMPPPUBSUBITEM_H #include "QXmppGlobal.h" #include class QDomElement; class QXmlStreamWriter; class QXmppElement; class QXmppPubSubItemPrivate; /// \brief The QXmppPubSubItem class represents a publish-subscribe item /// as defined by \xep{0060}: Publish-Subscribe. class QXMPP_EXPORT QXmppPubSubItem { public: QXmppPubSubItem(); QXmppPubSubItem(const QXmppPubSubItem &iq); ~QXmppPubSubItem(); QXmppPubSubItem &operator=(const QXmppPubSubItem &iq); QString id() const; void setId(const QString &id); QXmppElement contents() const; void setContents(const QXmppElement &contents); /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *writer) const; /// \endcond private: QSharedDataPointer d; }; #endif // QXMPPPUBSUBITEM_H qxmpp-1.4.0/src/base/QXmppPushEnableIq.cpp000066400000000000000000000107751402370562100204060ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Robert Märkisch * Linus Jahn * Jonah Brüchert * * 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 "QXmppPushEnableIq.h" #include "QXmppConstants_p.h" #include "QXmppDataForm.h" #include class QXmppPushEnableIqPrivate : public QSharedData { public: QString node; QString jid; QXmppPushEnableIq::Mode mode; QXmppDataForm dataForm; }; QXmppPushEnableIq::QXmppPushEnableIq() : d(new QXmppPushEnableIqPrivate()) { } QXmppPushEnableIq::QXmppPushEnableIq(const QXmppPushEnableIq &) = default; QXmppPushEnableIq::~QXmppPushEnableIq() = default; QXmppPushEnableIq &QXmppPushEnableIq::operator=(const QXmppPushEnableIq &) = default; /// /// \brief Returns the jid of the app server /// QString QXmppPushEnableIq::jid() const { return d->jid; } /// /// \brief Sets the jid of the app server /// void QXmppPushEnableIq::setJid(const QString &jid) { d->jid = jid; } /// /// \brief Returns the pubsub node on the app server used by the IQ /// QString QXmppPushEnableIq::node() const { return d->node; } /// /// \brief Set the pubsub note on the app server to be used by the IQ /// void QXmppPushEnableIq::setNode(const QString &node) { d->node = node; } /// /// \brief Returns the mode /// QXmppPushEnableIq::Mode QXmppPushEnableIq::mode() { return d->mode; } /// /// \brief Set whether the IQ should enable or disable push notifications /// void QXmppPushEnableIq::setMode(QXmppPushEnableIq::Mode mode) { d->mode = mode; } /// /// \brief Returns the data form containing the publish options which the user /// server Should send to the app server. /// /// It is only available for enable IQs. /// QXmppDataForm QXmppPushEnableIq::dataForm() const { return d->dataForm; } /// /// \brief Sets the data form containing the publish options which the user /// server Should send to the app server. /// /// It should only be set for enable IQs. /// void QXmppPushEnableIq::setDataForm(const QXmppDataForm &form) { d->dataForm = form; } /// /// \brief Checks whether a QDomElement is a push notification enable / disable /// IQ. /// bool QXmppPushEnableIq::isPushEnableIq(const QDomElement &element) { auto childElement = element.firstChildElement(); return childElement.namespaceURI() == ns_push && (childElement.tagName() == QStringLiteral("enable") || childElement.tagName() == QStringLiteral("disable")); } /// \cond void QXmppPushEnableIq::parseElementFromChild(const QDomElement &element) { QDomElement childElement = element.firstChildElement(); while (!childElement.isNull()) { if (childElement.namespaceURI() == ns_push) { if (childElement.tagName() == QStringLiteral("enable")) { d->mode = Enable; auto dataFormElement = childElement.firstChildElement("x"); if (!dataFormElement.isNull() && dataFormElement.namespaceURI() == ns_data) { QXmppDataForm dataForm; dataForm.parse(dataFormElement); d->dataForm = dataForm; } } else { d->mode = Disable; } d->jid = childElement.attribute(QStringLiteral("jid")); d->node = childElement.attribute(QStringLiteral("node")); break; } childElement = childElement.nextSiblingElement(); } } void QXmppPushEnableIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { switch (d->mode) { case Enable: writer->writeStartElement(QStringLiteral("enable")); break; case Disable: writer->writeStartElement(QStringLiteral("disable")); break; } writer->writeDefaultNamespace(ns_push); writer->writeAttribute(QStringLiteral("jid"), d->jid); writer->writeAttribute(QStringLiteral("node"), d->node); if (d->mode == Enable) { d->dataForm.toXml(writer); } writer->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/base/QXmppPushEnableIq.h000066400000000000000000000037171402370562100200510ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Robert Märkisch * Linus Jahn * Jonah Brüchert * * 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. * */ #pragma once #include class QXmppPushEnableIqPrivate; class QXmppDataForm; /// /// \brief This class represents an IQ to enable or disablepush notifications /// on the user server. /// /// \ingroup Stanzas /// /// \since QXmpp 1.3 /// class QXMPP_EXPORT QXmppPushEnableIq : public QXmppIq { public: QXmppPushEnableIq(); QXmppPushEnableIq(const QXmppPushEnableIq &); ~QXmppPushEnableIq(); QXmppPushEnableIq &operator=(const QXmppPushEnableIq &); /// /// \brief The Mode enum describes whether the IQ should enable or disable /// push notifications /// enum Mode : bool { Enable = true, Disable = false }; QString jid() const; void setJid(const QString &jid); QString node() const; void setNode(const QString &node); void setMode(Mode mode); Mode mode(); QXmppDataForm dataForm() const; void setDataForm(const QXmppDataForm &form); static bool isPushEnableIq(const QDomElement &element); protected: /// \cond void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: QSharedDataPointer d; }; qxmpp-1.4.0/src/base/QXmppRegisterIq.cpp000066400000000000000000000173161402370562100201420ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * Linus Jahn * * 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 "QXmppRegisterIq.h" #include "QXmppBitsOfBinaryDataList.h" #include "QXmppConstants_p.h" #include #include #define ELEMENT_REGISTERED QStringLiteral("registered") #define ELEMENT_REMOVE QStringLiteral("remove") class QXmppRegisterIqPrivate : public QSharedData { public: QXmppRegisterIqPrivate(); QXmppDataForm form; QString email; QString instructions; QString password; QString username; bool isRegistered; bool isRemove; QXmppBitsOfBinaryDataList bitsOfBinaryData; }; QXmppRegisterIqPrivate::QXmppRegisterIqPrivate() : isRegistered(false), isRemove(false) { } QXmppRegisterIq::QXmppRegisterIq() : d(new QXmppRegisterIqPrivate) { } QXmppRegisterIq::QXmppRegisterIq(const QXmppRegisterIq &other) = default; QXmppRegisterIq::~QXmppRegisterIq() = default; QXmppRegisterIq &QXmppRegisterIq::operator=(const QXmppRegisterIq &other) = default; /// Constructs a regular change password request. /// /// \param username The username of the account of which the password should be /// changed. /// \param newPassword The new password that should be set. /// \param to Optional JID of the registration service. If this is omitted, the /// IQ is automatically addressed to the local server. /// /// \since QXmpp 1.2 QXmppRegisterIq QXmppRegisterIq::createChangePasswordRequest(const QString &username, const QString &newPassword, const QString &to) { QXmppRegisterIq iq; iq.setType(QXmppIq::Set); iq.setTo(to); iq.setUsername(username); iq.setPassword(newPassword); return iq; } /// Constructs a regular unregistration request. /// /// \param to Optional JID of the registration service. If this is omitted, the /// IQ is automatically addressed to the local server. /// /// \since QXmpp 1.2 QXmppRegisterIq QXmppRegisterIq::createUnregistrationRequest(const QString &to) { QXmppRegisterIq iq; iq.setType(QXmppIq::Set); iq.setTo(to); iq.setIsRemove(true); return iq; } /// Returns the email for this registration IQ. QString QXmppRegisterIq::email() const { return d->email; } /// Sets the \a email for this registration IQ. void QXmppRegisterIq::setEmail(const QString &email) { d->email = email; } /// Returns the QXmppDataForm for this registration IQ. QXmppDataForm QXmppRegisterIq::form() const { return d->form; } /// Sets the QXmppDataForm for this registration IQ. /// /// \param form void QXmppRegisterIq::setForm(const QXmppDataForm &form) { d->form = form; } /// Returns the instructions for this registration IQ. QString QXmppRegisterIq::instructions() const { return d->instructions; } /// Sets the \a instructions for this registration IQ. void QXmppRegisterIq::setInstructions(const QString &instructions) { d->instructions = instructions; } /// Returns the password for this registration IQ. QString QXmppRegisterIq::password() const { return d->password; } /// Sets the \a password for this registration IQ. void QXmppRegisterIq::setPassword(const QString &password) { d->password = password; } /// Returns the username for this registration IQ. QString QXmppRegisterIq::username() const { return d->username; } /// Sets the \a username for this registration IQ. void QXmppRegisterIq::setUsername(const QString &username) { d->username = username; } /// /// Returns whether the account is registered. /// /// By default this is set to false. /// /// \since QXmpp 1.2 /// bool QXmppRegisterIq::isRegistered() const { return d->isRegistered; } /// /// Sets whether the account is registered. /// /// By default this is set to false. /// /// \since QXmpp 1.2 /// void QXmppRegisterIq::setIsRegistered(bool isRegistered) { d->isRegistered = isRegistered; } /// /// Returns whether to remove (unregister) the account. /// /// By default this is set to false. /// /// \since QXmpp 1.2 /// bool QXmppRegisterIq::isRemove() const { return d->isRemove; } /// /// Sets whether to remove (unregister) the account. /// /// By default this is set to false. /// /// \since QXmpp 1.2 /// void QXmppRegisterIq::setIsRemove(bool isRemove) { d->isRemove = isRemove; } /// /// Returns a list of data packages attached using \xep{0231}: Bits of Binary. /// /// This could be used to resolve a \c cid: URL of an CAPTCHA field of the /// form. /// /// \since QXmpp 1.2 /// QXmppBitsOfBinaryDataList QXmppRegisterIq::bitsOfBinaryData() const { return d->bitsOfBinaryData; } /// /// Returns a list of data attached using \xep{0231}: Bits of Binary. /// /// This could be used to resolve a \c cid: URL of an CAPTCHA field of the /// form. /// /// \since QXmpp 1.2 /// QXmppBitsOfBinaryDataList &QXmppRegisterIq::bitsOfBinaryData() { return d->bitsOfBinaryData; } /// /// Sets a list of \xep{0231}: Bits of Binary attachments to be included. /// /// \since QXmpp 1.2 /// void QXmppRegisterIq::setBitsOfBinaryData(const QXmppBitsOfBinaryDataList &bitsOfBinaryData) { d->bitsOfBinaryData = bitsOfBinaryData; } /// \cond bool QXmppRegisterIq::isRegisterIq(const QDomElement &element) { return (element.firstChildElement(QStringLiteral("query")).namespaceURI() == ns_register); } void QXmppRegisterIq::parseElementFromChild(const QDomElement &element) { QDomElement queryElement = element.firstChildElement(QStringLiteral("query")); d->instructions = queryElement.firstChildElement(QStringLiteral("instructions")).text(); d->username = queryElement.firstChildElement(QStringLiteral("username")).text(); d->password = queryElement.firstChildElement(QStringLiteral("password")).text(); d->email = queryElement.firstChildElement(QStringLiteral("email")).text(); d->form.parse(queryElement.firstChildElement(QStringLiteral("x"))); d->isRegistered = !queryElement.firstChildElement(ELEMENT_REGISTERED).isNull(); d->isRemove = !queryElement.firstChildElement(ELEMENT_REMOVE).isNull(); d->bitsOfBinaryData.parse(queryElement); } void QXmppRegisterIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("query")); writer->writeDefaultNamespace(ns_register); if (!d->instructions.isEmpty()) writer->writeTextElement(QStringLiteral("instructions"), d->instructions); if (d->isRegistered) writer->writeEmptyElement(ELEMENT_REGISTERED); if (d->isRemove) writer->writeEmptyElement(ELEMENT_REMOVE); if (!d->username.isEmpty()) writer->writeTextElement(QStringLiteral("username"), d->username); else if (!d->username.isNull()) writer->writeEmptyElement(QStringLiteral("username")); if (!d->password.isEmpty()) writer->writeTextElement(QStringLiteral("password"), d->password); else if (!d->password.isNull()) writer->writeEmptyElement(QStringLiteral("password")); if (!d->email.isEmpty()) writer->writeTextElement(QStringLiteral("email"), d->email); else if (!d->email.isNull()) writer->writeEmptyElement(QStringLiteral("email")); d->form.toXml(writer); d->bitsOfBinaryData.toXml(writer); writer->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/base/QXmppRegisterIq.h000066400000000000000000000050471402370562100176050ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * Linus Jahn * * 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 QXMPPREGISTERIQ_H #define QXMPPREGISTERIQ_H #include "QXmppDataForm.h" #include "QXmppIq.h" class QXmppBitsOfBinaryDataList; class QXmppRegisterIqPrivate; /// \brief The QXmppRegisterIq class represents a registration IQ /// as defined by \xep{0077}: In-Band Registration. /// /// It is used to create an account on the server. /// /// \ingroup Stanzas class QXMPP_EXPORT QXmppRegisterIq : public QXmppIq { public: QXmppRegisterIq(); QXmppRegisterIq(const QXmppRegisterIq &other); ~QXmppRegisterIq(); QXmppRegisterIq &operator=(const QXmppRegisterIq &other); static QXmppRegisterIq createChangePasswordRequest(const QString &username, const QString &newPassword, const QString &to = {}); static QXmppRegisterIq createUnregistrationRequest(const QString &to = {}); QString email() const; void setEmail(const QString &email); QXmppDataForm form() const; void setForm(const QXmppDataForm &form); QString instructions() const; void setInstructions(const QString &instructions); QString password() const; void setPassword(const QString &username); QString username() const; void setUsername(const QString &username); bool isRegistered() const; void setIsRegistered(bool isRegistered); bool isRemove() const; void setIsRemove(bool isRemove); QXmppBitsOfBinaryDataList bitsOfBinaryData() const; QXmppBitsOfBinaryDataList &bitsOfBinaryData(); void setBitsOfBinaryData(const QXmppBitsOfBinaryDataList &bitsOfBinaryData); /// \cond static bool isRegisterIq(const QDomElement &element); /// \endcond protected: /// \cond void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: QSharedDataPointer d; }; #endif qxmpp-1.4.0/src/base/QXmppResultSet.cpp000066400000000000000000000152601402370562100200120ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Olivier Goffart * * 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 "QXmppResultSet.h" #include "QXmppConstants_p.h" #include "QXmppUtils.h" #include #include QXmppResultSetQuery::QXmppResultSetQuery() : m_index(-1), m_max(-1) { } /// Returns the maximum number of results. /// /// \note -1 means no limit, 0 means no results are wanted. /// int QXmppResultSetQuery::max() const { return m_max; } /// Sets the maximum number of results. /// /// \note -1 means no limit, 0 means no results are wanted. void QXmppResultSetQuery::setMax(int max) { m_max = max; } /// Returns the index for the first element in the page. /// /// This is used for retrieving pages out of order. int QXmppResultSetQuery::index() const { return m_index; } /// Sets the index for the first element in the page. /// /// This is used for retrieving pages out of order. void QXmppResultSetQuery::setIndex(int index) { m_index = index; } /// Returns the UID of the first result in the next page. /// /// This is used for for paging backwards through results. QString QXmppResultSetQuery::before() const { return m_before; } /// Sets the UID of the first result in the next page. /// /// This is used for for paging backwards through results. void QXmppResultSetQuery::setBefore(const QString& before) { m_before = before; } /// Returns the UID of the last result in the previous page. /// /// This is used for for paging forwards through results. QString QXmppResultSetQuery::after() const { return m_after; } /// Sets the UID of the last result in the previous page. /// /// This is used for for paging forwards through results. void QXmppResultSetQuery::setAfter(const QString& after) { m_after = after; } /// Returns true if no result set information is present. bool QXmppResultSetQuery::isNull() const { return m_max == -1 && m_index == -1 && m_after.isNull() && m_before.isNull(); } /// \cond void QXmppResultSetQuery::parse(const QDomElement& element) { QDomElement setElement = (element.tagName() == QStringLiteral("set")) ? element : element.firstChildElement(QStringLiteral("set")); if (setElement.namespaceURI() == ns_rsm) { bool ok = false; m_max = setElement.firstChildElement(QStringLiteral("max")).text().toInt(&ok); if (!ok) m_max = -1; m_after = setElement.firstChildElement(QStringLiteral("after")).text(); m_before = setElement.firstChildElement(QStringLiteral("before")).text(); m_index = setElement.firstChildElement(QStringLiteral("index")).text().toInt(&ok); if (!ok) m_index = -1; } } void QXmppResultSetQuery::toXml(QXmlStreamWriter* writer) const { if (isNull()) return; writer->writeStartElement(QStringLiteral("set")); writer->writeDefaultNamespace(ns_rsm); if (m_max >= 0) helperToXmlAddTextElement(writer, QStringLiteral("max"), QString::number(m_max)); if (!m_after.isNull()) helperToXmlAddTextElement(writer, QStringLiteral("after"), m_after); if (!m_before.isNull()) helperToXmlAddTextElement(writer, QStringLiteral("before"), m_before); if (m_index >= 0) helperToXmlAddTextElement(writer, QStringLiteral("index"), QString::number(m_index)); writer->writeEndElement(); } /// \endcond QXmppResultSetReply::QXmppResultSetReply() : m_count(-1), m_index(-1) { } /// Returns the UID of the first result in the page. QString QXmppResultSetReply::first() const { return m_first; } /// Sets the UID of the first result in the page. void QXmppResultSetReply::setFirst(const QString& first) { m_first = first; } /// Returns the UID of the last result in the page. QString QXmppResultSetReply::last() const { return m_last; } /// Sets the UID of the last result in the page. void QXmppResultSetReply::setLast(const QString& last) { m_last = last; } /// Returns the total number of items in the set. /// /// \note This may be an approximate count. int QXmppResultSetReply::count() const { return m_count; } /// Sets the total number of items in the set. /// /// \note This may be an approximate count. void QXmppResultSetReply::setCount(int count) { m_count = count; } /// Returns the index for the first result in the page. /// /// This is used for retrieving pages out of order. /// /// \note This may be an approximate index. int QXmppResultSetReply::index() const { return m_index; } /// Sets the index for the first result in the page. /// /// This is used for retrieving pages out of order. /// /// \note This may be an approximate index. void QXmppResultSetReply::setIndex(int index) { m_index = index; } /// Returns true if no result set information is present. bool QXmppResultSetReply::isNull() const { return m_count == -1 && m_index == -1 && m_first.isNull() && m_last.isNull(); } /// \cond void QXmppResultSetReply::parse(const QDomElement& element) { QDomElement setElement = (element.tagName() == "set") ? element : element.firstChildElement("set"); if (setElement.namespaceURI() == ns_rsm) { m_count = setElement.firstChildElement("count").text().toInt(); QDomElement firstElem = setElement.firstChildElement("first"); m_first = firstElem.text(); bool ok = false; m_index = firstElem.attribute("index").toInt(&ok); if (!ok) m_index = -1; m_last = setElement.firstChildElement("last").text(); } } void QXmppResultSetReply::toXml(QXmlStreamWriter* writer) const { if (isNull()) return; writer->writeStartElement("set"); writer->writeDefaultNamespace(ns_rsm); if (!m_first.isNull() || m_index >= 0) { writer->writeStartElement("first"); if (m_index >= 0) writer->writeAttribute("index", QString::number(m_index)); writer->writeCharacters(m_first); writer->writeEndElement(); } if (!m_last.isNull()) helperToXmlAddTextElement(writer, "last", m_last); if (m_count >= 0) helperToXmlAddTextElement(writer, "count", QString::number(m_count)); writer->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/base/QXmppResultSet.h000066400000000000000000000042531402370562100174570ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Olivier Goffart * * 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 QXMPPRESULTSET_H #define QXMPPRESULTSET_H #include "QXmppStanza.h" #include /// \brief The QXmppResultSetQuery class represents a set element in a query /// as defined by \xep{0059}: Result Set Management. class QXMPP_EXPORT QXmppResultSetQuery { public: QXmppResultSetQuery(); int max() const; void setMax(int max); int index() const; void setIndex(int index); QString before() const; void setBefore(const QString &before); QString after() const; void setAfter(const QString &after); bool isNull() const; /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *writer) const; /// \endcond private: int m_index; int m_max; QString m_after; QString m_before; }; /// \brief The QXmppResultSetReply class represents a set element in a reply /// as defined by \xep{0059}: Result Set Management. class QXMPP_EXPORT QXmppResultSetReply { public: QXmppResultSetReply(); QString first() const; void setFirst(const QString &first); QString last() const; void setLast(const QString &last); int count() const; void setCount(int count); int index() const; void setIndex(int index); bool isNull() const; /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *writer) const; /// \endcond private: int m_count; int m_index; QString m_first; QString m_last; }; #endif // QXMPPRESULTSET_H qxmpp-1.4.0/src/base/QXmppRosterIq.cpp000066400000000000000000000266771402370562100176460ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Manjeet Dahiya * Jeremy Lainé * * 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 "QXmppRosterIq.h" #include "QXmppConstants_p.h" #include "QXmppUtils.h" #include #include #include class QXmppRosterIqPrivate : public QSharedData { public: QList 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(const QXmppRosterIq &) = default; QXmppRosterIq::~QXmppRosterIq() = default; QXmppRosterIq &QXmppRosterIq::operator=(const QXmppRosterIq &) = default; /// /// Adds an item to the roster IQ. /// /// \param item /// void QXmppRosterIq::addItem(const Item &item) { d->items.append(item); } /// /// Returns the roster IQ's items. /// QList QXmppRosterIq::items() const { return d->items; } /// /// Returns the roster version of IQ. /// /// \return version as a QString /// /// \since QXmpp 1.0 /// QString QXmppRosterIq::version() const { return d->version; } /// /// Sets the roster version of IQ. /// /// \param version as a QString /// /// \since QXmpp 1.0 /// void QXmppRosterIq::setVersion(const QString &version) { d->version = version; } /// /// Whether to annotate which items are MIX channels. /// /// \since QXmpp 1.3 /// 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. /// /// \since QXmpp 1.3 /// void QXmppRosterIq::setMixAnnotate(bool mixAnnotate) { d->mixAnnotate = mixAnnotate; } /// \cond bool QXmppRosterIq::isRosterIq(const QDomElement &element) { return (element.firstChildElement(QStringLiteral("query")).namespaceURI() == ns_roster); } void QXmppRosterIq::parseElementFromChild(const QDomElement &element) { QDomElement queryElement = element.firstChildElement(QStringLiteral("query")); setVersion(queryElement.attribute(QStringLiteral("ver"))); QDomElement itemElement = queryElement.firstChildElement(QStringLiteral("item")); while (!itemElement.isNull()) { QXmppRosterIq::Item item; item.parse(itemElement); d->items.append(item); itemElement = itemElement.nextSiblingElement(QStringLiteral("item")); } QDomElement annotateElement = queryElement.firstChildElement(QStringLiteral("annotate")); setMixAnnotate(!annotateElement.isNull() && annotateElement.namespaceURI() == ns_mix_roster); } void QXmppRosterIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("query")); writer->writeDefaultNamespace(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(QStringLiteral("ver"), version()); // XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements if (d->mixAnnotate) { writer->writeStartElement(QStringLiteral("annotate")); writer->writeAttribute(QStringLiteral("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: ItemPrivate(); QString bareJid; Item::SubscriptionType type; QString name; // can be subscribe/unsubscribe (attribute "ask") QString subscriptionStatus; QSet groups; bool approved; // XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements bool isMixChannel = false; QString mixParticipantId; }; QXmppRosterIq::ItemPrivate::ItemPrivate() : type(QXmppRosterIq::Item::NotSet), approved(false) { } /// /// Constructs a new roster entry. /// QXmppRosterIq::Item::Item() : d(new ItemPrivate) { } QXmppRosterIq::Item::Item(const QXmppRosterIq::Item &other) = default; QXmppRosterIq::Item::~Item() = default; QXmppRosterIq::Item &QXmppRosterIq::Item::operator=(const Item &other) = default; /// /// Returns the bareJid of the roster entry. /// /// \return bareJid as a QString /// QString QXmppRosterIq::Item::bareJid() const { return d->bareJid; } /// Sets the bareJid of the roster entry. /// /// \param bareJid as a QString /// void QXmppRosterIq::Item::setBareJid(const QString &bareJid) { d->bareJid = bareJid; } /// /// Returns the groups of the roster entry. /// /// \return QSet list of all the groups /// QSet QXmppRosterIq::Item::groups() const { return d->groups; } /// /// Sets the groups of the roster entry. /// /// \param groups list of all the groups as a QSet /// void QXmppRosterIq::Item::setGroups(const QSet &groups) { d->groups = groups; } /// /// Returns the name of the roster entry. /// /// \return name as a QString /// QString QXmppRosterIq::Item::name() const { return d->name; } /// /// Sets the name of the roster entry. /// /// \param name as a QString /// void QXmppRosterIq::Item::setName(const QString &name) { d->name = name; } /// /// Returns the subscription status of the roster entry. It is the "ask" /// attribute in the Roster IQ stanza. Its value can be "subscribe" or "unsubscribe" /// or empty. /// /// \return subscription status as a QString /// QString QXmppRosterIq::Item::subscriptionStatus() const { return d->subscriptionStatus; } /// /// Sets the subscription status of the roster entry. It is the "ask" /// attribute in the Roster IQ stanza. Its value can be "subscribe" or "unsubscribe" /// or empty. /// /// \param status as a QString /// void QXmppRosterIq::Item::setSubscriptionStatus(const QString &status) { d->subscriptionStatus = status; } /// /// Returns the subscription type of the roster entry. /// QXmppRosterIq::Item::SubscriptionType QXmppRosterIq::Item::subscriptionType() const { return d->type; } /// /// Sets the subscription type of the roster entry. /// /// \param type /// void QXmppRosterIq::Item::setSubscriptionType(SubscriptionType type) { d->type = type; } /// /// Returns whether the item has a pre-approved presence subscription. /// /// \since QXmpp 1.3 /// bool QXmppRosterIq::Item::isApproved() const { return d->approved; } /// /// Sets whether the item has a pre-approved presence subscription. /// /// This cannot be used to initiate a pre-approved subscription. For this /// purpose the client must send a <presence/> stanza of type /// \c subscribed to the user. /// /// \since QXmpp 1.3 /// void QXmppRosterIq::Item::setIsApproved(bool approved) { d->approved = approved; } QString QXmppRosterIq::Item::getSubscriptionTypeStr() const { switch (d->type) { case NotSet: return ""; case None: return "none"; case Both: return "both"; case From: return "from"; case To: return "to"; case Remove: return "remove"; default: { qWarning("QXmppRosterIq::Item::getTypeStr(): invalid type"); return ""; } } } void QXmppRosterIq::Item::setSubscriptionTypeFromStr(const QString &type) { if (type.isEmpty()) setSubscriptionType(NotSet); else if (type == QStringLiteral("none")) setSubscriptionType(None); else if (type == QStringLiteral("both")) setSubscriptionType(Both); else if (type == QStringLiteral("from")) setSubscriptionType(From); else if (type == QStringLiteral("to")) setSubscriptionType(To); else if (type == QStringLiteral("remove")) setSubscriptionType(Remove); else qWarning("QXmppRosterIq::Item::setTypeFromStr(): invalid type"); } /// /// Returns whether this is a MIX channel. /// /// \since QXmpp 1.3 /// bool QXmppRosterIq::Item::isMixChannel() const { return d->isMixChannel; } /// /// Sets whether this is a MIX channel. /// /// \since QXmpp 1.3 /// void QXmppRosterIq::Item::setIsMixChannel(bool isMixChannel) { d->isMixChannel = isMixChannel; } /// /// Returns the participant id for this MIX channel. /// /// \since QXmpp 1.3 /// QString QXmppRosterIq::Item::mixParticipantId() const { return d->mixParticipantId; } /// /// Sets the participant id for this MIX channel. /// /// \since QXmpp 1.3 /// void QXmppRosterIq::Item::setMixParticipantId(const QString &participantId) { d->mixParticipantId = participantId; } /// \cond void QXmppRosterIq::Item::parse(const QDomElement &element) { d->name = element.attribute(QStringLiteral("name")); d->bareJid = element.attribute(QStringLiteral("jid")); setSubscriptionTypeFromStr(element.attribute(QStringLiteral("subscription"))); setSubscriptionStatus(element.attribute(QStringLiteral("ask"))); // pre-approved const QString approved = element.attribute(QStringLiteral("approved")); d->approved = (approved == QStringLiteral("1") || approved == QStringLiteral("true")); // groups QDomElement groupElement = element.firstChildElement(QStringLiteral("group")); while (!groupElement.isNull()) { d->groups << groupElement.text(); groupElement = groupElement.nextSiblingElement(QStringLiteral("group")); } // XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements QDomElement channelElement = element.firstChildElement(QStringLiteral("channel")); if (!channelElement.isNull() && channelElement.namespaceURI() == ns_mix_roster) { d->isMixChannel = true; d->mixParticipantId = channelElement.attribute(QStringLiteral("participant-id")); } } void QXmppRosterIq::Item::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("item")); helperToXmlAddAttribute(writer, QStringLiteral("jid"), d->bareJid); helperToXmlAddAttribute(writer, QStringLiteral("name"), d->name); helperToXmlAddAttribute(writer, QStringLiteral("subscription"), getSubscriptionTypeStr()); helperToXmlAddAttribute(writer, QStringLiteral("ask"), subscriptionStatus()); if (d->approved) writer->writeAttribute(QStringLiteral("approved"), QStringLiteral("true")); QSet::const_iterator i = d->groups.constBegin(); while (i != d->groups.constEnd()) { helperToXmlAddTextElement(writer, QStringLiteral("group"), *i); ++i; } // XEP-0405: Mediated Information eXchange (MIX): Participant Server Requirements if (d->isMixChannel) { writer->writeStartElement(QStringLiteral("channel")); writer->writeAttribute(QStringLiteral("xmlns"), ns_mix_roster); helperToXmlAddAttribute(writer, QStringLiteral("participant-id"), d->mixParticipantId); writer->writeEndElement(); } writer->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/base/QXmppRosterIq.h000066400000000000000000000101621402370562100172710ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Manjeet Dahiya * Jeremy Lainé * * 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 QXMPPROSTERIQ_H #define QXMPPROSTERIQ_H #include "QXmppIq.h" #include #include #include class QXmppRosterIqPrivate; /// \brief The QXmppRosterIq class represents a roster IQ. /// /// \ingroup Stanzas class QXMPP_EXPORT QXmppRosterIq : public QXmppIq { public: class ItemPrivate; /// \brief The QXmppRosterIq::Item class represents a roster entry. class QXMPP_EXPORT Item { public: /// An enumeration for type of subscription with the bareJid in the roster. enum SubscriptionType { None = 0, ///< the user does not have a subscription to the ///< contact's presence information, and the contact does ///< not have a subscription to the user's presence information From = 1, ///< the contact has a subscription to the user's presence information, ///< but the user does not have a subscription to the contact's presence information To = 2, ///< the user has a subscription to the contact's presence information, ///< but the contact does not have a subscription to the user's presence information Both = 3, ///< both the user and the contact have subscriptions to each ///< other's presence information Remove = 4, ///< to delete a roster item NotSet = 8 ///< the subscription state was not specified }; Item(); Item(const Item &other); ~Item(); Item &operator=(const Item &other); QString bareJid() const; QSet groups() const; QString name() const; QString subscriptionStatus() const; SubscriptionType subscriptionType() const; bool isApproved() const; void setBareJid(const QString &); void setGroups(const QSet &); void setName(const QString &); void setSubscriptionStatus(const QString &); void setSubscriptionType(SubscriptionType); void setIsApproved(bool); // 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; /// \endcond private: QString getSubscriptionTypeStr() const; void setSubscriptionTypeFromStr(const QString &); QSharedDataPointer d; }; QXmppRosterIq(); QXmppRosterIq(const QXmppRosterIq &); ~QXmppRosterIq() override; QXmppRosterIq &operator=(const QXmppRosterIq &); QString version() const; void setVersion(const QString &); void addItem(const Item &); QList 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) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: QSharedDataPointer d; }; #endif // QXMPPROSTERIQ_H qxmpp-1.4.0/src/base/QXmppRpcIq.cpp000066400000000000000000000316541402370562100171030ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Ian Reinhart Geiser * Jeremy Lainé * * 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 "QXmppRpcIq.h" #include "QXmppConstants_p.h" #include "QXmppUtils.h" #include #include #include #include #include void QXmppRpcMarshaller::marshall(QXmlStreamWriter *writer, const QVariant &value) { writer->writeStartElement(QStringLiteral("value")); switch (value.type()) { case QVariant::Int: case QVariant::UInt: case QVariant::LongLong: case QVariant::ULongLong: writer->writeTextElement(QStringLiteral("i4"), value.toString()); break; case QVariant::Double: writer->writeTextElement(QStringLiteral("double"), value.toString()); break; case QVariant::Bool: writer->writeTextElement(QStringLiteral("boolean"), value.toBool() ? QStringLiteral("1") : QStringLiteral("0")); break; case QVariant::Date: writer->writeTextElement(QStringLiteral("dateTime.iso8601"), value.toDate().toString(Qt::ISODate)); break; case QVariant::DateTime: writer->writeTextElement(QStringLiteral("dateTime.iso8601"), value.toDateTime().toString(Qt::ISODate)); break; case QVariant::Time: writer->writeTextElement(QStringLiteral("dateTime.iso8601"), value.toTime().toString(Qt::ISODate)); break; case QVariant::StringList: case QVariant::List: { writer->writeStartElement(QStringLiteral("array")); writer->writeStartElement(QStringLiteral("data")); for (const auto &item : value.toList()) marshall(writer, item); writer->writeEndElement(); writer->writeEndElement(); break; } case QVariant::Map: { writer->writeStartElement(QStringLiteral("struct")); const QMap map = value.toMap(); QMap::ConstIterator index = map.begin(); while (index != map.end()) { writer->writeStartElement(QStringLiteral("member")); writer->writeTextElement(QStringLiteral("name"), index.key()); marshall(writer, *index); writer->writeEndElement(); ++index; } writer->writeEndElement(); break; } case QVariant::ByteArray: { writer->writeTextElement(QStringLiteral("base64"), value.toByteArray().toBase64()); break; } default: { if (value.isNull()) writer->writeEmptyElement(QStringLiteral("nil")); else if (value.canConvert(QVariant::String)) { writer->writeTextElement(QStringLiteral("string"), value.toString()); } break; } } writer->writeEndElement(); } QVariant QXmppRpcMarshaller::demarshall(const QDomElement &elem, QStringList &errors) { if (elem.tagName().toLower() != QStringLiteral("value")) { errors << "Bad param value"; return QVariant(); } if (!elem.firstChild().isElement()) { return QVariant(elem.text()); } const QDomElement typeData = elem.firstChild().toElement(); const QString typeName = typeData.tagName().toLower(); if (typeName == QStringLiteral("nil")) { return QVariant(); } if (typeName == QStringLiteral("string")) { return QVariant(typeData.text()); } else if (typeName == QStringLiteral("int") || typeName == QStringLiteral("i4")) { bool ok = false; QVariant val(typeData.text().toInt(&ok)); if (ok) return val; errors << "I was looking for an integer but data was courupt"; return QVariant(); } else if (typeName == QStringLiteral("double")) { bool ok = false; QVariant val(typeData.text().toDouble(&ok)); if (ok) return val; errors << "I was looking for an double but data was corrupt"; } else if (typeName == QStringLiteral("boolean")) return QVariant(typeData.text() == QStringLiteral("1") || typeData.text().toLower() == QStringLiteral("true")); else if (typeName == QStringLiteral("datetime") || typeName == QStringLiteral("datetime.iso8601")) return QVariant(QDateTime::fromString(typeData.text(), Qt::ISODate)); else if (typeName == QStringLiteral("array")) { QVariantList arr; QDomElement valueNode = typeData.firstChildElement(QStringLiteral("data")).firstChildElement(); while (!valueNode.isNull() && errors.isEmpty()) { arr.append(demarshall(valueNode, errors)); valueNode = valueNode.nextSiblingElement(); } return QVariant(arr); } else if (typeName == QStringLiteral("struct")) { QMap stct; QDomNode valueNode = typeData.firstChild(); while (!valueNode.isNull() && errors.isEmpty()) { const QDomElement memberNode = valueNode.toElement().elementsByTagName(QStringLiteral("name")).item(0).toElement(); const QDomElement dataNode = valueNode.toElement().elementsByTagName(QStringLiteral("value")).item(0).toElement(); stct[memberNode.text()] = demarshall(dataNode, errors); valueNode = valueNode.nextSibling(); } return QVariant(stct); } else if (typeName == QStringLiteral("base64")) { QVariant returnVariant; QByteArray dest; QByteArray src = typeData.text().toLatin1(); return QVariant(QByteArray::fromBase64(src)); } errors << QStringLiteral("Cannot handle type %1").arg(typeName); return QVariant(); } QXmppRpcErrorIq::QXmppRpcErrorIq() : QXmppIq(QXmppIq::Error) { } QXmppRpcInvokeIq QXmppRpcErrorIq::query() const { return m_query; } void QXmppRpcErrorIq::setQuery(const QXmppRpcInvokeIq &query) { m_query = query; } /// \cond bool QXmppRpcErrorIq::isRpcErrorIq(const QDomElement &element) { QString type = element.attribute(QStringLiteral("type")); QDomElement errorElement = element.firstChildElement(QStringLiteral("error")); QDomElement queryElement = element.firstChildElement(QStringLiteral("query")); return (type == QStringLiteral("error")) && !errorElement.isNull() && queryElement.namespaceURI() == ns_rpc; } void QXmppRpcErrorIq::parseElementFromChild(const QDomElement &element) { m_query.parseElementFromChild(element); } void QXmppRpcErrorIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { m_query.toXmlElementFromChild(writer); } /// \endcond QXmppRpcResponseIq::QXmppRpcResponseIq() : QXmppIq(QXmppIq::Result), m_faultCode(0) { } /// Returns the fault code. /// int QXmppRpcResponseIq::faultCode() const { return m_faultCode; } /// Sets the fault code. /// /// \param faultCode void QXmppRpcResponseIq::setFaultCode(int faultCode) { m_faultCode = faultCode; } /// Returns the fault string. /// QString QXmppRpcResponseIq::faultString() const { return m_faultString; } /// Sets the fault string. /// /// \param faultString void QXmppRpcResponseIq::setFaultString(const QString &faultString) { m_faultString = faultString; } /// Returns the response values. /// QVariantList QXmppRpcResponseIq::values() const { return m_values; } /// Sets the response values. /// /// \param values void QXmppRpcResponseIq::setValues(const QVariantList &values) { m_values = values; } /// \cond bool QXmppRpcResponseIq::isRpcResponseIq(const QDomElement &element) { QString type = element.attribute(QStringLiteral("type")); QDomElement dataElement = element.firstChildElement(QStringLiteral("query")); return dataElement.namespaceURI() == ns_rpc && type == QStringLiteral("result"); } void QXmppRpcResponseIq::parseElementFromChild(const QDomElement &element) { QDomElement queryElement = element.firstChildElement(QStringLiteral("query")); QDomElement methodElement = queryElement.firstChildElement(QStringLiteral("methodResponse")); const QDomElement contents = methodElement.firstChildElement(); if (contents.tagName().toLower() == QStringLiteral("params")) { QDomNode param = contents.firstChildElement(QStringLiteral("param")); while (!param.isNull()) { QStringList errors; const QVariant value = QXmppRpcMarshaller::demarshall(param.firstChildElement(QStringLiteral("value")), errors); if (!errors.isEmpty()) break; m_values << value; param = param.nextSiblingElement(QStringLiteral("param")); } } else if (contents.tagName().toLower() == QStringLiteral("fault")) { QStringList errors; const QDomElement errElement = contents.firstChildElement(QStringLiteral("value")); const QVariant error = QXmppRpcMarshaller::demarshall(errElement, errors); if (!errors.isEmpty()) return; m_faultCode = error.toMap()[QStringLiteral("faultCode")].toInt(); m_faultString = error.toMap()[QStringLiteral("faultString")].toString(); } } void QXmppRpcResponseIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("query")); writer->writeDefaultNamespace(ns_rpc); writer->writeStartElement(QStringLiteral("methodResponse")); if (m_faultCode) { writer->writeStartElement(QStringLiteral("fault")); QMap fault; fault[QStringLiteral("faultCode")] = m_faultCode; fault[QStringLiteral("faultString")] = m_faultString; QXmppRpcMarshaller::marshall(writer, fault); writer->writeEndElement(); } else if (!m_values.isEmpty()) { writer->writeStartElement(QStringLiteral("params")); for (const auto &arg : m_values) { writer->writeStartElement(QStringLiteral("param")); QXmppRpcMarshaller::marshall(writer, arg); writer->writeEndElement(); } writer->writeEndElement(); } writer->writeEndElement(); writer->writeEndElement(); } /// \endcond QXmppRpcInvokeIq::QXmppRpcInvokeIq() : QXmppIq(QXmppIq::Set) { } /// Returns the method arguments. /// QVariantList QXmppRpcInvokeIq::arguments() const { return m_arguments; } /// Sets the method arguments. /// /// \param arguments void QXmppRpcInvokeIq::setArguments(const QVariantList &arguments) { m_arguments = arguments; } /// Returns the method name. /// QString QXmppRpcInvokeIq::method() const { return m_method; } /// Sets the method name. /// /// \param method void QXmppRpcInvokeIq::setMethod(const QString &method) { m_method = method; } /// \cond bool QXmppRpcInvokeIq::isRpcInvokeIq(const QDomElement &element) { QString type = element.attribute(QStringLiteral("type")); QDomElement dataElement = element.firstChildElement(QStringLiteral("query")); return dataElement.namespaceURI() == ns_rpc && type == QStringLiteral("set"); } void QXmppRpcInvokeIq::parseElementFromChild(const QDomElement &element) { QDomElement queryElement = element.firstChildElement(QStringLiteral("query")); QDomElement methodElement = queryElement.firstChildElement(QStringLiteral("methodCall")); m_method = methodElement.firstChildElement(QStringLiteral("methodName")).text(); const QDomElement methodParams = methodElement.firstChildElement(QStringLiteral("params")); m_arguments.clear(); if (!methodParams.isNull()) { QDomNode param = methodParams.firstChildElement(QStringLiteral("param")); while (!param.isNull()) { QStringList errors; QVariant arg = QXmppRpcMarshaller::demarshall(param.firstChildElement(QStringLiteral("value")), errors); if (!errors.isEmpty()) break; m_arguments << arg; param = param.nextSiblingElement(QStringLiteral("param")); } } } void QXmppRpcInvokeIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("query")); writer->writeDefaultNamespace(ns_rpc); writer->writeStartElement(QStringLiteral("methodCall")); writer->writeTextElement(QStringLiteral("methodName"), m_method); if (!m_arguments.isEmpty()) { writer->writeStartElement(QStringLiteral("params")); for (const auto &arg : m_arguments) { writer->writeStartElement(QStringLiteral("param")); QXmppRpcMarshaller::marshall(writer, arg); writer->writeEndElement(); } writer->writeEndElement(); } writer->writeEndElement(); writer->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/base/QXmppRpcIq.h000066400000000000000000000061511402370562100165420ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Ian Reinhart Geiser * Jeremy Lainé * * 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 QXMPPRPCIQ_H #define QXMPPRPCIQ_H #include "QXmppIq.h" #include class QXMPP_EXPORT QXmppRpcMarshaller { public: static void marshall(QXmlStreamWriter *writer, const QVariant &value); static QVariant demarshall(const QDomElement &elem, QStringList &errors); }; /// \brief The QXmppRpcResponseIq class represents an IQ used to carry /// an RPC response as specified by \xep{0009}: Jabber-RPC. /// /// \ingroup Stanzas class QXMPP_EXPORT QXmppRpcResponseIq : public QXmppIq { public: QXmppRpcResponseIq(); int faultCode() const; void setFaultCode(int faultCode); QString faultString() const; void setFaultString(const QString &faultString); QVariantList values() const; void setValues(const QVariantList &values); /// \cond static bool isRpcResponseIq(const QDomElement &element); /// \endcond protected: /// \cond void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: int m_faultCode; QString m_faultString; QVariantList m_values; }; /// \brief The QXmppRpcInvokeIq class represents an IQ used to carry /// an RPC invocation as specified by \xep{0009}: Jabber-RPC. /// /// \ingroup Stanzas class QXMPP_EXPORT QXmppRpcInvokeIq : public QXmppIq { public: QXmppRpcInvokeIq(); QString method() const; void setMethod(const QString &method); QVariantList arguments() const; void setArguments(const QVariantList &arguments); /// \cond static bool isRpcInvokeIq(const QDomElement &element); /// \endcond protected: /// \cond void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: QVariantList m_arguments; QString m_method; friend class QXmppRpcErrorIq; }; class QXMPP_EXPORT QXmppRpcErrorIq : public QXmppIq { public: QXmppRpcErrorIq(); QXmppRpcInvokeIq query() const; void setQuery(const QXmppRpcInvokeIq &query); /// \cond static bool isRpcErrorIq(const QDomElement &element); /// \endcond protected: /// \cond void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: QXmppRpcInvokeIq m_query; }; #endif // QXMPPRPCIQ_H qxmpp-1.4.0/src/base/QXmppSasl.cpp000066400000000000000000000702021402370562100167570ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Manjeet Dahiya * Jeremy Lainé * * 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 "QXmppSasl_p.h" #include "QXmppUtils.h" #include #include #include #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 2) #define QT_HAS_SHA3_SUPPORT #endif const char *ns_xmpp_sasl = "urn:ietf:params:xml:ns:xmpp-sasl"; static QByteArray forcedNonce; // When adding new algorithms, also add them to QXmppSaslClient::availableMechanisms(). static const QMap SCRAM_ALGORITHMS = { { QStringLiteral("SCRAM-SHA-1"), QCryptographicHash::Sha1 }, { QStringLiteral("SCRAM-SHA-256"), QCryptographicHash::Sha256 }, { QStringLiteral("SCRAM-SHA-512"), QCryptographicHash::Sha512 }, #ifdef QT_HAS_SHA3_SUPPORT { QStringLiteral("SCRAM-SHA3-512"), QCryptographicHash::RealSha3_512 }, #endif }; // Returns the hash length in bytes (QCH::hashLength() only exists since 5.12). int hashLength(QCryptographicHash::Algorithm algorithm) { #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) return QCryptographicHash::hashLength(algorithm); #else switch (algorithm) { case QCryptographicHash::Sha1: return 160 / 8; case QCryptographicHash::Sha256: return 256 / 8; case QCryptographicHash::Sha512: #ifdef QT_HAS_SHA3_SUPPORT case QCryptographicHash::RealSha3_512: #endif return 512 / 8; default: return QCryptographicHash::hash({}, algorithm).size(); } #endif } // Calculate digest response for use with XMPP/SASL. static QByteArray calculateDigest(const QByteArray &method, const QByteArray &digestUri, const QByteArray &secret, const QByteArray &nonce, const QByteArray &cnonce, const QByteArray &nc) { const QByteArray A1 = secret + ':' + nonce + ':' + cnonce; const QByteArray A2 = method + ':' + digestUri; QByteArray HA1 = QCryptographicHash::hash(A1, QCryptographicHash::Md5).toHex(); QByteArray HA2 = QCryptographicHash::hash(A2, QCryptographicHash::Md5).toHex(); const QByteArray KD = HA1 + ':' + nonce + ':' + nc + ':' + cnonce + ":auth:" + HA2; 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(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()); } key += tkey; currentIteration++; } return key.left(dkLen); } static QByteArray generateNonce() { if (!forcedNonce.isEmpty()) return forcedNonce; QByteArray nonce = QXmppUtils::generateRandomBytes(32); // The random data can the '=' char is not valid as it is a delimiter, // so to be safe, base64 the nonce return nonce.toBase64(); } static QMap parseGS2(const QByteArray &ba) { QMap map; for (const auto &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) { } QString QXmppSaslAuth::mechanism() const { return m_mechanism; } void QXmppSaslAuth::setMechanism(const QString &mechanism) { m_mechanism = mechanism; } QByteArray QXmppSaslAuth::value() const { return m_value; } void QXmppSaslAuth::setValue(const QByteArray &value) { m_value = value; } void QXmppSaslAuth::parse(const QDomElement &element) { m_mechanism = element.attribute(QStringLiteral("mechanism")); m_value = QByteArray::fromBase64(element.text().toLatin1()); } void QXmppSaslAuth::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("auth")); writer->writeDefaultNamespace(ns_xmpp_sasl); writer->writeAttribute(QStringLiteral("mechanism"), m_mechanism); if (!m_value.isEmpty()) writer->writeCharacters(m_value.toBase64()); writer->writeEndElement(); } QXmppSaslChallenge::QXmppSaslChallenge(const QByteArray &value) : m_value(value) { } QByteArray QXmppSaslChallenge::value() const { return m_value; } void QXmppSaslChallenge::setValue(const QByteArray &value) { m_value = value; } void QXmppSaslChallenge::parse(const QDomElement &element) { m_value = QByteArray::fromBase64(element.text().toLatin1()); } void QXmppSaslChallenge::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("challenge")); writer->writeDefaultNamespace(ns_xmpp_sasl); if (!m_value.isEmpty()) writer->writeCharacters(m_value.toBase64()); writer->writeEndElement(); } QXmppSaslFailure::QXmppSaslFailure(const QString &condition) : m_condition(condition) { } QString QXmppSaslFailure::condition() const { return m_condition; } void QXmppSaslFailure::setCondition(const QString &condition) { m_condition = condition; } void QXmppSaslFailure::parse(const QDomElement &element) { m_condition = element.firstChildElement().tagName(); } void QXmppSaslFailure::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("failure")); writer->writeDefaultNamespace(ns_xmpp_sasl); if (!m_condition.isEmpty()) writer->writeEmptyElement(m_condition); writer->writeEndElement(); } QXmppSaslResponse::QXmppSaslResponse(const QByteArray &value) : m_value(value) { } QByteArray QXmppSaslResponse::value() const { return m_value; } void QXmppSaslResponse::setValue(const QByteArray &value) { m_value = value; } void QXmppSaslResponse::parse(const QDomElement &element) { m_value = QByteArray::fromBase64(element.text().toLatin1()); } void QXmppSaslResponse::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("response")); writer->writeDefaultNamespace(ns_xmpp_sasl); if (!m_value.isEmpty()) writer->writeCharacters(m_value.toBase64()); writer->writeEndElement(); } QXmppSaslSuccess::QXmppSaslSuccess() { } void QXmppSaslSuccess::parse(const QDomElement &element) { Q_UNUSED(element); } void QXmppSaslSuccess::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("success")); writer->writeDefaultNamespace(ns_xmpp_sasl); writer->writeEndElement(); } class QXmppSaslClientPrivate { public: QString host; QString serviceType; QString username; QString password; }; QXmppSaslClient::QXmppSaslClient(QObject *parent) : QXmppLoggable(parent), d(new QXmppSaslClientPrivate) { } QXmppSaslClient::~QXmppSaslClient() { delete d; } /// /// Returns a list of supported mechanisms. /// QStringList QXmppSaslClient::availableMechanisms() { return { #ifdef QT_HAS_SHA3_SUPPORT QStringLiteral("SCRAM-SHA3-512"), #endif QStringLiteral("SCRAM-SHA-512"), QStringLiteral("SCRAM-SHA-256"), QStringLiteral("SCRAM-SHA-1"), QStringLiteral("DIGEST-MD5"), QStringLiteral("PLAIN"), QStringLiteral("ANONYMOUS"), QStringLiteral("X-FACEBOOK-PLATFORM"), QStringLiteral("X-MESSENGER-OAUTH2"), QStringLiteral("X-OAUTH2"), }; } /// /// Creates an SASL client for the given mechanism. /// QXmppSaslClient *QXmppSaslClient::create(const QString &mechanism, QObject *parent) { if (mechanism == QStringLiteral("PLAIN")) { return new QXmppSaslClientPlain(parent); } else if (mechanism == QStringLiteral("DIGEST-MD5")) { return new QXmppSaslClientDigestMd5(parent); } else if (mechanism == QStringLiteral("ANONYMOUS")) { return new QXmppSaslClientAnonymous(parent); } else if (SCRAM_ALGORITHMS.contains(mechanism)) { return new QXmppSaslClientScram(SCRAM_ALGORITHMS.value(mechanism), parent); } else if (mechanism == QStringLiteral("X-FACEBOOK-PLATFORM")) { return new QXmppSaslClientFacebook(parent); } else if (mechanism == QStringLiteral("X-MESSENGER-OAUTH2")) { return new QXmppSaslClientWindowsLive(parent); } else if (mechanism == QStringLiteral("X-OAUTH2")) { return new QXmppSaslClientGoogle(parent); } else { return nullptr; } } /// Returns the host. QString QXmppSaslClient::host() const { return d->host; } /// Sets the host. void QXmppSaslClient::setHost(const QString &host) { d->host = host; } /// Returns the service type, e.g. "xmpp". QString QXmppSaslClient::serviceType() const { return d->serviceType; } /// Sets the service type, e.g. "xmpp". void QXmppSaslClient::setServiceType(const QString &serviceType) { d->serviceType = serviceType; } /// Returns the username. QString QXmppSaslClient::username() const { return d->username; } /// Sets the username. void QXmppSaslClient::setUsername(const QString &username) { d->username = username; } /// Returns the password. QString QXmppSaslClient::password() const { return d->password; } /// Sets the password. void QXmppSaslClient::setPassword(const QString &password) { d->password = password; } QXmppSaslClientAnonymous::QXmppSaslClientAnonymous(QObject *parent) : QXmppSaslClient(parent), m_step(0) { } QString QXmppSaslClientAnonymous::mechanism() const { return QStringLiteral("ANONYMOUS"); } bool QXmppSaslClientAnonymous::respond(const QByteArray &challenge, QByteArray &response) { Q_UNUSED(challenge); if (m_step == 0) { response = QByteArray(); m_step++; return true; } else { warning(QStringLiteral("QXmppSaslClientAnonymous : Invalid step")); return false; } } QXmppSaslClientDigestMd5::QXmppSaslClientDigestMd5(QObject *parent) : QXmppSaslClient(parent), m_nc(QByteArrayLiteral("00000001")), m_step(0) { m_cnonce = generateNonce(); } QString QXmppSaslClientDigestMd5::mechanism() const { return "DIGEST-MD5"; } bool QXmppSaslClientDigestMd5::respond(const QByteArray &challenge, QByteArray &response) { Q_UNUSED(challenge); const QByteArray digestUri = QStringLiteral("%1/%2").arg(serviceType(), host()).toUtf8(); if (m_step == 0) { response = QByteArray(); m_step++; return true; } else if (m_step == 1) { const QMap input = QXmppSaslDigestMd5::parseMessage(challenge); if (!input.contains(QByteArrayLiteral("nonce"))) { warning(QStringLiteral("QXmppSaslClientDigestMd5 : Invalid input on step 1")); return false; } // determine realm const QByteArray realm = input.value(QByteArrayLiteral("realm")); // determine quality of protection const QList qops = input.value(QByteArrayLiteral("qop"), QByteArrayLiteral("auth")).split(','); if (!qops.contains(QByteArrayLiteral("auth"))) { warning(QStringLiteral("QXmppSaslClientDigestMd5 : Invalid quality of protection")); return false; } m_nonce = input.value(QByteArrayLiteral("nonce")); m_secret = QCryptographicHash::hash( username().toUtf8() + QByteArrayLiteral(":") + realm + QByteArrayLiteral(":") + password().toUtf8(), QCryptographicHash::Md5); // Build response QMap output; output[QByteArrayLiteral("username")] = username().toUtf8(); if (!realm.isEmpty()) output[QByteArrayLiteral("realm")] = realm; output[QByteArrayLiteral("nonce")] = m_nonce; output[QByteArrayLiteral("qop")] = QByteArrayLiteral("auth"); output[QByteArrayLiteral("cnonce")] = m_cnonce; output[QByteArrayLiteral("nc")] = m_nc; output[QByteArrayLiteral("digest-uri")] = digestUri; output[QByteArrayLiteral("response")] = calculateDigest(QByteArrayLiteral("AUTHENTICATE"), digestUri, m_secret, m_nonce, m_cnonce, m_nc); output[QByteArrayLiteral("charset")] = QByteArrayLiteral("utf-8"); response = QXmppSaslDigestMd5::serializeMessage(output); m_step++; return true; } else if (m_step == 2) { const QMap input = QXmppSaslDigestMd5::parseMessage(challenge); // check new challenge if (input.value(QByteArrayLiteral("rspauth")) != calculateDigest(QByteArray(), digestUri, m_secret, m_nonce, m_cnonce, m_nc)) { warning(QStringLiteral("QXmppSaslClientDigestMd5 : Invalid challenge on step 2")); return false; } response = QByteArray(); m_step++; return true; } else { warning(QStringLiteral("QXmppSaslClientDigestMd5 : Invalid step")); return false; } } QXmppSaslClientFacebook::QXmppSaslClientFacebook(QObject *parent) : QXmppSaslClient(parent), m_step(0) { } QString QXmppSaslClientFacebook::mechanism() const { return QStringLiteral("X-FACEBOOK-PLATFORM"); } bool QXmppSaslClientFacebook::respond(const QByteArray &challenge, QByteArray &response) { if (m_step == 0) { // no initial response response = QByteArray(); m_step++; return true; } else if (m_step == 1) { // parse request QUrlQuery requestUrl(challenge); if (!requestUrl.hasQueryItem(QStringLiteral("method")) || !requestUrl.hasQueryItem(QStringLiteral("nonce"))) { warning(QStringLiteral("QXmppSaslClientFacebook : Invalid challenge, nonce or method missing")); return false; } // build response QUrlQuery responseUrl; responseUrl.addQueryItem(QStringLiteral("access_token"), password()); responseUrl.addQueryItem(QStringLiteral("api_key"), username()); responseUrl.addQueryItem(QStringLiteral("call_id"), nullptr); responseUrl.addQueryItem(QStringLiteral("method"), requestUrl.queryItemValue(QStringLiteral("method"))); responseUrl.addQueryItem(QStringLiteral("nonce"), requestUrl.queryItemValue(QStringLiteral("nonce"))); responseUrl.addQueryItem(QStringLiteral("v"), QStringLiteral("1.0")); response = responseUrl.query().toUtf8(); m_step++; return true; } else { warning(QStringLiteral("QXmppSaslClientFacebook : Invalid step")); return false; } } QXmppSaslClientGoogle::QXmppSaslClientGoogle(QObject *parent) : QXmppSaslClient(parent), m_step(0) { } QString QXmppSaslClientGoogle::mechanism() const { return QStringLiteral("X-OAUTH2"); } bool QXmppSaslClientGoogle::respond(const QByteArray &challenge, QByteArray &response) { Q_UNUSED(challenge); if (m_step == 0) { // send initial response response = QString('\0' + username() + '\0' + password()).toUtf8(); m_step++; return true; } else { warning(QStringLiteral("QXmppSaslClientGoogle : Invalid step")); return false; } } QXmppSaslClientPlain::QXmppSaslClientPlain(QObject *parent) : QXmppSaslClient(parent), m_step(0) { } QString QXmppSaslClientPlain::mechanism() const { return QStringLiteral("PLAIN"); } bool QXmppSaslClientPlain::respond(const QByteArray &challenge, QByteArray &response) { Q_UNUSED(challenge); if (m_step == 0) { response = QString('\0' + username() + '\0' + password()).toUtf8(); m_step++; return true; } else { warning(QStringLiteral("QXmppSaslClientPlain : Invalid step")); return false; } } QXmppSaslClientScram::QXmppSaslClientScram(QCryptographicHash::Algorithm algorithm, QObject *parent) : QXmppSaslClient(parent), m_algorithm(algorithm), m_step(0), m_dklen(hashLength(algorithm)) { const auto itr = std::find(SCRAM_ALGORITHMS.cbegin(), SCRAM_ALGORITHMS.cend(), algorithm); Q_ASSERT(itr != SCRAM_ALGORITHMS.cend()); m_nonce = generateNonce(); } QString QXmppSaslClientScram::mechanism() const { return SCRAM_ALGORITHMS.key(m_algorithm); } bool QXmppSaslClientScram::respond(const QByteArray &challenge, QByteArray &response) { Q_UNUSED(challenge); if (m_step == 0) { m_gs2Header = QByteArrayLiteral("n,,"); m_clientFirstMessageBare = QByteArrayLiteral("n=") + username().toUtf8() + QByteArrayLiteral(",r=") + m_nonce; response = m_gs2Header + m_clientFirstMessageBare; m_step++; return true; } else if (m_step == 1) { // validate input const QMap 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 = QByteArrayLiteral("c=") + m_gs2Header.toBase64() + QByteArrayLiteral(",r=") + nonce; const QByteArray saltedPassword = deriveKeyPbkdf2(m_algorithm, password().toUtf8(), salt, iterations, m_dklen); const QByteArray clientKey = QMessageAuthenticationCode::hash(QByteArrayLiteral("Client Key"), saltedPassword, m_algorithm); const QByteArray storedKey = QCryptographicHash::hash(clientKey, m_algorithm); const QByteArray authMessage = m_clientFirstMessageBare + QByteArrayLiteral(",") + challenge + QByteArrayLiteral(",") + clientFinalMessageBare; QByteArray clientProof = QMessageAuthenticationCode::hash(authMessage, storedKey, m_algorithm); std::transform(clientProof.cbegin(), clientProof.cend(), clientKey.cbegin(), clientProof.begin(), std::bit_xor()); const QByteArray serverKey = QMessageAuthenticationCode::hash(QByteArrayLiteral("Server Key"), saltedPassword, m_algorithm); m_serverSignature = QMessageAuthenticationCode::hash(authMessage, serverKey, m_algorithm); response = clientFinalMessageBare + QByteArrayLiteral(",p=") + clientProof.toBase64(); m_step++; return true; } else if (m_step == 2) { const QMap input = parseGS2(challenge); response = QByteArray(); m_step++; return QByteArray::fromBase64(input.value('v')) == m_serverSignature; } else { warning(QStringLiteral("QXmppSaslClientPlain : Invalid step")); return false; } } QXmppSaslClientWindowsLive::QXmppSaslClientWindowsLive(QObject *parent) : QXmppSaslClient(parent), m_step(0) { } QString QXmppSaslClientWindowsLive::mechanism() const { return QStringLiteral("X-MESSENGER-OAUTH2"); } bool QXmppSaslClientWindowsLive::respond(const QByteArray &challenge, QByteArray &response) { Q_UNUSED(challenge); if (m_step == 0) { // send initial response response = QByteArray::fromBase64(password().toLatin1()); m_step++; return true; } else { warning(QStringLiteral("QXmppSaslClientWindowsLive : Invalid step")); return false; } } class QXmppSaslServerPrivate { public: QString username; QString password; QByteArray passwordDigest; QString realm; }; QXmppSaslServer::QXmppSaslServer(QObject *parent) : QXmppLoggable(parent), d(new QXmppSaslServerPrivate) { } QXmppSaslServer::~QXmppSaslServer() { delete d; } /// Creates an SASL server for the given mechanism. QXmppSaslServer *QXmppSaslServer::create(const QString &mechanism, QObject *parent) { if (mechanism == QStringLiteral("PLAIN")) { return new QXmppSaslServerPlain(parent); } else if (mechanism == QStringLiteral("DIGEST-MD5")) { return new QXmppSaslServerDigestMd5(parent); } else if (mechanism == QStringLiteral("ANONYMOUS")) { return new QXmppSaslServerAnonymous(parent); } else { return nullptr; } } /// Returns the username. QString QXmppSaslServer::username() const { return d->username; } /// Sets the username. void QXmppSaslServer::setUsername(const QString &username) { d->username = username; } /// Returns the password. QString QXmppSaslServer::password() const { return d->password; } /// Sets the password. void QXmppSaslServer::setPassword(const QString &password) { d->password = password; } /// Returns the password digest. QByteArray QXmppSaslServer::passwordDigest() const { return d->passwordDigest; } /// Sets the password digest. void QXmppSaslServer::setPasswordDigest(const QByteArray &digest) { d->passwordDigest = digest; } /// Returns the realm. QString QXmppSaslServer::realm() const { return d->realm; } /// Sets the realm. void QXmppSaslServer::setRealm(const QString &realm) { d->realm = realm; } QXmppSaslServerAnonymous::QXmppSaslServerAnonymous(QObject *parent) : QXmppSaslServer(parent), m_step(0) { } QString QXmppSaslServerAnonymous::mechanism() const { return QStringLiteral("ANONYMOUS"); } QXmppSaslServer::Response QXmppSaslServerAnonymous::respond(const QByteArray &request, QByteArray &response) { Q_UNUSED(request); if (m_step == 0) { m_step++; response = QByteArray(); return Succeeded; } else { warning(QStringLiteral("QXmppSaslServerAnonymous : Invalid step")); return Failed; } } QXmppSaslServerDigestMd5::QXmppSaslServerDigestMd5(QObject *parent) : QXmppSaslServer(parent), m_step(0) { m_nonce = generateNonce(); } QString QXmppSaslServerDigestMd5::mechanism() const { return QStringLiteral("DIGEST-MD5"); } QXmppSaslServer::Response QXmppSaslServerDigestMd5::respond(const QByteArray &request, QByteArray &response) { if (m_step == 0) { QMap output; output[QByteArrayLiteral("nonce")] = m_nonce; if (!realm().isEmpty()) output[QByteArrayLiteral("realm")] = realm().toUtf8(); output[QByteArrayLiteral("qop")] = QByteArrayLiteral("auth"); output[QByteArrayLiteral("charset")] = QByteArrayLiteral("utf-8"); output[QByteArrayLiteral("algorithm")] = QByteArrayLiteral("md5-sess"); m_step++; response = QXmppSaslDigestMd5::serializeMessage(output); return Challenge; } else if (m_step == 1) { const QMap input = QXmppSaslDigestMd5::parseMessage(request); const QByteArray realm = input.value(QByteArrayLiteral("realm")); const QByteArray digestUri = input.value(QByteArrayLiteral("digest-uri")); if (input.value(QByteArrayLiteral("qop")) != QByteArrayLiteral("auth")) { warning(QStringLiteral("QXmppSaslServerDigestMd5 : Invalid quality of protection")); return Failed; } setUsername(QString::fromUtf8(input.value(QByteArrayLiteral("username")))); if (password().isEmpty() && passwordDigest().isEmpty()) return InputNeeded; m_nc = input.value(QByteArrayLiteral("nc")); m_cnonce = input.value(QByteArrayLiteral("cnonce")); if (!password().isEmpty()) { m_secret = QCryptographicHash::hash( username().toUtf8() + QByteArrayLiteral(":") + realm + QByteArrayLiteral(":") + password().toUtf8(), QCryptographicHash::Md5); } else { m_secret = passwordDigest(); } if (input.value(QByteArrayLiteral("response")) != calculateDigest(QByteArrayLiteral("AUTHENTICATE"), digestUri, m_secret, m_nonce, m_cnonce, m_nc)) return Failed; QMap output; output[QByteArrayLiteral("rspauth")] = calculateDigest(QByteArray(), digestUri, m_secret, m_nonce, m_cnonce, m_nc); m_step++; response = QXmppSaslDigestMd5::serializeMessage(output); return Challenge; } else if (m_step == 2) { m_step++; response = QByteArray(); return Succeeded; } else { warning(QStringLiteral("QXmppSaslServerDigestMd5 : Invalid step")); return Failed; } } QXmppSaslServerPlain::QXmppSaslServerPlain(QObject *parent) : QXmppSaslServer(parent), m_step(0) { } QString QXmppSaslServerPlain::mechanism() const { return QStringLiteral("PLAIN"); } QXmppSaslServer::Response QXmppSaslServerPlain::respond(const QByteArray &request, QByteArray &response) { if (m_step == 0) { if (request.isEmpty()) { response = QByteArray(); return Challenge; } QList auth = request.split('\0'); if (auth.size() != 3) { warning(QStringLiteral("QXmppSaslServerPlain : Invalid input")); return Failed; } setUsername(QString::fromUtf8(auth[1])); setPassword(QString::fromUtf8(auth[2])); m_step++; response = QByteArray(); return InputNeeded; } else { warning(QStringLiteral("QXmppSaslServerPlain : Invalid step")); return Failed; } } void QXmppSaslDigestMd5::setNonce(const QByteArray &nonce) { forcedNonce = nonce; } QMap QXmppSaslDigestMd5::parseMessage(const QByteArray &ba) { QMap map; int startIndex = 0; int pos = 0; while ((pos = ba.indexOf('=', startIndex)) >= 0) { // key get name and skip equals const QByteArray key = ba.mid(startIndex, pos - startIndex).trimmed(); pos++; // check whether string is quoted if (ba.at(pos) == '"') { // skip opening quote pos++; int endPos = ba.indexOf('"', pos); // skip quoted quotes while (endPos >= 0 && ba.at(endPos - 1) == '\\') endPos = ba.indexOf('"', endPos + 1); if (endPos < 0) { qWarning("Unfinished quoted string"); return map; } // unquote QByteArray value = ba.mid(pos, endPos - pos); value.replace("\\\"", "\""); value.replace("\\\\", "\\"); map[key] = value; // skip closing quote and comma startIndex = endPos + 2; } else { // non-quoted string int endPos = ba.indexOf(',', pos); if (endPos < 0) endPos = ba.size(); map[key] = ba.mid(pos, endPos - pos); // skip comma startIndex = endPos + 1; } } return map; } QByteArray QXmppSaslDigestMd5::serializeMessage(const QMap &map) { QByteArray ba; for (const auto &key : map.keys()) { if (!ba.isEmpty()) ba.append(','); ba.append(key + QByteArrayLiteral("=")); QByteArray value = map[key]; const char *separators = "()<>@,;:\\\"/[]?={} \t"; bool quote = false; for (const char *c = separators; *c; c++) { if (value.contains(*c)) { quote = true; break; } } if (quote) { value.replace(QByteArrayLiteral("\\"), QByteArrayLiteral("\\\\")); value.replace(QByteArrayLiteral("\""), QByteArrayLiteral("\\\"")); ba.append('"' + value + '"'); } else { ba.append(value); } } return ba; } qxmpp-1.4.0/src/base/QXmppSasl_p.h000066400000000000000000000206171402370562100167500ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Manjeet Dahiya * Jeremy Lainé * * 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 QXMPPSASL_P_H #define QXMPPSASL_P_H #include "QXmppGlobal.h" #include "QXmppLogger.h" #include "QXmppStanza.h" #include #include #include class QXmppSaslClientPrivate; class QXmppSaslServerPrivate; // // W A R N I N G // ------------- // // This file is not part of the QXmpp API. It exists for the convenience // of the QXmppIncomingClient and QXmppOutgoingClient classes. // // This header file may change from version to version without notice, // or even be removed. // // We mean it. // class QXMPP_AUTOTEST_EXPORT QXmppSaslClient : public QXmppLoggable { public: QXmppSaslClient(QObject *parent = nullptr); ~QXmppSaslClient() override; QString host() const; void setHost(const QString &host); QString serviceType() const; void setServiceType(const QString &serviceType); QString username() const; void setUsername(const QString &username); QString password() const; void setPassword(const QString &password); virtual QString mechanism() const = 0; virtual bool respond(const QByteArray &challenge, QByteArray &response) = 0; static QStringList availableMechanisms(); static QXmppSaslClient *create(const QString &mechanism, QObject *parent = nullptr); private: QXmppSaslClientPrivate *d; }; class QXMPP_AUTOTEST_EXPORT QXmppSaslServer : public QXmppLoggable { public: enum Response { Challenge = 0, Succeeded = 1, Failed = 2, InputNeeded = 3 }; QXmppSaslServer(QObject *parent = nullptr); ~QXmppSaslServer() override; QString username() const; void setUsername(const QString &username); QString password() const; void setPassword(const QString &password); QByteArray passwordDigest() const; void setPasswordDigest(const QByteArray &digest); QString realm() const; void setRealm(const QString &realm); virtual QString mechanism() const = 0; virtual Response respond(const QByteArray &challenge, QByteArray &response) = 0; static QXmppSaslServer *create(const QString &mechanism, QObject *parent = nullptr); private: QXmppSaslServerPrivate *d; }; class QXMPP_AUTOTEST_EXPORT QXmppSaslDigestMd5 { public: static void setNonce(const QByteArray &nonce); // message parsing and serialization static QMap parseMessage(const QByteArray &ba); static QByteArray serializeMessage(const QMap &map); }; class QXMPP_AUTOTEST_EXPORT QXmppSaslAuth : public QXmppStanza { public: QXmppSaslAuth(const QString &mechanism = QString(), const QByteArray &value = QByteArray()); QString mechanism() const; void setMechanism(const QString &mechanism); QByteArray value() const; void setValue(const QByteArray &value); /// \cond void parse(const QDomElement &element) override; void toXml(QXmlStreamWriter *writer) const override; /// \endcond private: QString m_mechanism; QByteArray m_value; }; class QXMPP_AUTOTEST_EXPORT QXmppSaslChallenge : public QXmppStanza { public: QXmppSaslChallenge(const QByteArray &value = QByteArray()); QByteArray value() const; void setValue(const QByteArray &value); /// \cond void parse(const QDomElement &element) override; void toXml(QXmlStreamWriter *writer) const override; /// \endcond private: QByteArray m_value; }; class QXMPP_AUTOTEST_EXPORT QXmppSaslFailure : public QXmppStanza { public: QXmppSaslFailure(const QString &condition = QString()); QString condition() const; void setCondition(const QString &condition); /// \cond void parse(const QDomElement &element) override; void toXml(QXmlStreamWriter *writer) const override; /// \endcond private: QString m_condition; }; class QXMPP_AUTOTEST_EXPORT QXmppSaslResponse : public QXmppStanza { public: QXmppSaslResponse(const QByteArray &value = QByteArray()); QByteArray value() const; void setValue(const QByteArray &value); /// \cond void parse(const QDomElement &element) override; void toXml(QXmlStreamWriter *writer) const override; /// \endcond private: QByteArray m_value; }; class QXMPP_AUTOTEST_EXPORT QXmppSaslSuccess : public QXmppStanza { public: QXmppSaslSuccess(); /// \cond void parse(const QDomElement &element) override; void toXml(QXmlStreamWriter *writer) const override; /// \endcond }; class QXmppSaslClientAnonymous : public QXmppSaslClient { public: QXmppSaslClientAnonymous(QObject *parent = nullptr); QString mechanism() const override; bool respond(const QByteArray &challenge, QByteArray &response) override; private: int m_step; }; class QXmppSaslClientDigestMd5 : public QXmppSaslClient { public: QXmppSaslClientDigestMd5(QObject *parent = nullptr); QString mechanism() const override; bool respond(const QByteArray &challenge, QByteArray &response) override; private: QByteArray m_cnonce; QByteArray m_nc; QByteArray m_nonce; QByteArray m_secret; int m_step; }; class QXmppSaslClientFacebook : public QXmppSaslClient { public: QXmppSaslClientFacebook(QObject *parent = nullptr); QString mechanism() const override; bool respond(const QByteArray &challenge, QByteArray &response) override; private: int m_step; }; class QXmppSaslClientGoogle : public QXmppSaslClient { public: QXmppSaslClientGoogle(QObject *parent = nullptr); QString mechanism() const override; bool respond(const QByteArray &challenge, QByteArray &response) override; private: int m_step; }; class QXmppSaslClientPlain : public QXmppSaslClient { public: 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; QByteArray m_gs2Header; QByteArray m_clientFirstMessageBare; QByteArray m_serverSignature; QByteArray m_nonce; }; class QXmppSaslClientWindowsLive : public QXmppSaslClient { public: QXmppSaslClientWindowsLive(QObject *parent = nullptr); QString mechanism() const override; bool respond(const QByteArray &challenge, QByteArray &response) override; private: int m_step; }; class QXmppSaslServerAnonymous : public QXmppSaslServer { public: QXmppSaslServerAnonymous(QObject *parent = nullptr); QString mechanism() const override; Response respond(const QByteArray &challenge, QByteArray &response) override; private: int m_step; }; class QXmppSaslServerDigestMd5 : public QXmppSaslServer { public: QXmppSaslServerDigestMd5(QObject *parent = nullptr); QString mechanism() const override; Response respond(const QByteArray &challenge, QByteArray &response) override; private: QByteArray m_cnonce; QByteArray m_nc; QByteArray m_nonce; QByteArray m_secret; int m_step; }; class QXmppSaslServerFacebook : public QXmppSaslServer { public: QXmppSaslServerFacebook(QObject *parent = nullptr); QString mechanism() const override; Response respond(const QByteArray &challenge, QByteArray &response) override; private: int m_step; }; class QXmppSaslServerPlain : public QXmppSaslServer { public: QXmppSaslServerPlain(QObject *parent = nullptr); QString mechanism() const override; Response respond(const QByteArray &challenge, QByteArray &response) override; private: int m_step; }; #endif qxmpp-1.4.0/src/base/QXmppSessionIq.cpp000066400000000000000000000024531402370562100177750ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Manjeet Dahiya * Jeremy Lainé * * 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 "QXmppSessionIq.h" #include "QXmppConstants_p.h" #include "QXmppUtils.h" #include #include /// \cond bool QXmppSessionIq::isSessionIq(const QDomElement &element) { QDomElement sessionElement = element.firstChildElement(QStringLiteral("session")); return (sessionElement.namespaceURI() == ns_session); } void QXmppSessionIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("session")); ; writer->writeDefaultNamespace(ns_session); writer->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/base/QXmppSessionIq.h000066400000000000000000000023231402370562100174360ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * Jeremy Lainé * * 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 QXMPPSESSIONIQ_H #define QXMPPSESSIONIQ_H #include "QXmppIq.h" /// \brief The QXmppSessionIq class represents an IQ used for session /// establishment as defined by RFC 3921. /// /// \ingroup Stanzas class QXMPP_EXPORT QXmppSessionIq : public QXmppIq { public: /// \cond static bool isSessionIq(const QDomElement &element); /// \endcond private: /// \cond void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond }; #endif // QXMPPSESSION_H qxmpp-1.4.0/src/base/QXmppSocks.cpp000066400000000000000000000215011402370562100171350ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppSocks.h" #include #include #include #include const static char SocksVersion = 5; enum AuthenticationMethod { NoAuthentication = 0, NoAcceptableMethod = 255 }; enum Command { ConnectCommand = 1, BindCommand = 2, AssociateCommand = 3 }; enum AddressType { IPv4Address = 1, DomainName = 3, IPv6Address = 4 }; enum ReplyType { Succeeded = 0, SocksFailure = 1, ConnectionNotAllowed = 2, NetworkUnreachable = 3, HostUnreachable = 4, ConnectionRefused = 5, TtlExpired = 6, CommandNotSupported = 7, AddressTypeNotSupported = 8 }; enum State { ConnectState = 0, CommandState = 1, ReadyState = 2 }; static QByteArray encodeHostAndPort(quint8 type, const QByteArray &host, quint16 port) { QByteArray buffer; QDataStream stream(&buffer, QIODevice::WriteOnly); // set host name quint8 hostLength = host.size(); stream << type; stream << hostLength; stream.writeRawData(host.constData(), hostLength); // set port stream << port; return buffer; } static bool parseHostAndPort(QDataStream &stream, quint8 &type, QByteArray &host, quint16 &port) { // get host name quint8 hostLength; stream >> type; stream >> hostLength; if (stream.status() != QDataStream::Ok) return false; host.resize(hostLength); if (stream.readRawData(host.data(), hostLength) != hostLength) { qWarning("Invalid host length"); return false; } // get port stream >> port; return stream.status() == QDataStream::Ok; } QXmppSocksClient::QXmppSocksClient(const QString &proxyHost, quint16 proxyPort, QObject *parent) : QTcpSocket(parent), m_proxyHost(proxyHost), m_proxyPort(proxyPort), m_step(ConnectState) { connect(this, &QAbstractSocket::connected, this, &QXmppSocksClient::slotConnected); connect(this, &QIODevice::readyRead, this, &QXmppSocksClient::slotReadyRead); } void QXmppSocksClient::connectToHost(const QString &hostName, quint16 hostPort) { m_hostName = hostName; m_hostPort = hostPort; QTcpSocket::connectToHost(m_proxyHost, m_proxyPort); } void QXmppSocksClient::slotConnected() { m_step = ConnectState; // disconnect from signal disconnect(this, &QAbstractSocket::connected, this, &QXmppSocksClient::slotConnected); // send connect to server QByteArray buffer; buffer.resize(3); buffer[0] = SocksVersion; buffer[1] = 0x01; // number of methods buffer[2] = NoAuthentication; write(buffer); } void QXmppSocksClient::slotReadyRead() { if (m_step == ConnectState) { // receive connect to server response QByteArray buffer = readAll(); if (buffer.size() != 2 || buffer.at(0) != SocksVersion || buffer.at(1) != NoAuthentication) { qWarning("QXmppSocksClient received an invalid response during handshake"); close(); return; } // advance state m_step = CommandState; // send CONNECT command buffer.resize(3); buffer[0] = SocksVersion; buffer[1] = ConnectCommand; buffer[2] = 0x00; // reserved buffer.append(encodeHostAndPort( DomainName, m_hostName.toLatin1(), m_hostPort)); write(buffer); } else if (m_step == CommandState) { // disconnect from signal disconnect(this, &QIODevice::readyRead, this, &QXmppSocksClient::slotReadyRead); // receive CONNECT response QByteArray buffer = read(3); if (buffer.size() != 3 || buffer.at(0) != SocksVersion || buffer.at(1) != Succeeded || buffer.at(2) != 0) { qWarning("QXmppSocksClient received an invalid response to CONNECT command"); close(); return; } // parse host quint8 hostType; QByteArray hostName; quint16 hostPort; QDataStream stream(this); if (!parseHostAndPort(stream, hostType, hostName, hostPort)) { qWarning("QXmppSocksClient could not parse type/host/port"); close(); return; } // FIXME : what do we do with the resulting name / port? // notify of connection m_step = ReadyState; emit ready(); } } QXmppSocksServer::QXmppSocksServer(QObject *parent) : QObject(parent) { m_server = new QTcpServer(this); connect(m_server, &QTcpServer::newConnection, this, &QXmppSocksServer::slotNewConnection); m_server_v6 = new QTcpServer(this); connect(m_server_v6, &QTcpServer::newConnection, this, &QXmppSocksServer::slotNewConnection); } void QXmppSocksServer::close() { m_server->close(); m_server_v6->close(); } bool QXmppSocksServer::listen(quint16 port) { if (!m_server->listen(QHostAddress::Any, port)) return false; // FIXME: this fails on Linux if /proc/sys/net/ipv6/bindv6only is 0 m_server_v6->listen(QHostAddress::AnyIPv6, m_server->serverPort()); return true; } quint16 QXmppSocksServer::serverPort() const { return m_server->serverPort(); } void QXmppSocksServer::slotNewConnection() { auto *server = qobject_cast(sender()); if (!server) return; QTcpSocket *socket = server->nextPendingConnection(); if (!socket) return; // register socket m_states.insert(socket, ConnectState); connect(socket, &QIODevice::readyRead, this, &QXmppSocksServer::slotReadyRead); } void QXmppSocksServer::slotReadyRead() { auto *socket = qobject_cast(sender()); if (!socket || !m_states.contains(socket)) return; if (m_states.value(socket) == ConnectState) { // receive connect to server request QByteArray buffer = socket->readAll(); if (buffer.size() < 3 || buffer.at(0) != SocksVersion || buffer.at(1) + 2 != buffer.size()) { qWarning("QXmppSocksServer received invalid handshake"); socket->close(); return; } // check authentication method bool foundMethod = false; for (int i = 2; i < buffer.size(); i++) { if (buffer.at(i) == NoAuthentication) { foundMethod = true; break; } } if (!foundMethod) { qWarning("QXmppSocksServer received bad authentication method"); buffer.resize(2); buffer[0] = SocksVersion; buffer[1] = static_cast(NoAcceptableMethod); socket->write(buffer); socket->close(); return; } // advance state m_states.insert(socket, CommandState); // send connect to server response buffer.resize(2); buffer[0] = SocksVersion; buffer[1] = NoAuthentication; socket->write(buffer); } else if (m_states.value(socket) == CommandState) { // disconnect from signals disconnect(socket, &QIODevice::readyRead, this, &QXmppSocksServer::slotReadyRead); // receive command QByteArray buffer = socket->read(3); if (buffer.size() != 3 || buffer.at(0) != SocksVersion || buffer.at(1) != ConnectCommand || buffer.at(2) != 0x00) { qWarning("QXmppSocksServer received an invalid command"); socket->close(); return; } // parse host quint8 hostType; QByteArray hostName; quint16 hostPort; QDataStream stream(socket); if (!parseHostAndPort(stream, hostType, hostName, hostPort)) { qWarning("QXmppSocksServer could not parse type/host/port"); socket->close(); return; } // notify of connection m_states.insert(socket, ReadyState); emit newConnection(socket, hostName, hostPort); // send response buffer.resize(3); buffer[0] = SocksVersion; buffer[1] = Succeeded; buffer[2] = 0x00; buffer.append(encodeHostAndPort( DomainName, hostName, hostPort)); socket->write(buffer); } } qxmpp-1.4.0/src/base/QXmppSocks.h000066400000000000000000000034141402370562100166050ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPSOCKS_H #define QXMPPSOCKS_H #include "QXmppGlobal.h" #include #include class QTcpServer; class QXMPP_EXPORT QXmppSocksClient : public QTcpSocket { Q_OBJECT public: QXmppSocksClient(const QString &proxyHost, quint16 proxyPort, QObject *parent = nullptr); void connectToHost(const QString &hostName, quint16 hostPort); Q_SIGNALS: void ready(); private Q_SLOTS: void slotConnected(); void slotReadyRead(); private: QString m_proxyHost; quint16 m_proxyPort; QString m_hostName; quint16 m_hostPort; int m_step; }; class QXMPP_EXPORT QXmppSocksServer : public QObject { Q_OBJECT public: QXmppSocksServer(QObject *parent = nullptr); void close(); bool listen(quint16 port = 0); quint16 serverPort() const; Q_SIGNALS: void newConnection(QTcpSocket *socket, QString hostName, quint16 port); private Q_SLOTS: void slotNewConnection(); void slotReadyRead(); private: QTcpServer *m_server; QTcpServer *m_server_v6; QMap m_states; }; #endif qxmpp-1.4.0/src/base/QXmppStanza.cpp000066400000000000000000000415651402370562100173270ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Manjeet Dahiya * Jeremy Lainé * Georg Rudoy * * 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 "QXmppStanza.h" #include "QXmppConstants_p.h" #include "QXmppStanza_p.h" #include "QXmppUtils.h" #include #include #include uint QXmppStanza::s_uniqeIdNo = 0; class QXmppExtendedAddressPrivate : public QSharedData { public: bool delivered; QString description; QString jid; QString type; }; /// Constructs an empty extended address. QXmppExtendedAddress::QXmppExtendedAddress() : d(new QXmppExtendedAddressPrivate()) { d->delivered = false; } /// Constructs a copy of other. /// /// \param other /// QXmppExtendedAddress::QXmppExtendedAddress(const QXmppExtendedAddress &other) = default; QXmppExtendedAddress::~QXmppExtendedAddress() = default; /// Assigns the other address to this one. /// /// \param other /// QXmppExtendedAddress &QXmppExtendedAddress::operator=(const QXmppExtendedAddress &other) { d = other.d; return *this; } /// Returns the human-readable description of the address. QString QXmppExtendedAddress::description() const { return d->description; } /// Sets the human-readable \a description of the address. void QXmppExtendedAddress::setDescription(const QString &description) { d->description = description; } /// Returns the JID of the address. QString QXmppExtendedAddress::jid() const { return d->jid; } /// Sets the JID of the address. void QXmppExtendedAddress::setJid(const QString &jid) { d->jid = jid; } /// Returns the type of the address. QString QXmppExtendedAddress::type() const { return d->type; } /// Sets the \a type of the address. void QXmppExtendedAddress::setType(const QString &type) { d->type = type; } /// Returns whether the stanza has been delivered to this address. bool QXmppExtendedAddress::isDelivered() const { return d->delivered; } /// Sets whether the stanza has been \a delivered to this address. void QXmppExtendedAddress::setDelivered(bool delivered) { d->delivered = delivered; } /// Checks whether this address is valid. The extended address is considered /// to be valid if at least type and JID fields are non-empty. bool QXmppExtendedAddress::isValid() const { return !d->type.isEmpty() && !d->jid.isEmpty(); } /// \cond void QXmppExtendedAddress::parse(const QDomElement &element) { d->delivered = element.attribute(QStringLiteral("delivered")) == QStringLiteral("true"); d->description = element.attribute(QStringLiteral("desc")); d->jid = element.attribute(QStringLiteral("jid")); d->type = element.attribute(QStringLiteral("type")); } void QXmppExtendedAddress::toXml(QXmlStreamWriter *xmlWriter) const { xmlWriter->writeStartElement(QStringLiteral("address")); if (d->delivered) xmlWriter->writeAttribute(QStringLiteral("delivered"), QStringLiteral("true")); if (!d->description.isEmpty()) xmlWriter->writeAttribute(QStringLiteral("desc"), d->description); xmlWriter->writeAttribute(QStringLiteral("jid"), d->jid); xmlWriter->writeAttribute(QStringLiteral("type"), d->type); xmlWriter->writeEndElement(); } /// \endcond class QXmppStanzaErrorPrivate : public QSharedData { public: QXmppStanzaErrorPrivate(); int code; std::optional type; std::optional condition; QString text; QString by; QString redirectionUri; // XEP-0363: HTTP File Upload bool fileTooLarge; qint64 maxFileSize; QDateTime retryDate; }; QXmppStanzaErrorPrivate::QXmppStanzaErrorPrivate() : code(0), fileTooLarge(false) { } /// /// Default constructor /// QXmppStanza::Error::Error() : d(new QXmppStanzaErrorPrivate) { } /// Copy constructor QXmppStanza::Error::Error(const QXmppStanza::Error &) = default; /// /// Initializes an error with a type, condition and text. /// QXmppStanza::Error::Error(Type type, Condition cond, const QString &text) : d(new QXmppStanzaErrorPrivate) { d->type = type; d->condition = cond; d->text = text; } /// /// Initializes an error with a type, condition and text (all from strings). /// QXmppStanza::Error::Error(const QString &type, const QString &cond, const QString &text) : d(new QXmppStanzaErrorPrivate) { d->text = text; d->type = typeFromString(type); d->condition = conditionFromString(cond); } /// Default destructor QXmppStanza::Error::~Error() = default; /// Copy operator QXmppStanza::Error &QXmppStanza::Error::operator=(const QXmppStanza::Error &) = default; /// /// Returns the human-readable description of the error. /// QString QXmppStanza::Error::text() const { return d->text; } /// /// Sets the description of the error. /// void QXmppStanza::Error::setText(const QString &text) { d->text = text; } /// /// Returns the error code. /// int QXmppStanza::Error::code() const { return d->code; } /// /// Sets the error code. /// void QXmppStanza::Error::setCode(int code) { d->code = code; } /// /// Returns the error condition. /// /// The conditions QXmppStanza::Error::Gone and QXmppStanza::Error::Redirect /// can be used in combination with redirectUri(). /// QXmppStanza::Error::Condition QXmppStanza::Error::condition() const { return d->condition.value_or(QXmppStanza::Error::Condition(-1)); } /// /// Sets the error condition. /// /// The conditions QXmppStanza::Error::Gone and QXmppStanza::Error::Redirect /// can be used in combination with setRedirectUri(). /// void QXmppStanza::Error::setCondition(QXmppStanza::Error::Condition cond) { if (int(cond) < 0) { d->condition = std::nullopt; return; } d->condition = cond; } /// /// Returns the type of the error. /// QXmppStanza::Error::Type QXmppStanza::Error::type() const { return d->type.value_or(QXmppStanza::Error::Type(-1)); } /// /// Returns the optional JID of the creator of the error. /// /// This is useful to ditinguish between errors generated by the local server /// and by the remote server for example. However, the value is optional. /// /// \since QXmpp 1.3 /// QString QXmppStanza::Error::by() const { return d->by; } /// /// Sets the optional JID of the creator of the error. /// /// This is useful to ditinguish between errors generated by the local server /// and by the remote server for example. However, the value is optional. /// /// \since QXmpp 1.3 /// void QXmppStanza::Error::setBy(const QString &by) { d->by = by; } /// /// Sets the type of the error. /// void QXmppStanza::Error::setType(QXmppStanza::Error::Type type) { if (int(type) < 0) { d->type = std::nullopt; return; } d->type = type; } /// /// Returns the optionally included redirection URI for the error conditions /// QXmppStanza::Error::Gone and QXmppStanza::Error::Redirect. /// /// \sa setRedirectionUri() /// /// \since QXmpp 1.3 /// QString QXmppStanza::Error::redirectionUri() const { return d->redirectionUri; } /// /// Sets the optional redirection URI for the error conditions /// QXmppStanza::Error::Gone and QXmppStanza::Error::Redirect. /// /// \sa redirectionUri() /// /// \since QXmpp 1.3 /// void QXmppStanza::Error::setRedirectionUri(const QString &redirectionUri) { d->redirectionUri = redirectionUri; } /// Returns true, if an HTTP File Upload failed, because the file was too /// large. /// /// \since QXmpp 1.1 bool QXmppStanza::Error::fileTooLarge() const { return d->fileTooLarge; } /// Sets whether the requested file for HTTP File Upload was too large. /// /// You should also set maxFileSize in this case. /// /// \since QXmpp 1.1 void QXmppStanza::Error::setFileTooLarge(bool fileTooLarge) { d->fileTooLarge = fileTooLarge; } /// Returns the maximum file size allowed for uploading via. HTTP File Upload. /// /// \since QXmpp 1.1 qint64 QXmppStanza::Error::maxFileSize() const { return d->maxFileSize; } /// Sets the maximum file size allowed for uploading via. HTTP File Upload. /// /// This sets fileTooLarge to true. /// /// \since QXmpp 1.1 void QXmppStanza::Error::setMaxFileSize(qint64 maxFileSize) { setFileTooLarge(true); d->maxFileSize = maxFileSize; } /// Returns when to retry the upload request via. HTTP File Upload. /// /// \since QXmpp 1.1 QDateTime QXmppStanza::Error::retryDate() const { return d->retryDate; } /// Sets the datetime when the client can retry to request the upload slot. void QXmppStanza::Error::setRetryDate(const QDateTime &retryDate) { d->retryDate = retryDate; } /// \cond void QXmppStanza::Error::parse(const QDomElement &errorElement) { d->code = errorElement.attribute(QStringLiteral("code")).toInt(); d->type = typeFromString(errorElement.attribute(QStringLiteral("type"))); d->by = errorElement.attribute(QStringLiteral("by")); QDomElement element = errorElement.firstChildElement(); while (!element.isNull()) { if (element.namespaceURI() == ns_stanza) { if (element.tagName() == QStringLiteral("text")) { d->text = element.text(); } else { d->condition = conditionFromString(element.tagName()); // redirection URI if (d->condition == Gone || d->condition == Redirect) { d->redirectionUri = element.text(); // .text() returns empty string if nothing was set if (d->redirectionUri.isEmpty()) d->redirectionUri.clear(); } } } else if (element.namespaceURI() == ns_http_upload) { // XEP-0363: HTTP File Upload // file is too large if (element.tagName() == QStringLiteral("file-too-large")) { d->fileTooLarge = true; d->maxFileSize = element.firstChildElement(QStringLiteral("max-file-size")) .text() .toLongLong(); // retry later } else if (element.tagName() == QStringLiteral("retry")) { d->retryDate = QXmppUtils::datetimeFromString( element.attribute(QStringLiteral("stamp"))); } } element = element.nextSiblingElement(); } } void QXmppStanza::Error::toXml(QXmlStreamWriter *writer) const { if (!d->condition && !d->type) return; writer->writeStartElement(QStringLiteral("error")); helperToXmlAddAttribute(writer, QStringLiteral("by"), d->by); if (d->type) { writer->writeAttribute(QStringLiteral("type"), typeToString(*d->type)); } if (d->code > 0) helperToXmlAddAttribute(writer, QStringLiteral("code"), QString::number(d->code)); if (d->condition) { writer->writeStartElement(conditionToString(*d->condition)); writer->writeDefaultNamespace(ns_stanza); // redirection URI if (!d->redirectionUri.isEmpty() && (d->condition == Gone || d->condition == Redirect)) { writer->writeCharacters(d->redirectionUri); } writer->writeEndElement(); } if (!d->text.isEmpty()) { writer->writeStartElement(QStringLiteral("text")); writer->writeAttribute(QStringLiteral("xml:lang"), QStringLiteral("en")); writer->writeDefaultNamespace(ns_stanza); writer->writeCharacters(d->text); writer->writeEndElement(); } // XEP-0363: HTTP File Upload if (d->fileTooLarge) { writer->writeStartElement(QStringLiteral("file-too-large")); writer->writeDefaultNamespace(ns_http_upload); helperToXmlAddTextElement(writer, QStringLiteral("max-file-size"), QString::number(d->maxFileSize)); writer->writeEndElement(); } else if (!d->retryDate.isNull() && d->retryDate.isValid()) { writer->writeStartElement(QStringLiteral("retry")); writer->writeDefaultNamespace(ns_http_upload); writer->writeAttribute(QStringLiteral("stamp"), QXmppUtils::datetimeToString(d->retryDate)); writer->writeEndElement(); } writer->writeEndElement(); } /// \endcond class QXmppStanzaPrivate : public QSharedData { public: QString to; QString from; QString id; QString lang; QXmppStanza::Error error; QXmppElementList extensions; QList extendedAddresses; }; /// Constructs a QXmppStanza with the specified sender and recipient. /// /// \param from /// \param to QXmppStanza::QXmppStanza(const QString &from, const QString &to) : d(new QXmppStanzaPrivate) { d->to = to; d->from = from; } /// Constructs a copy of \a other. QXmppStanza::QXmppStanza(const QXmppStanza &other) : d(other.d) { } /// Destroys a QXmppStanza. QXmppStanza::~QXmppStanza() { } /// Assigns \a other to this stanza. QXmppStanza &QXmppStanza::operator=(const QXmppStanza &other) { d = other.d; return *this; } /// Returns the stanza's recipient JID. /// QString QXmppStanza::to() const { return d->to; } /// Sets the stanza's recipient JID. /// /// \param to void QXmppStanza::setTo(const QString &to) { d->to = to; } /// Returns the stanza's sender JID. QString QXmppStanza::from() const { return d->from; } /// Sets the stanza's sender JID. /// /// \param from void QXmppStanza::setFrom(const QString &from) { d->from = from; } /// Returns the stanza's identifier. QString QXmppStanza::id() const { return d->id; } /// Sets the stanza's identifier. /// /// \param id void QXmppStanza::setId(const QString &id) { d->id = id; } /// Returns the stanza's language. QString QXmppStanza::lang() const { return d->lang; } /// Sets the stanza's language. /// /// \param lang void QXmppStanza::setLang(const QString &lang) { d->lang = lang; } /// Returns the stanza's error. QXmppStanza::Error QXmppStanza::error() const { return d->error; } /// Sets the stanza's error. /// /// \param error void QXmppStanza::setError(const QXmppStanza::Error &error) { d->error = error; } /// Returns the stanza's "extensions". /// /// Extensions are XML elements which are not handled internally by QXmpp. QXmppElementList QXmppStanza::extensions() const { return d->extensions; } /// Sets the stanza's "extensions". /// /// \param extensions void QXmppStanza::setExtensions(const QXmppElementList &extensions) { d->extensions = extensions; } /// Returns the stanza's extended addresses as defined by /// \xep{0033}: Extended Stanza Addressing. QList QXmppStanza::extendedAddresses() const { return d->extendedAddresses; } /// Sets the stanza's extended addresses as defined by /// \xep{0033}: Extended Stanza Addressing. void QXmppStanza::setExtendedAddresses(const QList &addresses) { d->extendedAddresses = addresses; } /// /// Indicates if the QXmppStanza is a stanza in the XMPP sense (i. e. a message, /// iq or presence) /// /// \since QXmpp 1.0 /// bool QXmppStanza::isXmppStanza() const { return false; } /// \cond void QXmppStanza::generateAndSetNextId() { // get back ++s_uniqeIdNo; d->id = "qxmpp" + QString::number(s_uniqeIdNo); } void QXmppStanza::parse(const QDomElement &element) { d->from = element.attribute("from"); d->to = element.attribute("to"); d->id = element.attribute("id"); d->lang = element.attribute("lang"); QDomElement errorElement = element.firstChildElement("error"); if (!errorElement.isNull()) d->error.parse(errorElement); // XEP-0033: Extended Stanza Addressing QDomElement addressElement = element.firstChildElement("addresses").firstChildElement("address"); while (!addressElement.isNull()) { QXmppExtendedAddress address; address.parse(addressElement); if (address.isValid()) d->extendedAddresses << address; addressElement = addressElement.nextSiblingElement("address"); } } void QXmppStanza::extensionsToXml(QXmlStreamWriter *xmlWriter) const { // XEP-0033: Extended Stanza Addressing if (!d->extendedAddresses.isEmpty()) { xmlWriter->writeStartElement("addresses"); xmlWriter->writeDefaultNamespace(ns_extended_addressing); for (const auto &address : d->extendedAddresses) address.toXml(xmlWriter); xmlWriter->writeEndElement(); } // other extensions for (const auto &extension : d->extensions) extension.toXml(xmlWriter); } /// \endcond qxmpp-1.4.0/src/base/QXmppStanza.h000066400000000000000000000202571402370562100167670ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Manjeet Dahiya * Jeremy Lainé * Georg Rudoy * * 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 QXMPPSTANZA_H #define QXMPPSTANZA_H #include #include #include // forward declarations of QXmlStream* classes will not work on Mac, we need to // include the whole header. // See http://lists.trolltech.com/qt-interest/2008-07/thread00798-0.html // for an explanation. #include "QXmppElement.h" #include class QXmppExtendedAddressPrivate; /// /// \brief Represents an extended address as defined by \xep{0033}: Extended /// Stanza Addressing. /// /// Extended addresses maybe of different types: some are defined by \xep{0033}, /// others are defined in separate XEPs (for instance \xep{0146}: Remote /// Controlling Clients). That is why the "type" property is a string rather /// than an enumerated type. /// class QXMPP_EXPORT QXmppExtendedAddress { public: QXmppExtendedAddress(); QXmppExtendedAddress(const QXmppExtendedAddress &); ~QXmppExtendedAddress(); QXmppExtendedAddress &operator=(const QXmppExtendedAddress &); QString description() const; void setDescription(const QString &description); QString jid() const; void setJid(const QString &jid); QString type() const; void setType(const QString &type); bool isDelivered() const; void setDelivered(bool); bool isValid() const; /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *writer) const; /// \endcond private: QSharedDataPointer d; }; class QXmppStanzaPrivate; class QXmppStanzaErrorPrivate; /// /// \defgroup Stanzas Stanzas /// /// All packets that are sent and received are serialized in Stanzas, so this /// includes IQ stanzas, message stanzas, presence stanzas and other stanzas. /// /// /// \brief The QXmppStanza class is the base class for all XMPP stanzas. /// /// \ingroup Stanzas /// class QXMPP_EXPORT QXmppStanza { public: /// /// \brief The Error class represents a stanza error. /// class QXMPP_EXPORT Error { public: /// The type represents the error type of stanza errors. /// /// The error descriptions are not detailed in here. The exact meaning /// can be found in the particular protocols using them. enum Type { Cancel, ///< The error is not temporary. Continue, ///< The error was only a warning. Modify, ///< The request needs to be changed and resent. Auth, ///< The request needs to be resent after authentication. Wait ///< The error is temporary, you should wait and resend. }; /// A detailed condition of the error enum Condition { BadRequest, ///< The request does not contain a valid schema. Conflict, ///< The request conflicts with another. FeatureNotImplemented, ///< The feature is not implemented. Forbidden, ///< The requesting entity does not posses the necessary privileges to perform the request. Gone, ///< The user or server can not be contacted at the address. This is used in combination with a redirection URI. InternalServerError, ///< The server has expierienced an internal error and can not process the request. ItemNotFound, ///< The requested item could not be found. JidMalformed, ///< The given JID is not valid. NotAcceptable, ///< The request does not meet the defined critera. NotAllowed, ///< No entity is allowed to perform the request. NotAuthorized, ///< The request should be resent after authentication. #if QXMPP_DEPRECATED_SINCE(1, 3) /// Payment is required to perform the request. /// \deprecated This error condition is deprecated since QXmpp 1.3 as it was not adopted in RFC6120. PaymentRequired Q_DECL_ENUMERATOR_DEPRECATED_X("The error was removed in RFC6120"), #endif RecipientUnavailable = 12, ///< The recipient is unavailable. Redirect, ///< The requested resource is available elsewhere. This is used in combination with a redirection URI. RegistrationRequired, ///< The requesting entity needs to register first. RemoteServerNotFound, ///< The remote server could not be found. RemoteServerTimeout, ///< The connection to the server could not be established or timed out. ResourceConstraint, ///< The recipient lacks system resources to perform the request. ServiceUnavailable, ///< The service is currently not available. SubscriptionRequired, ///< The requester needs to subscribe first. UndefinedCondition, ///< An undefined condition was hit. UnexpectedRequest, ///< The request was unexpected. PolicyViolation ///< The entity has violated a local server policy. \since QXmpp 1.3 }; Error(); Error(const Error &); Error(Type type, Condition cond, const QString &text = QString()); Error(const QString &type, const QString &cond, const QString &text = QString()); ~Error(); Error &operator=(const Error &); int code() const; void setCode(int code); QString text() const; void setText(const QString &text); Condition condition() const; void setCondition(Condition cond); void setType(Type type); Type type() const; QString by() const; void setBy(const QString &by); QString redirectionUri() const; void setRedirectionUri(const QString &redirectionUri); // XEP-0363: HTTP File Upload bool fileTooLarge() const; void setFileTooLarge(bool); qint64 maxFileSize() const; void setMaxFileSize(qint64); QDateTime retryDate() const; void setRetryDate(const QDateTime &); /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *writer) const; /// \endcond private: QSharedDataPointer d; }; QXmppStanza(const QString &from = QString(), const QString &to = QString()); QXmppStanza(const QXmppStanza &other); virtual ~QXmppStanza(); QXmppStanza &operator=(const QXmppStanza &other); QString to() const; void setTo(const QString &); QString from() const; void setFrom(const QString &); QString id() const; void setId(const QString &); QString lang() const; void setLang(const QString &); QXmppStanza::Error error() const; void setError(const QXmppStanza::Error &error); QXmppElementList extensions() const; void setExtensions(const QXmppElementList &elements); QList extendedAddresses() const; void setExtendedAddresses(const QList &extendedAddresses); virtual bool isXmppStanza() const; /// \cond virtual void parse(const QDomElement &element); virtual void toXml(QXmlStreamWriter *writer) const = 0; protected: void extensionsToXml(QXmlStreamWriter *writer) const; void generateAndSetNextId(); /// \endcond private: QSharedDataPointer d; static uint s_uniqeIdNo; }; Q_DECLARE_METATYPE(QXmppStanza::Error::Type); Q_DECLARE_METATYPE(QXmppStanza::Error::Condition); #endif // QXMPPSTANZA_H qxmpp-1.4.0/src/base/QXmppStanza_p.h000066400000000000000000000146731402370562100173130ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Niels Ole Salscheider * * 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 QXMPPSTANZA_P_H #define QXMPPSTANZA_P_H #include "QXmppStanza.h" #include // W A R N I N G // ------------- // // This file is not part of the QXmpp API. It exists for the convenience // of the QXmppStanza class. // // This header file may change from version to version without notice, // or even be removed. // // We mean it. // static QString conditionToString(QXmppStanza::Error::Condition condition) { switch (condition) { case QXmppStanza::Error::BadRequest: return "bad-request"; case QXmppStanza::Error::Conflict: return "conflict"; case QXmppStanza::Error::FeatureNotImplemented: return "feature-not-implemented"; case QXmppStanza::Error::Forbidden: return "forbidden"; case QXmppStanza::Error::Gone: return "gone"; case QXmppStanza::Error::InternalServerError: return "internal-server-error"; case QXmppStanza::Error::ItemNotFound: return "item-not-found"; case QXmppStanza::Error::JidMalformed: return "jid-malformed"; case QXmppStanza::Error::NotAcceptable: return "not-acceptable"; case QXmppStanza::Error::NotAllowed: return "not-allowed"; case QXmppStanza::Error::NotAuthorized: return "not-authorized"; QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED case QXmppStanza::Error::PaymentRequired: QT_WARNING_POP return "payment-required"; case QXmppStanza::Error::PolicyViolation: return "policy-violation"; case QXmppStanza::Error::RecipientUnavailable: return "recipient-unavailable"; case QXmppStanza::Error::Redirect: return "redirect"; case QXmppStanza::Error::RegistrationRequired: return "registration-required"; case QXmppStanza::Error::RemoteServerNotFound: return "remote-server-not-found"; case QXmppStanza::Error::RemoteServerTimeout: return "remote-server-timeout"; case QXmppStanza::Error::ResourceConstraint: return "resource-constraint"; case QXmppStanza::Error::ServiceUnavailable: return "service-unavailable"; case QXmppStanza::Error::SubscriptionRequired: return "subscription-required"; case QXmppStanza::Error::UndefinedCondition: return "undefined-condition"; case QXmppStanza::Error::UnexpectedRequest: return "unexpected-request"; } return {}; } static std::optional conditionFromString(const QString &string) { if (string == "bad-request") return QXmppStanza::Error::BadRequest; else if (string == "conflict") return QXmppStanza::Error::Conflict; else if (string == "feature-not-implemented") return QXmppStanza::Error::FeatureNotImplemented; else if (string == "forbidden") return QXmppStanza::Error::Forbidden; else if (string == "gone") return QXmppStanza::Error::Gone; else if (string == "internal-server-error") return QXmppStanza::Error::InternalServerError; else if (string == "item-not-found") return QXmppStanza::Error::ItemNotFound; else if (string == "jid-malformed") return QXmppStanza::Error::JidMalformed; else if (string == "not-acceptable") return QXmppStanza::Error::NotAcceptable; else if (string == "not-allowed") return QXmppStanza::Error::NotAllowed; else if (string == "not-authorized") return QXmppStanza::Error::NotAuthorized; else if (string == "payment-required") { QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED return QXmppStanza::Error::PaymentRequired; QT_WARNING_POP } else if (string == "policy-violation") return QXmppStanza::Error::PolicyViolation; else if (string == "recipient-unavailable") return QXmppStanza::Error::RecipientUnavailable; else if (string == "redirect") return QXmppStanza::Error::Redirect; else if (string == "registration-required") return QXmppStanza::Error::RegistrationRequired; else if (string == "remote-server-not-found") return QXmppStanza::Error::RemoteServerNotFound; else if (string == "remote-server-timeout") return QXmppStanza::Error::RemoteServerTimeout; else if (string == "resource-constraint") return QXmppStanza::Error::ResourceConstraint; else if (string == "service-unavailable") return QXmppStanza::Error::ServiceUnavailable; else if (string == "subscription-required") return QXmppStanza::Error::SubscriptionRequired; else if (string == "undefined-condition") return QXmppStanza::Error::UndefinedCondition; else if (string == "unexpected-request") return QXmppStanza::Error::UnexpectedRequest; return std::nullopt; } static QString typeToString(QXmppStanza::Error::Type type) { switch (type) { case QXmppStanza::Error::Cancel: return QStringLiteral("cancel"); case QXmppStanza::Error::Continue: return QStringLiteral("continue"); case QXmppStanza::Error::Modify: return QStringLiteral("modify"); case QXmppStanza::Error::Auth: return QStringLiteral("auth"); case QXmppStanza::Error::Wait: return QStringLiteral("wait"); } return {}; } static std::optional typeFromString(const QString &string) { if (string == QStringLiteral("cancel")) return QXmppStanza::Error::Cancel; else if (string == QStringLiteral("continue")) return QXmppStanza::Error::Continue; else if (string == QStringLiteral("modify")) return QXmppStanza::Error::Modify; else if (string == QStringLiteral("auth")) return QXmppStanza::Error::Auth; else if (string == QStringLiteral("wait")) return QXmppStanza::Error::Wait; return std::nullopt; } #endif qxmpp-1.4.0/src/base/QXmppStartTlsPacket.cpp000066400000000000000000000056211402370562100207700ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Linus Jahn * * 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 "QXmppStartTlsPacket.h" #include "QXmppConstants_p.h" #include #include const static QStringList STARTTLS_TYPES = { QStringLiteral("starttls"), QStringLiteral("proceed"), QStringLiteral("failure") }; /// Constructs a new QXmppStartTlsPacket /// /// \param type The type of the new QXmppStartTlsPacket. QXmppStartTlsPacket::QXmppStartTlsPacket(Type type) : m_type(type) { } QXmppStartTlsPacket::~QXmppStartTlsPacket() = default; /// Returns the type of the STARTTLS packet QXmppStartTlsPacket::Type QXmppStartTlsPacket::type() const { return m_type; } /// Sets the type of the STARTTLS packet void QXmppStartTlsPacket::setType(QXmppStartTlsPacket::Type type) { m_type = type; } /// \cond void QXmppStartTlsPacket::parse(const QDomElement &element) { if (!QXmppStartTlsPacket::isStartTlsPacket(element)) return; m_type = Type(STARTTLS_TYPES.indexOf(element.tagName())); } void QXmppStartTlsPacket::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement(STARTTLS_TYPES.at(int(m_type))); writer->writeDefaultNamespace(ns_tls); writer->writeEndElement(); } /// \endcond /// Checks whether the given \p element is a STARTTLS packet according to /// RFC6120. /// /// \param element The element that should be checked for being a STARTTLS packet. /// /// \returns True, if the element is a STARTTLS packet. bool QXmppStartTlsPacket::isStartTlsPacket(const QDomElement &element) { return element.namespaceURI() == ns_tls && STARTTLS_TYPES.contains(element.tagName()); } /// Checks whether the given \p element is a STARTTLS packet according to /// RFC6120 /// and has the correct type. /// /// \param element The element that should be checked for being a STARTTLS packet. /// \param type The type the element needs to have. /// /// \returns True, if the element is a STARTTLS packet and has the correct type. bool QXmppStartTlsPacket::isStartTlsPacket(const QDomElement &element, Type type) { return element.namespaceURI() == ns_tls && element.tagName() == STARTTLS_TYPES.at(int(type)); } qxmpp-1.4.0/src/base/QXmppStartTlsPacket.h000066400000000000000000000034071402370562100204350ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Linus Jahn * * 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 QXMPPSTARTTLSPACKET_H #define QXMPPSTARTTLSPACKET_H #include "QXmppStanza.h" /// /// \brief The QXmppStartTlsPacket represents packets used for initiating /// STARTTLS negotiation when connecting. /// /// \ingroup Stanzas /// class QXMPP_EXPORT QXmppStartTlsPacket : public QXmppStanza { public: /// The type of the STARTTLS packet. enum Type { StartTls, ///< Used by the client to initiate STARTTLS. Proceed, ///< Used by the server to accept STARTTLS. Failure ///< Used by the server to reject STARTTLS. }; QXmppStartTlsPacket(Type type = StartTls); ~QXmppStartTlsPacket() override; Type type() const; void setType(Type type); /// \cond void parse(const QDomElement &element) override; void toXml(QXmlStreamWriter *writer) const override; /// \endcond static bool isStartTlsPacket(const QDomElement &element); static bool isStartTlsPacket(const QDomElement &element, Type type); private: Type m_type; }; Q_DECLARE_METATYPE(QXmppStartTlsPacket::Type); #endif // QXMPPSTARTTLSPACKET_H qxmpp-1.4.0/src/base/QXmppStream.cpp000066400000000000000000000240701402370562100173120ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Manjeet Dahiya * Jeremy Lainé * * 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 "QXmppStream.h" #include "QXmppConstants_p.h" #include "QXmppLogger.h" #include "QXmppStanza.h" #include "QXmppStreamManagement_p.h" #include "QXmppUtils.h" #include #include #include #include #include #include #include #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) static bool randomSeeded = false; #endif class QXmppStreamPrivate { public: QXmppStreamPrivate(QXmppStream *stream); QString dataBuffer; QSslSocket *socket; // incoming stream state QString streamOpenElement; // stream management QXmppStreamManager streamManager; }; QXmppStreamPrivate::QXmppStreamPrivate(QXmppStream *stream) : socket(nullptr), streamManager(stream) { } /// /// Constructs a base XMPP stream. /// /// \param parent /// QXmppStream::QXmppStream(QObject *parent) : QXmppLoggable(parent), d(new QXmppStreamPrivate(this)) { #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) // Make sure the random number generator is seeded if (!randomSeeded) { qsrand(QTime(0, 0, 0).msecsTo(QTime::currentTime()) ^ reinterpret_cast(this)); randomSeeded = true; } #endif } /// /// Destroys a base XMPP stream. /// QXmppStream::~QXmppStream() { delete d; } /// /// Disconnects from the remote host. /// void QXmppStream::disconnectFromHost() { d->streamManager.handleDisconnect(); if (d->socket) { if (d->socket->state() == QAbstractSocket::ConnectedState) { sendData(QByteArrayLiteral("")); d->socket->flush(); } // FIXME: according to RFC 6120 section 4.4, we should wait for // the incoming stream to end before closing the socket d->socket->disconnectFromHost(); } } /// /// Handles a stream start event, which occurs when the underlying transport /// becomes ready (socket connected, encryption started). /// /// If you redefine handleStart(), make sure to call the base class's method. /// void QXmppStream::handleStart() { d->streamManager.handleStart(); d->dataBuffer.clear(); d->streamOpenElement.clear(); } /// /// Returns true if the stream is connected. /// bool QXmppStream::isConnected() const { return d->socket && d->socket->state() == QAbstractSocket::ConnectedState; } /// /// Sends raw data to the peer. /// /// \param data /// bool QXmppStream::sendData(const QByteArray &data) { logSent(QString::fromUtf8(data)); if (!d->socket || d->socket->state() != QAbstractSocket::ConnectedState) return false; return d->socket->write(data) == data.size(); } /// /// Sends an XMPP packet to the peer. /// /// \param packet /// bool QXmppStream::sendPacket(const QXmppStanza &packet) { // prepare packet QByteArray data; QXmlStreamWriter xmlStream(&data); packet.toXml(&xmlStream); // send packet bool success = sendData(data); // handle stream management d->streamManager.handlePacketSent(packet, data); return success; } /// /// Resets the stream management packages cache. /// /// This can be done to prevent that packages from the last connection are being /// resent. /// /// \since QXmpp 1.4 /// void QXmppStream::resetPacketCache() { d->streamManager.resetCache(); } /// /// Returns the QSslSocket used for this stream. /// QSslSocket *QXmppStream::socket() const { return d->socket; } /// /// Sets the QSslSocket used for this stream. /// void QXmppStream::setSocket(QSslSocket *socket) { d->socket = socket; if (!d->socket) return; // socket events connect(socket, &QAbstractSocket::connected, this, &QXmppStream::_q_socketConnected); connect(socket, &QSslSocket::encrypted, this, &QXmppStream::_q_socketEncrypted); #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) connect(socket, &QSslSocket::errorOccurred, this, &QXmppStream::_q_socketError); #else connect(socket, QOverload::of(&QSslSocket::error), this, &QXmppStream::_q_socketError); #endif connect(socket, &QIODevice::readyRead, this, &QXmppStream::_q_socketReadyRead); } void QXmppStream::_q_socketConnected() { info(QStringLiteral("Socket connected to %1 %2").arg(d->socket->peerAddress().toString(), QString::number(d->socket->peerPort()))); handleStart(); } void QXmppStream::_q_socketEncrypted() { debug("Socket encrypted"); handleStart(); } void QXmppStream::_q_socketError(QAbstractSocket::SocketError socketError) { Q_UNUSED(socketError); warning(QStringLiteral("Socket error: ") + socket()->errorString()); } void QXmppStream::_q_socketReadyRead() { processData(QString::fromUtf8(d->socket->readAll())); } void QXmppStream::processData(const QString &data) { // As we may only have partial XML content, we need to cache the received // data until it has been successfully parsed. In case it can't be parsed, // // There are only two small problems with the current strategy: // * When we receive a full stanza + a partial one, we can't parse the // first stanza until another stanza arrives that is complete. // * We don't know when we received invalid XML (would cause a growing // cache and a timeout after some time). // However, both issues could only be solved using an XML stream reader // which would cause many other problems since we don't actually use it for // parsing the content. d->dataBuffer.append(data); // // Check for whitespace pings // if (d->dataBuffer.isEmpty() || d->dataBuffer.trimmed().isEmpty()) { d->dataBuffer.clear(); logReceived({}); handleStanza({}); return; } // // Check whether we received a stream open or closing tag // static const QRegularExpression streamStartRegex(R"(^(<\?xml.*\?>)?\s*]*>)"); static const QRegularExpression streamEndRegex("$"); QRegularExpressionMatch streamOpenMatch; bool hasStreamOpen = d->streamOpenElement.isEmpty() && (streamOpenMatch = streamStartRegex.match(d->dataBuffer)).hasMatch(); bool hasStreamClose = streamEndRegex.match(d->dataBuffer).hasMatch(); // // The stream start/end and stanza packets can't be parsed without any // modifications with QDomDocument. This is because of multiple reasons: // * The open element is not considered valid without the // closing tag. // * Only the closing tag is of course not valid too. // * Stanzas/Nonzas need to have the correct stream namespaces set: // * For being able to parse // * For having the correct namespace (e.g. 'jabber:client') set to // stanzas and their child elements (e.g. of a message). // // The wrapping strategy looks like this: // * The stream open tag is cached once it arrives, for later access // * Incoming XML that has no open tag will be prepended by the // cached tag. // * Incoming XML that has no close tag will be appended by a // generic string "" // // The result is parsed by QDomDocument and the child elements of the stream // are processed. In case the received data contained a stream open tag, // the stream is processed (before the stanzas are processed). In case we // received a closing tag, the connection is closed. // auto wrappedStanzas = d->dataBuffer; if (!hasStreamOpen) { wrappedStanzas.prepend(d->streamOpenElement); } if (!hasStreamClose) { wrappedStanzas.append(QStringLiteral("")); } // // Try to parse the wrapped XML // QDomDocument doc; if (!doc.setContent(wrappedStanzas, true)) return; // // Success: We can clear the buffer and send a 'received' log message // logReceived(d->dataBuffer); d->dataBuffer.clear(); // process stream start if (hasStreamOpen) { d->streamOpenElement = streamOpenMatch.captured(); handleStream(doc.documentElement()); } // process stanzas auto stanza = doc.documentElement().firstChildElement(); for (; !stanza.isNull(); stanza = stanza.nextSiblingElement()) { // handle possible stream management packets first if (d->streamManager.handleStanza(stanza)) continue; // process all other kinds of packets handleStanza(stanza); } // process stream end if (hasStreamClose) { disconnectFromHost(); } } /// /// Enables Stream Management acks / reqs (\xep{0198}). /// /// \param resetSequenceNumber Indicates if the sequence numbers should be /// reset. This must be done if the stream is not resumed. /// /// \since QXmpp 1.0 /// void QXmppStream::enableStreamManagement(bool resetSequenceNumber) { d->streamManager.enableStreamManagement(resetSequenceNumber); } /// /// Returns the sequence number of the last incoming stanza (\xep{0198}). /// /// \since QXmpp 1.0 /// unsigned int QXmppStream::lastIncomingSequenceNumber() const { return d->streamManager.lastIncomingSequenceNumber(); } /// /// Sets the last acknowledged sequence number for outgoing stanzas /// (\xep{0198}). /// /// \since QXmpp 1.0 /// void QXmppStream::setAcknowledgedSequenceNumber(unsigned int sequenceNumber) { d->streamManager.setAcknowledgedSequenceNumber(sequenceNumber); } qxmpp-1.4.0/src/base/QXmppStream.h000066400000000000000000000047671402370562100167720ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Manjeet Dahiya * Jeremy Lainé * * 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 QXMPPSTREAM_H #define QXMPPSTREAM_H #include "QXmppLogger.h" #include #include class QDomElement; class QSslSocket; class QXmppStanza; class QXmppStreamPrivate; /// /// \brief The QXmppStream class is the base class for all XMPP streams. /// class QXMPP_EXPORT QXmppStream : public QXmppLoggable { Q_OBJECT public: QXmppStream(QObject *parent); ~QXmppStream() override; virtual bool isConnected() const; bool sendPacket(const QXmppStanza &); void resetPacketCache(); Q_SIGNALS: /// This signal is emitted when the stream is connected. void connected(); /// This signal is emitted when the stream is disconnected. void disconnected(); protected: // Access to underlying socket QSslSocket *socket() const; void setSocket(QSslSocket *socket); // Overridable methods virtual void handleStart(); /// Handles an incoming XMPP stanza. /// /// \param element virtual void handleStanza(const QDomElement &element) = 0; /// Handles an incoming XMPP stream start. /// /// \param element virtual void handleStream(const QDomElement &element) = 0; // XEP-0198: Stream Management void enableStreamManagement(bool resetSequenceNumber); unsigned int lastIncomingSequenceNumber() const; void setAcknowledgedSequenceNumber(unsigned int sequenceNumber); public Q_SLOTS: virtual void disconnectFromHost(); virtual bool sendData(const QByteArray &); private Q_SLOTS: void _q_socketConnected(); void _q_socketEncrypted(); void _q_socketError(QAbstractSocket::SocketError error); void _q_socketReadyRead(); private: void processData(const QString &data); friend class tst_QXmppStream; QXmppStreamPrivate *const d; }; #endif // QXMPPSTREAM_H qxmpp-1.4.0/src/base/QXmppStreamFeatures.cpp000066400000000000000000000250411402370562100210100ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppStreamFeatures.h" #include "QXmppConstants_p.h" #include class QXmppStreamFeaturesPrivate : public QSharedData { public: QXmppStreamFeaturesPrivate(); QXmppStreamFeatures::Mode bindMode; QXmppStreamFeatures::Mode sessionMode; QXmppStreamFeatures::Mode nonSaslAuthMode; QXmppStreamFeatures::Mode tlsMode; QXmppStreamFeatures::Mode streamManagementMode; QXmppStreamFeatures::Mode csiMode; QXmppStreamFeatures::Mode registerMode; bool preApprovedSubscriptionsSupported; bool rosterVersioningSupported; QStringList authMechanisms; QStringList compressionMethods; }; QXmppStreamFeaturesPrivate::QXmppStreamFeaturesPrivate() : bindMode(QXmppStreamFeatures::Disabled), sessionMode(QXmppStreamFeatures::Disabled), nonSaslAuthMode(QXmppStreamFeatures::Disabled), tlsMode(QXmppStreamFeatures::Disabled), streamManagementMode(QXmppStreamFeatures::Disabled), csiMode(QXmppStreamFeatures::Disabled), registerMode(QXmppStreamFeatures::Disabled), preApprovedSubscriptionsSupported(false) { } QXmppStreamFeatures::QXmppStreamFeatures() : d(new QXmppStreamFeaturesPrivate) { } QXmppStreamFeatures::QXmppStreamFeatures(const QXmppStreamFeatures &) = default; QXmppStreamFeatures::~QXmppStreamFeatures() = default; QXmppStreamFeatures &QXmppStreamFeatures::operator=(const QXmppStreamFeatures &) = default; QXmppStreamFeatures::Mode QXmppStreamFeatures::bindMode() const { return d->bindMode; } void QXmppStreamFeatures::setBindMode(QXmppStreamFeatures::Mode mode) { d->bindMode = mode; } QXmppStreamFeatures::Mode QXmppStreamFeatures::sessionMode() const { return d->sessionMode; } void QXmppStreamFeatures::setSessionMode(Mode mode) { d->sessionMode = mode; } QXmppStreamFeatures::Mode QXmppStreamFeatures::nonSaslAuthMode() const { return d->nonSaslAuthMode; } void QXmppStreamFeatures::setNonSaslAuthMode(QXmppStreamFeatures::Mode mode) { d->nonSaslAuthMode = mode; } QStringList QXmppStreamFeatures::authMechanisms() const { return d->authMechanisms; } void QXmppStreamFeatures::setAuthMechanisms(const QStringList &mechanisms) { d->authMechanisms = mechanisms; } QStringList QXmppStreamFeatures::compressionMethods() const { return d->compressionMethods; } void QXmppStreamFeatures::setCompressionMethods(const QStringList &methods) { d->compressionMethods = methods; } QXmppStreamFeatures::Mode QXmppStreamFeatures::tlsMode() const { return d->tlsMode; } void QXmppStreamFeatures::setTlsMode(QXmppStreamFeatures::Mode mode) { d->tlsMode = mode; } /// /// Returns the mode (disabled, enabled or required) for \xep{0198}: Stream /// Management /// /// \since QXmpp 1.0 /// QXmppStreamFeatures::Mode QXmppStreamFeatures::streamManagementMode() const { return d->streamManagementMode; } /// /// Sets the mode for \xep{0198}: Stream Management /// /// \param mode The mode to set. /// /// \since QXmpp 1.0 /// void QXmppStreamFeatures::setStreamManagementMode(QXmppStreamFeatures::Mode mode) { d->streamManagementMode = mode; } /// /// Returns the mode for \xep{0352}: Client State Indication /// /// \since QXmpp 1.0 /// QXmppStreamFeatures::Mode QXmppStreamFeatures::clientStateIndicationMode() const { return d->csiMode; } /// /// Sets the mode for \xep{0352}: Client State Indication /// /// \param mode The mode to set. /// /// \since QXmpp 1.0 /// void QXmppStreamFeatures::setClientStateIndicationMode(QXmppStreamFeatures::Mode mode) { d->csiMode = mode; } /// /// Returns the mode for \xep{0077}: In-Band Registration /// /// \since QXmpp 1.1 /// QXmppStreamFeatures::Mode QXmppStreamFeatures::registerMode() const { return d->registerMode; } /// /// Sets the mode for \xep{0077}: In-Band Registration /// /// \param mode The mode to set. /// /// \since QXmpp 1.1 /// void QXmppStreamFeatures::setRegisterMode(const QXmppStreamFeatures::Mode ®isterMode) { d->registerMode = registerMode; } /// /// Returns whether usage of Pre-Approved roster subscriptions is supported. /// /// \since QXmpp 1.3 /// bool QXmppStreamFeatures::preApprovedSubscriptionsSupported() const { return d->preApprovedSubscriptionsSupported; } /// /// Sets whether usage of Pre-Approved roster subscriptions is supported. /// /// \since QXmpp 1.3 /// void QXmppStreamFeatures::setPreApprovedSubscriptionsSupported(bool supported) { d->preApprovedSubscriptionsSupported = supported; } /// /// Returns whether roster versioning from RFC6121 is supported. /// /// \since QXmpp 1.3 /// bool QXmppStreamFeatures::rosterVersioningSupported() const { return d->rosterVersioningSupported; } /// /// Sets whether roster versioning from RFC6121 is supported. /// /// \since QXmpp 1.3 /// void QXmppStreamFeatures::setRosterVersioningSupported(bool supported) { d->rosterVersioningSupported = supported; } /// \cond bool QXmppStreamFeatures::isStreamFeatures(const QDomElement &element) { return element.namespaceURI() == ns_stream && element.tagName() == QStringLiteral("features"); } static QXmppStreamFeatures::Mode readFeature(const QDomElement &element, const char *tagName, const char *tagNs) { QDomElement subElement = element.firstChildElement(tagName); QXmppStreamFeatures::Mode mode = QXmppStreamFeatures::Disabled; while (!subElement.isNull()) { if (subElement.namespaceURI() == tagNs) { if (!subElement.firstChildElement(QStringLiteral("required")).isNull()) mode = QXmppStreamFeatures::Required; else if (mode != QXmppStreamFeatures::Required) mode = QXmppStreamFeatures::Enabled; } subElement = subElement.nextSiblingElement(tagName); } return mode; } static bool readBooleanFeature(const QDomElement &element, const QString &tagName, const QString &xmlns) { auto childElement = element.firstChildElement(tagName); while (!childElement.isNull()) { if (childElement.namespaceURI() == xmlns) { return true; } childElement = childElement.nextSiblingElement(tagName); } return false; } void QXmppStreamFeatures::parse(const QDomElement &element) { d->bindMode = readFeature(element, "bind", ns_bind); d->sessionMode = readFeature(element, "session", ns_session); d->nonSaslAuthMode = readFeature(element, "auth", ns_authFeature); d->tlsMode = readFeature(element, "starttls", ns_tls); d->streamManagementMode = readFeature(element, "sm", ns_stream_management); d->csiMode = readFeature(element, "csi", ns_csi); d->registerMode = readFeature(element, "register", ns_register_feature); d->preApprovedSubscriptionsSupported = readBooleanFeature(element, QStringLiteral("sub"), ns_pre_approval); d->rosterVersioningSupported = readBooleanFeature(element, QStringLiteral("ver"), ns_rosterver); // parse advertised compression methods QDomElement compression = element.firstChildElement(QStringLiteral("compression")); if (compression.namespaceURI() == ns_compressFeature) { QDomElement subElement = compression.firstChildElement(QStringLiteral("method")); while (!subElement.isNull()) { d->compressionMethods << subElement.text(); subElement = subElement.nextSiblingElement(QStringLiteral("method")); } } // parse advertised SASL Authentication mechanisms QDomElement mechs = element.firstChildElement(QStringLiteral("mechanisms")); if (mechs.namespaceURI() == ns_sasl) { QDomElement subElement = mechs.firstChildElement(QStringLiteral("mechanism")); while (!subElement.isNull()) { d->authMechanisms << subElement.text(); subElement = subElement.nextSiblingElement(QStringLiteral("mechanism")); } } } static void writeFeature(QXmlStreamWriter *writer, const char *tagName, const char *tagNs, QXmppStreamFeatures::Mode mode) { if (mode != QXmppStreamFeatures::Disabled) { writer->writeStartElement(tagName); writer->writeDefaultNamespace(tagNs); if (mode == QXmppStreamFeatures::Required) writer->writeEmptyElement(QStringLiteral("required")); writer->writeEndElement(); } } static void writeBoolenFeature(QXmlStreamWriter *writer, const QString &tagName, const QString &xmlns, bool enabled) { if (enabled) { writer->writeStartElement(tagName); writer->writeDefaultNamespace(xmlns); writer->writeEndElement(); } } void QXmppStreamFeatures::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("stream:features")); writeFeature(writer, "bind", ns_bind, d->bindMode); writeFeature(writer, "session", ns_session, d->sessionMode); writeFeature(writer, "auth", ns_authFeature, d->nonSaslAuthMode); writeFeature(writer, "starttls", ns_tls, d->tlsMode); writeFeature(writer, "sm", ns_stream_management, d->streamManagementMode); writeFeature(writer, "csi", ns_csi, d->csiMode); writeFeature(writer, "register", ns_register_feature, d->registerMode); writeBoolenFeature(writer, QStringLiteral("sub"), ns_pre_approval, d->preApprovedSubscriptionsSupported); writeBoolenFeature(writer, QStringLiteral("ver"), ns_rosterver, d->rosterVersioningSupported); if (!d->compressionMethods.isEmpty()) { writer->writeStartElement(QStringLiteral("compression")); writer->writeDefaultNamespace(ns_compressFeature); for (const auto &method : qAsConst(d->compressionMethods)) writer->writeTextElement(QStringLiteral("method"), method); writer->writeEndElement(); } if (!d->authMechanisms.isEmpty()) { writer->writeStartElement(QStringLiteral("mechanisms")); writer->writeDefaultNamespace(ns_sasl); for (const auto &mechanism : qAsConst(d->authMechanisms)) writer->writeTextElement(QStringLiteral("mechanism"), mechanism); writer->writeEndElement(); } writer->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/base/QXmppStreamFeatures.h000066400000000000000000000047201402370562100204560ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPSTREAMFEATURES_H #define QXMPPSTREAMFEATURES_H #include "QXmppStanza.h" #include class QXmppStreamFeaturesPrivate; /// /// \brief The QXmppStreamFeatures class represents the features returned by an /// XMPP server or client. /// class QXMPP_EXPORT QXmppStreamFeatures : public QXmppStanza { public: QXmppStreamFeatures(); QXmppStreamFeatures(const QXmppStreamFeatures &); ~QXmppStreamFeatures(); QXmppStreamFeatures &operator=(const QXmppStreamFeatures &); enum Mode { Disabled = 0, Enabled, Required }; Mode bindMode() const; void setBindMode(Mode mode); Mode sessionMode() const; void setSessionMode(Mode mode); Mode nonSaslAuthMode() const; void setNonSaslAuthMode(Mode mode); QStringList authMechanisms() const; void setAuthMechanisms(const QStringList &mechanisms); QStringList compressionMethods() const; void setCompressionMethods(const QStringList &methods); Mode tlsMode() const; void setTlsMode(Mode mode); Mode streamManagementMode() const; void setStreamManagementMode(Mode mode); Mode clientStateIndicationMode() const; void setClientStateIndicationMode(Mode mode); Mode registerMode() const; void setRegisterMode(const Mode ®isterMode); bool preApprovedSubscriptionsSupported() const; void setPreApprovedSubscriptionsSupported(bool); bool rosterVersioningSupported() const; void setRosterVersioningSupported(bool); /// \cond void parse(const QDomElement &element) override; void toXml(QXmlStreamWriter *writer) const override; /// \endcond static bool isStreamFeatures(const QDomElement &element); private: QSharedDataPointer d; }; #endif qxmpp-1.4.0/src/base/QXmppStreamInitiationIq.cpp000066400000000000000000000073511402370562100216370ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppConstants_p.h" #include "QXmppStreamInitiationIq_p.h" #include "QXmppUtils.h" #include QXmppDataForm QXmppStreamInitiationIq::featureForm() const { return m_featureForm; } void QXmppStreamInitiationIq::setFeatureForm(const QXmppDataForm &form) { m_featureForm = form; } QXmppTransferFileInfo QXmppStreamInitiationIq::fileInfo() const { return m_fileInfo; } void QXmppStreamInitiationIq::setFileInfo(const QXmppTransferFileInfo &fileInfo) { m_fileInfo = fileInfo; } QString QXmppStreamInitiationIq::mimeType() const { return m_mimeType; } void QXmppStreamInitiationIq::setMimeType(const QString &mimeType) { m_mimeType = mimeType; } QXmppStreamInitiationIq::Profile QXmppStreamInitiationIq::profile() const { return m_profile; } void QXmppStreamInitiationIq::setProfile(QXmppStreamInitiationIq::Profile profile) { m_profile = profile; } QString QXmppStreamInitiationIq::siId() const { return m_siId; } void QXmppStreamInitiationIq::setSiId(const QString &id) { m_siId = id; } /// \cond bool QXmppStreamInitiationIq::isStreamInitiationIq(const QDomElement &element) { QDomElement siElement = element.firstChildElement(QStringLiteral("si")); return (siElement.namespaceURI() == ns_stream_initiation); } void QXmppStreamInitiationIq::parseElementFromChild(const QDomElement &element) { QDomElement siElement = element.firstChildElement(QStringLiteral("si")); m_siId = siElement.attribute(QStringLiteral("id")); m_mimeType = siElement.attribute(QStringLiteral("mime-type")); if (siElement.attribute(QStringLiteral("profile")) == ns_stream_initiation_file_transfer) m_profile = FileTransfer; else m_profile = None; QDomElement itemElement = siElement.firstChildElement(); while (!itemElement.isNull()) { if (itemElement.tagName() == QStringLiteral("feature") && itemElement.namespaceURI() == ns_feature_negotiation) { m_featureForm.parse(itemElement.firstChildElement()); } else if (itemElement.tagName() == QStringLiteral("file") && itemElement.namespaceURI() == ns_stream_initiation_file_transfer) { m_fileInfo.parse(itemElement); } itemElement = itemElement.nextSiblingElement(); } } void QXmppStreamInitiationIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("si")); writer->writeDefaultNamespace(ns_stream_initiation); helperToXmlAddAttribute(writer, QStringLiteral("id"), m_siId); helperToXmlAddAttribute(writer, QStringLiteral("mime-type"), m_mimeType); if (m_profile == FileTransfer) helperToXmlAddAttribute(writer, QStringLiteral("profile"), ns_stream_initiation_file_transfer); if (!m_fileInfo.isNull()) m_fileInfo.toXml(writer); if (!m_featureForm.isNull()) { writer->writeStartElement(QStringLiteral("feature")); writer->writeDefaultNamespace(ns_feature_negotiation); m_featureForm.toXml(writer); writer->writeEndElement(); } writer->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/base/QXmppStreamInitiationIq_p.h000066400000000000000000000041741402370562100216230ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPSTREAMINITIATIONIQ_P_H #define QXMPPSTREAMINITIATIONIQ_P_H #include "QXmppDataForm.h" #include "QXmppIq.h" #include "QXmppTransferManager.h" #include // // W A R N I N G // ------------- // // This file is not part of the QXmpp API. It exists for the convenience // of the QXmppTransferManager class. // // This header file may change from version to version without notice, // or even be removed. // // We mean it. // class QXMPP_AUTOTEST_EXPORT QXmppStreamInitiationIq : public QXmppIq { public: enum Profile { None = 0, FileTransfer }; QXmppDataForm featureForm() const; void setFeatureForm(const QXmppDataForm &form); QXmppTransferFileInfo fileInfo() const; void setFileInfo(const QXmppTransferFileInfo &info); QString mimeType() const; void setMimeType(const QString &mimeType); QXmppStreamInitiationIq::Profile profile() const; void setProfile(QXmppStreamInitiationIq::Profile profile); QString siId() const; void setSiId(const QString &id); static bool isStreamInitiationIq(const QDomElement &element); protected: /// \cond void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: QXmppDataForm m_featureForm; QXmppTransferFileInfo m_fileInfo; QString m_mimeType; Profile m_profile; QString m_siId; }; #endif qxmpp-1.4.0/src/base/QXmppStreamManagement.cpp000066400000000000000000000301151402370562100213040ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Niels Ole Salscheider * * 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 "QXmppConstants_p.h" #include "QXmppGlobal.h" #include "QXmppStanza_p.h" #include "QXmppStream.h" #include "QXmppStreamManagement_p.h" QXmppStreamManagementEnable::QXmppStreamManagementEnable(const bool resume, const unsigned max) : m_resume(resume), m_max(max) { } bool QXmppStreamManagementEnable::resume() const { return m_resume; } void QXmppStreamManagementEnable::setResume(bool resume) { m_resume = resume; } unsigned QXmppStreamManagementEnable::max() const { return m_max; } void QXmppStreamManagementEnable::setMax(const unsigned max) { m_max = max; } bool QXmppStreamManagementEnable::isStreamManagementEnable(const QDomElement &element) { return element.tagName() == QLatin1String("enable") && element.namespaceURI() == ns_stream_management; } void QXmppStreamManagementEnable::parse(const QDomElement &element) { QString resume = element.attribute(QStringLiteral("resume")); m_resume = resume == QStringLiteral("true") || resume == QStringLiteral("1"); m_max = element.attribute(QStringLiteral("max")).toUInt(); } void QXmppStreamManagementEnable::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("enable")); writer->writeDefaultNamespace(ns_stream_management); if (m_resume) writer->writeAttribute(QStringLiteral("resume"), QStringLiteral("true")); if (m_max > 0) writer->writeAttribute(QStringLiteral("max"), QString::number(m_max)); writer->writeEndElement(); } QXmppStreamManagementEnabled::QXmppStreamManagementEnabled(const bool resume, const QString id, const unsigned max, const QString location) : m_resume(resume), m_id(id), m_max(max), m_location(location) { } bool QXmppStreamManagementEnabled::resume() const { return m_resume; } void QXmppStreamManagementEnabled::setResume(const bool resume) { m_resume = resume; } QString QXmppStreamManagementEnabled::id() const { return m_id; } void QXmppStreamManagementEnabled::setId(const QString id) { m_id = id; } unsigned QXmppStreamManagementEnabled::max() const { return m_max; } void QXmppStreamManagementEnabled::setMax(const unsigned max) { m_max = max; } QString QXmppStreamManagementEnabled::location() const { return m_location; } void QXmppStreamManagementEnabled::setLocation(const QString location) { m_location = location; } bool QXmppStreamManagementEnabled::isStreamManagementEnabled(const QDomElement &element) { return element.tagName() == QLatin1String("enabled") && element.namespaceURI() == ns_stream_management; } void QXmppStreamManagementEnabled::parse(const QDomElement &element) { QString resume = element.attribute(QStringLiteral("resume")); m_resume = resume == QStringLiteral("true") || resume == QStringLiteral("1"); m_max = element.attribute(QStringLiteral("max")).toUInt(); m_location = element.attribute(QStringLiteral("location")); } void QXmppStreamManagementEnabled::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("enable")); writer->writeDefaultNamespace(ns_stream_management); if (m_resume) writer->writeAttribute(QStringLiteral("resume"), QStringLiteral("true")); if (m_max > 0) writer->writeAttribute(QStringLiteral("max"), QString::number(m_max)); if (!m_location.isEmpty()) writer->writeAttribute(QStringLiteral("location"), m_location); writer->writeEndElement(); } QXmppStreamManagementResume::QXmppStreamManagementResume(const unsigned h, const QString &previd) : m_h(h), m_previd(previd) { } unsigned QXmppStreamManagementResume::h() const { return m_h; } void QXmppStreamManagementResume::setH(const unsigned h) { m_h = h; } QString QXmppStreamManagementResume::prevId() const { return m_previd; } void QXmppStreamManagementResume::setPrevId(const QString &previd) { m_previd = previd; } bool QXmppStreamManagementResume::isStreamManagementResume(const QDomElement &element) { return element.tagName() == QLatin1String("resume") && element.namespaceURI() == ns_stream_management; } void QXmppStreamManagementResume::parse(const QDomElement &element) { m_h = element.attribute(QStringLiteral("h")).toUInt(); m_previd = element.attribute(QStringLiteral("previd")); } void QXmppStreamManagementResume::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("resume")); writer->writeAttribute(QStringLiteral("h"), QString::number(m_h)); writer->writeAttribute(QStringLiteral("previd"), m_previd); writer->writeEndElement(); } QXmppStreamManagementResumed::QXmppStreamManagementResumed(const unsigned h, const QString &previd) : m_h(h), m_previd(previd) { } unsigned QXmppStreamManagementResumed::h() const { return m_h; } void QXmppStreamManagementResumed::setH(const unsigned h) { m_h = h; } QString QXmppStreamManagementResumed::prevId() const { return m_previd; } void QXmppStreamManagementResumed::setPrevId(const QString &previd) { m_previd = previd; } bool QXmppStreamManagementResumed::isStreamManagementResumed(const QDomElement &element) { return element.tagName() == QLatin1String("resumed") && element.namespaceURI() == ns_stream_management; } void QXmppStreamManagementResumed::parse(const QDomElement &element) { m_h = element.attribute(QStringLiteral("h")).toUInt(); m_previd = element.attribute(QStringLiteral("previd")); } void QXmppStreamManagementResumed::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("resumed")); writer->writeAttribute(QStringLiteral("h"), QString::number(m_h)); writer->writeAttribute(QStringLiteral("previd"), m_previd); writer->writeEndElement(); } QXmppStreamManagementFailed::QXmppStreamManagementFailed(const QXmppStanza::Error::Condition error) : m_error(error) { } QXmppStanza::Error::Condition QXmppStreamManagementFailed::error() const { return m_error; } void QXmppStreamManagementFailed::setError(const QXmppStanza::Error::Condition error) { m_error = error; } bool QXmppStreamManagementFailed::isStreamManagementFailed(const QDomElement &element) { return element.tagName() == QLatin1String("failed") && element.namespaceURI() == ns_stream_management; } void QXmppStreamManagementFailed::parse(const QDomElement &element) { QDomElement childElement = element.firstChildElement(); if (!childElement.isNull() && childElement.namespaceURI() == ns_stanza) { m_error = conditionFromString(childElement.tagName()).value_or(QXmppStanza::Error::Condition(-1)); } } void QXmppStreamManagementFailed::toXml(QXmlStreamWriter *writer) const { QString errorString = conditionToString(m_error); writer->writeStartElement(QStringLiteral("failed")); writer->writeDefaultNamespace(ns_stream_management); writer->writeStartElement(errorString); writer->writeDefaultNamespace(ns_stanza); writer->writeEndElement(); writer->writeEndElement(); } QXmppStreamManagementAck::QXmppStreamManagementAck(const unsigned seqNo) : m_seqNo(seqNo) { } unsigned QXmppStreamManagementAck::seqNo() const { return m_seqNo; } void QXmppStreamManagementAck::setSeqNo(const unsigned seqNo) { m_seqNo = seqNo; } void QXmppStreamManagementAck::parse(const QDomElement &element) { m_seqNo = element.attribute(QStringLiteral("h")).toUInt(); } void QXmppStreamManagementAck::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("a")); writer->writeDefaultNamespace(ns_stream_management); writer->writeAttribute(QStringLiteral("h"), QString::number(m_seqNo)); writer->writeEndElement(); } bool QXmppStreamManagementAck::isStreamManagementAck(const QDomElement &element) { return element.tagName() == QLatin1String("a") && element.namespaceURI() == ns_stream_management; } bool QXmppStreamManagementReq::isStreamManagementReq(const QDomElement &element) { return element.tagName() == QLatin1String("r") && element.namespaceURI() == ns_stream_management; } void QXmppStreamManagementReq::toXml(QXmlStreamWriter *writer) { writer->writeStartElement("r"); writer->writeDefaultNamespace(ns_stream_management); writer->writeEndElement(); } QXmppStreamManager::QXmppStreamManager(QXmppStream *stream) : stream(stream) { } unsigned int QXmppStreamManager::lastIncomingSequenceNumber() const { return m_lastIncomingSequenceNumber; } void QXmppStreamManager::handleDisconnect() { m_enabled = false; } void QXmppStreamManager::handleStart() { m_enabled = false; } void QXmppStreamManager::handlePacketSent(const QXmppStanza &packet, const QByteArray &data) { if (m_enabled && packet.isXmppStanza()) { m_unacknowledgedStanzas.insert(++m_lastOutgoingSequenceNumber, data); sendAcknowledgementRequest(); } } bool QXmppStreamManager::handleStanza(const QDomElement &stanza) { if (QXmppStreamManagementAck::isStreamManagementAck(stanza)) { handleAcknowledgement(stanza); return true; } if (QXmppStreamManagementReq::isStreamManagementReq(stanza)) { sendAcknowledgement(); return true; } if (stanza.tagName() == QLatin1String("message") || stanza.tagName() == QLatin1String("presence") || stanza.tagName() == QLatin1String("iq")) { m_lastIncomingSequenceNumber++; } return false; } void QXmppStreamManager::enableStreamManagement(bool resetSequenceNumber) { m_enabled = true; if (resetSequenceNumber) { m_lastOutgoingSequenceNumber = 0; m_lastIncomingSequenceNumber = 0; // resend unacked stanzas if (!m_unacknowledgedStanzas.isEmpty()) { const auto oldUnackedStanzas = m_unacknowledgedStanzas; m_unacknowledgedStanzas.clear(); for (const auto &value : oldUnackedStanzas) { m_unacknowledgedStanzas.insert(++m_lastOutgoingSequenceNumber, value); stream->sendData(value); } sendAcknowledgementRequest(); } } else { // resend unacked stanzas if (!m_unacknowledgedStanzas.isEmpty()) { for (const auto &value : std::as_const(m_unacknowledgedStanzas)) { stream->sendData(value); } sendAcknowledgementRequest(); } } } void QXmppStreamManager::setAcknowledgedSequenceNumber(unsigned int sequenceNumber) { for (auto it = m_unacknowledgedStanzas.begin(); it != m_unacknowledgedStanzas.end();) { if (it.key() <= sequenceNumber) { it = m_unacknowledgedStanzas.erase(it); } else { break; } } } void QXmppStreamManager::handleAcknowledgement(const QDomElement &element) { if (!m_enabled) return; QXmppStreamManagementAck ack; ack.parse(element); setAcknowledgedSequenceNumber(ack.seqNo()); } void QXmppStreamManager::sendAcknowledgement() { if (!m_enabled) return; // prepare packet QByteArray data; QXmlStreamWriter xmlStream(&data); QXmppStreamManagementAck ack(m_lastIncomingSequenceNumber); ack.toXml(&xmlStream); // send packet stream->sendData(data); } void QXmppStreamManager::sendAcknowledgementRequest() { if (!m_enabled) return; // prepare packet QByteArray data; QXmlStreamWriter xmlStream(&data); QXmppStreamManagementReq::toXml(&xmlStream); // send packet stream->sendData(data); } void QXmppStreamManager::resetCache() { m_unacknowledgedStanzas.clear(); } qxmpp-1.4.0/src/base/QXmppStreamManagement_p.h000066400000000000000000000132341402370562100212730ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Niels Ole Salscheider * * 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 QXMPPSTREAMMANAGEMENT_P_H #define QXMPPSTREAMMANAGEMENT_P_H #include "QXmppGlobal.h" #include "QXmppStanza.h" #include #include class QXmppStream; // // W A R N I N G // ------------- // // This file is not part of the QXmpp API. It exists for the convenience // of the QXmppIncomingClient and QXmppOutgoingClient classes. // // This header file may change from version to version without notice, // or even be removed. // // We mean it. // class QXMPP_AUTOTEST_EXPORT QXmppStreamManagementEnable { public: QXmppStreamManagementEnable(const bool resume = false, const unsigned max = 0); bool resume() const; void setResume(const bool resume); unsigned max() const; void setMax(const unsigned max); static bool isStreamManagementEnable(const QDomElement &element); /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *writer) const; /// \endcond private: bool m_resume; unsigned m_max; }; class QXMPP_AUTOTEST_EXPORT QXmppStreamManagementEnabled { public: QXmppStreamManagementEnabled(const bool resume = false, const QString id = QString(), const unsigned max = 0, const QString location = QString()); bool resume() const; void setResume(const bool resume); QString id() const; void setId(const QString id); unsigned max() const; void setMax(const unsigned max); QString location() const; void setLocation(const QString location); static bool isStreamManagementEnabled(const QDomElement &element); /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *writer) const; /// \endcond private: bool m_resume; QString m_id; unsigned m_max; QString m_location; }; class QXMPP_AUTOTEST_EXPORT QXmppStreamManagementResume { public: QXmppStreamManagementResume(const unsigned h = 0, const QString &previd = QString()); unsigned h() const; void setH(const unsigned h); QString prevId() const; void setPrevId(const QString &id); static bool isStreamManagementResume(const QDomElement &element); /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *writer) const; /// \endcond private: unsigned m_h; QString m_previd; }; class QXMPP_AUTOTEST_EXPORT QXmppStreamManagementResumed { public: QXmppStreamManagementResumed(const unsigned h = 0, const QString &previd = QString()); unsigned h() const; void setH(const unsigned h); QString prevId() const; void setPrevId(const QString &id); static bool isStreamManagementResumed(const QDomElement &element); /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *writer) const; /// \endcond private: unsigned m_h; QString m_previd; }; class QXMPP_AUTOTEST_EXPORT QXmppStreamManagementFailed { public: QXmppStreamManagementFailed(const QXmppStanza::Error::Condition error = QXmppStanza::Error::UndefinedCondition); QXmppStanza::Error::Condition error() const; void setError(const QXmppStanza::Error::Condition error); static bool isStreamManagementFailed(const QDomElement &element); /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *writer) const; /// \endcond private: QXmppStanza::Error::Condition m_error; }; class QXMPP_AUTOTEST_EXPORT QXmppStreamManagementAck { public: QXmppStreamManagementAck(const unsigned seqNo = 0); unsigned seqNo() const; void setSeqNo(const unsigned seqNo); static bool isStreamManagementAck(const QDomElement &element); /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *writer) const; /// \endcond private: unsigned m_seqNo; }; class QXMPP_AUTOTEST_EXPORT QXmppStreamManagementReq { public: static bool isStreamManagementReq(const QDomElement &element); /// \cond static void toXml(QXmlStreamWriter *writer); /// \endcond }; // // This manager is used in the QXmppStream. It contains the parts of stream // management that are shared between server and client connections. // class QXmppStreamManager { public: explicit QXmppStreamManager(QXmppStream *stream); unsigned int lastIncomingSequenceNumber() const; void handleDisconnect(); void handleStart(); void handlePacketSent(const QXmppStanza &packet, const QByteArray &data); bool handleStanza(const QDomElement &stanza); void resetCache(); void enableStreamManagement(bool resetSequenceNumber); void setAcknowledgedSequenceNumber(unsigned int sequenceNumber); private: void handleAcknowledgement(const QDomElement &element); void sendAcknowledgement(); void sendAcknowledgementRequest(); QXmppStream *stream; bool m_enabled = false; QMap m_unacknowledgedStanzas; unsigned int m_lastOutgoingSequenceNumber = 0; unsigned int m_lastIncomingSequenceNumber = 0; }; #endif qxmpp-1.4.0/src/base/QXmppStun.cpp000066400000000000000000002462271402370562100170220ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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. * */ #define QXMPP_DEBUG_STUN #include "QXmppStun_p.h" #include "QXmppUtils.h" #include #include #include #include #include #include #include #define STUN_ID_SIZE 12 #define STUN_RTO_INTERVAL 500 #define STUN_RTO_MAX 7 static const quint32 STUN_MAGIC = 0x2112A442; static const quint16 STUN_HEADER = 20; static const quint8 STUN_IPV4 = 0x01; static const quint8 STUN_IPV6 = 0x02; static const char *gathering_states[] = { "new", "gathering", "complete" }; static const char *pair_states[] = { "frozen", "waiting", "in-progress", "succeeded", "failed" }; enum AttributeType { MappedAddress = 0x0001, // RFC5389 ChangeRequest = 0x0003, // RFC5389 SourceAddress = 0x0004, // RFC5389 ChangedAddress = 0x0005, // RFC5389 Username = 0x0006, // RFC5389 MessageIntegrity = 0x0008, // RFC5389 ErrorCode = 0x0009, // RFC5389 ChannelNumber = 0x000c, // RFC5766 : TURN Lifetime = 0x000d, // RFC5766 : TURN XorPeerAddress = 0x0012, // RFC5766 : TURN DataAttr = 0x0013, // RFC5766 : TURN Realm = 0x0014, // RFC5389 Nonce = 0x0015, // RFC5389 XorRelayedAddress = 0x0016, // RFC5766 : TURN EvenPort = 0x0018, // RFC5766 : TURN RequestedTransport = 0x0019, // RFC5766 : TURN XorMappedAddress = 0x0020, // RFC5389 ReservationToken = 0x0022, // RFC5766 : TURN Priority = 0x0024, // RFC5245 UseCandidate = 0x0025, // RFC5245 Software = 0x8022, // RFC5389 Fingerprint = 0x8028, // RFC5389 IceControlled = 0x8029, // RFC5245 IceControlling = 0x802a, // RFC5245 OtherAddress = 0x802c // RFC5780 }; // FIXME : we need to set local preference to discriminate between // multiple IP addresses static quint32 candidatePriority(const QXmppJingleCandidate &candidate, int localPref = 65535) { int typePref; switch (candidate.type()) { case QXmppJingleCandidate::HostType: typePref = 126; break; case QXmppJingleCandidate::PeerReflexiveType: typePref = 110; break; case QXmppJingleCandidate::ServerReflexiveType: typePref = 100; break; default: typePref = 0; } return (1 << 24) * typePref + (1 << 8) * localPref + (256 - candidate.component()); } static QString computeFoundation(QXmppJingleCandidate::Type type, const QString &protocol, const QHostAddress &baseAddress) { QCryptographicHash hash(QCryptographicHash::Md5); hash.addData((QString::number(type) + protocol + baseAddress.toString()).toUtf8()); return hash.result().toHex(); } static bool isIPv6LinkLocalAddress(const QHostAddress &addr) { if (addr.protocol() != QAbstractSocket::IPv6Protocol) return false; Q_IPV6ADDR ipv6addr = addr.toIPv6Address(); return (((ipv6addr[0] << 8) + ipv6addr[1]) & 0xffc0) == 0xfe80; } static bool isLoopbackAddress(const QHostAddress &addr) { return (addr.toIPv4Address() & 0xff000000) == 0x7f000000; } // Returns true if the two addresses are compatible. static bool isCompatibleAddress(const QHostAddress &a1, const QHostAddress &a2) { return a1.protocol() == a2.protocol() && isIPv6LinkLocalAddress(a1) == isIPv6LinkLocalAddress(a2); } static bool decodeAddress(QDataStream &stream, quint16 a_length, QHostAddress &address, quint16 &port, const QByteArray &xorId = QByteArray()) { if (a_length < 4) return false; quint8 reserved, protocol; quint16 rawPort; stream >> reserved; stream >> protocol; stream >> rawPort; if (xorId.isEmpty()) port = rawPort; else port = rawPort ^ (STUN_MAGIC >> 16); if (protocol == STUN_IPV4) { if (a_length != 8) return false; quint32 addr; stream >> addr; if (xorId.isEmpty()) address = QHostAddress(addr); else address = QHostAddress(addr ^ STUN_MAGIC); } else if (protocol == STUN_IPV6) { if (a_length != 20) return false; Q_IPV6ADDR addr; stream.readRawData((char *)&addr, sizeof(addr)); if (!xorId.isEmpty()) { QByteArray xpad; QDataStream(&xpad, QIODevice::WriteOnly) << STUN_MAGIC; xpad += xorId; for (int i = 0; i < 16; i++) addr[i] ^= xpad[i]; } address = QHostAddress(addr); } else { return false; } return true; } static void encodeAddress(QDataStream &stream, quint16 type, const QHostAddress &address, quint16 port, const QByteArray &xorId = QByteArray()) { const quint8 reserved = 0; if (address.protocol() == QAbstractSocket::IPv4Protocol) { stream << type; stream << quint16(8); stream << reserved; stream << quint8(STUN_IPV4); quint32 addr = address.toIPv4Address(); if (!xorId.isEmpty()) { port ^= (STUN_MAGIC >> 16); addr ^= STUN_MAGIC; } stream << port; stream << addr; } else if (address.protocol() == QAbstractSocket::IPv6Protocol) { stream << type; stream << quint16(20); stream << reserved; stream << quint8(STUN_IPV6); Q_IPV6ADDR addr = address.toIPv6Address(); if (!xorId.isEmpty()) { port ^= (STUN_MAGIC >> 16); QByteArray xpad; QDataStream(&xpad, QIODevice::WriteOnly) << STUN_MAGIC; xpad += xorId; for (int i = 0; i < 16; i++) addr[i] ^= xpad[i]; } stream << port; stream.writeRawData((char *)&addr, sizeof(addr)); } else { qWarning("Cannot write STUN attribute for unknown IP version"); } } static void addAddress(QDataStream &stream, quint16 type, const QHostAddress &host, quint16 port, const QByteArray &xorId = QByteArray()) { if (port && !host.isNull() && (host.protocol() == QAbstractSocket::IPv4Protocol || host.protocol() == QAbstractSocket::IPv6Protocol)) { encodeAddress(stream, type, host, port, xorId); } } static void encodeString(QDataStream &stream, quint16 type, const QString &string) { const QByteArray utf8string = string.toUtf8(); stream << type; stream << quint16(utf8string.size()); stream.writeRawData(utf8string.data(), utf8string.size()); if (utf8string.size() % 4) { const QByteArray padding(4 - (utf8string.size() % 4), 0); stream.writeRawData(padding.data(), padding.size()); } } static void setBodyLength(QByteArray &buffer, qint16 length) { QDataStream stream(&buffer, QIODevice::WriteOnly); stream.device()->seek(2); stream << length; } /// Constructs a new QXmppStunMessage. QXmppStunMessage::QXmppStunMessage() : errorCode(0), changedPort(0), mappedPort(0), otherPort(0), sourcePort(0), xorMappedPort(0), xorPeerPort(0), xorRelayedPort(0), useCandidate(false), m_cookie(STUN_MAGIC), m_type(0), m_changeRequest(0), m_channelNumber(0), m_lifetime(0), m_priority(0) { m_id = QByteArray(STUN_ID_SIZE, 0); } quint32 QXmppStunMessage::cookie() const { return m_cookie; } void QXmppStunMessage::setCookie(quint32 cookie) { m_cookie = cookie; } QByteArray QXmppStunMessage::id() const { return m_id; } void QXmppStunMessage::setId(const QByteArray &id) { Q_ASSERT(id.size() == STUN_ID_SIZE); m_id = id; } quint16 QXmppStunMessage::messageClass() const { return m_type & 0x0110; } quint16 QXmppStunMessage::messageMethod() const { return m_type & 0x3eef; } quint16 QXmppStunMessage::type() const { return m_type; } void QXmppStunMessage::setType(quint16 type) { m_type = type; } /// Returns the CHANGE-REQUEST attribute, indicating whether to change /// the IP and / or port from which the response is sent. quint32 QXmppStunMessage::changeRequest() const { return m_changeRequest; } /// Sets the CHANGE-REQUEST attribute, indicating whether to change /// the IP and / or port from which the response is sent. /// /// \param changeRequest void QXmppStunMessage::setChangeRequest(quint32 changeRequest) { m_changeRequest = changeRequest; m_attributes << ChangeRequest; } /// Returns the CHANNEL-NUMBER attribute. quint16 QXmppStunMessage::channelNumber() const { return m_channelNumber; } /// Sets the CHANNEL-NUMBER attribute. /// /// \param channelNumber void QXmppStunMessage::setChannelNumber(quint16 channelNumber) { m_channelNumber = channelNumber; m_attributes << ChannelNumber; } /// Returns the DATA attribute. QByteArray QXmppStunMessage::data() const { return m_data; } /// Sets the DATA attribute. void QXmppStunMessage::setData(const QByteArray &data) { m_data = data; m_attributes << DataAttr; } /// Returns the LIFETIME attribute, indicating the duration in seconds for /// which the server will maintain an allocation. quint32 QXmppStunMessage::lifetime() const { return m_lifetime; } /// Sets the LIFETIME attribute, indicating the duration in seconds for /// which the server will maintain an allocation. /// /// \param lifetime void QXmppStunMessage::setLifetime(quint32 lifetime) { m_lifetime = lifetime; m_attributes << Lifetime; } /// Returns the NONCE attribute. QByteArray QXmppStunMessage::nonce() const { return m_nonce; } /// Sets the NONCE attribute. /// /// \param nonce void QXmppStunMessage::setNonce(const QByteArray &nonce) { m_nonce = nonce; m_attributes << Nonce; } /// Returns the PRIORITY attribute, the priority that would be assigned to /// a peer reflexive candidate discovered during the ICE check. quint32 QXmppStunMessage::priority() const { return m_priority; } /// Sets the PRIORITY attribute, the priority that would be assigned to /// a peer reflexive candidate discovered during the ICE check. /// /// \param priority void QXmppStunMessage::setPriority(quint32 priority) { m_priority = priority; m_attributes << Priority; } /// Returns the REALM attribute. QString QXmppStunMessage::realm() const { return m_realm; } /// Sets the REALM attribute. /// /// \param realm void QXmppStunMessage::setRealm(const QString &realm) { m_realm = realm; m_attributes << Realm; } /// Returns the REQUESTED-TRANSPORT attribute. quint8 QXmppStunMessage::requestedTransport() const { return m_requestedTransport; } /// Sets the REQUESTED-TRANSPORT attribute. /// /// \param requestedTransport void QXmppStunMessage::setRequestedTransport(quint8 requestedTransport) { m_requestedTransport = requestedTransport; m_attributes << RequestedTransport; } /// Returns the RESERVATION-TOKEN attribute. QByteArray QXmppStunMessage::reservationToken() const { return m_reservationToken; } /// Sets the RESERVATION-TOKEN attribute. /// /// \param reservationToken void QXmppStunMessage::setReservationToken(const QByteArray &reservationToken) { m_reservationToken = reservationToken; m_reservationToken.resize(8); m_attributes << ReservationToken; } /// Returns the SOFTWARE attribute, containing a textual description of the /// software being used. QString QXmppStunMessage::software() const { return m_software; } /// Sets the SOFTWARE attribute, containing a textual description of the /// software being used. /// /// \param software void QXmppStunMessage::setSoftware(const QString &software) { m_software = software; m_attributes << Software; } /// Returns the USERNAME attribute, containing the username to use for /// authentication. QString QXmppStunMessage::username() const { return m_username; } /// Sets the USERNAME attribute, containing the username to use for /// authentication. /// /// \param username void QXmppStunMessage::setUsername(const QString &username) { m_username = username; m_attributes << Username; } /// Decodes a QXmppStunMessage and checks its integrity using the given key. /// /// \param buffer /// \param key /// \param errors bool QXmppStunMessage::decode(const QByteArray &buffer, const QByteArray &key, QStringList *errors) { QStringList silent; if (!errors) errors = &silent; if (buffer.size() < STUN_HEADER) { *errors << QLatin1String("Received a truncated STUN packet"); return false; } // parse STUN header QDataStream stream(buffer); quint16 length; stream >> m_type; stream >> length; stream >> m_cookie; stream.readRawData(m_id.data(), m_id.size()); if (length != buffer.size() - STUN_HEADER) { *errors << QLatin1String("Received an invalid STUN packet"); return false; } // parse STUN attributes int done = 0; bool after_integrity = false; while (done < length) { quint16 a_type, a_length; stream >> a_type; stream >> a_length; const int pad_length = 4 * ((a_length + 3) / 4) - a_length; // only FINGERPRINT is allowed after MESSAGE-INTEGRITY if (after_integrity && a_type != Fingerprint) { *errors << QString("Skipping attribute %1 after MESSAGE-INTEGRITY").arg(QString::number(a_type)); stream.skipRawData(a_length + pad_length); done += 4 + a_length + pad_length; continue; } if (a_type == Priority) { // PRIORITY if (a_length != sizeof(m_priority)) return false; stream >> m_priority; m_attributes << Priority; } else if (a_type == ErrorCode) { // ERROR-CODE if (a_length < 4) return false; quint16 reserved; quint8 errorCodeHigh, errorCodeLow; stream >> reserved; stream >> errorCodeHigh; stream >> errorCodeLow; errorCode = errorCodeHigh * 100 + errorCodeLow; QByteArray phrase(a_length - 4, 0); stream.readRawData(phrase.data(), phrase.size()); errorPhrase = QString::fromUtf8(phrase); } else if (a_type == UseCandidate) { // USE-CANDIDATE if (a_length != 0) return false; useCandidate = true; } else if (a_type == ChannelNumber) { // CHANNEL-NUMBER if (a_length != 4) return false; stream >> m_channelNumber; stream.skipRawData(2); m_attributes << ChannelNumber; } else if (a_type == DataAttr) { // DATA m_data.resize(a_length); stream.readRawData(m_data.data(), m_data.size()); m_attributes << DataAttr; } else if (a_type == Lifetime) { // LIFETIME if (a_length != sizeof(m_lifetime)) return false; stream >> m_lifetime; m_attributes << Lifetime; } else if (a_type == Nonce) { // NONCE m_nonce.resize(a_length); stream.readRawData(m_nonce.data(), m_nonce.size()); m_attributes << Nonce; } else if (a_type == Realm) { // REALM QByteArray utf8Realm(a_length, 0); stream.readRawData(utf8Realm.data(), utf8Realm.size()); m_realm = QString::fromUtf8(utf8Realm); m_attributes << Realm; } else if (a_type == RequestedTransport) { // REQUESTED-TRANSPORT if (a_length != 4) return false; stream >> m_requestedTransport; stream.skipRawData(3); m_attributes << RequestedTransport; } else if (a_type == ReservationToken) { // RESERVATION-TOKEN if (a_length != 8) return false; m_reservationToken.resize(a_length); stream.readRawData(m_reservationToken.data(), m_reservationToken.size()); m_attributes << ReservationToken; } else if (a_type == Software) { // SOFTWARE QByteArray utf8Software(a_length, 0); stream.readRawData(utf8Software.data(), utf8Software.size()); m_software = QString::fromUtf8(utf8Software); m_attributes << Software; } else if (a_type == Username) { // USERNAME QByteArray utf8Username(a_length, 0); stream.readRawData(utf8Username.data(), utf8Username.size()); m_username = QString::fromUtf8(utf8Username); m_attributes << Username; } else if (a_type == MappedAddress) { // MAPPED-ADDRESS if (!decodeAddress(stream, a_length, mappedHost, mappedPort)) { *errors << QLatin1String("Bad MAPPED-ADDRESS"); return false; } } else if (a_type == ChangeRequest) { // CHANGE-REQUEST if (a_length != sizeof(m_changeRequest)) return false; stream >> m_changeRequest; m_attributes << ChangeRequest; } else if (a_type == SourceAddress) { // SOURCE-ADDRESS if (!decodeAddress(stream, a_length, sourceHost, sourcePort)) { *errors << QLatin1String("Bad SOURCE-ADDRESS"); return false; } } else if (a_type == ChangedAddress) { // CHANGED-ADDRESS if (!decodeAddress(stream, a_length, changedHost, changedPort)) { *errors << QLatin1String("Bad CHANGED-ADDRESS"); return false; } } else if (a_type == OtherAddress) { // OTHER-ADDRESS if (!decodeAddress(stream, a_length, otherHost, otherPort)) { *errors << QLatin1String("Bad OTHER-ADDRESS"); return false; } } else if (a_type == XorMappedAddress) { // XOR-MAPPED-ADDRESS if (!decodeAddress(stream, a_length, xorMappedHost, xorMappedPort, m_id)) { *errors << QLatin1String("Bad XOR-MAPPED-ADDRESS"); return false; } } else if (a_type == XorPeerAddress) { // XOR-PEER-ADDRESS if (!decodeAddress(stream, a_length, xorPeerHost, xorPeerPort, m_id)) { *errors << QLatin1String("Bad XOR-PEER-ADDRESS"); return false; } } else if (a_type == XorRelayedAddress) { // XOR-RELAYED-ADDRESS if (!decodeAddress(stream, a_length, xorRelayedHost, xorRelayedPort, m_id)) { *errors << QLatin1String("Bad XOR-RELAYED-ADDRESS"); return false; } } else if (a_type == MessageIntegrity) { // MESSAGE-INTEGRITY if (a_length != 20) return false; QByteArray integrity(20, 0); stream.readRawData(integrity.data(), integrity.size()); // check HMAC-SHA1 if (!key.isEmpty()) { QByteArray copy = buffer.left(STUN_HEADER + done); setBodyLength(copy, done + 24); if (integrity != QXmppUtils::generateHmacSha1(key, copy)) { *errors << QLatin1String("Bad message integrity"); return false; } } // from here onwards, only FINGERPRINT is allowed after_integrity = true; } else if (a_type == Fingerprint) { // FINGERPRINT if (a_length != 4) return false; quint32 fingerprint; stream >> fingerprint; // check CRC32 QByteArray copy = buffer.left(STUN_HEADER + done); setBodyLength(copy, done + 8); const quint32 expected = QXmppUtils::generateCrc32(copy) ^ 0x5354554eL; if (fingerprint != expected) { *errors << QLatin1String("Bad fingerprint"); return false; } // stop parsing, no more attributes are allowed return true; } else if (a_type == IceControlling) { /// ICE-CONTROLLING if (a_length != 8) return false; iceControlling.resize(a_length); stream.readRawData(iceControlling.data(), iceControlling.size()); } else if (a_type == IceControlled) { /// ICE-CONTROLLED if (a_length != 8) return false; iceControlled.resize(a_length); stream.readRawData(iceControlled.data(), iceControlled.size()); } else { // Unknown attribute stream.skipRawData(a_length); *errors << QStringLiteral("Skipping unknown attribute %1").arg(QString::number(a_type)); } stream.skipRawData(pad_length); done += 4 + a_length + pad_length; } return true; } /// Encodes the current QXmppStunMessage, optionally calculating the /// message integrity attribute using the given key. /// /// \param key /// \param addFingerprint QByteArray QXmppStunMessage::encode(const QByteArray &key, bool addFingerprint) const { QByteArray buffer; QDataStream stream(&buffer, QIODevice::WriteOnly); // encode STUN header quint16 length = 0; stream << m_type; stream << length; stream << m_cookie; stream.writeRawData(m_id.data(), m_id.size()); // MAPPED-ADDRESS addAddress(stream, MappedAddress, mappedHost, mappedPort); // CHANGE-REQUEST if (m_attributes.contains(ChangeRequest)) { stream << quint16(ChangeRequest); stream << quint16(sizeof(m_changeRequest)); stream << m_changeRequest; } // SOURCE-ADDRESS addAddress(stream, SourceAddress, sourceHost, sourcePort); // CHANGED-ADDRESS addAddress(stream, ChangedAddress, changedHost, changedPort); // OTHER-ADDRESS addAddress(stream, OtherAddress, otherHost, otherPort); // XOR-MAPPED-ADDRESS addAddress(stream, XorMappedAddress, xorMappedHost, xorMappedPort, m_id); // XOR-PEER-ADDRESS addAddress(stream, XorPeerAddress, xorPeerHost, xorPeerPort, m_id); // XOR-RELAYED-ADDRESS addAddress(stream, XorRelayedAddress, xorRelayedHost, xorRelayedPort, m_id); // ERROR-CODE if (errorCode) { const quint16 reserved = 0; const quint8 errorCodeHigh = errorCode / 100; const quint8 errorCodeLow = errorCode % 100; const QByteArray phrase = errorPhrase.toUtf8(); stream << quint16(ErrorCode); stream << quint16(phrase.size() + 4); stream << reserved; stream << errorCodeHigh; stream << errorCodeLow; stream.writeRawData(phrase.data(), phrase.size()); if (phrase.size() % 4) { const QByteArray padding(4 - (phrase.size() % 4), 0); stream.writeRawData(padding.data(), padding.size()); } } // PRIORITY if (m_attributes.contains(Priority)) { stream << quint16(Priority); stream << quint16(sizeof(m_priority)); stream << m_priority; } // USE-CANDIDATE if (useCandidate) { stream << quint16(UseCandidate); stream << quint16(0); } // CHANNEL-NUMBER if (m_attributes.contains(ChannelNumber)) { stream << quint16(ChannelNumber); stream << quint16(4); stream << m_channelNumber; stream << quint16(0); } // DATA if (m_attributes.contains(DataAttr)) { stream << quint16(DataAttr); stream << quint16(m_data.size()); stream.writeRawData(m_data.data(), m_data.size()); if (m_data.size() % 4) { const QByteArray padding(4 - (m_data.size() % 4), 0); stream.writeRawData(padding.data(), padding.size()); } } // LIFETIME if (m_attributes.contains(Lifetime)) { stream << quint16(Lifetime); stream << quint16(sizeof(m_lifetime)); stream << m_lifetime; } // NONCE if (m_attributes.contains(Nonce)) { stream << quint16(Nonce); stream << quint16(m_nonce.size()); stream.writeRawData(m_nonce.data(), m_nonce.size()); } // REALM if (m_attributes.contains(Realm)) encodeString(stream, Realm, m_realm); // REQUESTED-TRANSPORT if (m_attributes.contains(RequestedTransport)) { const QByteArray reserved(3, 0); stream << quint16(RequestedTransport); stream << quint16(4); stream << m_requestedTransport; stream.writeRawData(reserved.data(), reserved.size()); } // RESERVATION-TOKEN if (m_attributes.contains(ReservationToken)) { stream << quint16(ReservationToken); stream << quint16(m_reservationToken.size()); stream.writeRawData(m_reservationToken.data(), m_reservationToken.size()); } // SOFTWARE if (m_attributes.contains(Software)) encodeString(stream, Software, m_software); // USERNAME if (m_attributes.contains(Username)) encodeString(stream, Username, m_username); // ICE-CONTROLLING or ICE-CONTROLLED if (!iceControlling.isEmpty()) { stream << quint16(IceControlling); stream << quint16(iceControlling.size()); stream.writeRawData(iceControlling.data(), iceControlling.size()); } else if (!iceControlled.isEmpty()) { stream << quint16(IceControlled); stream << quint16(iceControlled.size()); stream.writeRawData(iceControlled.data(), iceControlled.size()); } // set body length setBodyLength(buffer, buffer.size() - STUN_HEADER); // MESSAGE-INTEGRITY if (!key.isEmpty()) { setBodyLength(buffer, buffer.size() - STUN_HEADER + 24); QByteArray integrity = QXmppUtils::generateHmacSha1(key, buffer); stream << quint16(MessageIntegrity); stream << quint16(integrity.size()); stream.writeRawData(integrity.data(), integrity.size()); } // FINGERPRINT if (addFingerprint) { setBodyLength(buffer, buffer.size() - STUN_HEADER + 8); quint32 fingerprint = QXmppUtils::generateCrc32(buffer) ^ 0x5354554eL; stream << quint16(Fingerprint); stream << quint16(sizeof(fingerprint)); stream << fingerprint; } return buffer; } /// If the given packet looks like a STUN message, returns the message /// type, otherwise returns 0. /// /// \param buffer /// \param cookie /// \param id quint16 QXmppStunMessage::peekType(const QByteArray &buffer, quint32 &cookie, QByteArray &id) { if (buffer.size() < STUN_HEADER) return 0; // parse STUN header QDataStream stream(buffer); quint16 type; quint16 length; stream >> type; stream >> length; stream >> cookie; if (length != buffer.size() - STUN_HEADER) return 0; id.resize(STUN_ID_SIZE); stream.readRawData(id.data(), id.size()); return type; } QString QXmppStunMessage::toString() const { QStringList dumpLines; QString typeName; switch (messageMethod()) { case Binding: typeName = QStringLiteral("Binding"); break; case SharedSecret: typeName = QStringLiteral("Shared Secret"); break; case Allocate: typeName = QStringLiteral("Allocate"); break; case Refresh: typeName = QStringLiteral("Refresh"); break; case Send: typeName = QStringLiteral("Send"); break; case Data: typeName = QStringLiteral("Data"); break; case CreatePermission: typeName = QStringLiteral("CreatePermission"); break; case ChannelBind: typeName = QStringLiteral("ChannelBind"); break; default: typeName = QStringLiteral("Unknown"); break; } switch (messageClass()) { case Request: typeName += QStringLiteral(" Request"); break; case Indication: typeName += QStringLiteral(" Indication"); break; case Response: typeName += QStringLiteral(" Response"); break; case Error: typeName += QStringLiteral(" Error"); break; default: break; } dumpLines << QStringLiteral(" type %1 (%2)") .arg(typeName) .arg(QString::number(m_type)); dumpLines << QStringLiteral(" id %1").arg(QString::fromLatin1(m_id.toHex())); // attributes if (m_attributes.contains(ChannelNumber)) dumpLines << QStringLiteral(" * CHANNEL-NUMBER %1").arg(QString::number(m_channelNumber)); if (errorCode) dumpLines << QStringLiteral(" * ERROR-CODE %1 %2") .arg(QString::number(errorCode), errorPhrase); if (m_attributes.contains(Lifetime)) dumpLines << QStringLiteral(" * LIFETIME %1").arg(QString::number(m_lifetime)); if (m_attributes.contains(Nonce)) dumpLines << QStringLiteral(" * NONCE %1").arg(QString::fromLatin1(m_nonce)); if (m_attributes.contains(Realm)) dumpLines << QStringLiteral(" * REALM %1").arg(m_realm); if (m_attributes.contains(RequestedTransport)) dumpLines << QStringLiteral(" * REQUESTED-TRANSPORT 0x%1").arg(QString::number(m_requestedTransport, 16)); if (m_attributes.contains(ReservationToken)) dumpLines << QStringLiteral(" * RESERVATION-TOKEN %1").arg(QString::fromLatin1(m_reservationToken.toHex())); if (m_attributes.contains(Software)) dumpLines << QStringLiteral(" * SOFTWARE %1").arg(m_software); if (m_attributes.contains(Username)) dumpLines << QStringLiteral(" * USERNAME %1").arg(m_username); if (mappedPort) dumpLines << QStringLiteral(" * MAPPED-ADDRESS %1 %2") .arg(mappedHost.toString(), QString::number(mappedPort)); if (m_attributes.contains(ChangeRequest)) dumpLines << QStringLiteral(" * CHANGE-REQUEST %1") .arg(QString::number(m_changeRequest)); if (sourcePort) dumpLines << QStringLiteral(" * SOURCE-ADDRESS %1 %2") .arg(sourceHost.toString(), QString::number(sourcePort)); if (changedPort) dumpLines << QStringLiteral(" * CHANGED-ADDRESS %1 %2") .arg(changedHost.toString(), QString::number(changedPort)); if (otherPort) dumpLines << QStringLiteral(" * OTHER-ADDRESS %1 %2") .arg(otherHost.toString(), QString::number(otherPort)); if (xorMappedPort) dumpLines << QStringLiteral(" * XOR-MAPPED-ADDRESS %1 %2") .arg(xorMappedHost.toString(), QString::number(xorMappedPort)); if (xorPeerPort) dumpLines << QStringLiteral(" * XOR-PEER-ADDRESS %1 %2") .arg(xorPeerHost.toString(), QString::number(xorPeerPort)); if (xorRelayedPort) dumpLines << QStringLiteral(" * XOR-RELAYED-ADDRESS %1 %2") .arg(xorRelayedHost.toString(), QString::number(xorRelayedPort)); if (m_attributes.contains(Priority)) dumpLines << QStringLiteral(" * PRIORITY %1").arg(QString::number(m_priority)); if (!iceControlling.isEmpty()) dumpLines << QStringLiteral(" * ICE-CONTROLLING %1") .arg(QString::fromLatin1(iceControlling.toHex())); if (!iceControlled.isEmpty()) dumpLines << QStringLiteral(" * ICE-CONTROLLED %1") .arg(QString::fromLatin1(iceControlled.toHex())); if (useCandidate) dumpLines << QStringLiteral(" * USE-CANDIDATE"); return dumpLines.join("\n"); } /// Constructs a new QXmppStunTransaction. /// /// \param request /// \param receiver QXmppStunTransaction::QXmppStunTransaction(const QXmppStunMessage &request, QObject *receiver) : QXmppLoggable(receiver), m_request(request), m_tries(0) { bool check; Q_UNUSED(check) check = connect(this, SIGNAL(writeStun(QXmppStunMessage)), receiver, SLOT(writeStun(QXmppStunMessage))); Q_ASSERT(check); check = connect(this, SIGNAL(finished()), receiver, SLOT(transactionFinished())); Q_ASSERT(check); // RTO timer m_retryTimer = new QTimer(this); m_retryTimer->setSingleShot(true); connect(m_retryTimer, &QTimer::timeout, this, &QXmppStunTransaction::retry); // send packet immediately m_retryTimer->start(0); } void QXmppStunTransaction::readStun(const QXmppStunMessage &response) { if (response.messageClass() == QXmppStunMessage::Error || response.messageClass() == QXmppStunMessage::Response) { m_response = response; m_retryTimer->stop(); emit finished(); } } /// Returns the STUN request. QXmppStunMessage QXmppStunTransaction::request() const { return m_request; } /// Returns the STUN response. QXmppStunMessage QXmppStunTransaction::response() const { return m_response; } void QXmppStunTransaction::retry() { if (m_tries >= STUN_RTO_MAX) { m_response.setType(QXmppStunMessage::Error); m_response.errorPhrase = QLatin1String("Request timed out"); emit finished(); return; } // resend request emit writeStun(m_request); m_retryTimer->start(m_tries ? 2 * m_retryTimer->interval() : STUN_RTO_INTERVAL); m_tries++; } /// Constructs a new QXmppTurnAllocation. /// /// \param parent QXmppTurnAllocation::QXmppTurnAllocation(QObject *parent) : QXmppIceTransport(parent), m_relayedPort(0), m_turnPort(0), m_channelNumber(0x4000), m_lifetime(600), m_state(UnconnectedState) { socket = new QUdpSocket(this); socket->setSocketOption(QAbstractSocket::LowDelayOption, 1); connect(socket, &QIODevice::readyRead, this, &QXmppTurnAllocation::readyRead); m_timer = new QTimer(this); m_timer->setSingleShot(true); connect(m_timer, &QTimer::timeout, this, &QXmppTurnAllocation::refresh); // channels are valid 600s, we refresh every 500s m_channelTimer = new QTimer(this); m_channelTimer->setInterval(500 * 1000); connect(m_channelTimer, &QTimer::timeout, this, &QXmppTurnAllocation::refreshChannels); } /// Destroys the TURN allocation. QXmppTurnAllocation::~QXmppTurnAllocation() { if (m_state == ConnectedState) disconnectFromHost(); } /// Allocates the TURN allocation. void QXmppTurnAllocation::connectToHost() { if (m_state != UnconnectedState) return; // start listening for UDP if (socket->state() == QAbstractSocket::UnconnectedState) { if (!socket->bind()) { warning(QStringLiteral("Could not start listening for TURN")); return; } } // send allocate request QXmppStunMessage request; request.setType(QXmppStunMessage::Allocate | QXmppStunMessage::Request); request.setId(QXmppUtils::generateRandomBytes(STUN_ID_SIZE)); request.setLifetime(m_lifetime); request.setRequestedTransport(0x11); m_transactions << new QXmppStunTransaction(request, this); // update state setState(ConnectingState); } /// Releases the TURN allocation. void QXmppTurnAllocation::disconnectFromHost() { m_channelTimer->stop(); m_timer->stop(); // clear channels and any outstanding transactions m_channels.clear(); for (auto *transaction : m_transactions) delete transaction; m_transactions.clear(); // end allocation if (m_state == ConnectedState) { QXmppStunMessage request; request.setType(QXmppStunMessage::Refresh | QXmppStunMessage::Request); request.setId(QXmppUtils::generateRandomBytes(STUN_ID_SIZE)); request.setNonce(m_nonce); request.setRealm(m_realm); request.setUsername(m_username); request.setLifetime(0); m_transactions << new QXmppStunTransaction(request, this); setState(ClosingState); } else { setState(UnconnectedState); } } QXmppJingleCandidate QXmppTurnAllocation::localCandidate(int component) const { QXmppJingleCandidate candidate; candidate.setComponent(component); candidate.setHost(relayedHost()); candidate.setId(QXmppUtils::generateStanzaHash(10)); candidate.setPort(relayedPort()); candidate.setProtocol("udp"); candidate.setType(QXmppJingleCandidate::RelayedType); candidate.setPriority(candidatePriority(candidate)); candidate.setFoundation(computeFoundation( candidate.type(), candidate.protocol(), candidate.host())); return candidate; } void QXmppTurnAllocation::readyRead() { QByteArray buffer; QHostAddress remoteHost; quint16 remotePort; while (socket->hasPendingDatagrams()) { const qint64 size = socket->pendingDatagramSize(); buffer.resize(size); socket->readDatagram(buffer.data(), buffer.size(), &remoteHost, &remotePort); handleDatagram(buffer, remoteHost, remotePort); } } void QXmppTurnAllocation::handleDatagram(const QByteArray &buffer, const QHostAddress &remoteHost, quint16 remotePort) { // demultiplex channel data if (buffer.size() >= 4 && (buffer[0] & 0xc0) == 0x40) { QDataStream stream(buffer); quint16 channel, length; stream >> channel; stream >> length; if (m_state == ConnectedState && m_channels.contains(channel) && length <= buffer.size() - 4) { emit datagramReceived(buffer.mid(4, length), m_channels[channel].first, m_channels[channel].second); } return; } // parse STUN message QXmppStunMessage message; QStringList errors; if (!message.decode(buffer, QByteArray(), &errors)) { for (const auto &error : errors) warning(error); return; } #ifdef QXMPP_DEBUG_STUN logReceived(QStringLiteral("TURN packet from %1 port %2\n%3").arg(remoteHost.toString(), QString::number(remotePort), message.toString())); #endif // find transaction for (auto *transaction : m_transactions) { if (transaction->request().id() == message.id() && transaction->request().messageMethod() == message.messageMethod()) { transaction->readStun(message); return; } } } /// Refresh allocation. void QXmppTurnAllocation::refresh() { QXmppStunMessage request; request.setType(QXmppStunMessage::Refresh | QXmppStunMessage::Request); request.setId(QXmppUtils::generateRandomBytes(STUN_ID_SIZE)); request.setNonce(m_nonce); request.setRealm(m_realm); request.setUsername(m_username); m_transactions << new QXmppStunTransaction(request, this); } /// Refresh channel bindings. void QXmppTurnAllocation::refreshChannels() { for (const auto &channel : m_channels.keys()) { QXmppStunMessage request; request.setType(QXmppStunMessage::ChannelBind | QXmppStunMessage::Request); request.setId(QXmppUtils::generateRandomBytes(STUN_ID_SIZE)); request.setNonce(m_nonce); request.setRealm(m_realm); request.setUsername(m_username); request.setChannelNumber(channel); request.xorPeerHost = m_channels[channel].first; request.xorPeerPort = m_channels[channel].second; m_transactions << new QXmppStunTransaction(request, this); } } /// Returns the relayed host address, i.e. the address on the server /// used to communicate with peers. QHostAddress QXmppTurnAllocation::relayedHost() const { return m_relayedHost; } /// Returns the relayed port, i.e. the port on the server used to communicate /// with peers. quint16 QXmppTurnAllocation::relayedPort() const { return m_relayedPort; } /// Sets the password used to authenticate with the TURN server. /// /// \param password void QXmppTurnAllocation::setPassword(const QString &password) { m_password = password; } /// Sets the TURN server to use. /// /// \param host The address of the TURN server. /// \param port The port of the TURN server. void QXmppTurnAllocation::setServer(const QHostAddress &host, quint16 port) { m_turnHost = host; m_turnPort = port; } /// Sets the \a user used for authentication with the TURN server. /// /// \param user void QXmppTurnAllocation::setUser(const QString &user) { m_username = user; } /// Returns the current state of the allocation. /// QXmppTurnAllocation::AllocationState QXmppTurnAllocation::state() const { return m_state; } void QXmppTurnAllocation::setState(AllocationState state) { if (state == m_state) return; m_state = state; if (m_state == ConnectedState) { emit connected(); } else if (m_state == UnconnectedState) { m_timer->stop(); emit disconnected(); } } void QXmppTurnAllocation::transactionFinished() { auto *transaction = qobject_cast(sender()); if (!transaction || !m_transactions.removeAll(transaction)) return; transaction->deleteLater(); // handle authentication const QXmppStunMessage reply = transaction->response(); if (reply.messageClass() == QXmppStunMessage::Error && reply.errorCode == 401 && (reply.nonce() != m_nonce && reply.realm() != m_realm)) { // update long-term credentials m_nonce = reply.nonce(); m_realm = reply.realm(); QCryptographicHash hash(QCryptographicHash::Md5); hash.addData((m_username + ":" + m_realm + ":" + m_password).toUtf8()); m_key = hash.result(); // retry request QXmppStunMessage request(transaction->request()); request.setId(QXmppUtils::generateRandomBytes(STUN_ID_SIZE)); request.setNonce(m_nonce); request.setRealm(m_realm); request.setUsername(m_username); m_transactions << new QXmppStunTransaction(request, this); return; } const quint16 method = transaction->request().messageMethod(); if (method == QXmppStunMessage::Allocate) { if (reply.messageClass() == QXmppStunMessage::Error) { warning(QStringLiteral("Allocation failed: %1 %2").arg(QString::number(reply.errorCode), reply.errorPhrase)); setState(UnconnectedState); return; } if (reply.xorRelayedHost.isNull() || reply.xorRelayedHost.protocol() != QAbstractSocket::IPv4Protocol || !reply.xorRelayedPort) { warning("Allocation did not yield a valid relayed address"); setState(UnconnectedState); return; } // store relayed address m_relayedHost = reply.xorRelayedHost; m_relayedPort = reply.xorRelayedPort; // schedule refresh m_lifetime = reply.lifetime(); m_timer->start((m_lifetime - 60) * 1000); setState(ConnectedState); } else if (method == QXmppStunMessage::ChannelBind) { if (reply.messageClass() == QXmppStunMessage::Error) { warning(QStringLiteral("ChannelBind failed: %1 %2").arg(QString::number(reply.errorCode), reply.errorPhrase)); // remove channel m_channels.remove(transaction->request().channelNumber()); if (m_channels.isEmpty()) m_channelTimer->stop(); return; } } else if (method == QXmppStunMessage::Refresh) { if (reply.messageClass() == QXmppStunMessage::Error) { warning(QStringLiteral("Refresh failed: %1 %2").arg(QString::number(reply.errorCode), reply.errorPhrase)); setState(UnconnectedState); return; } if (m_state == ClosingState) { setState(UnconnectedState); return; } // schedule refresh m_lifetime = reply.lifetime(); m_timer->start((m_lifetime - 60) * 1000); } } qint64 QXmppTurnAllocation::writeDatagram(const QByteArray &data, const QHostAddress &host, quint16 port) { if (m_state != ConnectedState) return -1; const Address addr = qMakePair(host, port); quint16 channel = m_channels.key(addr); if (!channel) { channel = m_channelNumber++; m_channels.insert(channel, addr); // bind channel QXmppStunMessage request; request.setType(QXmppStunMessage::ChannelBind | QXmppStunMessage::Request); request.setId(QXmppUtils::generateRandomBytes(STUN_ID_SIZE)); request.setNonce(m_nonce); request.setRealm(m_realm); request.setUsername(m_username); request.setChannelNumber(channel); request.xorPeerHost = host; request.xorPeerPort = port; m_transactions << new QXmppStunTransaction(request, this); // schedule refresh if (!m_channelTimer->isActive()) m_channelTimer->start(); } // send data QByteArray channelData; channelData.reserve(4 + data.size()); QDataStream stream(&channelData, QIODevice::WriteOnly); stream << channel; stream << quint16(data.size()); stream.writeRawData(data.data(), data.size()); if (socket->writeDatagram(channelData, m_turnHost, m_turnPort) == channelData.size()) return data.size(); else return -1; } void QXmppTurnAllocation::writeStun(const QXmppStunMessage &message) { socket->writeDatagram(message.encode(m_key), m_turnHost, m_turnPort); #ifdef QXMPP_DEBUG_STUN logSent(QStringLiteral("TURN packet to %1 port %2\n%3").arg(m_turnHost.toString(), QString::number(m_turnPort), message.toString())); #endif } QXmppUdpTransport::QXmppUdpTransport(QUdpSocket *socket, QObject *parent) : QXmppIceTransport(parent), m_socket(socket) { connect(m_socket, &QIODevice::readyRead, this, &QXmppUdpTransport::readyRead); } QXmppUdpTransport::~QXmppUdpTransport() { } void QXmppUdpTransport::disconnectFromHost() { m_socket->close(); } QXmppJingleCandidate QXmppUdpTransport::localCandidate(int component) const { QXmppJingleCandidate candidate; candidate.setComponent(component); // remove scope ID from IPv6 non-link local addresses QHostAddress addr(m_socket->localAddress()); if (addr.protocol() == QAbstractSocket::IPv6Protocol && !isIPv6LinkLocalAddress(addr)) { addr.setScopeId(QString()); } candidate.setHost(addr); candidate.setId(QXmppUtils::generateStanzaHash(10)); candidate.setPort(m_socket->localPort()); candidate.setProtocol(QStringLiteral("udp")); candidate.setType(QXmppJingleCandidate::HostType); candidate.setPriority(candidatePriority(candidate)); candidate.setFoundation(computeFoundation( candidate.type(), candidate.protocol(), candidate.host())); return candidate; } void QXmppUdpTransport::readyRead() { QByteArray buffer; QHostAddress remoteHost; quint16 remotePort; while (m_socket->hasPendingDatagrams()) { const qint64 size = m_socket->pendingDatagramSize(); buffer.resize(size); m_socket->readDatagram(buffer.data(), buffer.size(), &remoteHost, &remotePort); emit datagramReceived(buffer, remoteHost, remotePort); } } qint64 QXmppUdpTransport::writeDatagram(const QByteArray &data, const QHostAddress &host, quint16 port) { QHostAddress remoteHost = host; if (isIPv6LinkLocalAddress(host)) remoteHost.setScopeId(m_socket->localAddress().scopeId()); return m_socket->writeDatagram(data, remoteHost, port); } class CandidatePair : public QXmppLoggable { public: enum State { FrozenState, WaitingState, InProgressState, SucceededState, FailedState }; CandidatePair(int component, bool controlling, QObject *parent); quint64 priority() const; State state() const; void setState(State state); QString toString() const; bool nominated; bool nominating; QXmppJingleCandidate remote; QXmppJingleCandidate reflexive; QXmppIceTransport *transport; QXmppStunTransaction *transaction; private: int m_component; bool m_controlling; State m_state; }; static bool candidatePairPtrLessThan(const CandidatePair *p1, const CandidatePair *p2) { return p1->priority() > p2->priority(); } CandidatePair::CandidatePair(int component, bool controlling, QObject *parent) : QXmppLoggable(parent), nominated(false), nominating(false), transport(nullptr), transaction(nullptr), m_component(component), m_controlling(controlling), m_state(WaitingState) { } quint64 CandidatePair::priority() const { const QXmppJingleCandidate local = transport->localCandidate(m_component); // see RFC 5245 - 5.7.2. Computing Pair Priority and Ordering Pairs const quint32 G = m_controlling ? local.priority() : remote.priority(); const quint32 D = m_controlling ? remote.priority() : local.priority(); return (quint64(1) << 32) * qMin(G, D) + 2 * qMax(G, D) + (G > D ? 1 : 0); } CandidatePair::State CandidatePair::state() const { return m_state; } void CandidatePair::setState(CandidatePair::State state) { m_state = state; info(QStringLiteral("ICE pair changed to state %1 %2").arg(QLatin1String(pair_states[state]), toString())); } QString CandidatePair::toString() const { const QXmppJingleCandidate candidate = transport->localCandidate(m_component); QString str = QStringLiteral("%1 port %2").arg(remote.host().toString(), QString::number(remote.port())); if (candidate.type() == QXmppJingleCandidate::HostType) str += QStringLiteral(" (local %1 port %2)").arg(candidate.host().toString(), QString::number(candidate.port())); else str += QStringLiteral(" (relayed)"); if (!reflexive.host().isNull() && reflexive.port()) str += QStringLiteral(" (reflexive %1 port %2)").arg(reflexive.host().toString(), QString::number(reflexive.port())); return str; } class QXmppIcePrivate { public: QXmppIcePrivate(); bool iceControlling; QString localUser; QString localPassword; QString remoteUser; QString remotePassword; QList> stunServers; QByteArray tieBreaker; }; QXmppIcePrivate::QXmppIcePrivate() : iceControlling(false) { localUser = QXmppUtils::generateStanzaHash(4); localPassword = QXmppUtils::generateStanzaHash(22); tieBreaker = QXmppUtils::generateRandomBytes(8); } struct QXmppIceTransportDetails { QXmppIceTransport *transport; QHostAddress stunHost; quint16 stunPort; }; class QXmppIceComponentPrivate { public: QXmppIceComponentPrivate(int component, QXmppIcePrivate *config, QXmppIceComponent *qq); bool addRemoteCandidate(const QXmppJingleCandidate &candidate); CandidatePair *findPair(QXmppStunTransaction *transaction); void performCheck(CandidatePair *pair, bool nominate); void setSockets(QList sockets); void setTurnServer(const QHostAddress &host, quint16 port); void setTurnUser(const QString &user); void setTurnPassword(const QString &password); void writeStun(const QXmppStunMessage &message, QXmppIceTransport *transport, const QHostAddress &remoteHost, quint16 remotePort); CandidatePair *activePair; const int component; const QXmppIcePrivate *const config; CandidatePair *fallbackPair; QXmppIceConnection::GatheringState gatheringState; QList localCandidates; quint32 peerReflexivePriority; QList remoteCandidates; QList pairs; QList transports; QTimer *timer; // STUN server QMap stunTransactions; // TURN server QXmppTurnAllocation *turnAllocation; bool turnConfigured; private: QXmppIceComponent *q; }; QXmppIceComponentPrivate::QXmppIceComponentPrivate(int component_, QXmppIcePrivate *config_, QXmppIceComponent *qq) : activePair(nullptr), component(component_), config(config_), fallbackPair(nullptr), gatheringState(QXmppIceConnection::NewGatheringState), peerReflexivePriority(0), timer(nullptr), turnAllocation(nullptr), turnConfigured(false), q(qq) { } bool QXmppIceComponentPrivate::addRemoteCandidate(const QXmppJingleCandidate &candidate) { if (candidate.component() != component || (candidate.type() != QXmppJingleCandidate::HostType && candidate.type() != QXmppJingleCandidate::RelayedType && candidate.type() != QXmppJingleCandidate::ServerReflexiveType) || candidate.protocol() != QStringLiteral("udp") || (candidate.host().protocol() != QAbstractSocket::IPv4Protocol && candidate.host().protocol() != QAbstractSocket::IPv6Protocol)) return false; for (const auto &c : remoteCandidates) if (c.host() == candidate.host() && c.port() == candidate.port()) return false; remoteCandidates << candidate; for (auto *transport : transports) { // only pair compatible addresses const QXmppJingleCandidate local = transport->localCandidate(component); if (!isCompatibleAddress(local.host(), candidate.host())) continue; auto *pair = new CandidatePair(component, config->iceControlling, q); pair->remote = candidate; pair->transport = transport; pairs << pair; if (!fallbackPair && local.type() == QXmppJingleCandidate::HostType) fallbackPair = pair; } std::sort(pairs.begin(), pairs.end(), candidatePairPtrLessThan); return true; } CandidatePair *QXmppIceComponentPrivate::findPair(QXmppStunTransaction *transaction) { for (auto *pair : pairs) { if (pair->transaction == transaction) return pair; } return nullptr; } void QXmppIceComponentPrivate::performCheck(CandidatePair *pair, bool nominate) { QXmppStunMessage message; message.setId(QXmppUtils::generateRandomBytes(STUN_ID_SIZE)); message.setType(QXmppStunMessage::Binding | QXmppStunMessage::Request); message.setPriority(peerReflexivePriority); message.setUsername(QStringLiteral("%1:%2").arg(config->remoteUser, config->localUser)); if (config->iceControlling) { message.iceControlling = config->tieBreaker; message.useCandidate = true; } else { message.iceControlled = config->tieBreaker; } pair->nominating = nominate; pair->setState(CandidatePair::InProgressState); pair->transaction = new QXmppStunTransaction(message, q); } void QXmppIceComponentPrivate::setSockets(QList sockets) { // clear previous candidates and sockets localCandidates.clear(); qDeleteAll(pairs); for (auto *transport : transports) if (transport != turnAllocation) delete transport; transports.clear(); // store candidates for (auto *socket : sockets) { socket->setParent(q); auto *transport = new QXmppUdpTransport(socket, q); QObject::connect(transport, &QXmppIceTransport::datagramReceived, q, &QXmppIceComponent::handleDatagram); QXmppJingleCandidate candidate = transport->localCandidate(component); transports << transport; localCandidates << candidate; } // start STUN checks stunTransactions.clear(); for (auto &stunServer : config->stunServers) { QXmppStunMessage request; request.setType(QXmppStunMessage::Binding | QXmppStunMessage::Request); for (auto *transport : transports) { const QXmppJingleCandidate local = transport->localCandidate(component); if (!isCompatibleAddress(local.host(), stunServer.first)) { continue; } request.setId(QXmppUtils::generateRandomBytes(STUN_ID_SIZE)); auto *transaction = new QXmppStunTransaction(request, q); stunTransactions.insert(transaction, { transport, stunServer.first, stunServer.second }); } } // connect to TURN server if (turnConfigured) { transports << turnAllocation; turnAllocation->connectToHost(); } q->updateGatheringState(); } void QXmppIceComponentPrivate::setTurnServer(const QHostAddress &host, quint16 port) { turnAllocation->setServer(host, port); turnConfigured = !host.isNull() && port; } void QXmppIceComponentPrivate::setTurnUser(const QString &user) { turnAllocation->setUser(user); } void QXmppIceComponentPrivate::setTurnPassword(const QString &password) { turnAllocation->setPassword(password); } void QXmppIceComponentPrivate::writeStun(const QXmppStunMessage &message, QXmppIceTransport *transport, const QHostAddress &address, quint16 port) { const QString messagePassword = (message.type() & 0xFF00) ? config->localPassword : config->remotePassword; const QByteArray data = message.encode(messagePassword.toUtf8()); transport->writeDatagram(data, address, port); #ifdef QXMPP_DEBUG_STUN q->logSent(QStringLiteral("STUN packet to %1 port %2\n%3").arg(address.toString(), QString::number(port), message.toString())); #endif } /// Constructs a new QXmppIceComponent. /// /// \param parent QXmppIceComponent::QXmppIceComponent(int component, QXmppIcePrivate *config, QObject *parent) : QXmppLoggable(parent) { d = new QXmppIceComponentPrivate(component, config, this); d->timer = new QTimer(this); d->timer->setInterval(500); connect(d->timer, &QTimer::timeout, this, &QXmppIceComponent::checkCandidates); d->turnAllocation = new QXmppTurnAllocation(this); connect(d->turnAllocation, &QXmppTurnAllocation::connected, this, &QXmppIceComponent::turnConnected); connect(d->turnAllocation, &QXmppIceTransport::datagramReceived, this, &QXmppIceComponent::handleDatagram); connect(d->turnAllocation, &QXmppTurnAllocation::disconnected, this, &QXmppIceComponent::updateGatheringState); // calculate peer-reflexive candidate priority // see RFC 5245 - 7.1.2.1. PRIORITY and USE-CANDIDATE QXmppJingleCandidate reflexive; reflexive.setComponent(d->component); reflexive.setType(QXmppJingleCandidate::PeerReflexiveType); d->peerReflexivePriority = candidatePriority(reflexive); setObjectName(QStringLiteral("STUN(%1)").arg(QString::number(d->component))); } /// Destroys the QXmppIceComponent. QXmppIceComponent::~QXmppIceComponent() { qDeleteAll(d->pairs); delete d; } /// Returns the component id for the current socket, e.g. 1 for RTP /// and 2 for RTCP. int QXmppIceComponent::component() const { return d->component; } void QXmppIceComponent::checkCandidates() { if (d->config->remoteUser.isEmpty()) return; debug(QStringLiteral("Checking remote candidates")); for (auto *pair : d->pairs) { if (pair->state() == CandidatePair::WaitingState) { d->performCheck(pair, d->config->iceControlling); break; } } } /// Stops ICE connectivity checks and closes the underlying sockets. void QXmppIceComponent::close() { for (auto *transport : d->transports) transport->disconnectFromHost(); d->turnAllocation->disconnectFromHost(); d->timer->stop(); d->activePair = nullptr; } /// Starts ICE connectivity checks. void QXmppIceComponent::connectToHost() { if (d->activePair) return; checkCandidates(); d->timer->start(); } /// Returns true if ICE negotiation completed, false otherwise. bool QXmppIceComponent::isConnected() const { return d->activePair != nullptr; } /// Returns the list of local candidates. QList QXmppIceComponent::localCandidates() const { return d->localCandidates; } void QXmppIceComponent::handleDatagram(const QByteArray &buffer, const QHostAddress &remoteHost, quint16 remotePort) { auto *transport = qobject_cast(sender()); if (!transport) return; // if this is not a STUN message, emit it quint32 messageCookie; QByteArray messageId; quint16 messageType = QXmppStunMessage::peekType(buffer, messageCookie, messageId); if (!messageType || messageCookie != STUN_MAGIC) { // use this as an opportunity to flag a potential pair for (auto *pair : d->pairs) { if (pair->remote.host() == remoteHost && pair->remote.port() == remotePort) { d->fallbackPair = pair; break; } } emit datagramReceived(buffer); return; } // check if it's STUN QXmppStunTransaction *stunTransaction = nullptr; for (auto *t : d->stunTransactions.keys()) { if (t->request().id() == messageId && d->stunTransactions.value(t).transport == transport) { stunTransaction = t; break; } } // determine password to use QString messagePassword; if (!stunTransaction) { messagePassword = (messageType & 0xFF00) ? d->config->remotePassword : d->config->localPassword; if (messagePassword.isEmpty()) return; } // parse STUN message QXmppStunMessage message; QStringList errors; if (!message.decode(buffer, messagePassword.toUtf8(), &errors)) { for (const auto &error : errors) warning(error); return; } #ifdef QXMPP_DEBUG_STUN logReceived(QStringLiteral("STUN packet from %1 port %2\n%3").arg(remoteHost.toString(), QString::number(remotePort), message.toString())); #endif // we only want binding requests and responses if (message.messageMethod() != QXmppStunMessage::Binding) return; // STUN checks if (stunTransaction) { stunTransaction->readStun(message); return; } // process message from peer CandidatePair *pair = nullptr; if (message.messageClass() == QXmppStunMessage::Request) { // check for role conflict if (d->config->iceControlling && (!message.iceControlling.isEmpty() || message.useCandidate)) { warning(QStringLiteral("Role conflict, expected to be controlling")); return; } else if (!d->config->iceControlling && !message.iceControlled.isEmpty()) { warning(QStringLiteral("Role conflict, expected to be controlled")); return; } // send a binding response QXmppStunMessage response; response.setId(message.id()); response.setType(QXmppStunMessage::Binding | QXmppStunMessage::Response); response.xorMappedHost = remoteHost; response.xorMappedPort = remotePort; d->writeStun(response, transport, remoteHost, remotePort); // find or create remote candidate QXmppJingleCandidate remoteCandidate; bool remoteCandidateFound = false; for (const auto &c : d->remoteCandidates) { if (c.host() == remoteHost && c.port() == remotePort) { remoteCandidate = c; remoteCandidateFound = true; break; } } if (!remoteCandidateFound) { // 7.2.1.3. Learning Peer Reflexive Candidates remoteCandidate.setComponent(d->component); remoteCandidate.setHost(remoteHost); remoteCandidate.setId(QXmppUtils::generateStanzaHash(10)); remoteCandidate.setPort(remotePort); remoteCandidate.setPriority(message.priority()); remoteCandidate.setProtocol(QStringLiteral("udp")); remoteCandidate.setType(QXmppJingleCandidate::PeerReflexiveType); remoteCandidate.setFoundation(QXmppUtils::generateStanzaHash(32)); d->remoteCandidates << remoteCandidate; } // construct pair for (auto *ptr : d->pairs) { if (ptr->transport == transport && ptr->remote.host() == remoteHost && ptr->remote.port() == remotePort) { pair = ptr; break; } } if (!pair) { pair = new CandidatePair(d->component, d->config->iceControlling, this); pair->remote = remoteCandidate; pair->transport = transport; d->pairs << pair; std::sort(d->pairs.begin(), d->pairs.end(), candidatePairPtrLessThan); } switch (pair->state()) { case CandidatePair::FrozenState: case CandidatePair::WaitingState: case CandidatePair::FailedState: // send a triggered connectivity test if (!d->config->remoteUser.isEmpty()) d->performCheck(pair, pair->nominating || d->config->iceControlling || message.useCandidate); break; case CandidatePair::InProgressState: // FIXME: force retransmit now pair->nominating = pair->nominating || message.useCandidate; break; case CandidatePair::SucceededState: if (message.useCandidate) pair->nominated = true; break; } } else if (message.messageClass() == QXmppStunMessage::Response || message.messageClass() == QXmppStunMessage::Error) { // find the pair for this transaction for (auto *ptr : d->pairs) { if (ptr->transaction && ptr->transaction->request().id() == message.id()) { pair = ptr; break; } } if (!pair) return; // check remote host and port if (remoteHost != pair->remote.host() || remotePort != pair->remote.port()) { QXmppStunMessage error; error.setType(QXmppStunMessage::Error); error.errorPhrase = QStringLiteral("Received response from unexpected %1:%1").arg(remoteHost.toString(), QString::number(remotePort)); pair->transaction->readStun(error); return; } pair->transaction->readStun(message); } // signal completion if (pair && pair->nominated) { d->timer->stop(); if (!d->activePair || pair->priority() > d->activePair->priority()) { info(QStringLiteral("ICE pair selected %1 (priority: %2)").arg(pair->toString(), QString::number(pair->priority()))); const bool wasConnected = (d->activePair != nullptr); d->activePair = pair; if (!wasConnected) emit connected(); } } } void QXmppIceComponent::transactionFinished() { auto *transaction = qobject_cast(sender()); transaction->deleteLater(); // ICE checks CandidatePair *pair = d->findPair(transaction); if (pair) { const QXmppStunMessage response = transaction->response(); if (response.messageClass() == QXmppStunMessage::Response) { // store peer-reflexive address if (!response.xorMappedHost.isNull() && response.xorMappedPort != 0) { pair->reflexive.setHost(response.xorMappedHost); pair->reflexive.setPort(response.xorMappedPort); } pair->setState(CandidatePair::SucceededState); if (pair->nominating) { // outgoing media can flow pair->nominated = true; } } else { debug(QStringLiteral("ICE forward check failed %1 (error %2)").arg(pair->toString(), transaction->response().errorPhrase)); pair->setState(CandidatePair::FailedState); } pair->transaction = nullptr; return; } // STUN checks QXmppIceTransport *transport = d->stunTransactions.value(transaction).transport; if (transport) { const QXmppStunMessage response = transaction->response(); if (response.messageClass() == QXmppStunMessage::Response) { // determine server-reflexive address QHostAddress reflexiveHost; quint16 reflexivePort = 0; if (!response.xorMappedHost.isNull() && response.xorMappedPort != 0) { reflexiveHost = response.xorMappedHost; reflexivePort = response.xorMappedPort; } else if (!response.mappedHost.isNull() && response.mappedPort != 0) { reflexiveHost = response.mappedHost; reflexivePort = response.mappedPort; } else { warning(QStringLiteral("STUN server did not provide a reflexive address")); return; } // check whether this candidates is already known for (const auto &candidate : d->localCandidates) { if (candidate.host() == reflexiveHost && candidate.port() == reflexivePort && candidate.type() == QXmppJingleCandidate::ServerReflexiveType) return; } // add the new local candidate debug(QStringLiteral("Adding server-reflexive candidate %1 port %2").arg(reflexiveHost.toString(), QString::number(reflexivePort))); QXmppJingleCandidate candidate; candidate.setComponent(d->component); candidate.setHost(reflexiveHost); candidate.setId(QXmppUtils::generateStanzaHash(10)); candidate.setPort(reflexivePort); candidate.setProtocol("udp"); candidate.setType(QXmppJingleCandidate::ServerReflexiveType); candidate.setPriority(candidatePriority(candidate)); candidate.setFoundation(computeFoundation( candidate.type(), candidate.protocol(), transport->localCandidate(d->component).host())); d->localCandidates << candidate; emit localCandidatesChanged(); } else { debug(QStringLiteral("STUN test failed (error %1)").arg(transaction->response().errorPhrase)); } d->stunTransactions.remove(transaction); updateGatheringState(); return; } } void QXmppIceComponent::turnConnected() { const QXmppJingleCandidate candidate = d->turnAllocation->localCandidate(d->component); // add the new local candidate debug(QStringLiteral("Adding relayed candidate %1 port %2").arg(candidate.host().toString(), QString::number(candidate.port()))); d->localCandidates << candidate; emit localCandidatesChanged(); updateGatheringState(); } static QList reservePort(const QList &addresses, quint16 port, QObject *parent) { QList sockets; for (const auto &address : addresses) { auto *socket = new QUdpSocket(parent); socket->setSocketOption(QAbstractSocket::LowDelayOption, 1); sockets << socket; if (!socket->bind(address, port)) { qDeleteAll(sockets); sockets.clear(); break; } } return sockets; } /// Returns the list of local network addresses. QList QXmppIceComponent::discoverAddresses() { QList addresses; for (const auto &interface : QNetworkInterface::allInterfaces()) { if (!(interface.flags() & QNetworkInterface::IsRunning) || interface.flags() & QNetworkInterface::IsLoopBack) continue; for (const auto &entry : interface.addressEntries()) { QHostAddress ip = entry.ip(); if ((ip.protocol() != QAbstractSocket::IPv4Protocol && ip.protocol() != QAbstractSocket::IPv6Protocol) || entry.netmask().isNull()) continue; // FIXME: for some reason we can have loopback addresses // even if the interface does not have the loopback flag if (isLoopbackAddress(ip)) continue; // FIXME: for now skip IPv6 link-local addresses, seems to upset // clients such as empathy if (isIPv6LinkLocalAddress(ip)) { ip.setScopeId(interface.name()); continue; } addresses << ip; } } return addresses; } /// Tries to bind \a count UDP sockets on each of the given \a addresses. /// /// The port numbers are chosen so that they are consecutive, starting at /// an even port. This makes them suitable for RTP/RTCP sockets pairs. /// /// \param addresses The network address on which to bind the sockets. /// \param count The number of ports to reserve. /// \param parent The parent object for the sockets. QList QXmppIceComponent::reservePorts(const QList &addresses, int count, QObject *parent) { QList sockets; if (addresses.isEmpty() || !count) return sockets; const int expectedSize = addresses.size() * count; quint16 port = 49152; while (sockets.size() != expectedSize) { // reserve first port (even number) if (port % 2) port++; QList socketChunk; while (socketChunk.isEmpty() && port <= 65536 - count) { socketChunk = reservePort(addresses, port, parent); if (socketChunk.isEmpty()) port += 2; } if (socketChunk.isEmpty()) return sockets; // reserve other ports sockets << socketChunk; for (int i = 1; i < count; ++i) { socketChunk = reservePort(addresses, ++port, parent); if (socketChunk.isEmpty()) break; sockets << socketChunk; } // cleanup if we failed if (sockets.size() != expectedSize) { qDeleteAll(sockets); sockets.clear(); } } return sockets; } /// Sends a data packet to the remote party. /// /// \param datagram qint64 QXmppIceComponent::sendDatagram(const QByteArray &datagram) { CandidatePair *pair = d->activePair ? d->activePair : d->fallbackPair; if (!pair) return -1; return pair->transport->writeDatagram(datagram, pair->remote.host(), pair->remote.port()); } void QXmppIceComponent::updateGatheringState() { QXmppIceConnection::GatheringState newGatheringState; if (d->transports.isEmpty()) newGatheringState = QXmppIceConnection::NewGatheringState; else if (!d->stunTransactions.isEmpty() || d->turnAllocation->state() == QXmppTurnAllocation::ConnectingState) newGatheringState = QXmppIceConnection::BusyGatheringState; else newGatheringState = QXmppIceConnection::CompleteGatheringState; if (newGatheringState != d->gatheringState) { d->gatheringState = newGatheringState; emit gatheringStateChanged(); } } void QXmppIceComponent::writeStun(const QXmppStunMessage &message) { auto *transaction = qobject_cast(sender()); // ICE checks CandidatePair *pair = d->findPair(transaction); if (pair) { d->writeStun(message, pair->transport, pair->remote.host(), pair->remote.port()); return; } // STUN checks QXmppIceTransportDetails transportDetails = d->stunTransactions.value(transaction); QXmppIceTransport *transport = transportDetails.transport; if (transport) { transport->writeDatagram(message.encode(), transportDetails.stunHost, transportDetails.stunPort); #ifdef QXMPP_DEBUG_STUN logSent(QStringLiteral("STUN packet to %1 port %2\n%3").arg(transportDetails.stunHost.toString(), QString::number(transportDetails.stunPort), message.toString())); #endif return; } } class QXmppIceConnectionPrivate : public QXmppIcePrivate { public: QXmppIceConnectionPrivate(); QMap components; QTimer *connectTimer; QXmppIceConnection::GatheringState gatheringState; QHostAddress turnHost; quint16 turnPort; QString turnUser; QString turnPassword; }; QXmppIceConnectionPrivate::QXmppIceConnectionPrivate() : connectTimer(nullptr), gatheringState(QXmppIceConnection::NewGatheringState), turnPort(0) { } /// Constructs a new ICE connection. /// /// \param parent QXmppIceConnection::QXmppIceConnection(QObject *parent) : QXmppLoggable(parent), d(new QXmppIceConnectionPrivate()) { // timer to limit connection time to 30 seconds d->connectTimer = new QTimer(this); d->connectTimer->setInterval(30000); d->connectTimer->setSingleShot(true); connect(d->connectTimer, &QTimer::timeout, this, &QXmppIceConnection::slotTimeout); } QXmppIceConnection::~QXmppIceConnection() { delete d; } /// Returns the given component of this ICE connection. /// /// \param component QXmppIceComponent *QXmppIceConnection::component(int component) { return d->components.value(component); } /// Adds a component to this ICE connection, for instance 1 for RTP /// or 2 for RTCP. /// /// \param component void QXmppIceConnection::addComponent(int component) { if (d->components.contains(component)) { warning(QStringLiteral("Already have component %1").arg(QString::number(component))); return; } auto *socket = new QXmppIceComponent(component, d, this); socket->d->setTurnServer(d->turnHost, d->turnPort); socket->d->setTurnUser(d->turnUser); socket->d->setTurnPassword(d->turnPassword); connect(socket, &QXmppIceComponent::localCandidatesChanged, this, &QXmppIceConnection::localCandidatesChanged); connect(socket, &QXmppIceComponent::connected, this, &QXmppIceConnection::slotConnected); connect(socket, &QXmppIceComponent::gatheringStateChanged, this, &QXmppIceConnection::slotGatheringStateChanged); d->components[component] = socket; } /// Adds a candidate for one of the remote components. /// /// \param candidate void QXmppIceConnection::addRemoteCandidate(const QXmppJingleCandidate &candidate) { QXmppIceComponent *socket = d->components.value(candidate.component()); if (!socket) { warning(QStringLiteral("Not adding candidate for unknown component %1").arg(QString::number(candidate.component()))); return; } socket->d->addRemoteCandidate(candidate); } /// Binds the local sockets to the specified addresses. /// /// \param addresses The addresses on which to listen. bool QXmppIceConnection::bind(const QList &addresses) { // reserve ports QList sockets = QXmppIceComponent::reservePorts(addresses, d->components.size()); if (sockets.isEmpty() && !addresses.isEmpty()) return false; // assign sockets QList keys = d->components.keys(); std::sort(keys.begin(), keys.end()); int s = 0; for (const auto k : keys) { d->components[k]->d->setSockets(sockets.mid(s, addresses.size())); s += addresses.size(); } return true; } /// Closes the ICE connection. void QXmppIceConnection::close() { d->connectTimer->stop(); for (auto *socket : d->components.values()) socket->close(); } /// Starts ICE connectivity checks. void QXmppIceConnection::connectToHost() { if (isConnected() || d->connectTimer->isActive()) return; for (auto *socket : d->components.values()) socket->connectToHost(); d->connectTimer->start(); } /// Returns true if ICE negotiation completed, false otherwise. bool QXmppIceConnection::isConnected() const { for (auto *socket : d->components.values()) if (!socket->isConnected()) return false; return true; } QXmppIceConnection::GatheringState QXmppIceConnection::gatheringState() const { return d->gatheringState; } /// Sets whether the local party has the ICE controlling role. /// /// \a note This must be called only once, immediately after creating /// the connection. void QXmppIceConnection::setIceControlling(bool controlling) { d->iceControlling = controlling; } /// Returns the list of local HOST CANDIDATES candidates by iterating /// over the available network interfaces. QList QXmppIceConnection::localCandidates() const { QList candidates; for (auto *socket : d->components.values()) candidates += socket->localCandidates(); return candidates; } /// Returns the local user fragment. QString QXmppIceConnection::localUser() const { return d->localUser; } /// Returns the local password. QString QXmppIceConnection::localPassword() const { return d->localPassword; } /// Sets the remote user fragment. /// /// \param user void QXmppIceConnection::setRemoteUser(const QString &user) { d->remoteUser = user; } /// Sets the remote password. /// /// \param password void QXmppIceConnection::setRemotePassword(const QString &password) { d->remotePassword = password; } /// Sets multiple STUN servers to use to determine server-reflexive addresses /// and ports. /// /// \note This may only be called prior to calling bind(). /// /// \param servers List of the STUN servers. /// /// \since QXmpp 1.3 void QXmppIceConnection::setStunServers(const QList> &servers) { d->stunServers = servers; } /// Sets a single STUN server to use to determine server-reflexive addresses /// and ports. /// /// \note This may only be called prior to calling bind(). /// /// \param host The address of the STUN server. /// \param port The port of the STUN server. void QXmppIceConnection::setStunServer(const QHostAddress &host, quint16 port) { d->stunServers.clear(); d->stunServers.push_back(QPair(host, port)); } /// Sets the TURN server to use to relay packets in double-NAT configurations. /// /// \note This may only be called prior to calling bind(). /// /// \param host The address of the TURN server. /// \param port The port of the TURN server. void QXmppIceConnection::setTurnServer(const QHostAddress &host, quint16 port) { d->turnHost = host; d->turnPort = port; for (auto *socket : d->components.values()) socket->d->setTurnServer(host, port); } /// Sets the \a user used for authentication with the TURN server. /// /// \note This may only be called prior to calling bind(). /// /// \param user void QXmppIceConnection::setTurnUser(const QString &user) { d->turnUser = user; for (auto *socket : d->components.values()) socket->d->setTurnUser(user); } /// Sets the \a password used for authentication with the TURN server. /// /// \note This may only be called prior to calling bind(). /// /// \param password void QXmppIceConnection::setTurnPassword(const QString &password) { d->turnPassword = password; for (auto *socket : d->components.values()) socket->d->setTurnPassword(password); } void QXmppIceConnection::slotConnected() { for (auto *socket : d->components.values()) if (!socket->isConnected()) return; info(QStringLiteral("ICE negotiation completed")); d->connectTimer->stop(); emit connected(); } void QXmppIceConnection::slotGatheringStateChanged() { GatheringState newGatheringState; bool allComplete = true; bool allNew = true; for (auto *socket : d->components.values()) { if (socket->d->gatheringState != CompleteGatheringState) allComplete = false; if (socket->d->gatheringState != NewGatheringState) allNew = false; } if (allNew) newGatheringState = NewGatheringState; else if (allComplete) newGatheringState = CompleteGatheringState; else newGatheringState = BusyGatheringState; if (newGatheringState != d->gatheringState) { info(QStringLiteral("ICE gathering state changed from '%1' to '%2'").arg(gathering_states[d->gatheringState], gathering_states[newGatheringState])); d->gatheringState = newGatheringState; emit gatheringStateChanged(); } } void QXmppIceConnection::slotTimeout() { warning(QStringLiteral("ICE negotiation timed out")); for (auto *socket : d->components.values()) socket->close(); emit disconnected(); } QXmppIceTransport::QXmppIceTransport(QObject *parent) : QXmppLoggable(parent) { } QXmppIceTransport::~QXmppIceTransport() { } qxmpp-1.4.0/src/base/QXmppStun.h000066400000000000000000000214201402370562100164510ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPSTUN_H #define QXMPPSTUN_H #include "QXmppJingleIq.h" #include "QXmppLogger.h" #include class CandidatePair; class QDataStream; class QUdpSocket; class QTimer; class QXmppIceComponentPrivate; class QXmppIceConnectionPrivate; class QXmppIcePrivate; /// \internal /// /// The QXmppStunMessage class represents a STUN message. /// class QXMPP_EXPORT QXmppStunMessage { public: enum MethodType { Binding = 0x1, SharedSecret = 0x2, Allocate = 0x3, Refresh = 0x4, Send = 0x6, Data = 0x7, CreatePermission = 0x8, ChannelBind = 0x9 }; enum ClassType { Request = 0x000, Indication = 0x010, Response = 0x100, Error = 0x110 }; QXmppStunMessage(); quint32 cookie() const; void setCookie(quint32 cookie); QByteArray id() const; void setId(const QByteArray &id); quint16 messageClass() const; quint16 messageMethod() const; quint16 type() const; void setType(quint16 type); // attributes quint32 changeRequest() const; void setChangeRequest(quint32 changeRequest); quint16 channelNumber() const; void setChannelNumber(quint16 channelNumber); QByteArray data() const; void setData(const QByteArray &data); quint32 lifetime() const; void setLifetime(quint32 changeRequest); QByteArray nonce() const; void setNonce(const QByteArray &nonce); quint32 priority() const; void setPriority(quint32 priority); QString realm() const; void setRealm(const QString &realm); QByteArray reservationToken() const; void setReservationToken(const QByteArray &reservationToken); quint8 requestedTransport() const; void setRequestedTransport(quint8 requestedTransport); QString software() const; void setSoftware(const QString &software); QString username() const; 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 = nullptr); QString toString() const; static quint16 peekType(const QByteArray &buffer, quint32 &cookie, QByteArray &id); // attributes int errorCode; QString errorPhrase; QByteArray iceControlling; QByteArray iceControlled; QHostAddress changedHost; quint16 changedPort; QHostAddress mappedHost; quint16 mappedPort; QHostAddress otherHost; quint16 otherPort; QHostAddress sourceHost; quint16 sourcePort; QHostAddress xorMappedHost; quint16 xorMappedPort; QHostAddress xorPeerHost; quint16 xorPeerPort; QHostAddress xorRelayedHost; quint16 xorRelayedPort; bool useCandidate; private: quint32 m_cookie; QByteArray m_id; quint16 m_type; // attributes QSet m_attributes; quint32 m_changeRequest; quint16 m_channelNumber; QByteArray m_data; quint32 m_lifetime; QByteArray m_nonce; quint32 m_priority; QString m_realm; quint8 m_requestedTransport; QByteArray m_reservationToken; QString m_software; QString m_username; }; /// \brief The QXmppIceComponent class represents a piece of a media stream /// requiring a single transport address, as defined by RFC 5245 /// (Interactive Connectivity Establishment). class QXMPP_EXPORT QXmppIceComponent : public QXmppLoggable { Q_OBJECT public: ~QXmppIceComponent() override; int component() const; bool isConnected() const; QList localCandidates() const; static QList discoverAddresses(); static QList reservePorts(const QList &addresses, int count, QObject *parent = nullptr); public Q_SLOTS: void close(); void connectToHost(); qint64 sendDatagram(const QByteArray &datagram); private Q_SLOTS: void checkCandidates(); void handleDatagram(const QByteArray &datagram, const QHostAddress &host, quint16 port); void turnConnected(); void transactionFinished(); void updateGatheringState(); void writeStun(const QXmppStunMessage &request); Q_SIGNALS: /// \brief This signal is emitted once ICE negotiation succeeds. void connected(); /// \brief This signal is emitted when a data packet is received. void datagramReceived(const QByteArray &datagram); /// \internal This signal is emitted when the gathering state of local candidates changes. void gatheringStateChanged(); /// \brief This signal is emitted when the list of local candidates changes. void localCandidatesChanged(); private: QXmppIceComponent(int component, QXmppIcePrivate *config, QObject *parent = nullptr); QXmppIceComponentPrivate *d; friend class QXmppIceComponentPrivate; friend class QXmppIceConnection; }; /// \brief The QXmppIceConnection class represents a set of UDP sockets /// capable of performing Interactive Connectivity Establishment (RFC 5245). /// /// A typical example is: /// /// \code /// QXmppIceConnection *connection = new QXmppIceConnection(); /// connection->setIceControlling(true); /// connection->addComponent(1); /// /// // if needed, set STUN / TURN configuration /// // connection->setStunServer(..); /// // connection->setTurnServer(..); /// /// // start listening /// connection->bind(QXmppIceComponent::discoverAddresses()); /// /// // receive remote information: user, password, candidates /// // ... /// /// // set remote information and start connecting /// connection->setRemoteUser("foo"); /// connection->setRemoteUser("bar"); /// connection->addRemoteCandidate(..); /// connection->connectToHost(); /// /// \endcode class QXMPP_EXPORT QXmppIceConnection : public QXmppLoggable { Q_OBJECT /// /// The ICE gathering state, that is the discovery of local candidates /// /// \since QXmpp 0.9.3 /// Q_PROPERTY(QXmppIceConnection::GatheringState gatheringState READ gatheringState NOTIFY gatheringStateChanged) public: /// /// This enum describes the gathering state of the ICE connection. /// /// \since QXmpp 0.9.3 /// enum GatheringState { NewGatheringState, BusyGatheringState, CompleteGatheringState }; Q_ENUM(GatheringState) QXmppIceConnection(QObject *parent = nullptr); ~QXmppIceConnection() override; QXmppIceComponent *component(int component); void addComponent(int component); void setIceControlling(bool controlling); QList localCandidates() const; QString localUser() const; QString localPassword() const; void addRemoteCandidate(const QXmppJingleCandidate &candidate); void setRemoteUser(const QString &user); void setRemotePassword(const QString &password); void setStunServers(const QList> &servers); 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); bool bind(const QList &addresses); bool isConnected() const; // documentation needs to be here, see https://stackoverflow.com/questions/49192523/ /// /// Returns the ICE gathering state, that is the discovery of local /// candidates. /// /// \since QXmpp 0.9.3 /// GatheringState gatheringState() const; Q_SIGNALS: /// \brief This signal is emitted once ICE negotiation succeeds. void connected(); /// \brief This signal is emitted when ICE negotiation fails. void disconnected(); /// /// \brief This signal is emitted when the gathering state of local candidates changes. /// /// \since QXmpp 0.9.3 /// void gatheringStateChanged(); /// \brief This signal is emitted when the list of local candidates changes. void localCandidatesChanged(); public Q_SLOTS: void close(); void connectToHost(); private Q_SLOTS: void slotConnected(); void slotGatheringStateChanged(); void slotTimeout(); private: QXmppIceConnectionPrivate *d; }; #endif qxmpp-1.4.0/src/base/QXmppStun_p.h000066400000000000000000000114251402370562100167740ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPSTUN_P_H #define QXMPPSTUN_P_H #include "QXmppStun.h" class QUdpSocket; class QTimer; // // W A R N I N G // ------------- // // This file is not part of the QXmpp API. // // This header file may change from version to version without notice, // or even be removed. // // We mean it. // /// \internal /// /// The QXmppStunTransaction class represents a STUN transaction. /// class QXMPP_EXPORT QXmppStunTransaction : public QXmppLoggable { Q_OBJECT public: QXmppStunTransaction(const QXmppStunMessage &request, QObject *parent); QXmppStunMessage request() const; QXmppStunMessage response() const; Q_SIGNALS: void finished(); void writeStun(const QXmppStunMessage &request); public Q_SLOTS: void readStun(const QXmppStunMessage &response); private Q_SLOTS: void retry(); private: QXmppStunMessage m_request; QXmppStunMessage m_response; QTimer *m_retryTimer; int m_tries; }; class QXMPP_EXPORT QXmppIceTransport : public QXmppLoggable { Q_OBJECT public: 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; public Q_SLOTS: virtual void disconnectFromHost() = 0; Q_SIGNALS: /// \brief This signal is emitted when a data packet is received. void datagramReceived(const QByteArray &data, const QHostAddress &host, quint16 port); }; /// \internal /// /// The QXmppTurnAllocation class represents a TURN allocation as defined /// by RFC 5766 Traversal Using Relays around NAT (TURN). /// class QXMPP_EXPORT QXmppTurnAllocation : public QXmppIceTransport { Q_OBJECT public: enum AllocationState { UnconnectedState, ConnectingState, ConnectedState, ClosingState }; QXmppTurnAllocation(QObject *parent = nullptr); ~QXmppTurnAllocation() override; QHostAddress relayedHost() const; quint16 relayedPort() const; AllocationState state() const; void setServer(const QHostAddress &host, quint16 port = 3478); void setUser(const QString &user); void setPassword(const QString &password); QXmppJingleCandidate localCandidate(int component) const override; qint64 writeDatagram(const QByteArray &data, const QHostAddress &host, quint16 port) override; Q_SIGNALS: /// \brief This signal is emitted once TURN allocation succeeds. void connected(); /// \brief This signal is emitted when TURN allocation fails. void disconnected(); public Q_SLOTS: void connectToHost(); void disconnectFromHost() override; private Q_SLOTS: void readyRead(); void refresh(); void refreshChannels(); void transactionFinished(); void writeStun(const QXmppStunMessage &message); private: void handleDatagram(const QByteArray &datagram, const QHostAddress &host, quint16 port); void setState(AllocationState state); QUdpSocket *socket; QTimer *m_timer; QTimer *m_channelTimer; QString m_password; QString m_username; QHostAddress m_relayedHost; quint16 m_relayedPort; QHostAddress m_turnHost; quint16 m_turnPort; // channels typedef QPair Address; quint16 m_channelNumber; QMap m_channels; // state quint32 m_lifetime; QByteArray m_key; QString m_realm; QByteArray m_nonce; AllocationState m_state; QList m_transactions; }; /// \internal /// /// The QXmppUdpTransport class represents a UDP transport. /// class QXMPP_EXPORT QXmppUdpTransport : public QXmppIceTransport { Q_OBJECT public: QXmppUdpTransport(QUdpSocket *socket, QObject *parent = nullptr); ~QXmppUdpTransport() override; QXmppJingleCandidate localCandidate(int component) const override; qint64 writeDatagram(const QByteArray &data, const QHostAddress &host, quint16 port) override; public Q_SLOTS: void disconnectFromHost() override; private Q_SLOTS: void readyRead(); private: QUdpSocket *m_socket; }; #endif qxmpp-1.4.0/src/base/QXmppUtils.cpp000066400000000000000000000256351402370562100171670ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Manjeet Dahiya * Jeremy Lainé * * 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 "QXmppUtils.h" #include "QXmppLogger.h" #include #include #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) #include #endif #include #include #include #include #include // adapted from public domain source by Ross Williams and Eric Durbin // FIXME : is this valid for big-endian machines? static quint32 crctable[256] = { 0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL, 0x076DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L, 0x0EDB8832L, 0x79DCB8A4L, 0xE0D5E91EL, 0x97D2D988L, 0x09B64C2BL, 0x7EB17CBDL, 0xE7B82D07L, 0x90BF1D91L, 0x1DB71064L, 0x6AB020F2L, 0xF3B97148L, 0x84BE41DEL, 0x1ADAD47DL, 0x6DDDE4EBL, 0xF4D4B551L, 0x83D385C7L, 0x136C9856L, 0x646BA8C0L, 0xFD62F97AL, 0x8A65C9ECL, 0x14015C4FL, 0x63066CD9L, 0xFA0F3D63L, 0x8D080DF5L, 0x3B6E20C8L, 0x4C69105EL, 0xD56041E4L, 0xA2677172L, 0x3C03E4D1L, 0x4B04D447L, 0xD20D85FDL, 0xA50AB56BL, 0x35B5A8FAL, 0x42B2986CL, 0xDBBBC9D6L, 0xACBCF940L, 0x32D86CE3L, 0x45DF5C75L, 0xDCD60DCFL, 0xABD13D59L, 0x26D930ACL, 0x51DE003AL, 0xC8D75180L, 0xBFD06116L, 0x21B4F4B5L, 0x56B3C423L, 0xCFBA9599L, 0xB8BDA50FL, 0x2802B89EL, 0x5F058808L, 0xC60CD9B2L, 0xB10BE924L, 0x2F6F7C87L, 0x58684C11L, 0xC1611DABL, 0xB6662D3DL, 0x76DC4190L, 0x01DB7106L, 0x98D220BCL, 0xEFD5102AL, 0x71B18589L, 0x06B6B51FL, 0x9FBFE4A5L, 0xE8B8D433L, 0x7807C9A2L, 0x0F00F934L, 0x9609A88EL, 0xE10E9818L, 0x7F6A0DBBL, 0x086D3D2DL, 0x91646C97L, 0xE6635C01L, 0x6B6B51F4L, 0x1C6C6162L, 0x856530D8L, 0xF262004EL, 0x6C0695EDL, 0x1B01A57BL, 0x8208F4C1L, 0xF50FC457L, 0x65B0D9C6L, 0x12B7E950L, 0x8BBEB8EAL, 0xFCB9887CL, 0x62DD1DDFL, 0x15DA2D49L, 0x8CD37CF3L, 0xFBD44C65L, 0x4DB26158L, 0x3AB551CEL, 0xA3BC0074L, 0xD4BB30E2L, 0x4ADFA541L, 0x3DD895D7L, 0xA4D1C46DL, 0xD3D6F4FBL, 0x4369E96AL, 0x346ED9FCL, 0xAD678846L, 0xDA60B8D0L, 0x44042D73L, 0x33031DE5L, 0xAA0A4C5FL, 0xDD0D7CC9L, 0x5005713CL, 0x270241AAL, 0xBE0B1010L, 0xC90C2086L, 0x5768B525L, 0x206F85B3L, 0xB966D409L, 0xCE61E49FL, 0x5EDEF90EL, 0x29D9C998L, 0xB0D09822L, 0xC7D7A8B4L, 0x59B33D17L, 0x2EB40D81L, 0xB7BD5C3BL, 0xC0BA6CADL, 0xEDB88320L, 0x9ABFB3B6L, 0x03B6E20CL, 0x74B1D29AL, 0xEAD54739L, 0x9DD277AFL, 0x04DB2615L, 0x73DC1683L, 0xE3630B12L, 0x94643B84L, 0x0D6D6A3EL, 0x7A6A5AA8L, 0xE40ECF0BL, 0x9309FF9DL, 0x0A00AE27L, 0x7D079EB1L, 0xF00F9344L, 0x8708A3D2L, 0x1E01F268L, 0x6906C2FEL, 0xF762575DL, 0x806567CBL, 0x196C3671L, 0x6E6B06E7L, 0xFED41B76L, 0x89D32BE0L, 0x10DA7A5AL, 0x67DD4ACCL, 0xF9B9DF6FL, 0x8EBEEFF9L, 0x17B7BE43L, 0x60B08ED5L, 0xD6D6A3E8L, 0xA1D1937EL, 0x38D8C2C4L, 0x4FDFF252L, 0xD1BB67F1L, 0xA6BC5767L, 0x3FB506DDL, 0x48B2364BL, 0xD80D2BDAL, 0xAF0A1B4CL, 0x36034AF6L, 0x41047A60L, 0xDF60EFC3L, 0xA867DF55L, 0x316E8EEFL, 0x4669BE79L, 0xCB61B38CL, 0xBC66831AL, 0x256FD2A0L, 0x5268E236L, 0xCC0C7795L, 0xBB0B4703L, 0x220216B9L, 0x5505262FL, 0xC5BA3BBEL, 0xB2BD0B28L, 0x2BB45A92L, 0x5CB36A04L, 0xC2D7FFA7L, 0xB5D0CF31L, 0x2CD99E8BL, 0x5BDEAE1DL, 0x9B64C2B0L, 0xEC63F226L, 0x756AA39CL, 0x026D930AL, 0x9C0906A9L, 0xEB0E363FL, 0x72076785L, 0x05005713L, 0x95BF4A82L, 0xE2B87A14L, 0x7BB12BAEL, 0x0CB61B38L, 0x92D28E9BL, 0xE5D5BE0DL, 0x7CDCEFB7L, 0x0BDBDF21L, 0x86D3D2D4L, 0xF1D4E242L, 0x68DDB3F8L, 0x1FDA836EL, 0x81BE16CDL, 0xF6B9265BL, 0x6FB077E1L, 0x18B74777L, 0x88085AE6L, 0xFF0F6A70L, 0x66063BCAL, 0x11010B5CL, 0x8F659EFFL, 0xF862AE69L, 0x616BFFD3L, 0x166CCF45L, 0xA00AE278L, 0xD70DD2EEL, 0x4E048354L, 0x3903B3C2L, 0xA7672661L, 0xD06016F7L, 0x4969474DL, 0x3E6E77DBL, 0xAED16A4AL, 0xD9D65ADCL, 0x40DF0B66L, 0x37D83BF0L, 0xA9BCAE53L, 0xDEBB9EC5L, 0x47B2CF7FL, 0x30B5FFE9L, 0xBDBDF21CL, 0xCABAC28AL, 0x53B39330L, 0x24B4A3A6L, 0xBAD03605L, 0xCDD70693L, 0x54DE5729L, 0x23D967BFL, 0xB3667A2EL, 0xC4614AB8L, 0x5D681B02L, 0x2A6F2B94L, 0xB40BBE37L, 0xC30C8EA1L, 0x5A05DF1BL, 0x2D02EF8DL }; /// /// Parses a date-time from a string according to /// \xep{0082}: XMPP Date and Time Profiles. /// QDateTime QXmppUtils::datetimeFromString(const QString &str) { // Qt::ISODate parses milliseconds, but doesn't output them return QDateTime::fromString(str, Qt::ISODate).toUTC(); } /// /// Serializes a date-time to a string according to /// \xep{0082}: XMPP Date and Time Profiles. /// QString QXmppUtils::datetimeToString(const QDateTime &dt) { if (dt.time().msec()) return dt.toUTC().toString(Qt::ISODateWithMs); return dt.toUTC().toString(Qt::ISODate); } /// /// Parses a timezone offset (in seconds) from a string according to /// \xep{0082}: XMPP Date and Time Profiles. /// int QXmppUtils::timezoneOffsetFromString(const QString &str) { static const QRegularExpression timezoneRegex(QStringLiteral("(Z|([+-])([0-9]{2}):([0-9]{2}))")); const auto match = timezoneRegex.match(str); if (!match.hasMatch()) return 0; // No offset from UTC if (match.captured(1) == u'Z') return 0; // Calculate offset const int offset = match.captured(3).toInt() * 3600 + match.captured(4).toInt() * 60; if (match.captured(2) == u'-') return -offset; return offset; } /// /// Serializes a timezone offset (in seconds) to a string according to /// \xep{0082}: XMPP Date and Time Profiles. /// QString QXmppUtils::timezoneOffsetToString(int secs) { if (!secs) return QStringLiteral("Z"); const QTime tzoTime = QTime(0, 0, 0).addSecs(qAbs(secs)); return (secs < 0 ? QStringLiteral("-") : QStringLiteral("+")) + tzoTime.toString(QStringLiteral("hh:mm")); } /// Returns the domain for the given \a jid. QString QXmppUtils::jidToDomain(const QString &jid) { return jidToBareJid(jid).split(QStringLiteral("@")).last(); } /// Returns the resource for the given \a jid. QString QXmppUtils::jidToResource(const QString &jid) { const int pos = jid.indexOf(QChar('/')); if (pos < 0) return QString(); return jid.mid(pos + 1); } /// Returns the user for the given \a jid. QString QXmppUtils::jidToUser(const QString &jid) { const int pos = jid.indexOf(QChar('@')); if (pos < 0) return QString(); return jid.left(pos); } /// Returns the bare jid (i.e. without resource) for the given \a jid. QString QXmppUtils::jidToBareJid(const QString &jid) { const int pos = jid.indexOf(QChar('/')); if (pos < 0) return jid; return jid.left(pos); } /// Calculates the CRC32 checksum for the given input. quint32 QXmppUtils::generateCrc32(const QByteArray &in) { quint32 result = 0xffffffff; for (char n : in) result = (result >> 8) ^ (crctable[(result & 0xff) ^ (quint8)n]); return result ^= 0xffffffff; } static QByteArray generateHmac(QCryptographicHash::Algorithm algorithm, const QByteArray &key, const QByteArray &text) { QCryptographicHash hasher(algorithm); const int B = 64; QByteArray kpad = key + QByteArray(B - key.size(), 0); QByteArray ba; for (int i = 0; i < B; ++i) ba += kpad[i] ^ 0x5c; QByteArray tmp; for (int i = 0; i < B; ++i) tmp += kpad[i] ^ 0x36; hasher.addData(tmp); hasher.addData(text); ba += hasher.result(); hasher.reset(); hasher.addData(ba); return hasher.result(); } /// Generates the MD5 HMAC for the given \a key and \a text. QByteArray QXmppUtils::generateHmacMd5(const QByteArray &key, const QByteArray &text) { return generateHmac(QCryptographicHash::Md5, key, text); } /// Generates the SHA1 HMAC for the given \a key and \a text. QByteArray QXmppUtils::generateHmacSha1(const QByteArray &key, const QByteArray &text) { return generateHmac(QCryptographicHash::Sha1, key, text); } /// Generates a random integer x between 0 and N-1. /// /// \param N int QXmppUtils::generateRandomInteger(int N) { Q_ASSERT(N > 0 && N <= RAND_MAX); int val; #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) while (N <= (val = QRandomGenerator::global()->generate() / (RAND_MAX / N))) { } #else while (N <= (val = qrand() / (RAND_MAX / N))) { } #endif return val; } /// Returns a random byte array of the specified size. /// /// \param length QByteArray QXmppUtils::generateRandomBytes(int length) { QByteArray bytes(length, 'm'); for (int i = 0; i < length; ++i) bytes[i] = (char)generateRandomInteger(256); return bytes; } /// /// Creates a new stanza id in the UUID format. /// /// \since QXmpp 1.3 /// QString QXmppUtils::generateStanzaUuid() { #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) return QUuid::createUuid().toString(QUuid::WithoutBraces); #else return QUuid::createUuid().toString().mid(1, 36); #endif } /// /// Returns a random alphanumerical string of the specified size. /// /// Since QXmpp 1.3 this will generate a UUID, if the specified \p length is 36 /// which is also the new default value. The returned string is still 36 /// characters long, but will contain dashes (as specified in the UUID format). /// /// \note It is recommended to use UUIDs for cases where IDs must be unique and /// are possibly stored permanently. This can be done using /// QXmppUtils::generateStanzaUuid(). However, since that function is only /// available since QXmpp 1.3, you may also want to continue to use this /// function because of compatibility reasons. /// /// \param length /// QString QXmppUtils::generateStanzaHash(int length) { if (length == 36) return QXmppUtils::generateStanzaUuid(); const QString somechars = QStringLiteral("1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); const int N = somechars.size(); QString hashResult; for (int idx = 0; idx < length; ++idx) hashResult += somechars[generateRandomInteger(N)]; return hashResult; } void helperToXmlAddAttribute(QXmlStreamWriter *stream, const QString &name, const QString &value) { if (!value.isEmpty()) stream->writeAttribute(name, value); } void helperToXmlAddTextElement(QXmlStreamWriter *stream, const QString &name, const QString &value) { if (!value.isEmpty()) stream->writeTextElement(name, value); else stream->writeEmptyElement(name); } qxmpp-1.4.0/src/base/QXmppUtils.h000066400000000000000000000045671402370562100166350ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Manjeet Dahiya * Jeremy Lainé * * 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 QXMPPUTILS_H #define QXMPPUTILS_H // forward declarations of QXmlStream* classes will not work on Mac, we need to // include the whole header. // See http://lists.trolltech.com/qt-interest/2008-07/thread00798-0.html // for an explanation. #include "QXmppGlobal.h" #include class QByteArray; class QDateTime; class QDomElement; class QString; /// \brief The QXmppUtils class contains static utility functions. /// class QXMPP_EXPORT QXmppUtils { public: // XEP-0082: XMPP Date and Time Profiles static QDateTime datetimeFromString(const QString& str); static QString datetimeToString(const QDateTime& dt); static int timezoneOffsetFromString(const QString& str); static QString timezoneOffsetToString(int secs); static QString jidToDomain(const QString& jid); static QString jidToResource(const QString& jid); static QString jidToUser(const QString& jid); static QString jidToBareJid(const QString& jid); static quint32 generateCrc32(const QByteArray& input); static QByteArray generateHmacMd5(const QByteArray& key, const QByteArray& text); static QByteArray generateHmacSha1(const QByteArray& key, const QByteArray& text); static int generateRandomInteger(int N); static QByteArray generateRandomBytes(int length); static QString generateStanzaUuid(); static QString generateStanzaHash(int length = 36); }; void helperToXmlAddAttribute(QXmlStreamWriter* stream, const QString& name, const QString& value); void helperToXmlAddTextElement(QXmlStreamWriter* stream, const QString& name, const QString& value); #endif // QXMPPUTILS_H qxmpp-1.4.0/src/base/QXmppVCardIq.cpp000066400000000000000000000651451402370562100173600ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 "QXmppVCardIq.h" #include "QXmppConstants_p.h" #include "QXmppUtils.h" #include #include static QString getImageType(const QByteArray &contents) { if (contents.startsWith("\x89PNG\x0d\x0a\x1a\x0a")) return QStringLiteral("image/png"); else if (contents.startsWith("\x8aMNG")) return QStringLiteral("video/x-mng"); else if (contents.startsWith("GIF8")) return QStringLiteral("image/gif"); else if (contents.startsWith("BM")) return QStringLiteral("image/bmp"); else if (contents.contains("/* XPM */")) return QStringLiteral("image/x-xpm"); else if (contents.contains("country; } /// Sets the country. void QXmppVCardAddress::setCountry(const QString &country) { d->country = country; } /// Returns the locality. QString QXmppVCardAddress::locality() const { return d->locality; } /// Sets the locality. void QXmppVCardAddress::setLocality(const QString &locality) { d->locality = locality; } /// Returns the postcode. QString QXmppVCardAddress::postcode() const { return d->postcode; } /// Sets the postcode. void QXmppVCardAddress::setPostcode(const QString &postcode) { d->postcode = postcode; } /// Returns the region. QString QXmppVCardAddress::region() const { return d->region; } /// Sets the region. void QXmppVCardAddress::setRegion(const QString ®ion) { d->region = region; } /// Returns the street address. QString QXmppVCardAddress::street() const { return d->street; } /// Sets the street address. void QXmppVCardAddress::setStreet(const QString &street) { d->street = street; } /// Returns the address type, which is a combination of TypeFlag. QXmppVCardAddress::Type QXmppVCardAddress::type() const { return d->type; } /// Sets the address \a type, which is a combination of TypeFlag. void QXmppVCardAddress::setType(QXmppVCardAddress::Type type) { d->type = type; } /// \cond void QXmppVCardAddress::parse(const QDomElement &element) { if (!element.firstChildElement(QStringLiteral("HOME")).isNull()) d->type |= Home; if (!element.firstChildElement(QStringLiteral("WORK")).isNull()) d->type |= Work; if (!element.firstChildElement(QStringLiteral("POSTAL")).isNull()) d->type |= Postal; if (!element.firstChildElement(QStringLiteral("PREF")).isNull()) d->type |= Preferred; d->country = element.firstChildElement(QStringLiteral("CTRY")).text(); d->locality = element.firstChildElement(QStringLiteral("LOCALITY")).text(); d->postcode = element.firstChildElement(QStringLiteral("PCODE")).text(); d->region = element.firstChildElement(QStringLiteral("REGION")).text(); d->street = element.firstChildElement(QStringLiteral("STREET")).text(); } void QXmppVCardAddress::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("ADR")); if (d->type & Home) writer->writeEmptyElement(QStringLiteral("HOME")); if (d->type & Work) writer->writeEmptyElement(QStringLiteral("WORK")); if (d->type & Postal) writer->writeEmptyElement(QStringLiteral("POSTAL")); if (d->type & Preferred) writer->writeEmptyElement(QStringLiteral("PREF")); if (!d->country.isEmpty()) writer->writeTextElement(QStringLiteral("CTRY"), d->country); if (!d->locality.isEmpty()) writer->writeTextElement(QStringLiteral("LOCALITY"), d->locality); if (!d->postcode.isEmpty()) writer->writeTextElement(QStringLiteral("PCODE"), d->postcode); if (!d->region.isEmpty()) writer->writeTextElement(QStringLiteral("REGION"), d->region); if (!d->street.isEmpty()) writer->writeTextElement(QStringLiteral("STREET"), d->street); writer->writeEndElement(); } /// \endcond class QXmppVCardEmailPrivate : public QSharedData { public: QXmppVCardEmailPrivate() : type(QXmppVCardEmail::None) {}; QString address; QXmppVCardEmail::Type type; }; /// Constructs an empty e-mail address. QXmppVCardEmail::QXmppVCardEmail() : d(new QXmppVCardEmailPrivate) { } /// Constructs a copy of \a other. QXmppVCardEmail::QXmppVCardEmail(const QXmppVCardEmail &other) : d(other.d) { } QXmppVCardEmail::~QXmppVCardEmail() { } /// Assigns \a other to this e-mail address. QXmppVCardEmail &QXmppVCardEmail::operator=(const QXmppVCardEmail &other) { d = other.d; return *this; } /// \brief Checks if two email objects represent the same email address. bool operator==(const QXmppVCardEmail &left, const QXmppVCardEmail &right) { return left.type() == right.type() && left.address() == right.address(); } /// \brief Checks if two email objects represent different email addresses. bool operator!=(const QXmppVCardEmail &left, const QXmppVCardEmail &right) { return !(left == right); } /// Returns the e-mail address. QString QXmppVCardEmail::address() const { return d->address; } /// Sets the e-mail \a address. void QXmppVCardEmail::setAddress(const QString &address) { d->address = address; } /// Returns the e-mail type, which is a combination of TypeFlag. QXmppVCardEmail::Type QXmppVCardEmail::type() const { return d->type; } /// Sets the e-mail \a type, which is a combination of TypeFlag. void QXmppVCardEmail::setType(QXmppVCardEmail::Type type) { d->type = type; } /// \cond void QXmppVCardEmail::parse(const QDomElement &element) { if (!element.firstChildElement(QStringLiteral("HOME")).isNull()) d->type |= Home; if (!element.firstChildElement(QStringLiteral("WORK")).isNull()) d->type |= Work; if (!element.firstChildElement(QStringLiteral("INTERNET")).isNull()) d->type |= Internet; if (!element.firstChildElement(QStringLiteral("PREF")).isNull()) d->type |= Preferred; if (!element.firstChildElement(QStringLiteral("X400")).isNull()) d->type |= X400; d->address = element.firstChildElement(QStringLiteral("USERID")).text(); } void QXmppVCardEmail::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("EMAIL")); if (d->type & Home) writer->writeEmptyElement(QStringLiteral("HOME")); if (d->type & Work) writer->writeEmptyElement(QStringLiteral("WORK")); if (d->type & Internet) writer->writeEmptyElement(QStringLiteral("INTERNET")); if (d->type & Preferred) writer->writeEmptyElement(QStringLiteral("PREF")); if (d->type & X400) writer->writeEmptyElement(QStringLiteral("X400")); writer->writeTextElement(QStringLiteral("USERID"), d->address); writer->writeEndElement(); } /// \endcond class QXmppVCardPhonePrivate : public QSharedData { public: QXmppVCardPhonePrivate() : type(QXmppVCardPhone::None) {}; QString number; QXmppVCardPhone::Type type; }; /// Constructs an empty phone number. QXmppVCardPhone::QXmppVCardPhone() : d(new QXmppVCardPhonePrivate) { } /// Constructs a copy of \a other. QXmppVCardPhone::QXmppVCardPhone(const QXmppVCardPhone &other) : d(other.d) { } QXmppVCardPhone::~QXmppVCardPhone() { } /// Assigns \a other to this phone number. QXmppVCardPhone &QXmppVCardPhone::operator=(const QXmppVCardPhone &other) { d = other.d; return *this; } /// Returns the phone number. QString QXmppVCardPhone::number() const { return d->number; } /// \brief Checks if two phone objects represent the same phone number. bool operator==(const QXmppVCardPhone &left, const QXmppVCardPhone &right) { return left.type() == right.type() && left.number() == right.number(); } /// \brief Checks if two phone objects represent different phone numbers. bool operator!=(const QXmppVCardPhone &left, const QXmppVCardPhone &right) { return !(left == right); } /// Sets the phone \a number. void QXmppVCardPhone::setNumber(const QString &number) { d->number = number; } /// Returns the phone number type, which is a combination of TypeFlag. QXmppVCardPhone::Type QXmppVCardPhone::type() const { return d->type; } /// Sets the phone number \a type, which is a combination of TypeFlag. void QXmppVCardPhone::setType(QXmppVCardPhone::Type type) { d->type = type; } /// \cond void QXmppVCardPhone::parse(const QDomElement &element) { if (!element.firstChildElement(QStringLiteral("HOME")).isNull()) d->type |= Home; if (!element.firstChildElement(QStringLiteral("WORK")).isNull()) d->type |= Work; if (!element.firstChildElement(QStringLiteral("VOICE")).isNull()) d->type |= Voice; if (!element.firstChildElement(QStringLiteral("FAX")).isNull()) d->type |= Fax; if (!element.firstChildElement(QStringLiteral("PAGER")).isNull()) d->type |= Pager; if (!element.firstChildElement(QStringLiteral("MSG")).isNull()) d->type |= Messaging; if (!element.firstChildElement(QStringLiteral("CELL")).isNull()) d->type |= Cell; if (!element.firstChildElement(QStringLiteral("VIDEO")).isNull()) d->type |= Video; if (!element.firstChildElement(QStringLiteral("BBS")).isNull()) d->type |= BBS; if (!element.firstChildElement(QStringLiteral("MODEM")).isNull()) d->type |= Modem; if (!element.firstChildElement(QStringLiteral("ISDN")).isNull()) d->type |= ISDN; if (!element.firstChildElement(QStringLiteral("PCS")).isNull()) d->type |= PCS; if (!element.firstChildElement(QStringLiteral("PREF")).isNull()) d->type |= Preferred; d->number = element.firstChildElement(QStringLiteral("NUMBER")).text(); } void QXmppVCardPhone::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("TEL")); if (d->type & Home) writer->writeEmptyElement(QStringLiteral("HOME")); if (d->type & Work) writer->writeEmptyElement(QStringLiteral("WORK")); if (d->type & Voice) writer->writeEmptyElement(QStringLiteral("VOICE")); if (d->type & Fax) writer->writeEmptyElement(QStringLiteral("FAX")); if (d->type & Pager) writer->writeEmptyElement(QStringLiteral("PAGER")); if (d->type & Messaging) writer->writeEmptyElement(QStringLiteral("MSG")); if (d->type & Cell) writer->writeEmptyElement(QStringLiteral("CELL")); if (d->type & Video) writer->writeEmptyElement(QStringLiteral("VIDEO")); if (d->type & BBS) writer->writeEmptyElement(QStringLiteral("BBS")); if (d->type & Modem) writer->writeEmptyElement(QStringLiteral("MODEM")); if (d->type & ISDN) writer->writeEmptyElement(QStringLiteral("ISDN")); if (d->type & PCS) writer->writeEmptyElement(QStringLiteral("PCS")); if (d->type & Preferred) writer->writeEmptyElement(QStringLiteral("PREF")); writer->writeTextElement(QStringLiteral("NUMBER"), d->number); writer->writeEndElement(); } /// \endcond class QXmppVCardOrganizationPrivate : public QSharedData { public: QString organization; QString unit; QString role; QString title; }; /// Constructs an empty organization information. QXmppVCardOrganization::QXmppVCardOrganization() : d(new QXmppVCardOrganizationPrivate) { } /// Constructs a copy of \a other. QXmppVCardOrganization::QXmppVCardOrganization(const QXmppVCardOrganization &other) : d(other.d) { } QXmppVCardOrganization::~QXmppVCardOrganization() { } /// Assigns \a other to this organization info. QXmppVCardOrganization &QXmppVCardOrganization::operator=(const QXmppVCardOrganization &other) { d = other.d; return *this; } /// \brief Checks if two organization objects represent the same organization. bool operator==(const QXmppVCardOrganization &left, const QXmppVCardOrganization &right) { return left.organization() == right.organization() && left.unit() == right.unit() && left.title() == right.title() && left.role() == right.role(); } /// \brief Checks if two organization objects represent different organizations. bool operator!=(const QXmppVCardOrganization &left, const QXmppVCardOrganization &right) { return !(left == right); } /// Returns the name of the organization. QString QXmppVCardOrganization::organization() const { return d->organization; } /// Sets the organization \a name. void QXmppVCardOrganization::setOrganization(const QString &name) { d->organization = name; } /// Returns the organization unit (also known as department). QString QXmppVCardOrganization::unit() const { return d->unit; } /// Sets the \a unit within the organization. void QXmppVCardOrganization::setUnit(const QString &unit) { d->unit = unit; } /// Returns the job role within the organization. QString QXmppVCardOrganization::role() const { return d->role; } /// Sets the job \a role within the organization. void QXmppVCardOrganization::setRole(const QString &role) { d->role = role; } /// Returns the job title within the organization. QString QXmppVCardOrganization::title() const { return d->title; } /// Sets the job \a title within the organization. void QXmppVCardOrganization::setTitle(const QString &title) { d->title = title; } /// \cond void QXmppVCardOrganization::parse(const QDomElement &cardElem) { d->title = cardElem.firstChildElement(QStringLiteral("TITLE")).text(); d->role = cardElem.firstChildElement(QStringLiteral("ROLE")).text(); const QDomElement &orgElem = cardElem.firstChildElement(QStringLiteral("ORG")); d->organization = orgElem.firstChildElement(QStringLiteral("ORGNAME")).text(); d->unit = orgElem.firstChildElement(QStringLiteral("ORGUNIT")).text(); } void QXmppVCardOrganization::toXml(QXmlStreamWriter *stream) const { if (!d->unit.isEmpty() || !d->organization.isEmpty()) { stream->writeStartElement(QStringLiteral("ORG")); stream->writeTextElement(QStringLiteral("ORGNAME"), d->organization); stream->writeTextElement(QStringLiteral("ORGUNIT"), d->unit); stream->writeEndElement(); } helperToXmlAddTextElement(stream, QStringLiteral("TITLE"), d->title); helperToXmlAddTextElement(stream, QStringLiteral("ROLE"), d->role); } /// \endcond class QXmppVCardIqPrivate : public QSharedData { public: QDate birthday; QString description; QString firstName; QString fullName; QString lastName; QString middleName; QString nickName; QString url; // not as 64 base QByteArray photo; QString photoType; QList addresses; QList emails; QList phones; QXmppVCardOrganization organization; }; /// Constructs a QXmppVCardIq for the specified recipient. /// /// \param jid QXmppVCardIq::QXmppVCardIq(const QString &jid) : QXmppIq(), d(new QXmppVCardIqPrivate) { // for self jid should be empty setTo(jid); } /// Constructs a copy of \a other. QXmppVCardIq::QXmppVCardIq(const QXmppVCardIq &other) : QXmppIq(other), d(other.d) { } QXmppVCardIq::~QXmppVCardIq() { } /// Assigns \a other to this vCard IQ. QXmppVCardIq &QXmppVCardIq::operator=(const QXmppVCardIq &other) { QXmppIq::operator=(other); d = other.d; return *this; } /// \brief Checks if two VCard objects represent the same VCard. bool operator==(const QXmppVCardIq &left, const QXmppVCardIq &right) { return left.birthday() == right.birthday() && left.description() == right.description() && left.email() == right.email() && left.firstName() == right.firstName() && left.fullName() == right.fullName() && left.lastName() == right.lastName() && left.middleName() == right.middleName() && left.nickName() == right.nickName() && left.photo() == right.photo() && left.photoType() == right.photoType() && left.url() == right.url() && left.addresses() == right.addresses() && left.emails() == right.emails() && left.phones() == right.phones() && left.organization() == right.organization(); } /// \brief Checks if two VCard objects represent different VCards. bool operator!=(const QXmppVCardIq &left, const QXmppVCardIq &right) { return !(left == right); } /// Returns the date of birth of the individual associated with the vCard. /// QDate QXmppVCardIq::birthday() const { return d->birthday; } /// Sets the date of birth of the individual associated with the vCard. /// /// \param birthday void QXmppVCardIq::setBirthday(const QDate &birthday) { d->birthday = birthday; } /// Returns the free-form descriptive text. QString QXmppVCardIq::description() const { return d->description; } /// Sets the free-form descriptive text. void QXmppVCardIq::setDescription(const QString &description) { d->description = description; } /// Returns the email address. /// QString QXmppVCardIq::email() const { if (d->emails.isEmpty()) return QString(); else return d->emails.first().address(); } /// Sets the email address. /// /// \param email void QXmppVCardIq::setEmail(const QString &email) { QXmppVCardEmail first; first.setAddress(email); first.setType(QXmppVCardEmail::Internet); d->emails = QList() << first; } /// Returns the first name. /// QString QXmppVCardIq::firstName() const { return d->firstName; } /// Sets the first name. /// /// \param firstName void QXmppVCardIq::setFirstName(const QString &firstName) { d->firstName = firstName; } /// Returns the full name. /// QString QXmppVCardIq::fullName() const { return d->fullName; } /// Sets the full name. /// /// \param fullName void QXmppVCardIq::setFullName(const QString &fullName) { d->fullName = fullName; } /// Returns the last name. /// QString QXmppVCardIq::lastName() const { return d->lastName; } /// Sets the last name. /// /// \param lastName void QXmppVCardIq::setLastName(const QString &lastName) { d->lastName = lastName; } /// Returns the middle name. /// QString QXmppVCardIq::middleName() const { return d->middleName; } /// Sets the middle name. /// /// \param middleName void QXmppVCardIq::setMiddleName(const QString &middleName) { d->middleName = middleName; } /// Returns the nickname. /// QString QXmppVCardIq::nickName() const { return d->nickName; } /// Sets the nickname. /// /// \param nickName void QXmppVCardIq::setNickName(const QString &nickName) { d->nickName = nickName; } /// Returns the URL associated with the vCard. It can represent the user's /// homepage or a location at which you can find real-time information about /// the vCard. QString QXmppVCardIq::url() const { return d->url; } /// Sets the URL associated with the vCard. It can represent the user's /// homepage or a location at which you can find real-time information about /// the vCard. /// /// \param url void QXmppVCardIq::setUrl(const QString &url) { d->url = url; } /// Returns the photo's binary contents. /// /// If you want to use the photo as a QImage you can use: /// /// \code /// QBuffer buffer; /// buffer.setData(myCard.photo()); /// buffer.open(QIODevice::ReadOnly); /// QImageReader imageReader(&buffer); /// QImage myImage = imageReader.read(); /// \endcode QByteArray QXmppVCardIq::photo() const { return d->photo; } /// Sets the photo's binary contents. void QXmppVCardIq::setPhoto(const QByteArray &photo) { d->photo = photo; } /// Returns the photo's MIME type. QString QXmppVCardIq::photoType() const { return d->photoType; } /// Sets the photo's MIME type. void QXmppVCardIq::setPhotoType(const QString &photoType) { d->photoType = photoType; } /// Returns the addresses. QList QXmppVCardIq::addresses() const { return d->addresses; } /// Sets the addresses. void QXmppVCardIq::setAddresses(const QList &addresses) { d->addresses = addresses; } /// Returns the e-mail addresses. QList QXmppVCardIq::emails() const { return d->emails; } /// Sets the e-mail addresses. void QXmppVCardIq::setEmails(const QList &emails) { d->emails = emails; } /// Returns the phone numbers. QList QXmppVCardIq::phones() const { return d->phones; } /// Sets the phone numbers. void QXmppVCardIq::setPhones(const QList &phones) { d->phones = phones; } /// Returns the organization info. QXmppVCardOrganization QXmppVCardIq::organization() const { return d->organization; } /// Sets the organization info. void QXmppVCardIq::setOrganization(const QXmppVCardOrganization &org) { d->organization = org; } /// \cond bool QXmppVCardIq::isVCard(const QDomElement &nodeRecv) { return nodeRecv.firstChildElement(QStringLiteral("vCard")).namespaceURI() == ns_vcard; } void QXmppVCardIq::parseElementFromChild(const QDomElement &nodeRecv) { // vCard QDomElement cardElement = nodeRecv.firstChildElement(QStringLiteral("vCard")); d->birthday = QDate::fromString(cardElement.firstChildElement(QStringLiteral("BDAY")).text(), QStringLiteral("yyyy-MM-dd")); d->description = cardElement.firstChildElement(QStringLiteral("DESC")).text(); d->fullName = cardElement.firstChildElement(QStringLiteral("FN")).text(); d->nickName = cardElement.firstChildElement(QStringLiteral("NICKNAME")).text(); QDomElement nameElement = cardElement.firstChildElement(QStringLiteral("N")); d->firstName = nameElement.firstChildElement(QStringLiteral("GIVEN")).text(); d->lastName = nameElement.firstChildElement(QStringLiteral("FAMILY")).text(); d->middleName = nameElement.firstChildElement(QStringLiteral("MIDDLE")).text(); d->url = cardElement.firstChildElement(QStringLiteral("URL")).text(); QDomElement photoElement = cardElement.firstChildElement(QStringLiteral("PHOTO")); QByteArray base64data = photoElement.firstChildElement(QStringLiteral("BINVAL")).text().toLatin1(); d->photo = QByteArray::fromBase64(base64data); d->photoType = photoElement.firstChildElement(QStringLiteral("TYPE")).text(); QDomElement child = cardElement.firstChildElement(); while (!child.isNull()) { if (child.tagName() == QStringLiteral("ADR")) { QXmppVCardAddress address; address.parse(child); d->addresses << address; } else if (child.tagName() == QStringLiteral("EMAIL")) { QXmppVCardEmail email; email.parse(child); d->emails << email; } else if (child.tagName() == QStringLiteral("TEL")) { QXmppVCardPhone phone; phone.parse(child); d->phones << phone; } child = child.nextSiblingElement(); } d->organization.parse(cardElement); } void QXmppVCardIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("vCard")); writer->writeDefaultNamespace(ns_vcard); for (const QXmppVCardAddress &address : d->addresses) address.toXml(writer); if (d->birthday.isValid()) helperToXmlAddTextElement(writer, QStringLiteral("BDAY"), d->birthday.toString(QStringLiteral("yyyy-MM-dd"))); if (!d->description.isEmpty()) helperToXmlAddTextElement(writer, QStringLiteral("DESC"), d->description); for (const QXmppVCardEmail &email : d->emails) email.toXml(writer); if (!d->fullName.isEmpty()) helperToXmlAddTextElement(writer, QStringLiteral("FN"), d->fullName); if (!d->nickName.isEmpty()) helperToXmlAddTextElement(writer, QStringLiteral("NICKNAME"), d->nickName); if (!d->firstName.isEmpty() || !d->lastName.isEmpty() || !d->middleName.isEmpty()) { writer->writeStartElement("N"); if (!d->firstName.isEmpty()) helperToXmlAddTextElement(writer, QStringLiteral("GIVEN"), d->firstName); if (!d->lastName.isEmpty()) helperToXmlAddTextElement(writer, QStringLiteral("FAMILY"), d->lastName); if (!d->middleName.isEmpty()) helperToXmlAddTextElement(writer, QStringLiteral("MIDDLE"), d->middleName); writer->writeEndElement(); } for (const QXmppVCardPhone &phone : d->phones) phone.toXml(writer); if (!photo().isEmpty()) { writer->writeStartElement(QStringLiteral("PHOTO")); QString photoType = d->photoType; if (photoType.isEmpty()) photoType = getImageType(d->photo); helperToXmlAddTextElement(writer, QStringLiteral("TYPE"), photoType); helperToXmlAddTextElement(writer, QStringLiteral("BINVAL"), d->photo.toBase64()); writer->writeEndElement(); } if (!d->url.isEmpty()) helperToXmlAddTextElement(writer, QStringLiteral("URL"), d->url); d->organization.toXml(writer); writer->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/base/QXmppVCardIq.h000066400000000000000000000171061402370562100170170ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 QXMPPVCARDIQ_H #define QXMPPVCARDIQ_H #include "QXmppIq.h" #include #include #include class QXmppVCardAddressPrivate; class QXmppVCardEmailPrivate; class QXmppVCardPhonePrivate; class QXmppVCardOrganizationPrivate; class QXmppVCardIqPrivate; /// \brief Represent a vCard address. class QXMPP_EXPORT QXmppVCardAddress { public: /// \brief Describes e-mail address types. enum TypeFlag { None = 0x0, Home = 0x1, Work = 0x2, Postal = 0x4, Preferred = 0x8 }; Q_DECLARE_FLAGS(Type, TypeFlag) QXmppVCardAddress(); QXmppVCardAddress(const QXmppVCardAddress &other); ~QXmppVCardAddress(); QXmppVCardAddress &operator=(const QXmppVCardAddress &other); QString country() const; void setCountry(const QString &country); QString locality() const; void setLocality(const QString &locality); QString postcode() const; void setPostcode(const QString &postcode); QString region() const; void setRegion(const QString ®ion); QString street() const; void setStreet(const QString &street); Type type() const; void setType(Type type); /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *stream) const; /// \endcond private: QSharedDataPointer d; }; QXMPP_EXPORT bool operator==(const QXmppVCardAddress &, const QXmppVCardAddress &); QXMPP_EXPORT bool operator!=(const QXmppVCardAddress &, const QXmppVCardAddress &); /// \brief Represents a vCard e-mail address. class QXMPP_EXPORT QXmppVCardEmail { public: /// \brief Describes e-mail address types. enum TypeFlag { None = 0x0, Home = 0x1, Work = 0x2, Internet = 0x4, Preferred = 0x8, X400 = 0x10 }; Q_DECLARE_FLAGS(Type, TypeFlag) QXmppVCardEmail(); QXmppVCardEmail(const QXmppVCardEmail &other); ~QXmppVCardEmail(); QXmppVCardEmail &operator=(const QXmppVCardEmail &other); QString address() const; void setAddress(const QString &address); Type type() const; void setType(Type type); /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *stream) const; /// \endcond private: QSharedDataPointer d; }; QXMPP_EXPORT bool operator==(const QXmppVCardEmail &, const QXmppVCardEmail &); QXMPP_EXPORT bool operator!=(const QXmppVCardEmail &, const QXmppVCardEmail &); /// \brief Represents a vCard phone number. class QXMPP_EXPORT QXmppVCardPhone { public: /// \brief Describes phone number types. enum TypeFlag { None = 0x0, Home = 0x1, Work = 0x2, Voice = 0x4, Fax = 0x8, Pager = 0x10, Messaging = 0x20, Cell = 0x40, Video = 0x80, BBS = 0x100, Modem = 0x200, ISDN = 0x400, PCS = 0x800, Preferred = 0x1000 }; Q_DECLARE_FLAGS(Type, TypeFlag) QXmppVCardPhone(); QXmppVCardPhone(const QXmppVCardPhone &other); ~QXmppVCardPhone(); QXmppVCardPhone &operator=(const QXmppVCardPhone &other); QString number() const; void setNumber(const QString &number); Type type() const; void setType(Type type); /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *stream) const; /// \endcond private: QSharedDataPointer d; }; QXMPP_EXPORT bool operator==(const QXmppVCardPhone &, const QXmppVCardPhone &); QXMPP_EXPORT bool operator!=(const QXmppVCardPhone &, const QXmppVCardPhone &); /// \brief Represents organization information in XMPP vCards. /// /// This contains both information about organization itself and /// information about job position in the organization. class QXMPP_EXPORT QXmppVCardOrganization { public: QXmppVCardOrganization(); QXmppVCardOrganization(const QXmppVCardOrganization &other); ~QXmppVCardOrganization(); QXmppVCardOrganization &operator=(const QXmppVCardOrganization &other); QString organization() const; void setOrganization(const QString &); QString unit() const; void setUnit(const QString &); QString title() const; void setTitle(const QString &); QString role() const; void setRole(const QString &); /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *stream) const; /// \endcond private: QSharedDataPointer d; }; QXMPP_EXPORT bool operator==(const QXmppVCardOrganization &, const QXmppVCardOrganization &); QXMPP_EXPORT bool operator!=(const QXmppVCardOrganization &, const QXmppVCardOrganization &); /// \brief Represents the XMPP vCard. /// /// The functions names are self explanatory. /// Look at QXmppVCardManager and \xep{0054}: vcard-temp for more details. /// /// There are many field of XMPP vCard which are not present in /// this class. File a issue for the same. We will add the requested /// field to this class. /// class QXMPP_EXPORT QXmppVCardIq : public QXmppIq { public: QXmppVCardIq(const QString &bareJid = QString()); QXmppVCardIq(const QXmppVCardIq &other); ~QXmppVCardIq() override; QXmppVCardIq &operator=(const QXmppVCardIq &other); QDate birthday() const; void setBirthday(const QDate &birthday); QString description() const; void setDescription(const QString &description); QString email() const; void setEmail(const QString &); QString firstName() const; void setFirstName(const QString &); QString fullName() const; void setFullName(const QString &); QString lastName() const; void setLastName(const QString &); QString middleName() const; void setMiddleName(const QString &); QString nickName() const; void setNickName(const QString &); QByteArray photo() const; void setPhoto(const QByteArray &); QString photoType() const; void setPhotoType(const QString &type); QString url() const; void setUrl(const QString &); QList addresses() const; void setAddresses(const QList &addresses); QList emails() const; void setEmails(const QList &emails); QList phones() const; void setPhones(const QList &phones); QXmppVCardOrganization organization() const; void setOrganization(const QXmppVCardOrganization &); /// \cond static bool isVCard(const QDomElement &element); /// \endcond protected: /// \cond void parseElementFromChild(const QDomElement &) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: QSharedDataPointer d; }; QXMPP_EXPORT bool operator==(const QXmppVCardIq &, const QXmppVCardIq &); QXMPP_EXPORT bool operator!=(const QXmppVCardIq &, const QXmppVCardIq &); #endif // QXMPPVCARDIQ_H qxmpp-1.4.0/src/base/QXmppVersionIq.cpp000066400000000000000000000051361402370562100200000ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppVersionIq.h" #include "QXmppConstants_p.h" #include "QXmppUtils.h" #include /// Returns the name of the software. /// QString QXmppVersionIq::name() const { return m_name; } /// Sets the name of the software. /// /// \param name void QXmppVersionIq::setName(const QString &name) { m_name = name; } /// Returns the operating system. /// QString QXmppVersionIq::os() const { return m_os; } /// Sets the operating system. /// /// \param os void QXmppVersionIq::setOs(const QString &os) { m_os = os; } /// Returns the software version. /// QString QXmppVersionIq::version() const { return m_version; } /// Sets the software version. /// /// \param version void QXmppVersionIq::setVersion(const QString &version) { m_version = version; } /// \cond bool QXmppVersionIq::isVersionIq(const QDomElement &element) { QDomElement queryElement = element.firstChildElement(QStringLiteral("query")); return queryElement.namespaceURI() == ns_version; } void QXmppVersionIq::parseElementFromChild(const QDomElement &element) { QDomElement queryElement = element.firstChildElement(QStringLiteral("query")); m_name = queryElement.firstChildElement(QStringLiteral("name")).text(); m_os = queryElement.firstChildElement(QStringLiteral("os")).text(); m_version = queryElement.firstChildElement(QStringLiteral("version")).text(); } void QXmppVersionIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("query")); writer->writeDefaultNamespace(ns_version); if (!m_name.isEmpty()) helperToXmlAddTextElement(writer, QStringLiteral("name"), m_name); if (!m_os.isEmpty()) helperToXmlAddTextElement(writer, QStringLiteral("os"), m_os); if (!m_version.isEmpty()) helperToXmlAddTextElement(writer, QStringLiteral("version"), m_version); writer->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/base/QXmppVersionIq.h000066400000000000000000000030321402370562100174360ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPVERSIONIQ_H #define QXMPPVERSIONIQ_H #include "QXmppIq.h" /// \brief The QXmppVersionIq class represents an IQ for conveying a software /// version as defined by \xep{0092}: Software Version. /// /// \ingroup Stanzas class QXMPP_EXPORT QXmppVersionIq : public QXmppIq { public: QString name() const; void setName(const QString &name); QString os() const; void setOs(const QString &os); QString version() const; void setVersion(const QString &version); /// \cond static bool isVersionIq(const QDomElement &element); /// \endcond protected: /// \cond void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; /// \endcond private: QString m_name; QString m_os; QString m_version; }; #endif qxmpp-1.4.0/src/client/000077500000000000000000000000001402370562100147265ustar00rootroot00000000000000qxmpp-1.4.0/src/client/QXmppArchiveManager.cpp000066400000000000000000000111151402370562100212730ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppArchiveManager.h" #include "QXmppArchiveIq.h" #include "QXmppClient.h" #include "QXmppConstants_p.h" #include /// \cond QStringList QXmppArchiveManager::discoveryFeatures() const { // XEP-0036: Message Archiving return QStringList() << ns_archive; } bool QXmppArchiveManager::handleStanza(const QDomElement &element) { if (element.tagName() != "iq") return false; // XEP-0136: Message Archiving if (QXmppArchiveChatIq::isArchiveChatIq(element)) { QXmppArchiveChatIq archiveIq; archiveIq.parse(element); emit archiveChatReceived(archiveIq.chat(), archiveIq.resultSetReply()); return true; } else if (QXmppArchiveListIq::isArchiveListIq(element)) { QXmppArchiveListIq archiveIq; archiveIq.parse(element); emit archiveListReceived(archiveIq.chats(), archiveIq.resultSetReply()); return true; } else if (QXmppArchivePrefIq::isArchivePrefIq(element)) { // TODO: handle preference iq QXmppArchivePrefIq archiveIq; archiveIq.parse(element); return true; } return false; } /// \endcond /// Retrieves the list of available collections. Once the results are /// received, the archiveListReceived() signal will be emitted. /// /// \param jid JID you want conversations with. /// \param start Optional start time. /// \param end Optional end time. /// \param rsm Optional Result Set Management query /// void QXmppArchiveManager::listCollections(const QString &jid, const QDateTime &start, const QDateTime &end, const QXmppResultSetQuery &rsm) { QXmppArchiveListIq packet; packet.setResultSetQuery(rsm); packet.setWith(jid); packet.setStart(start); packet.setEnd(end); client()->sendPacket(packet); } /// \overload /// Retrieves the list of available collections. Once the results are /// received, the archiveListReceived() signal will be emitted. /// /// \param jid JID you want conversations with. /// \param start Start time. /// \param end End time. /// \param max Maximum number of collections to list. /// void QXmppArchiveManager::listCollections(const QString &jid, const QDateTime &start, const QDateTime &end, int max) { QXmppResultSetQuery rsm; rsm.setMax(max); listCollections(jid, start, end, rsm); } /// Removes the specified collection(s). /// /// \param jid The JID of the collection /// \param start Optional start time. /// \param end Optional end time. /// void QXmppArchiveManager::removeCollections(const QString &jid, const QDateTime &start, const QDateTime &end) { QXmppArchiveRemoveIq packet; packet.setType(QXmppIq::Set); packet.setWith(jid); packet.setStart(start); packet.setEnd(end); client()->sendPacket(packet); } /// Retrieves the specified collection. Once the results are received, /// the archiveChatReceived() will be emitted. /// /// \param jid The JID of the collection /// \param start The start time of the collection. /// \param rsm Optional Result Set Management query /// void QXmppArchiveManager::retrieveCollection(const QString &jid, const QDateTime &start, const QXmppResultSetQuery &rsm) { QXmppArchiveRetrieveIq packet; packet.setResultSetQuery(rsm); packet.setStart(start); packet.setWith(jid); client()->sendPacket(packet); } /// \overload /// Retrieves the specified collection. Once the results are received, /// the archiveChatReceived() will be emitted. /// /// \param jid The JID of the collection /// \param start The start time of the collection. /// \param max Maximum number of messages to retrieve. /// void QXmppArchiveManager::retrieveCollection(const QString &jid, const QDateTime &start, int max) { QXmppResultSetQuery rsm; rsm.setMax(max); retrieveCollection(jid, start, rsm); } #if 0 void QXmppArchiveManager::getPreferences() { QXmppArchivePrefIq packet; client()->sendPacket(packet); } #endif qxmpp-1.4.0/src/client/QXmppArchiveManager.h000066400000000000000000000052471402370562100207510ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPARCHIVEMANAGER_H #define QXMPPARCHIVEMANAGER_H #include "QXmppArchiveIq.h" #include "QXmppClientExtension.h" #include "QXmppResultSet.h" #include /// \brief The QXmppArchiveManager class makes it possible to access message /// archives as defined by \xep{0136}: Message Archiving. /// /// To make use of this manager, you need to instantiate it and load it into /// the QXmppClient instance as follows: /// /// \code /// QXmppArchiveManager *manager = new QXmppArchiveManager; /// client->addExtension(manager); /// \endcode /// /// \note Few servers support message archiving. Check if the server in use supports /// this XEP. /// /// \ingroup Managers class QXMPP_EXPORT QXmppArchiveManager : public QXmppClientExtension { Q_OBJECT public: void listCollections(const QString &jid, const QDateTime &start = QDateTime(), const QDateTime &end = QDateTime(), const QXmppResultSetQuery &rsm = QXmppResultSetQuery()); void listCollections(const QString &jid, const QDateTime &start, const QDateTime &end, int max); void removeCollections(const QString &jid, const QDateTime &start = QDateTime(), const QDateTime &end = QDateTime()); void retrieveCollection(const QString &jid, const QDateTime &start, const QXmppResultSetQuery &rsm = QXmppResultSetQuery()); void retrieveCollection(const QString &jid, const QDateTime &start, int max); /// \cond QStringList discoveryFeatures() const override; bool handleStanza(const QDomElement &element) override; /// \endcond Q_SIGNALS: /// This signal is emitted when archive list is received /// after calling listCollections() void archiveListReceived(const QList &, const QXmppResultSetReply &rsm = QXmppResultSetReply()); /// This signal is emitted when archive chat is received /// after calling retrieveCollection() void archiveChatReceived(const QXmppArchiveChat &, const QXmppResultSetReply &rsm = QXmppResultSetReply()); }; #endif qxmpp-1.4.0/src/client/QXmppAttentionManager.cpp000066400000000000000000000202631402370562100216630ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Linus Jahn * * 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 "QXmppAttentionManager.h" // Qt #include // QXmpp #include "QXmppClient.h" #include "QXmppConstants_p.h" #include "QXmppMessage.h" #include "QXmppRosterManager.h" #include "QXmppUtils.h" /// /// \class QXmppAttentionManager /// /// \brief The QXmppAttentionManager class manages attention requests as defined /// by \xep{0224}: Attention. /// /// The manager also does some checks, including rate limiting and checking /// whether the senders are trusted (aka. in the roster). /// /// Rate limited messages are not emitted on the normal attentionRequested() /// signal and are sent on the attentionRequestRateLimited() signal instead. /// /// To use this manager you still need to instantiate it and register it with /// the QXmppClient: /// /// \code /// auto *attentionManager = new QXmppAttentionManager(); /// client->addExtension(attentionManager); /// \endcode /// /// \since QXmpp 1.4 /// /// /// \fn QXmppAttentionManager::attentionRequested /// /// This signal is emitted when an attention request was received and it passed /// the rate limiter. /// /// \param message The message with the attention request that was received. /// \param isTrusted Whether the sender of the message exists in the user's /// roster. /// /// /// \fn QXmppAttentionManager::attentionRequestRateLimited /// /// This signal is emitted when an attention request did not pass the rate /// limiter. /// /// \param message The message with the attention request that did not pass the /// rate limiter. /// struct PastRequest { QString bareJid; QDateTime timestamp; }; class QXmppAttentionManagerPrivate : public QObject { public: QXmppAttentionManagerPrivate(QXmppAttentionManager *parent, quint8 allowedAttempts, QTime timeFrame); bool checkRateLimit(const QString &bareJid); void cleanUp(); quint8 allowedAttempts; QTime allowedAttemptsTimeInterval; // map of bare JIDs and time of previous requests QVector previousRequests; QTimer *cleanUpTimer; }; /// /// \brief QXmppAttentionManager::QXmppAttentionManager /// \param allowedAttempts /// \param timeFrame /// QXmppAttentionManager::QXmppAttentionManager(quint8 allowedAttempts, QTime timeFrame) : d(new QXmppAttentionManagerPrivate(this, allowedAttempts, timeFrame)) { } /// /// Destructor /// QXmppAttentionManager::~QXmppAttentionManager() { delete d; } /// /// Returns the \xep{0224}: Attention feature. /// QStringList QXmppAttentionManager::discoveryFeatures() const { return { ns_attention }; } /// /// Returns the number of allowed attempts of attentions from a bare JID in the /// set time frame. /// /// \sa setAllowedAttempts() /// \sa allowedAttemptsTimeInterval() /// \sa setAllowedAttemptsTimeInterval() /// quint8 QXmppAttentionManager::allowedAttempts() const { return d->allowedAttempts; } /// /// Sets the number of allowed attempts of attentions from a bare JID in the set /// time frame. /// /// \sa allowedAttempts() /// \sa allowedAttemptsTimeInterval() /// \sa setAllowedAttemptsTimeInterval() /// void QXmppAttentionManager::setAllowedAttempts(quint8 allowedAttempts) { d->allowedAttempts = allowedAttempts; } /// /// Returns the time interval for the allowed attempts for rate limiting. /// /// \sa setAllowedAttemptsTimeInterval() /// \sa allowedAttempts() /// \sa setAllowedAttemptsTimeInterval() /// QTime QXmppAttentionManager::allowedAttemptsTimeInterval() const { return d->allowedAttemptsTimeInterval; } /// /// Returns the time interval for the allowed attempts for rate limiting. /// /// \sa allowedAttemptsTimeInterval() /// \sa allowedAttempts() /// \sa setAllowedAttempts() /// void QXmppAttentionManager::setAllowedAttemptsTimeInterval(QTime interval) { d->allowedAttemptsTimeInterval = interval; } /// /// Sends a message of type chat with an attention request to the specified JID. /// /// \xep{0224} allows to include other elements with an attention request, but /// the QXmppAttentionManager has no method for this purpose. However, such a /// request can still be made manually. /// /// \param jid The address to which the request should be sent. /// \param message The message body to include in the attention request. /// /// \return The ID of the sent message, if sent successfully, a null string /// otherwise. In case an ID is returned, it also corresponds to the origin ID /// of the message as defined by \xep{0359}: Unique and Stable Stanza IDs. /// QString QXmppAttentionManager::requestAttention(const QString &jid, const QString &message) { QXmppMessage msg; // The XEP recommends to use type headline, but the message body might still // be of interest later, so we use type chat to allow caching. msg.setType(QXmppMessage::Chat); msg.setId(QXmppUtils::generateStanzaUuid()); msg.setOriginId(msg.id()); msg.setTo(jid); msg.setBody(message); msg.setAttentionRequested(true); if (client()->sendPacket(msg)) return msg.id(); return {}; } void QXmppAttentionManager::setClient(QXmppClient *client) { QXmppClientExtension::setClient(client); connect(client, &QXmppClient::messageReceived, this, &QXmppAttentionManager::handleMessageReceived); } /// /// Empty reimplementation /// bool QXmppAttentionManager::handleStanza(const QDomElement &) { return false; } void QXmppAttentionManager::handleMessageReceived(const QXmppMessage &message) { if (!message.isAttentionRequested() || !message.stamp().isNull()) return; const QString bareJid = QXmppUtils::jidToBareJid(message.from()); // ignore messages from our own bare JID (e.g. carbon or IM-NG message) if (bareJid == client()->configuration().jidBare()) return; // check rate limit if (!d->checkRateLimit(bareJid)) { emit attentionRequestRateLimited(message); return; } bool isTrusted = false; if (auto *rosterManager = client()->findExtension()) { isTrusted = rosterManager->getRosterBareJids().contains(bareJid); } emit attentionRequested(message, isTrusted); } QXmppAttentionManagerPrivate::QXmppAttentionManagerPrivate(QXmppAttentionManager *parent, quint8 allowedAttempts, QTime timeFrame) : allowedAttempts(allowedAttempts), allowedAttemptsTimeInterval(timeFrame), cleanUpTimer(new QTimer(parent)) { QObject::connect(cleanUpTimer, &QTimer::timeout, [this]() { cleanUp(); }); } /// /// Returns true if the request passes the rate limit. /// bool QXmppAttentionManagerPrivate::checkRateLimit(const QString &bareJid) { // add to request to cache previousRequests << PastRequest { bareJid, QDateTime::currentDateTimeUtc() }; // start timer to remove request again if (!cleanUpTimer->isActive()) cleanUpTimer->start(allowedAttemptsTimeInterval.msecsSinceStartOfDay()); // check whether there are too many requests int count = std::count_if(previousRequests.cbegin(), previousRequests.cend(), [=](const PastRequest &request) { return request.bareJid == bareJid; }); return count <= allowedAttempts; } /// /// Removes the first entry and reschedules the timer to remove the next. /// void QXmppAttentionManagerPrivate::cleanUp() { previousRequests.removeFirst(); if (!previousRequests.isEmpty()) { // reschedule timer for next removal int next = allowedAttemptsTimeInterval.msecsSinceStartOfDay() - previousRequests.first().timestamp.msecsTo(QDateTime::currentDateTimeUtc()); if (next < 1) cleanUp(); else cleanUpTimer->start(next); } } qxmpp-1.4.0/src/client/QXmppAttentionManager.h000066400000000000000000000035401402370562100213270ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Linus Jahn * * 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 QXMPPATTENTIONMANAGER_H #define QXMPPATTENTIONMANAGER_H #include "QXmppClientExtension.h" #include class QXmppAttentionManagerPrivate; class QXmppMessage; class QXMPP_EXPORT QXmppAttentionManager : public QXmppClientExtension { Q_OBJECT public: QXmppAttentionManager(quint8 allowedAttempts = 3, QTime timeFrame = QTime(0, 15, 0)); ~QXmppAttentionManager(); QStringList discoveryFeatures() const override; quint8 allowedAttempts() const; void setAllowedAttempts(quint8 allowedAttempts); QTime allowedAttemptsTimeInterval() const; void setAllowedAttemptsTimeInterval(QTime interval); public Q_SLOTS: QString requestAttention(const QString &jid, const QString &message = {}); Q_SIGNALS: void attentionRequested(const QXmppMessage &message, bool isTrusted); void attentionRequestRateLimited(const QXmppMessage &message); protected: void setClient(QXmppClient *client) override; bool handleStanza(const QDomElement &stanza) override; private Q_SLOTS: void handleMessageReceived(const QXmppMessage &message); private: QXmppAttentionManagerPrivate *const d; }; #endif // QXMPPATTENTIONMANAGER_H qxmpp-1.4.0/src/client/QXmppBookmarkManager.cpp000066400000000000000000000120751402370562100214650ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppBookmarkManager.h" #include "QXmppBookmarkSet.h" #include "QXmppClient.h" #include "QXmppConstants_p.h" #include "QXmppIq.h" #include "QXmppUtils.h" #include // The QXmppPrivateStorageIq class represents an XML private storage IQ // as defined by XEP-0049: Private XML Storage. // // FIXME: currently, we only handle bookmarks class QXmppPrivateStorageIq : public QXmppIq { public: QXmppBookmarkSet bookmarks() const; void setBookmarks(const QXmppBookmarkSet &bookmark); static bool isPrivateStorageIq(const QDomElement &element); protected: void parseElementFromChild(const QDomElement &element) override; void toXmlElementFromChild(QXmlStreamWriter *writer) const override; private: QXmppBookmarkSet m_bookmarks; }; QXmppBookmarkSet QXmppPrivateStorageIq::bookmarks() const { return m_bookmarks; } void QXmppPrivateStorageIq::setBookmarks(const QXmppBookmarkSet &bookmarks) { m_bookmarks = bookmarks; } bool QXmppPrivateStorageIq::isPrivateStorageIq(const QDomElement &element) { const QDomElement queryElement = element.firstChildElement("query"); return queryElement.namespaceURI() == ns_private && QXmppBookmarkSet::isBookmarkSet(queryElement.firstChildElement()); } void QXmppPrivateStorageIq::parseElementFromChild(const QDomElement &element) { const QDomElement queryElement = element.firstChildElement("query"); m_bookmarks.parse(queryElement.firstChildElement()); } void QXmppPrivateStorageIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement("query"); writer->writeDefaultNamespace(ns_private); m_bookmarks.toXml(writer); writer->writeEndElement(); } class QXmppBookmarkManagerPrivate { public: QXmppBookmarkSet bookmarks; QXmppBookmarkSet pendingBookmarks; QString pendingId; bool bookmarksReceived; }; /// Constructs a new bookmark manager. /// QXmppBookmarkManager::QXmppBookmarkManager() : d(new QXmppBookmarkManagerPrivate) { d->bookmarksReceived = false; } /// Destroys a bookmark manager. /// QXmppBookmarkManager::~QXmppBookmarkManager() { delete d; } /// Returns true if the bookmarks have been received from the server, /// false otherwise. /// bool QXmppBookmarkManager::areBookmarksReceived() const { return d->bookmarksReceived; } /// Returns the bookmarks stored on the server. /// /// Before calling this method, check that the bookmarks /// have indeed been received by calling areBookmarksReceived(). /// QXmppBookmarkSet QXmppBookmarkManager::bookmarks() const { return d->bookmarks; } /// Stores the bookmarks on the server. /// /// \param bookmarks bool QXmppBookmarkManager::setBookmarks(const QXmppBookmarkSet &bookmarks) { QXmppPrivateStorageIq iq; iq.setType(QXmppIq::Set); iq.setBookmarks(bookmarks); if (!client()->sendPacket(iq)) return false; d->pendingBookmarks = bookmarks; d->pendingId = iq.id(); return true; } /// \cond void QXmppBookmarkManager::setClient(QXmppClient *client) { QXmppClientExtension::setClient(client); connect(client, &QXmppClient::connected, this, &QXmppBookmarkManager::slotConnected); connect(client, &QXmppClient::disconnected, this, &QXmppBookmarkManager::slotDisconnected); } bool QXmppBookmarkManager::handleStanza(const QDomElement &stanza) { if (stanza.tagName() == "iq") { if (QXmppPrivateStorageIq::isPrivateStorageIq(stanza)) { QXmppPrivateStorageIq iq; iq.parse(stanza); if (iq.type() == QXmppIq::Result) { d->bookmarks = iq.bookmarks(); d->bookmarksReceived = true; emit bookmarksReceived(d->bookmarks); } return true; } else if (!d->pendingId.isEmpty() && stanza.attribute("id") == d->pendingId) { QXmppIq iq; iq.parse(stanza); if (iq.type() == QXmppIq::Result) { d->bookmarks = d->pendingBookmarks; emit bookmarksReceived(d->bookmarks); } d->pendingId = QString(); return true; } } return false; } /// \endcond void QXmppBookmarkManager::slotConnected() { QXmppPrivateStorageIq iq; iq.setType(QXmppIq::Get); client()->sendPacket(iq); } void QXmppBookmarkManager::slotDisconnected() { d->bookmarks = QXmppBookmarkSet(); d->bookmarksReceived = false; } qxmpp-1.4.0/src/client/QXmppBookmarkManager.h000066400000000000000000000033531402370562100211310ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPBOOKMARKMANAGER_H #define QXMPPBOOKMARKMANAGER_H #include "QXmppClientExtension.h" #include class QXmppBookmarkManagerPrivate; class QXmppBookmarkSet; /// \brief The QXmppBookmarkManager class allows you to store and retrieve /// bookmarks as defined by \xep{0048}: Bookmarks. /// class QXMPP_EXPORT QXmppBookmarkManager : public QXmppClientExtension { Q_OBJECT public: QXmppBookmarkManager(); ~QXmppBookmarkManager() override; bool areBookmarksReceived() const; QXmppBookmarkSet bookmarks() const; bool setBookmarks(const QXmppBookmarkSet &bookmarks); /// \cond bool handleStanza(const QDomElement &stanza) override; /// \endcond Q_SIGNALS: /// This signal is emitted when bookmarks are received. void bookmarksReceived(const QXmppBookmarkSet &bookmarks); protected: /// \cond void setClient(QXmppClient *client) override; /// \endcond private Q_SLOTS: void slotConnected(); void slotDisconnected(); private: QXmppBookmarkManagerPrivate *const d; }; #endif qxmpp-1.4.0/src/client/QXmppCall.cpp000066400000000000000000000514341402370562100173020ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppCall.h" #include "QXmppCallManager.h" #include "QXmppCallManager_p.h" #include "QXmppCallStream.h" #include "QXmppCallStream_p.h" #include "QXmppCall_p.h" #include "QXmppClient.h" #include "QXmppConstants_p.h" #include "QXmppJingleIq.h" #include "QXmppStun.h" #include "QXmppUtils.h" #include #include #include QXmppCallPrivate::QXmppCallPrivate(QXmppCall *qq) : direction(QXmppCall::IncomingDirection), manager(0), state(QXmppCall::ConnectingState), nextId(0), q(qq) { qRegisterMetaType(); filterGStreamerFormats(videoCodecs); filterGStreamerFormats(audioCodecs); pipeline = gst_pipeline_new(nullptr); if (!pipeline) { qFatal("Failed to create pipeline"); return; } rtpbin = gst_element_factory_make("rtpbin", nullptr); if (!rtpbin) { qFatal("Failed to create rtpbin"); return; } // We do not want to build up latency over time g_object_set(rtpbin, "drop-on-latency", true, "async-handling", true, "latency", 25, nullptr); if (!gst_bin_add(GST_BIN(pipeline), rtpbin)) { qFatal("Could not add rtpbin to the pipeline"); } g_signal_connect_swapped(rtpbin, "pad-added", G_CALLBACK(+[](QXmppCallPrivate *p, GstPad *pad) { p->padAdded(pad); }), this); g_signal_connect_swapped(rtpbin, "request-pt-map", G_CALLBACK(+[](QXmppCallPrivate *p, uint sessionId, uint pt) { p->ptMap(sessionId, pt); }), this); g_signal_connect_swapped(rtpbin, "on-ssrc-active", G_CALLBACK(+[](QXmppCallPrivate *p, uint sessionId, uint ssrc) { p->ssrcActive(sessionId, ssrc); }), this); if (gst_element_set_state(pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { qFatal("Unable to set the pipeline to the playing state"); return; } } QXmppCallPrivate::~QXmppCallPrivate() { if (gst_element_set_state(pipeline, GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE) { qFatal("Unable to set the pipeline to the null state"); } for (auto stream : streams) { delete stream; } gst_object_unref(pipeline); } void QXmppCallPrivate::ssrcActive(uint sessionId, uint ssrc) { Q_UNUSED(ssrc) GstElement *rtpSession; g_signal_emit_by_name(rtpbin, "get-session", static_cast(sessionId), &rtpSession); // TODO: implement bitrate controller } void QXmppCallPrivate::padAdded(GstPad *pad) { auto nameParts = QString(gst_pad_get_name(pad)).split("_"); if (nameParts.size() < 4) { return; } if (nameParts[0] == QLatin1String("send") && nameParts[1] == QLatin1String("rtp") && nameParts[2] == QLatin1String("src")) { if (nameParts.size() != 4) { return; } int sessionId = nameParts[3].toInt(); auto stream = findStreamById(sessionId); stream->d->addRtpSender(pad); } else if (nameParts[0] == QLatin1String("recv") || nameParts[1] == QLatin1String("rtp") || nameParts[2] == QLatin1String("src")) { if (nameParts.size() != 6) { return; } int sessionId = nameParts[3].toInt(); int pt = nameParts[5].toInt(); auto stream = findStreamById(sessionId); if (stream->media() == VIDEO_MEDIA) { for (auto &codec : videoCodecs) { if (codec.pt == pt) { stream->d->addDecoder(pad, codec); return; } } } else if (stream->media() == AUDIO_MEDIA) { for (auto &codec : audioCodecs) { if (codec.pt == pt) { stream->d->addDecoder(pad, codec); return; } } } } } GstCaps *QXmppCallPrivate::ptMap(uint sessionId, uint pt) { auto stream = findStreamById(sessionId); for (auto &payloadType : stream->d->payloadTypes) { if (payloadType.id() == pt) { return gst_caps_new_simple("application/x-rtp", "media", G_TYPE_STRING, stream->media().toLatin1().data(), "clock-rate", G_TYPE_INT, payloadType.clockrate(), "encoding-name", G_TYPE_STRING, payloadType.name().toLatin1().data(), nullptr); } } q->warning(QString("Remote party %1 transmits wrong %2 payload for call %3").arg(jid, stream->media(), sid)); return nullptr; } bool QXmppCallPrivate::isFormatSupported(const QString &codecName) const { GstElementFactory *factory; factory = gst_element_factory_find(codecName.toLatin1().data()); if (!factory) { return false; } g_object_unref(factory); return true; } void QXmppCallPrivate::filterGStreamerFormats(QList &formats) { auto it = formats.begin(); while (it != formats.end()) { bool supported = isFormatSupported(it->gstPay) && isFormatSupported(it->gstDepay) && isFormatSupported(it->gstEnc) && isFormatSupported(it->gstDec); if (!supported) { it = formats.erase(it); } else { ++it; } } } QXmppCallStream *QXmppCallPrivate::findStreamByMedia(const QString &media) { for (auto stream : streams) { if (stream->media() == media) { return stream; } } return nullptr; } QXmppCallStream *QXmppCallPrivate::findStreamByName(const QString &name) { for (auto stream : streams) { if (stream->name() == name) { return stream; } } return nullptr; } QXmppCallStream *QXmppCallPrivate::findStreamById(const int id) { for (auto stream : streams) { if (stream->id() == id) { return stream; } } return nullptr; } void QXmppCallPrivate::handleAck(const QXmppIq &ack) { const QString id = ack.id(); for (int i = 0; i < requests.size(); ++i) { if (id == requests[i].id()) { // process acknowledgement const QXmppJingleIq request = requests.takeAt(i); q->debug(QString("Received ACK for packet %1").arg(id)); // handle termination if (request.action() == QXmppJingleIq::SessionTerminate) q->terminated(); return; } } } bool QXmppCallPrivate::handleDescription(QXmppCallStream *stream, const QXmppJingleIq::Content &content) { stream->d->payloadTypes = content.payloadTypes(); auto it = stream->d->payloadTypes.begin(); bool foundCandidate = false; while (it != stream->d->payloadTypes.end()) { bool dynamic = it->id() >= 96; bool supported = false; auto codecs = stream->media() == AUDIO_MEDIA ? audioCodecs : videoCodecs; for (auto &codec : codecs) { if (dynamic) { if (codec.name == it->name() && codec.clockrate == it->clockrate() && codec.channels == it->channels()) { if (!foundCandidate) { stream->d->addEncoder(codec); foundCandidate = true; } supported = true; /* Adopt id from other side. */ codec.pt = it->id(); } } else { if (codec.pt == it->id() && codec.clockrate == it->clockrate() && codec.channels == it->channels()) { if (!foundCandidate) { stream->d->addEncoder(codec); foundCandidate = true; } supported = true; /* Keep our name just to be sure */ codec.name = it->name(); } } } if (!supported) { it = stream->d->payloadTypes.erase(it); } else { ++it; } } if (stream->d->payloadTypes.empty()) { q->warning(QString("Remote party %1 did not provide any known %2 payloads for call %3").arg(jid, stream->media(), sid)); return false; } return true; } bool QXmppCallPrivate::handleTransport(QXmppCallStream *stream, const QXmppJingleIq::Content &content) { stream->d->connection->setRemoteUser(content.transportUser()); stream->d->connection->setRemotePassword(content.transportPassword()); for (const QXmppJingleCandidate &candidate : content.transportCandidates()) { stream->d->connection->addRemoteCandidate(candidate); } // perform ICE negotiation if (!content.transportCandidates().isEmpty()) { stream->d->connection->connectToHost(); } return true; } void QXmppCallPrivate::handleRequest(const QXmppJingleIq &iq) { const QXmppJingleIq::Content content = iq.contents().isEmpty() ? QXmppJingleIq::Content() : iq.contents().first(); if (iq.action() == QXmppJingleIq::SessionAccept) { if (direction == QXmppCall::IncomingDirection) { q->warning("Ignoring Session-Accept for an incoming call"); return; } // send ack sendAck(iq); // check content description and transport QXmppCallStream *stream = findStreamByName(content.name()); if (!stream || !handleDescription(stream, content) || !handleTransport(stream, content)) { // terminate call terminate(QXmppJingleIq::Reason::FailedApplication); return; } // check for call establishment setState(QXmppCall::ActiveState); } else if (iq.action() == QXmppJingleIq::SessionInfo) { // notify user QTimer::singleShot(0, q, SIGNAL(ringing())); } else if (iq.action() == QXmppJingleIq::SessionTerminate) { // send ack sendAck(iq); // terminate q->info(QString("Remote party %1 terminated call %2").arg(iq.from(), iq.sid())); q->terminated(); } else if (iq.action() == QXmppJingleIq::ContentAccept) { // send ack sendAck(iq); // check content description and transport QXmppCallStream *stream = findStreamByName(content.name()); if (!stream || !handleDescription(stream, content) || !handleTransport(stream, content)) { // FIXME: what action? return; } } else if (iq.action() == QXmppJingleIq::ContentAdd) { // send ack sendAck(iq); // check media stream does not exist yet QXmppCallStream *stream = findStreamByName(content.name()); if (stream) return; // create media stream stream = createStream(content.descriptionMedia(), content.creator(), content.name()); if (!stream) return; streams << stream; // check content description if (!handleDescription(stream, content) || !handleTransport(stream, content)) { QXmppJingleIq iq; iq.setTo(q->jid()); iq.setType(QXmppIq::Set); iq.setAction(QXmppJingleIq::ContentReject); iq.setSid(q->sid()); iq.reason().setType(QXmppJingleIq::Reason::FailedApplication); sendRequest(iq); streams.removeAll(stream); delete stream; return; } // accept content QXmppJingleIq iq; iq.setTo(q->jid()); iq.setType(QXmppIq::Set); iq.setAction(QXmppJingleIq::ContentAccept); iq.setSid(q->sid()); iq.addContent(localContent(stream)); sendRequest(iq); } else if (iq.action() == QXmppJingleIq::TransportInfo) { // send ack sendAck(iq); // check content transport QXmppCallStream *stream = findStreamByName(content.name()); if (!stream || !handleTransport(stream, content)) { // FIXME: what action? return; } } } QXmppCallStream *QXmppCallPrivate::createStream(const QString &media, const QString &creator, const QString &name) { bool check; Q_UNUSED(check); Q_ASSERT(manager); if (media != AUDIO_MEDIA && media != VIDEO_MEDIA) { q->warning(QString("Unsupported media type %1").arg(media)); return nullptr; } if (!isFormatSupported("rtpbin")) { q->warning("The rtpbin GStreamer plugin is missing. Calls are not possible."); return nullptr; } QXmppCallStream *stream = new QXmppCallStream(pipeline, rtpbin, media, creator, name, ++nextId); // Fill local payload payload types auto &codecs = media == AUDIO_MEDIA ? audioCodecs : videoCodecs; for (auto &codec : codecs) { QXmppJinglePayloadType payloadType; payloadType.setId(codec.pt); payloadType.setName(codec.name); payloadType.setChannels(codec.channels); payloadType.setClockrate(codec.clockrate); stream->d->payloadTypes.append(payloadType); } // ICE connection stream->d->connection->setIceControlling(direction == QXmppCall::OutgoingDirection); stream->d->connection->setStunServers(manager->d->stunServers); stream->d->connection->setTurnServer(manager->d->turnHost, manager->d->turnPort); stream->d->connection->setTurnUser(manager->d->turnUser); stream->d->connection->setTurnPassword(manager->d->turnPassword); stream->d->connection->bind(QXmppIceComponent::discoverAddresses()); // connect signals check = QObject::connect(stream->d->connection, SIGNAL(localCandidatesChanged()), q, SLOT(localCandidatesChanged())); Q_ASSERT(check); check = QObject::connect(stream->d->connection, SIGNAL(disconnected()), q, SLOT(hangup())); Q_ASSERT(check); Q_EMIT q->streamCreated(stream); return stream; } QXmppJingleIq::Content QXmppCallPrivate::localContent(QXmppCallStream *stream) const { QXmppJingleIq::Content content; content.setCreator(stream->creator()); content.setName(stream->name()); content.setSenders("both"); // description content.setDescriptionMedia(stream->media()); content.setDescriptionSsrc(stream->d->localSsrc); content.setPayloadTypes(stream->d->payloadTypes); // transport content.setTransportUser(stream->d->connection->localUser()); content.setTransportPassword(stream->d->connection->localPassword()); content.setTransportCandidates(stream->d->connection->localCandidates()); return content; } /// Sends an acknowledgement for a Jingle IQ. /// bool QXmppCallPrivate::sendAck(const QXmppJingleIq &iq) { QXmppIq ack; ack.setId(iq.id()); ack.setTo(iq.from()); ack.setType(QXmppIq::Result); return manager->client()->sendPacket(ack); } bool QXmppCallPrivate::sendInvite() { // create audio stream QXmppCallStream *stream = findStreamByMedia(AUDIO_MEDIA); Q_ASSERT(stream); QXmppJingleIq iq; iq.setTo(jid); iq.setType(QXmppIq::Set); iq.setAction(QXmppJingleIq::SessionInitiate); iq.setInitiator(ownJid); iq.setSid(sid); iq.addContent(localContent(stream)); return sendRequest(iq); } /// Sends a Jingle IQ and adds it to outstanding requests. /// bool QXmppCallPrivate::sendRequest(const QXmppJingleIq &iq) { requests << iq; return manager->client()->sendPacket(iq); } void QXmppCallPrivate::setState(QXmppCall::State newState) { if (state != newState) { state = newState; Q_EMIT q->stateChanged(state); if (state == QXmppCall::ActiveState) Q_EMIT q->connected(); else if (state == QXmppCall::FinishedState) Q_EMIT q->finished(); } } /// Request graceful call termination void QXmppCallPrivate::terminate(QXmppJingleIq::Reason::Type reasonType) { if (state == QXmppCall::DisconnectingState || state == QXmppCall::FinishedState) return; // hangup call QXmppJingleIq iq; iq.setTo(jid); iq.setType(QXmppIq::Set); iq.setAction(QXmppJingleIq::SessionTerminate); iq.setSid(sid); iq.reason().setType(reasonType); sendRequest(iq); setState(QXmppCall::DisconnectingState); // schedule forceful termination in 5s QTimer::singleShot(5000, q, SLOT(terminated())); } QXmppCall::QXmppCall(const QString &jid, QXmppCall::Direction direction, QXmppCallManager *parent) : QXmppLoggable(parent) { d = new QXmppCallPrivate(this); d->direction = direction; d->jid = jid; d->ownJid = parent->client()->configuration().jid(); d->manager = parent; } QXmppCall::~QXmppCall() { delete d; } /// Call this method if you wish to accept an incoming call. /// void QXmppCall::accept() { if (d->direction == IncomingDirection && d->state == ConnectingState) { Q_ASSERT(d->streams.size() == 1); QXmppCallStream *stream = d->streams.first(); // accept incoming call QXmppJingleIq iq; iq.setTo(d->jid); iq.setType(QXmppIq::Set); iq.setAction(QXmppJingleIq::SessionAccept); iq.setResponder(d->ownJid); iq.setSid(d->sid); iq.addContent(d->localContent(stream)); d->sendRequest(iq); // notify user d->manager->callStarted(this); // check for call establishment d->setState(QXmppCall::ActiveState); } } /// Returns the GStreamer pipeline. /// /// \since QXmpp 1.3 GstElement *QXmppCall::pipeline() const { return d->pipeline; } /// Returns the RTP stream for the audio data. /// /// \since QXmpp 1.3 QXmppCallStream *QXmppCall::audioStream() const { return d->findStreamByMedia(AUDIO_MEDIA); } /// Returns the RTP stream for the video data. /// /// \since QXmpp 1.3 QXmppCallStream *QXmppCall::videoStream() const { return d->findStreamByMedia(VIDEO_MEDIA); } void QXmppCall::terminated() { // close streams for (auto stream : d->streams) { stream->d->connection->close(); } // update state d->setState(QXmppCall::FinishedState); } /// Returns the call's direction. /// QXmppCall::Direction QXmppCall::direction() const { return d->direction; } /// Hangs up the call. /// void QXmppCall::hangup() { d->terminate(QXmppJingleIq::Reason::None); } /// Sends a transport-info to inform the remote party of new local candidates. /// void QXmppCall::localCandidatesChanged() { // find the stream QXmppIceConnection *conn = qobject_cast(sender()); QXmppCallStream *stream = 0; for (auto ptr : d->streams) { if (ptr->d->connection == conn) { stream = ptr; break; } } if (!stream) return; QXmppJingleIq iq; iq.setTo(d->jid); iq.setType(QXmppIq::Set); iq.setAction(QXmppJingleIq::TransportInfo); iq.setSid(d->sid); iq.addContent(d->localContent(stream)); d->sendRequest(iq); } /// Returns the remote party's JID. /// QString QXmppCall::jid() const { return d->jid; } /// Returns the call's session identifier. /// QString QXmppCall::sid() const { return d->sid; } /// Returns the call's state. /// /// \sa stateChanged() QXmppCall::State QXmppCall::state() const { return d->state; } /// Starts sending video to the remote party. void QXmppCall::addVideo() { if (d->state != QXmppCall::ActiveState) { warning("Cannot add video, call is not active"); return; } QXmppCallStream *stream = d->findStreamByMedia(VIDEO_MEDIA); if (stream) { return; } // create video stream QLatin1String creator = (d->direction == QXmppCall::OutgoingDirection) ? QLatin1String("initiator") : QLatin1String("responder"); stream = d->createStream(VIDEO_MEDIA, creator, QLatin1String("webcam")); d->streams << stream; // build request QXmppJingleIq iq; iq.setTo(d->jid); iq.setType(QXmppIq::Set); iq.setAction(QXmppJingleIq::ContentAdd); iq.setSid(d->sid); iq.addContent(d->localContent(stream)); d->sendRequest(iq); } qxmpp-1.4.0/src/client/QXmppCall.h000066400000000000000000000066251402370562100167510ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPCALL_H #define QXMPPCALL_H #include "QXmppCallStream.h" #include "QXmppClientExtension.h" #include "QXmppLogger.h" #include #include class QHostAddress; class QXmppCallPrivate; class QXmppCallManager; class QXmppCallManagerPrivate; /// \brief The QXmppCall class represents a Voice-Over-IP call to a remote party. /// /// \note THIS API IS NOT FINALIZED YET class QXMPP_EXPORT QXmppCall : public QXmppLoggable { Q_OBJECT Q_PROPERTY(Direction direction READ direction CONSTANT) Q_PROPERTY(QString jid READ jid CONSTANT) Q_PROPERTY(State state READ state NOTIFY stateChanged) public: /// This enum is used to describe the direction of a call. enum Direction { IncomingDirection, ///< The call is incoming. OutgoingDirection ///< The call is outgoing. }; Q_ENUM(Direction) /// This enum is used to describe the state of a call. enum State { ConnectingState = 0, ///< The call is being connected. ActiveState = 1, ///< The call is active. DisconnectingState = 2, ///< The call is being disconnected. FinishedState = 3 ///< The call is finished. }; Q_ENUM(State) ~QXmppCall(); QXmppCall::Direction direction() const; QString jid() const; QString sid() const; QXmppCall::State state() const; GstElement *pipeline() const; QXmppCallStream *audioStream() const; QXmppCallStream *videoStream() const; signals: /// \brief This signal is emitted when a call is connected. /// /// Once this signal is emitted, you can connect a QAudioOutput and /// QAudioInput to the call. You can determine the appropriate clockrate /// and the number of channels by calling payloadType(). void connected(); /// \brief This signal is emitted when a call is finished. /// /// Note: Do not delete the call in the slot connected to this signal, /// instead use deleteLater(). void finished(); /// \brief This signal is emitted when the remote party is ringing. void ringing(); /// \brief This signal is emitted when the call state changes. void stateChanged(QXmppCall::State state); /// \brief This signal is emitted when a stream is created. void streamCreated(QXmppCallStream *stream); public slots: void accept(); void hangup(); void addVideo(); private slots: void localCandidatesChanged(); void terminated(); private: QXmppCall(const QString &jid, QXmppCall::Direction direction, QXmppCallManager *parent); QXmppCallPrivate *d; friend class QXmppCallManager; friend class QXmppCallManagerPrivate; friend class QXmppCallPrivate; }; Q_DECLARE_METATYPE(QXmppCall::State) #endif qxmpp-1.4.0/src/client/QXmppCallManager.cpp000066400000000000000000000200721402370562100205670ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppCallManager.h" #include "QXmppCall.h" #include "QXmppCallManager_p.h" #include "QXmppCall_p.h" #include "QXmppClient.h" #include "QXmppConstants_p.h" #include "QXmppJingleIq.h" #include "QXmppStun.h" #include "QXmppUtils.h" #include #include #include QXmppCallManagerPrivate::QXmppCallManagerPrivate(QXmppCallManager *qq) : turnPort(0), q(qq) { // Initialize GStreamer gst_init(nullptr, nullptr); } QXmppCall *QXmppCallManagerPrivate::findCall(const QString &sid) const { for (auto *call : calls) if (call->sid() == sid) return call; return nullptr; } QXmppCall *QXmppCallManagerPrivate::findCall(const QString &sid, QXmppCall::Direction direction) const { for (auto *call : calls) if (call->sid() == sid && call->direction() == direction) return call; return nullptr; } /// Constructs a QXmppCallManager object to handle incoming and outgoing /// Voice-Over-IP calls. /// QXmppCallManager::QXmppCallManager() { d = new QXmppCallManagerPrivate(this); } /// Destroys the QXmppCallManager object. QXmppCallManager::~QXmppCallManager() { delete d; } /// \cond QStringList QXmppCallManager::discoveryFeatures() const { return QStringList() << ns_jingle // XEP-0166 : Jingle << ns_jingle_rtp // XEP-0167 : Jingle RTP Sessions << ns_jingle_rtp_audio << ns_jingle_rtp_video << ns_jingle_ice_udp; // XEP-0176 : Jingle ICE-UDP Transport Method } bool QXmppCallManager::handleStanza(const QDomElement &element) { if (element.tagName() == "iq") { // XEP-0166: Jingle if (QXmppJingleIq::isJingleIq(element)) { QXmppJingleIq jingleIq; jingleIq.parse(element); _q_jingleIqReceived(jingleIq); return true; } } return false; } void QXmppCallManager::setClient(QXmppClient *client) { QXmppClientExtension::setClient(client); connect(client, &QXmppClient::disconnected, this, &QXmppCallManager::_q_disconnected); connect(client, &QXmppClient::iqReceived, this, &QXmppCallManager::_q_iqReceived); connect(client, &QXmppClient::presenceReceived, this, &QXmppCallManager::_q_presenceReceived); } /// \endcond /// Initiates a new outgoing call to the specified recipient. /// /// \param jid QXmppCall *QXmppCallManager::call(const QString &jid) { if (jid.isEmpty()) { warning("Refusing to call an empty jid"); return nullptr; } if (jid == client()->configuration().jid()) { warning("Refusing to call self"); return nullptr; } QXmppCall *call = new QXmppCall(jid, QXmppCall::OutgoingDirection, this); QXmppCallStream *stream = call->d->createStream("audio", "initiator", "microphone"); call->d->streams << stream; call->d->sid = QXmppUtils::generateStanzaHash(); // register call d->calls << call; connect(call, &QObject::destroyed, this, &QXmppCallManager::_q_callDestroyed); emit callStarted(call); call->d->sendInvite(); return call; } /// Sets multiple STUN servers to use to determine server-reflexive addresses /// and ports. /// /// \note This may only be called prior to calling bind(). /// /// \param servers List of the STUN servers. /// /// \since QXmpp 1.3 void QXmppCallManager::setStunServers(const QList> &servers) { d->stunServers = servers; } /// Sets a single STUN server to use to determine server-reflexive addresses /// and ports. /// /// \note This may only be called prior to calling bind(). /// /// \param host The address of the STUN server. /// \param port The port of the STUN server. void QXmppCallManager::setStunServer(const QHostAddress &host, quint16 port) { d->stunServers.clear(); d->stunServers.push_back(QPair(host, port)); } /// Sets the TURN server to use to relay packets in double-NAT configurations. /// /// \param host The address of the TURN server. /// \param port The port of the TURN server. void QXmppCallManager::setTurnServer(const QHostAddress &host, quint16 port) { d->turnHost = host; d->turnPort = port; } /// Sets the \a user used for authentication with the TURN server. /// /// \param user void QXmppCallManager::setTurnUser(const QString &user) { d->turnUser = user; } /// Sets the \a password used for authentication with the TURN server. /// /// \param password void QXmppCallManager::setTurnPassword(const QString &password) { d->turnPassword = password; } /// Handles call destruction. void QXmppCallManager::_q_callDestroyed(QObject *object) { d->calls.removeAll(static_cast(object)); } /// Handles disconnection from server. void QXmppCallManager::_q_disconnected() { for (auto *call : d->calls) call->d->terminate(QXmppJingleIq::Reason::Gone); } /// Handles acknowledgements. /// void QXmppCallManager::_q_iqReceived(const QXmppIq &ack) { if (ack.type() != QXmppIq::Result) return; // find request for (auto *call : d->calls) call->d->handleAck(ack); } /// Handles a Jingle IQ. /// void QXmppCallManager::_q_jingleIqReceived(const QXmppJingleIq &iq) { if (iq.type() != QXmppIq::Set) return; if (iq.action() == QXmppJingleIq::SessionInitiate) { // build call QXmppCall *call = new QXmppCall(iq.from(), QXmppCall::IncomingDirection, this); call->d->sid = iq.sid(); const QXmppJingleIq::Content content = iq.contents().isEmpty() ? QXmppJingleIq::Content() : iq.contents().first(); QXmppCallStream *stream = call->d->createStream(content.descriptionMedia(), content.creator(), content.name()); if (!stream) return; call->d->streams << stream; // send ack call->d->sendAck(iq); // check content description and transport if (!call->d->handleDescription(stream, content) || !call->d->handleTransport(stream, content)) { // terminate call call->d->terminate(QXmppJingleIq::Reason::FailedApplication); call->terminated(); delete call; return; } // register call d->calls << call; connect(call, &QObject::destroyed, this, &QXmppCallManager::_q_callDestroyed); // send ringing indication QXmppJingleIq ringing; ringing.setTo(call->jid()); ringing.setType(QXmppIq::Set); ringing.setAction(QXmppJingleIq::SessionInfo); ringing.setSid(call->sid()); ringing.setRinging(true); call->d->sendRequest(ringing); // notify user emit callReceived(call); return; } else { // for all other requests, require a valid call QXmppCall *call = d->findCall(iq.sid()); if (!call) { warning(QString("Remote party %1 sent a request for an unknown call %2").arg(iq.from(), iq.sid())); return; } call->d->handleRequest(iq); } } /// Handles a presence. void QXmppCallManager::_q_presenceReceived(const QXmppPresence &presence) { if (presence.type() != QXmppPresence::Unavailable) return; for (auto *call : d->calls) { if (presence.from() == call->jid()) { // the remote party has gone away, terminate call call->d->terminate(QXmppJingleIq::Reason::Gone); } } } qxmpp-1.4.0/src/client/QXmppCallManager.h000066400000000000000000000064061402370562100202410ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPCALLMANAGER_H #define QXMPPCALLMANAGER_H #include "QXmppCall.h" #include "QXmppClientExtension.h" #include "QXmppLogger.h" #include #include #include class QHostAddress; class QXmppCallManagerPrivate; class QXmppIq; class QXmppJingleCandidate; class QXmppJingleIq; class QXmppJinglePayloadType; class QXmppPresence; /// \brief The QXmppCallManager class provides support for making and /// receiving voice calls. /// /// Session initiation is performed as described by \xep{0166}: Jingle, /// \xep{0167}: Jingle RTP Sessions and \xep{0176}: Jingle ICE-UDP Transport /// Method. /// /// The data stream is connected using Interactive Connectivity Establishment /// (RFC 5245) and data is transferred using Real Time Protocol (RFC 3550) /// packets. /// /// To make use of this manager, you need to instantiate it and load it into /// the QXmppClient instance as follows: /// /// \code /// QXmppCallManager *manager = new QXmppCallManager; /// client->addExtension(manager); /// \endcode /// /// \ingroup Managers class QXMPP_EXPORT QXmppCallManager : public QXmppClientExtension { Q_OBJECT public: QXmppCallManager(); ~QXmppCallManager() override; void setStunServers(const QList> &servers); 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 override; bool handleStanza(const QDomElement &element) override; /// \endcond Q_SIGNALS: /// This signal is emitted when a new incoming call is received. /// /// To accept the call, invoke the call's QXmppCall::accept() method. /// To refuse the call, invoke the call's QXmppCall::hangup() method. void callReceived(QXmppCall *call); /// This signal is emitted when a call (incoming or outgoing) is started. void callStarted(QXmppCall *call); public Q_SLOTS: QXmppCall *call(const QString &jid); protected: /// \cond void setClient(QXmppClient *client) override; /// \endcond private Q_SLOTS: void _q_callDestroyed(QObject *object); void _q_disconnected(); void _q_iqReceived(const QXmppIq &iq); void _q_jingleIqReceived(const QXmppJingleIq &iq); void _q_presenceReceived(const QXmppPresence &presence); private: QXmppCallManagerPrivate *d; friend class QXmppCall; friend class QXmppCallPrivate; friend class QXmppCallManagerPrivate; }; #endif qxmpp-1.4.0/src/client/QXmppCallManager_p.h000066400000000000000000000027501402370562100205560ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPCALLMANAGER_P_H #define QXMPPCALLMANAGER_P_H #include "QXmppCall.h" #include #include class QXmppCallManager; // W A R N I N G // ------------- // // This file is not part of the QXmpp API. // This header file may change from version to version without notice, // or even be removed. // // We mean it. // class QXmppCallManagerPrivate { public: QXmppCallManagerPrivate(QXmppCallManager *qq); QXmppCall *findCall(const QString &sid) const; QXmppCall *findCall(const QString &sid, QXmppCall::Direction direction) const; QList calls; QList> stunServers; QHostAddress turnHost; quint16 turnPort; QString turnUser; QString turnPassword; private: QXmppCallManager *q; }; #endif qxmpp-1.4.0/src/client/QXmppCallStream.cpp000066400000000000000000000307631402370562100204600ustar00rootroot00000000000000/* * Copyright (C) 2020 The QXmpp developers * * Author: * Niels Ole Salscheider * * 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 "QXmppCallStream.h" #include "QXmppCallStream_p.h" #include "QXmppCall_p.h" #include "QXmppStun.h" #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) #include #endif #include #include QXmppCallStreamPrivate::QXmppCallStreamPrivate(QXmppCallStream *parent, GstElement *pipeline_, GstElement *rtpbin_, QString media_, QString creator_, QString name_, int id_) : QObject(parent), q(parent), pipeline(pipeline_), rtpbin(rtpbin_), sendPad(nullptr), receivePad(nullptr), encoderBin(nullptr), decoderBin(nullptr), sendPadCB(nullptr), receivePadCB(nullptr), media(media_), creator(creator_), name(name_), id(id_) { #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) localSsrc = QRandomGenerator::global()->generate(); #else localSsrc = qrand(); #endif iceReceiveBin = gst_bin_new(QStringLiteral("receive_%1").arg(id).toLatin1().data()); iceSendBin = gst_bin_new(QStringLiteral("send_%1").arg(id).toLatin1().data()); gst_bin_add_many(GST_BIN(pipeline), iceReceiveBin, iceSendBin, nullptr); internalRtpPad = gst_ghost_pad_new_no_target(nullptr, GST_PAD_SINK); internalRtcpPad = gst_ghost_pad_new_no_target(nullptr, GST_PAD_SINK); if (!gst_element_add_pad(iceSendBin, internalRtpPad) || !gst_element_add_pad(iceSendBin, internalRtcpPad)) { qFatal("Failed to add pads to send bin"); } connection = new QXmppIceConnection(this); connection->addComponent(RTP_COMPONENT); connection->addComponent(RTCP_COMPONENT); apprtpsink = gst_element_factory_make("appsink", nullptr); apprtcpsink = gst_element_factory_make("appsink", nullptr); if (!apprtpsink || !apprtcpsink) { qFatal("Failed to create appsinks"); } g_signal_connect_swapped(apprtpsink, "new-sample", G_CALLBACK(+[](QXmppCallStreamPrivate *p, GstElement *appsink) -> GstFlowReturn { return p->sendDatagram(appsink, RTP_COMPONENT); }), this); g_signal_connect_swapped(apprtcpsink, "new-sample", G_CALLBACK(+[](QXmppCallStreamPrivate *p, GstElement *appsink) -> GstFlowReturn { return p->sendDatagram(appsink, RTCP_COMPONENT); }), this); apprtpsrc = gst_element_factory_make("appsrc", nullptr); apprtcpsrc = gst_element_factory_make("appsrc", nullptr); if (!apprtpsrc || !apprtcpsrc) { qFatal("Failed to create appsrcs"); } // TODO check these parameters g_object_set(apprtpsink, "emit-signals", true, "async", false, "max-buffers", 1, "drop", true, nullptr); g_object_set(apprtcpsink, "emit-signals", true, "async", false, nullptr); g_object_set(apprtpsrc, "is-live", true, "max-latency", 5000000, nullptr); g_object_set(apprtcpsrc, "is-live", true, nullptr); connect(connection->component(RTP_COMPONENT), &QXmppIceComponent::datagramReceived, [&](const QByteArray &datagram) { datagramReceived(datagram, apprtpsrc); }); connect(connection->component(RTCP_COMPONENT), &QXmppIceComponent::datagramReceived, [&](const QByteArray &datagram) { datagramReceived(datagram, apprtcpsrc); }); if (!gst_bin_add(GST_BIN(iceReceiveBin), apprtpsrc) || !gst_bin_add(GST_BIN(iceReceiveBin), apprtcpsrc)) { qFatal("Failed to add appsrcs to receive bin"); } if (!gst_element_link_pads(apprtpsrc, "src", rtpbin, QStringLiteral("recv_rtp_sink_%1").arg(id).toLatin1().data()) || !gst_element_link_pads(apprtcpsrc, "src", rtpbin, QStringLiteral("recv_rtcp_sink_%1").arg(id).toLatin1().data())) { qFatal("Failed to link receive pads"); } // We need frequent RTCP reports for the bandwidth controller GstElement *rtpSession; g_signal_emit_by_name(rtpbin, "get-session", static_cast(id), &rtpSession); g_object_set(rtpSession, "rtcp-min-interval", 100000000, nullptr); gst_element_sync_state_with_parent(iceReceiveBin); gst_element_sync_state_with_parent(iceSendBin); } QXmppCallStreamPrivate::~QXmppCallStreamPrivate() { connection->close(); // Remove elements from pipeline if ((encoderBin && !gst_bin_remove(GST_BIN(pipeline), encoderBin)) || (decoderBin && !gst_bin_remove(GST_BIN(pipeline), decoderBin)) || !gst_bin_remove(GST_BIN(pipeline), iceSendBin) || !gst_bin_remove(GST_BIN(pipeline), iceReceiveBin)) { qFatal("Failed to remove bins from pipeline"); } } GstFlowReturn QXmppCallStreamPrivate::sendDatagram(GstElement *appsink, int component) { GstSample *sample; g_signal_emit_by_name(appsink, "pull-sample", &sample); if (!sample) { qFatal("Could not get sample"); return GST_FLOW_ERROR; } GstMapInfo mapInfo; GstBuffer *buffer = gst_sample_get_buffer(sample); if (!buffer) { qFatal("Could not get buffer"); return GST_FLOW_ERROR; } if (!gst_buffer_map(buffer, &mapInfo, GST_MAP_READ)) { qFatal("Could not map buffer"); return GST_FLOW_ERROR; } QByteArray datagram; datagram.resize(mapInfo.size); std::memcpy(datagram.data(), mapInfo.data, mapInfo.size); gst_buffer_unmap(buffer, &mapInfo); gst_sample_unref(sample); if (connection->component(component)->isConnected() && connection->component(component)->sendDatagram(datagram) != datagram.size()) { return GST_FLOW_ERROR; } return GST_FLOW_OK; } void QXmppCallStreamPrivate::datagramReceived(const QByteArray &datagram, GstElement *appsrc) { GstBuffer *buffer = gst_buffer_new_and_alloc(datagram.size()); GstMapInfo mapInfo; if (!gst_buffer_map(buffer, &mapInfo, GST_MAP_WRITE)) { qFatal("Could not map buffer"); return; } std::memcpy(mapInfo.data, datagram.data(), mapInfo.size); gst_buffer_unmap(buffer, &mapInfo); GstFlowReturn ret; g_signal_emit_by_name(appsrc, "push-buffer", buffer, &ret); gst_buffer_unref(buffer); } void QXmppCallStreamPrivate::addEncoder(QXmppCallPrivate::GstCodec &codec) { // Remove old encoder and payloader if they exist if (encoderBin) { if (!gst_bin_remove(GST_BIN(pipeline), encoderBin)) { qFatal("Failed to remove existing encoder bin"); } } encoderBin = gst_bin_new(QStringLiteral("encoder_%1").arg(id).toLatin1().data()); if (!gst_bin_add(GST_BIN(pipeline), encoderBin)) { qFatal("Failed to add encoder bin to wrapper"); return; } sendPad = gst_ghost_pad_new_no_target(nullptr, GST_PAD_SINK); gst_element_add_pad(encoderBin, sendPad); // Create new elements GstElement *queue = gst_element_factory_make("queue", nullptr); if (!queue) { qFatal("Failed to create queue"); return; } GstElement *pay = gst_element_factory_make(codec.gstPay.toLatin1().data(), nullptr); if (!pay) { qFatal("Failed to create payloader"); return; } g_object_set(pay, "pt", codec.pt, "ssrc", localSsrc, nullptr); GstElement *encoder = gst_element_factory_make(codec.gstEnc.toLatin1().data(), nullptr); if (!encoder) { qFatal("Failed to create encoder"); return; } for (auto &encProp : codec.encProps) { g_object_set(encoder, encProp.name.toLatin1().data(), encProp.value, nullptr); } gst_bin_add_many(GST_BIN(encoderBin), queue, encoder, pay, nullptr); if (!gst_element_link_pads(pay, "src", rtpbin, QStringLiteral("send_rtp_sink_%1").arg(id).toLatin1().data()) || !gst_element_link_many(queue, encoder, pay, nullptr)) { qFatal("Could not link all encoder pads"); return; } if (!gst_ghost_pad_set_target(GST_GHOST_PAD(sendPad), gst_element_get_static_pad(queue, "sink"))) { qFatal("Failed to set send pad"); return; } if (sendPadCB) { sendPadCB(sendPad); } gst_element_sync_state_with_parent(encoderBin); addRtcpSender(gst_element_get_request_pad(rtpbin, QStringLiteral("send_rtcp_src_%1").arg(id).toLatin1().data())); } void QXmppCallStreamPrivate::addDecoder(GstPad *pad, QXmppCallPrivate::GstCodec &codec) { // Remove old decoder and depayloader if they exist if (decoderBin) { if (!gst_bin_remove(GST_BIN(pipeline), decoderBin)) { qFatal("Failed to remove existing decoder bin"); } } decoderBin = gst_bin_new(QStringLiteral("decoder_%1").arg(id).toLatin1().data()); if (!gst_bin_add(GST_BIN(pipeline), decoderBin)) { qFatal("Failed to add decoder bin to wrapper"); return; } receivePad = gst_ghost_pad_new_no_target(nullptr, GST_PAD_SRC); internalReceivePad = gst_ghost_pad_new_no_target(nullptr, GST_PAD_SINK); gst_element_add_pad(decoderBin, receivePad); gst_element_add_pad(decoderBin, internalReceivePad); // Create new elements GstElement *depay = gst_element_factory_make(codec.gstDepay.toLatin1().data(), nullptr); if (!depay) { qFatal("Failed to create depayloader"); return; } GstElement *decoder = gst_element_factory_make(codec.gstDec.toLatin1().data(), nullptr); if (!decoder) { qFatal("Failed to create decoder"); return; } GstElement *queue = gst_element_factory_make("queue", nullptr); if (!queue) { qFatal("Failed to create queue"); return; } gst_bin_add_many(GST_BIN(decoderBin), depay, decoder, queue, nullptr); if (!gst_ghost_pad_set_target(GST_GHOST_PAD(internalReceivePad), gst_element_get_static_pad(depay, "sink")) || gst_pad_link(pad, internalReceivePad) != GST_PAD_LINK_OK || !gst_element_link_many(depay, decoder, queue, nullptr) || !gst_ghost_pad_set_target(GST_GHOST_PAD(receivePad), gst_element_get_static_pad(queue, "src"))) { qFatal("Could not link all decoder pads"); return; } gst_element_sync_state_with_parent(decoderBin); if (receivePadCB) { receivePadCB(receivePad); } } void QXmppCallStreamPrivate::addRtpSender(GstPad *pad) { if (!gst_bin_add(GST_BIN(iceSendBin), apprtpsink)) { qFatal("Failed to add rtp sink to send bin"); } gst_element_sync_state_with_parent(apprtpsink); if (!gst_ghost_pad_set_target(GST_GHOST_PAD(internalRtpPad), gst_element_get_static_pad(apprtpsink, "sink")) || gst_pad_link(pad, internalRtpPad) != GST_PAD_LINK_OK) { qFatal("Failed to link rtp pads"); } } void QXmppCallStreamPrivate::addRtcpSender(GstPad *pad) { if (!gst_bin_add(GST_BIN(iceSendBin), apprtcpsink)) { qFatal("Failed to add rtcp sink to send bin"); } gst_element_sync_state_with_parent(apprtcpsink); if (!gst_ghost_pad_set_target(GST_GHOST_PAD(internalRtcpPad), gst_element_get_static_pad(apprtcpsink, "sink")) || gst_pad_link(pad, internalRtcpPad) != GST_PAD_LINK_OK) { qFatal("Failed to link rtcp pads"); } } QXmppCallStream::QXmppCallStream(GstElement *pipeline, GstElement *rtpbin, QString media, QString creator, QString name, int id) { d = new QXmppCallStreamPrivate(this, pipeline, rtpbin, media, creator, name, id); } QString QXmppCallStream::creator() const { return d->creator; } QString QXmppCallStream::media() const { return d->media; } QString QXmppCallStream::name() const { return d->name; } int QXmppCallStream::id() const { return d->id; } void QXmppCallStream::setReceivePadCallback(std::function cb) { d->receivePadCB = cb; if (d->receivePad) { d->receivePadCB(d->receivePad); } } void QXmppCallStream::setSendPadCallback(std::function cb) { d->sendPadCB = cb; if (d->sendPad) { d->sendPadCB(d->sendPad); } } qxmpp-1.4.0/src/client/QXmppCallStream.h000066400000000000000000000032631402370562100201200ustar00rootroot00000000000000/* * Copyright (C) 2020 The QXmpp developers * * Author: * Niels Ole Salscheider * * 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 QXMPPCALLSTREAM_H #define QXMPPCALLSTREAM_H #include #include #include typedef struct _GstPad GstPad; typedef struct _GstElement GstElement; class QXmppCallStreamPrivate; class QXmppIceConnection; class QXmppCall; class QXmppCallPrivate; /// \brief The QXmppCallStream class represents an RTP stream in a VoIP call. /// /// \note THIS API IS NOT FINALIZED YET /// /// \since QXmpp 1.3 class QXMPP_EXPORT QXmppCallStream : public QObject { Q_OBJECT public: QString creator() const; QString media() const; QString name() const; int id() const; void setReceivePadCallback(std::function cb); void setSendPadCallback(std::function cb); private: QXmppCallStream(GstElement *pipeline, GstElement *rtpbin, QString media, QString creator, QString name, int id); QXmppCallStreamPrivate *d; friend class QXmppCall; friend class QXmppCallPrivate; }; #endif qxmpp-1.4.0/src/client/QXmppCallStream_p.h000066400000000000000000000051371402370562100204410ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Niels Ole Salscheider * * 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 QXMPPCALLSTREAM_P_H #define QXMPPCALLSTREAM_P_H #include "QXmppCall_p.h" #include "QXmppJingleIq.h" #include #include #include #include class QXmppIceConnection; // W A R N I N G // ------------- // // This file is not part of the QXmpp API. // This header file may change from version to version without notice, // or even be removed. // // We mean it. // static const int RTP_COMPONENT = 1; static const int RTCP_COMPONENT = 2; static const QLatin1String AUDIO_MEDIA("audio"); static const QLatin1String VIDEO_MEDIA("video"); class QXmppCallStreamPrivate : public QObject { Q_OBJECT public: QXmppCallStreamPrivate(QXmppCallStream *parent, GstElement *pipeline_, GstElement *rtpbin_, QString media_, QString creator_, QString name_, int id_); ~QXmppCallStreamPrivate(); GstFlowReturn sendDatagram(GstElement *appsink, int component); void datagramReceived(const QByteArray &datagram, GstElement *appsrc); void addEncoder(QXmppCallPrivate::GstCodec &codec); void addDecoder(GstPad *pad, QXmppCallPrivate::GstCodec &codec); void addRtpSender(GstPad *pad); void addRtcpSender(GstPad *pad); QXmppCallStream *q; quint32 localSsrc; GstElement *pipeline; GstElement *rtpbin; GstPad *sendPad; GstPad *receivePad; GstPad *internalReceivePad; GstPad *internalRtpPad; GstPad *internalRtcpPad; GstElement *encoderBin; GstElement *decoderBin; GstElement *iceReceiveBin; GstElement *iceSendBin; GstElement *apprtpsrc; GstElement *apprtcpsrc; GstElement *apprtpsink; GstElement *apprtcpsink; std::function sendPadCB; std::function receivePadCB; QXmppIceConnection *connection; QString media; QString creator; QString name; int id; QList payloadTypes; }; #endif qxmpp-1.4.0/src/client/QXmppCall_p.h000066400000000000000000000134321402370562100172620ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPCALL_P_H #define QXMPPCALL_P_H #include "QXmppCall.h" #include "QXmppJingleIq.h" #include #include // W A R N I N G // ------------- // // This file is not part of the QXmpp API. // This header file may change from version to version without notice, // or even be removed. // // We mean it. // class QXmppCallStream; class QXmppCallPrivate : public QObject { Q_OBJECT public: struct GstCodec { int pt; QString name; int channels; int clockrate; QString gstPay; QString gstDepay; QString gstEnc; QString gstDec; struct Property { QString name; int value; }; // Use e.g. gst-inspect-1.0 x264enc to find good encoder settings for live streaming QList encProps; }; QXmppCallPrivate(QXmppCall *qq); ~QXmppCallPrivate(); void ssrcActive(uint sessionId, uint ssrc); void padAdded(GstPad *pad); GstCaps *ptMap(uint sessionId, uint pt); bool isFormatSupported(const QString &codecName) const; void filterGStreamerFormats(QList &formats); QXmppCallStream *createStream(const QString &media, const QString &creator, const QString &name); QXmppCallStream *findStreamByMedia(const QString &media); QXmppCallStream *findStreamByName(const QString &name); QXmppCallStream *findStreamById(const int id); QXmppJingleIq::Content localContent(QXmppCallStream *stream) const; void handleAck(const QXmppIq &iq); bool handleDescription(QXmppCallStream *stream, const QXmppJingleIq::Content &content); void handleRequest(const QXmppJingleIq &iq); bool handleTransport(QXmppCallStream *stream, const QXmppJingleIq::Content &content); void setState(QXmppCall::State state); bool sendAck(const QXmppJingleIq &iq); bool sendInvite(); bool sendRequest(const QXmppJingleIq &iq); void terminate(QXmppJingleIq::Reason::Type reasonType); QXmppCall::Direction direction; QString jid; QString ownJid; QXmppCallManager *manager; QList requests; QString sid; QXmppCall::State state; GstElement *pipeline; GstElement *rtpbin; // Media streams QList streams; int nextId; // Supported codecs QList videoCodecs = { { .pt = 100, .name = "H264", .channels = 1, .clockrate = 90000, .gstPay = "rtph264pay", .gstDepay = "rtph264depay", .gstEnc = "x264enc", .gstDec = "avdec_h264", .encProps = { { "tune", 4 }, { "speed-preset", 3 }, {"byte-stream", true}, { "bitrate", 512 } } }, { .pt = 99, .name = "VP8", .channels = 1, .clockrate = 90000, .gstPay = "rtpvp8pay", .gstDepay = "rtpvp8depay", .gstEnc = "vp8enc", .gstDec = "vp8dec", .encProps = { { "deadline", 20000 }, { "target-bitrate", 512000 } } }, // vp9enc and x265enc seem to be very slow. Give them a lower priority for now. { .pt = 102, .name = "H265", .channels = 1, .clockrate = 90000, .gstPay = "rtph265pay", .gstDepay = "rtph265depay", .gstEnc = "x265enc", .gstDec = "avdec_h265", .encProps = { { "tune", 4 }, { "speed-preset", 3 }, { "bitrate", 512 } } }, { .pt = 101, .name = "VP9", .channels = 1, .clockrate = 90000, .gstPay = "rtpvp9pay", .gstDepay = "rtpvp9depay", .gstEnc = "vp9enc", .gstDec = "vp9dec", .encProps = { { "deadline", 20000 }, { "target-bitrate", 512000 } } } }; QList audioCodecs = { { .pt = 98, .name = "OPUS", .channels = 2, .clockrate = 48000, .gstPay = "rtpopuspay", .gstDepay = "rtpopusdepay", .gstEnc = "opusenc", .gstDec = "opusdec" }, { .pt = 98, .name = "OPUS", .channels = 1, .clockrate = 48000, .gstPay = "rtpopuspay", .gstDepay = "rtpopusdepay", .gstEnc = "opusenc", .gstDec = "opusdec" }, { .pt = 97, .name = "SPEEX", .channels = 1, .clockrate = 48000, .gstPay = "rtpspeexpay", .gstDepay = "rtpspeexdepay", .gstEnc = "speexenc", .gstDec = "speexdec" }, { .pt = 97, .name = "SPEEX", .channels = 1, .clockrate = 44100, .gstPay = "rtpspeexpay", .gstDepay = "rtpspeexdepay", .gstEnc = "speexenc", .gstDec = "speexdec" }, { .pt = 96, .name = "AAC", .channels = 2, .clockrate = 48000, .gstPay = "rtpmp4apay", .gstDepay = "rtpmp4adepay", .gstEnc = "avenc_aac", .gstDec = "avdec_aac" }, { .pt = 96, .name = "AAC", .channels = 2, .clockrate = 44100, .gstPay = "rtpmp4apay", .gstDepay = "rtpmp4adepay", .gstEnc = "avenc_aac", .gstDec = "avdec_aac" }, { .pt = 96, .name = "AAC", .channels = 1, .clockrate = 48000, .gstPay = "rtpmp4apay", .gstDepay = "rtpmp4adepay", .gstEnc = "avenc_aac", .gstDec = "avdec_aac" }, { .pt = 96, .name = "AAC", .channels = 1, .clockrate = 44100, .gstPay = "rtpmp4apay", .gstDepay = "rtpmp4adepay", .gstEnc = "avenc_aac", .gstDec = "avdec_aac" }, { .pt = 8, .name = "PCMA", .channels = 1, .clockrate = 8000, .gstPay = "rtppcmapay", .gstDepay = "rtppcmadepay", .gstEnc = "alawenc", .gstDec = "alawdec" }, { .pt = 0, .name = "PCMU", .channels = 1, .clockrate = 8000, .gstPay = "rtppcmupay", .gstDepay = "rtppcmudepay", .gstEnc = "mulawenc", .gstDec = "mulawdec" } }; private: QXmppCall *q; }; #endif qxmpp-1.4.0/src/client/QXmppCarbonManager.cpp000066400000000000000000000067161402370562100211310ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 "QXmppCarbonManager.h" #include "QXmppClient.h" #include "QXmppConstants_p.h" #include "QXmppDiscoveryManager.h" #include "QXmppMessage.h" #include "QXmppUtils.h" #include QXmppCarbonManager::QXmppCarbonManager() : m_carbonsEnabled(false) { } QXmppCarbonManager::~QXmppCarbonManager() { } /// /// Returns whether message carbons are currently enabled /// bool QXmppCarbonManager::carbonsEnabled() const { return m_carbonsEnabled; } /// /// Enables or disables message carbons for this connection. /// /// This function does not check whether the server supports /// message carbons, but just sends the corresponding stanza /// to the server, so one must check in advance by using the /// discovery manager. /// /// By default, carbon copies are disabled. /// void QXmppCarbonManager::setCarbonsEnabled(bool enabled) { if (m_carbonsEnabled == enabled) return; m_carbonsEnabled = enabled; if (client()) { QXmppIq iq(QXmppIq::Set); QXmppElement carbonselement; carbonselement.setTagName(m_carbonsEnabled ? "enable" : "disable"); carbonselement.setAttribute("xmlns", ns_carbons); iq.setExtensions(QXmppElementList() << carbonselement); client()->sendPacket(iq); } } /// \cond QStringList QXmppCarbonManager::discoveryFeatures() const { return QStringList() << ns_carbons; } bool QXmppCarbonManager::handleStanza(const QDomElement &element) { if (element.tagName() != "message") return false; bool sent = true; QDomElement carbon = element.firstChildElement("sent"); if (carbon.isNull()) { carbon = element.firstChildElement("received"); sent = false; } if (carbon.isNull() || carbon.namespaceURI() != ns_carbons) return false; // carbon copies must always come from our bare JID if (element.attribute("from") != client()->configuration().jidBare()) { info("Received carbon copy from possible attacker trying to use CVE-2017-5603."); return false; } auto forwarded = carbon.firstChildElement("forwarded"); auto messageElement = forwarded.firstChildElement("message"); if (messageElement.isNull()) return false; QXmppMessage message; message.parse(messageElement); if (sent) emit messageSent(message); else emit messageReceived(message); return true; } /// \endcond /// /// \fn QXmppCarbonManager::messageReceived() /// /// Emitted when a message was received from someone else and directed to /// another resource. /// /// If you connect this signal to the QXmppClient::messageReceived signal, they /// will appear as normal messages. /// /// /// \fn QXmppCarbonManager::messageSent() /// /// Emitted when another resource sent a message to someone else. /// qxmpp-1.4.0/src/client/QXmppCarbonManager.h000066400000000000000000000032241402370562100205650ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 QXMPPCARBONMANAGER_H #define QXMPPCARBONMANAGER_H #include "QXmppClientExtension.h" class QXmppMessage; /// /// \brief The QXmppCarbonManager class handles message carbons /// as described in \xep{0280}: Message Carbons. /// /// This class emits signals whenever another resource of the /// currently connected client account sent or received a message. /// /// \ingroup Managers /// /// \since QXmpp 1.0 /// class QXMPP_EXPORT QXmppCarbonManager : public QXmppClientExtension { Q_OBJECT public: QXmppCarbonManager(); ~QXmppCarbonManager() override; bool carbonsEnabled() const; void setCarbonsEnabled(bool enabled); /// \cond QStringList discoveryFeatures() const override; bool handleStanza(const QDomElement &element) override; /// \endcond Q_SIGNALS: void messageReceived(const QXmppMessage &); void messageSent(const QXmppMessage &); private: bool m_carbonsEnabled; }; #endif // QXMPPCARBONMANAGER_H qxmpp-1.4.0/src/client/QXmppClient.cpp000066400000000000000000000444411402370562100176450ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 "QXmppClient.h" #include "QXmppClientExtension.h" #include "QXmppClient_p.h" #include "QXmppConstants_p.h" #include "QXmppDiscoveryIq.h" #include "QXmppDiscoveryManager.h" #include "QXmppEntityTimeManager.h" #include "QXmppLogger.h" #include "QXmppMessage.h" #include "QXmppOutgoingClient.h" #include "QXmppRosterManager.h" #include "QXmppTlsManager_p.h" #include "QXmppUtils.h" #include "QXmppVCardManager.h" #include "QXmppVersionManager.h" #include #include /// \cond QXmppClientPrivate::QXmppClientPrivate(QXmppClient* qq) : clientPresence(QXmppPresence::Available), logger(nullptr), stream(nullptr), receivedConflict(false), reconnectionTries(0), reconnectionTimer(nullptr), isActive(true), q(qq) { } void QXmppClientPrivate::addProperCapability(QXmppPresence& presence) { auto* ext = q->findExtension(); if (ext) { presence.setCapabilityHash("sha-1"); presence.setCapabilityNode(ext->clientCapabilitiesNode()); presence.setCapabilityVer(ext->capabilities().verificationString()); } } int QXmppClientPrivate::getNextReconnectTime() const { if (reconnectionTries < 5) return 10 * 1000; else if (reconnectionTries < 10) return 20 * 1000; else if (reconnectionTries < 15) return 40 * 1000; else return 60 * 1000; } QStringList QXmppClientPrivate::discoveryFeatures() { return { // XEP-0004: Data Forms ns_data, // XEP-0059: Result Set Management ns_rsm, // XEP-0066: Out of Band Data ns_oob, // XEP-0071: XHTML-IM ns_xhtml_im, // XEP-0085: Chat State Notifications ns_chat_states, // XEP-0115: Entity Capabilities ns_capabilities, // XEP-0199: XMPP Ping ns_ping, // XEP-0249: Direct MUC Invitations ns_conference, // XEP-0308: Last Message Correction ns_message_correct, // XEP-0333: Chat Markers ns_chat_markers, // XEP-0334: Message Processing Hints ns_message_processing_hints, // XEP-0359: Unique and Stable Stanza IDs ns_sid, // XEP-0367: Message Attaching ns_message_attaching, // XEP-0380: Explicit Message Encryption ns_eme, // XEP-0382: Spoiler messages ns_spoiler, // XEP-0428: Fallback Indication ns_fallback_indication, }; } /// \endcond /// Creates a QXmppClient object. /// \param parent is passed to the QObject's constructor. /// The default value is 0. QXmppClient::QXmppClient(QObject* parent) : QXmppLoggable(parent), d(new QXmppClientPrivate(this)) { d->stream = new QXmppOutgoingClient(this); d->addProperCapability(d->clientPresence); connect(d->stream, &QXmppOutgoingClient::elementReceived, this, &QXmppClient::_q_elementReceived); connect(d->stream, &QXmppOutgoingClient::messageReceived, this, &QXmppClient::messageReceived); connect(d->stream, &QXmppOutgoingClient::presenceReceived, this, &QXmppClient::presenceReceived); connect(d->stream, &QXmppOutgoingClient::iqReceived, this, &QXmppClient::iqReceived); connect(d->stream, &QXmppOutgoingClient::sslErrors, this, &QXmppClient::sslErrors); connect(d->stream->socket(), &QAbstractSocket::stateChanged, this, &QXmppClient::_q_socketStateChanged); connect(d->stream, &QXmppStream::connected, this, &QXmppClient::_q_streamConnected); connect(d->stream, &QXmppStream::disconnected, this, &QXmppClient::_q_streamDisconnected); connect(d->stream, &QXmppOutgoingClient::error, this, &QXmppClient::_q_streamError); // reconnection d->reconnectionTimer = new QTimer(this); d->reconnectionTimer->setSingleShot(true); connect(d->reconnectionTimer, &QTimer::timeout, this, &QXmppClient::_q_reconnect); // logging setLogger(QXmppLogger::getLogger()); addExtension(new QXmppTlsManager); addExtension(new QXmppRosterManager(this)); addExtension(new QXmppVCardManager); addExtension(new QXmppVersionManager); addExtension(new QXmppEntityTimeManager()); addExtension(new QXmppDiscoveryManager()); } /// Destructor, destroys the QXmppClient object. /// QXmppClient::~QXmppClient() { delete d; } /// Registers a new \a extension with the client. /// /// \param extension bool QXmppClient::addExtension(QXmppClientExtension* extension) { return insertExtension(d->extensions.size(), extension); } /// Registers a new \a extension with the client at the given \a index. /// /// \param index /// \param extension bool QXmppClient::insertExtension(int index, QXmppClientExtension* extension) { if (d->extensions.contains(extension)) { qWarning("Cannot add extension, it has already been added"); return false; } extension->setParent(this); extension->setClient(this); d->extensions.insert(index, extension); return true; } /// Unregisters the given extension from the client. If the extension /// is found, it will be destroyed. /// /// \param extension bool QXmppClient::removeExtension(QXmppClientExtension* extension) { if (d->extensions.contains(extension)) { d->extensions.removeAll(extension); delete extension; return true; } else { qWarning("Cannot remove extension, it was never added"); return false; } } /// Returns a list containing all the client's extensions. /// QList QXmppClient::extensions() { return d->extensions; } /// Returns a modifiable reference to the current configuration of QXmppClient. /// \return Reference to the QXmppClient's configuration for the connection. QXmppConfiguration& QXmppClient::configuration() { return d->stream->configuration(); } /// /// Attempts to connect to the XMPP server. Server details and other configurations /// are specified using the config parameter. Use signals connected(), error(QXmppClient::Error) /// and disconnected() to know the status of the connection. /// /// \param config Specifies the configuration object for connecting the XMPP server. /// This contains the host name, user, password etc. See QXmppConfiguration for details. /// \param initialPresence The initial presence which will be set for this user /// after establishing the session. The default value is QXmppPresence::Available /// void QXmppClient::connectToServer(const QXmppConfiguration& config, const QXmppPresence& initialPresence) { // reset package cache from last connection if (d->stream->configuration().jidBare() != config.jidBare()) d->stream->resetPacketCache(); d->stream->configuration() = config; d->clientPresence = initialPresence; d->addProperCapability(d->clientPresence); d->stream->connectToHost(); } /// Overloaded function to simply connect to an XMPP server with a JID and password. /// /// \param jid JID for the account. /// \param password Password for the account. void QXmppClient::connectToServer(const QString& jid, const QString& password) { QXmppConfiguration config; config.setJid(jid); config.setPassword(password); connectToServer(config); } /// After successfully connecting to the server use this function to send /// stanzas to the server. This function can solely be used to send various kind /// of stanzas to the server. QXmppStanza is a parent class of all the stanzas /// QXmppMessage, QXmppPresence, QXmppIq, QXmppBind, QXmppRosterIq, QXmppSession /// and QXmppVCard. /// /// \return Returns true if the packet was sent, false otherwise. /// /// Following code snippet illustrates how to send a message using this function: /// \code /// QXmppMessage message(from, to, message); /// client.sendPacket(message); /// \endcode /// /// \param packet A valid XMPP stanza. It can be an iq, a message or a presence stanza. /// bool QXmppClient::sendPacket(const QXmppStanza& packet) { return d->stream->sendPacket(packet); } /// Disconnects the client and the current presence of client changes to /// QXmppPresence::Unavailable and status text changes to "Logged out". /// /// \note Make sure that the clientPresence is changed to /// QXmppPresence::Available, if you are again calling connectToServer() after /// calling the disconnectFromServer() function. /// void QXmppClient::disconnectFromServer() { // cancel reconnection d->reconnectionTimer->stop(); d->clientPresence.setType(QXmppPresence::Unavailable); d->clientPresence.setStatusText("Logged out"); if (d->stream->isConnected()) sendPacket(d->clientPresence); d->stream->disconnectFromHost(); } /// Returns true if the client has authenticated with the XMPP server. bool QXmppClient::isAuthenticated() const { return d->stream->isAuthenticated(); } /// Returns true if the client is connected to the XMPP server. /// bool QXmppClient::isConnected() const { return d->stream->isConnected(); } /// /// Returns true if the current client state is "active", false if it is /// "inactive". See \xep{0352}: Client State Indication for details. /// /// On connect this is always reset to true. /// /// \since QXmpp 1.0 /// bool QXmppClient::isActive() const { return d->isActive; } /// /// Sets the client state as described in \xep{0352}: Client State Indication. /// /// On connect this is always reset to true. /// /// \since QXmpp 1.0 /// void QXmppClient::setActive(bool active) { if (active != d->isActive && isConnected() && d->stream->isClientStateIndicationEnabled()) { d->isActive = active; QString packet = "<%1 xmlns='%2'/>"; d->stream->sendData(packet.arg(active ? "active" : "inactive", ns_csi).toUtf8()); } } /// /// Returns the current \xep{0198}: Stream Management state of the connection. /// /// Upon connection of the client this can be used to check whether the /// previous stream has been resumed. /// /// \since QXmpp 1.4 /// QXmppClient::StreamManagementState QXmppClient::streamManagementState() const { if (d->stream->isStreamManagementEnabled()) { if (d->stream->isStreamResumed()) return ResumedStream; return NewStream; } return NoStreamManagement; } /// 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() instead. QXmppRosterManager& QXmppClient::rosterManager() { return *findExtension(); } /// Utility function to send message to all the resources associated with the /// specified bareJid. If there are no resources available, that is the contact /// is offline or not present in the roster, it will still send a message to /// the bareJid. /// /// \note Usage of this method is discouraged because most modern clients use /// carbon messages (\xep{0280}: Message Carbons) and MAM (\xep{0313}: Message /// Archive Management) and so could possibly receive messages multiple times /// or not receive them at all. /// \c QXmppClient::sendPacket() should be used instead with a \c QXmppMessage. /// /// \param bareJid bareJid of the receiving entity /// \param message Message string to be sent. void QXmppClient::sendMessage(const QString& bareJid, const QString& message) { QXmppRosterManager* rosterManager = findExtension(); const QStringList resources = rosterManager ? rosterManager->getResources(bareJid) : QStringList(); if (!resources.isEmpty()) { for (const auto& resource : resources) { sendPacket( QXmppMessage({}, bareJid + QStringLiteral("/") + resource, message)); } } else { sendPacket(QXmppMessage({}, bareJid, message)); } } QXmppClient::State QXmppClient::state() const { if (d->stream->isConnected()) return QXmppClient::ConnectedState; else if (d->stream->socket()->state() != QAbstractSocket::UnconnectedState && d->stream->socket()->state() != QAbstractSocket::ClosingState) return QXmppClient::ConnectingState; else return QXmppClient::DisconnectedState; } /// Returns the client's current presence. /// QXmppPresence QXmppClient::clientPresence() const { return d->clientPresence; } /// Changes the presence of the connected client. /// /// The connection to the server will be updated accordingly: /// /// \li If the presence type is QXmppPresence::Unavailable, the connection /// to the server will be closed. /// /// \li Otherwise, the connection to the server will be established /// as needed. /// /// \param presence QXmppPresence object /// void QXmppClient::setClientPresence(const QXmppPresence& presence) { d->clientPresence = presence; d->addProperCapability(d->clientPresence); if (presence.type() == QXmppPresence::Unavailable) { // cancel reconnection d->reconnectionTimer->stop(); // NOTE: we can't call disconnect() because it alters // the client presence if (d->stream->isConnected()) sendPacket(d->clientPresence); d->stream->disconnectFromHost(); } else if (d->stream->isConnected()) sendPacket(d->clientPresence); else connectToServer(d->stream->configuration(), presence); } /// Returns the socket error if error() is QXmppClient::SocketError. /// QAbstractSocket::SocketError QXmppClient::socketError() { return d->stream->socket()->error(); } /// Returns the human-readable description of the last socket error if error() is QXmppClient::SocketError. QString QXmppClient::socketErrorString() const { return d->stream->socket()->errorString(); } /// Returns the XMPP stream error if QXmppClient::Error is QXmppClient::XmppStreamError. QXmppStanza::Error::Condition QXmppClient::xmppStreamError() { return d->stream->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() instead. /// QXmppVCardManager& QXmppClient::vCardManager() { return *findExtension(); } /// /// 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() instead. /// QXmppVersionManager& QXmppClient::versionManager() { return *findExtension(); } /// Give extensions a chance to handle incoming stanzas. /// /// \param element /// \param handled void QXmppClient::_q_elementReceived(const QDomElement& element, bool& handled) { for (auto* extension : d->extensions) { if (extension->handleStanza(element)) { handled = true; return; } } } void QXmppClient::_q_reconnect() { if (d->stream->configuration().autoReconnectionEnabled()) { debug("Reconnecting to server"); d->stream->connectToHost(); } } void QXmppClient::_q_socketStateChanged(QAbstractSocket::SocketState socketState) { Q_UNUSED(socketState); emit stateChanged(state()); } /// At connection establishment, send initial presence. void QXmppClient::_q_streamConnected() { d->receivedConflict = false; d->reconnectionTries = 0; d->isActive = true; // notify managers emit connected(); emit stateChanged(QXmppClient::ConnectedState); // send initial presence if (d->stream->isAuthenticated()) sendPacket(d->clientPresence); } void QXmppClient::_q_streamDisconnected() { // notify managers emit disconnected(); emit stateChanged(QXmppClient::DisconnectedState); } void QXmppClient::_q_streamError(QXmppClient::Error err) { if (d->stream->configuration().autoReconnectionEnabled()) { if (err == QXmppClient::XmppStreamError) { // if we receive a resource conflict, inhibit reconnection if (d->stream->xmppStreamError() == QXmppStanza::Error::Conflict) d->receivedConflict = true; } else if (err == QXmppClient::SocketError && !d->receivedConflict) { // schedule reconnect d->reconnectionTimer->start(d->getNextReconnectTime()); } else if (err == QXmppClient::KeepAliveError) { // if we got a keepalive error, reconnect in one second d->reconnectionTimer->start(1000); } } // notify managers emit error(err); } QXmppLogger* QXmppClient::logger() const { return d->logger; } /// Sets the QXmppLogger associated with the current QXmppClient. void QXmppClient::setLogger(QXmppLogger* logger) { if (logger != d->logger) { if (d->logger) { disconnect(this, &QXmppLoggable::logMessage, d->logger, &QXmppLogger::log); disconnect(this, &QXmppLoggable::setGauge, d->logger, &QXmppLogger::setGauge); disconnect(this, &QXmppLoggable::updateCounter, d->logger, &QXmppLogger::updateCounter); } d->logger = logger; if (d->logger) { connect(this, &QXmppLoggable::logMessage, d->logger, &QXmppLogger::log); connect(this, &QXmppLoggable::setGauge, d->logger, &QXmppLogger::setGauge); connect(this, &QXmppLoggable::updateCounter, d->logger, &QXmppLogger::updateCounter); } emit loggerChanged(d->logger); } } qxmpp-1.4.0/src/client/QXmppClient.h000066400000000000000000000247451402370562100173170ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 QXMPPCLIENT_H #define QXMPPCLIENT_H #include "QXmppConfiguration.h" #include "QXmppLogger.h" #include "QXmppPresence.h" #include #include #include class QXmppClientExtension; class QXmppClientPrivate; class QXmppPresence; class QXmppMessage; class QXmppIq; class QXmppStream; class QXmppInternalClientExtension; // managers class QXmppDiscoveryIq; class QXmppRosterManager; class QXmppVCardManager; class QXmppVersionManager; /// /// \defgroup Core Core classes /// /// Core classes include all necessary classes to build a basic client or /// server application. This for example also includes the logging class /// QXmppLogger. /// /// /// \defgroup Managers Managers /// /// Managers are used to extend the basic QXmppClient. Some of them are loaded /// by default, others need to be added to the client using /// QXmppClient::addExtension(). /// /// /// \brief The QXmppClient class is the main class for using QXmpp. /// /// It provides the user all the required functionality to connect to the /// server and perform operations afterwards. /// /// This class will provide the handle/reference to QXmppRosterManager /// (roster management), QXmppVCardManager (vCard manager), and /// QXmppVersionManager (software version information). /// /// By default, the client will automatically try reconnecting to the server. /// You can change that behaviour using /// QXmppConfiguration::setAutoReconnectionEnabled(). /// /// Not all the managers or extensions have been enabled by default. One can /// enable/disable the managers using the functions \c addExtension() and /// \c removeExtension(). \c findExtension() can be used to find a /// reference/pointer to a particular instantiated and enabled manager. /// /// List of managers enabled by default: /// - QXmppRosterManager /// - QXmppVCardManager /// - QXmppVersionManager /// - QXmppDiscoveryManager /// - QXmppEntityTimeManager /// /// \ingroup Core /// class QXMPP_EXPORT QXmppClient : public QXmppLoggable { Q_OBJECT /// The QXmppLogger associated with the current QXmppClient Q_PROPERTY(QXmppLogger *logger READ logger WRITE setLogger NOTIFY loggerChanged) /// The client's current state Q_PROPERTY(State state READ state NOTIFY stateChanged) public: /// An enumeration for type of error. /// Error could come due a TCP socket or XML stream or due to various stanzas. enum Error { NoError, ///< No error. SocketError, ///< Error due to TCP socket. KeepAliveError, ///< Error due to no response to a keep alive. XmppStreamError, ///< Error due to XML stream. }; Q_ENUM(Error) /// This enumeration describes a client state. enum State { DisconnectedState, ///< Disconnected from the server. ConnectingState, ///< Trying to connect to the server. ConnectedState ///< Connected to the server. }; Q_ENUM(State) /// Describes the use of \xep{0198}: Stream Management enum StreamManagementState { /// Stream Management is not used. NoStreamManagement, /// Stream Management is used and the previous stream has not been resumed. NewStream, /// Stream Management is used and the previous stream has been resumed. ResumedStream }; QXmppClient(QObject *parent = nullptr); ~QXmppClient() override; bool addExtension(QXmppClientExtension *extension); bool insertExtension(int index, QXmppClientExtension *extension); bool removeExtension(QXmppClientExtension *extension); QList extensions(); /// /// \brief Returns the extension which can be cast into type T*, or 0 /// if there is no such extension. /// /// Usage example: /// \code /// QXmppDiscoveryManager* ext = client->findExtension(); /// if(ext) /// { /// //extension found, do stuff... /// } /// \endcode /// template T *findExtension() { const QList list = extensions(); for (auto ext : list) { T *extension = qobject_cast(ext); if (extension) return extension; } return nullptr; } /// /// \brief Returns the index of an extension /// /// Usage example: /// \code /// int index = client->indexOfExtension(); /// if (index > 0) { /// // extension found, do stuff... /// } else { /// // extension not found /// } /// \endcode /// /// \since QXmpp 1.2 /// template int indexOfExtension() { auto list = extensions(); for (int i = 0; i < list.size(); ++i) { if (qobject_cast(list.at(i)) != nullptr) return i; } return -1; } bool isAuthenticated() const; bool isConnected() const; bool isActive() const; void setActive(bool active); StreamManagementState streamManagementState() const; QXmppPresence clientPresence() const; void setClientPresence(const QXmppPresence &presence); QXmppConfiguration &configuration(); // documentation needs to be here, see https://stackoverflow.com/questions/49192523/ /// Returns the QXmppLogger associated with the current QXmppClient. QXmppLogger *logger() const; void setLogger(QXmppLogger *logger); QAbstractSocket::SocketError socketError(); QString socketErrorString() const; // documentation needs to be here, see https://stackoverflow.com/questions/49192523/ /// Returns the client's current state. State state() const; QXmppStanza::Error::Condition xmppStreamError(); #if QXMPP_DEPRECATED_SINCE(1, 1) QT_DEPRECATED_X("Use QXmppClient::findExtension() instead") QXmppRosterManager &rosterManager(); QT_DEPRECATED_X("Use QXmppClient::findExtension() instead") QXmppVCardManager &vCardManager(); QT_DEPRECATED_X("Use QXmppClient::findExtension() instead") QXmppVersionManager &versionManager(); #endif Q_SIGNALS: /// This signal is emitted when the client connects successfully to the /// XMPP server i.e. when a successful XMPP connection is established. /// XMPP Connection involves following sequential steps: /// - TCP socket connection /// - Client sends start stream /// - Server sends start stream /// - TLS negotiation (encryption) /// - Authentication /// - Resource binding /// - Session establishment /// /// After all these steps a successful XMPP connection is established and /// connected() signal is emitted. /// /// After the connected() signal is emitted QXmpp will send the roster /// request to the server. On receiving the roster, QXmpp will emit /// QXmppRosterManager::rosterReceived(). After this signal, /// QXmppRosterManager object gets populated and you can use /// \c findExtension() to get the handle of /// QXmppRosterManager object. void connected(); /// This signal is emitted when the XMPP connection disconnects. void disconnected(); /// This signal is emitted when the XMPP connection encounters any error. /// The QXmppClient::Error parameter specifies the type of error occurred. /// It could be due to TCP socket or the xml stream or the stanza. /// Depending upon the type of error occurred use the respective get function to /// know the error. void error(QXmppClient::Error); /// This signal is emitted when the logger changes. void loggerChanged(QXmppLogger *logger); /// Notifies that an XMPP message stanza is received. The QXmppMessage /// parameter contains the details of the message sent to this client. /// In other words whenever someone sends you a message this signal is /// emitted. void messageReceived(const QXmppMessage &message); /// Notifies that an XMPP presence stanza is received. The QXmppPresence /// parameter contains the details of the presence sent to this client. /// This signal is emitted when someone login/logout or when someone's status /// changes Busy, Idle, Invisible etc. void presenceReceived(const QXmppPresence &presence); /// This signal is emitted when IQs of type result or error are received by /// the client and no registered QXmppClientExtension could handle it. /// /// This is useful when it is only important to check whether the response /// of an IQ was successful. However, the recommended way is still to use an /// additional QXmppClientExtension for this kind of tasks. void iqReceived(const QXmppIq &iq); /// This signal is emitted to indicate that one or more SSL errors were /// encountered while establishing the identity of the server. void sslErrors(const QList &errors); /// This signal is emitted when the client state changes. void stateChanged(QXmppClient::State state); public Q_SLOTS: void connectToServer(const QXmppConfiguration &, const QXmppPresence &initialPresence = QXmppPresence()); void connectToServer(const QString &jid, const QString &password); void disconnectFromServer(); bool sendPacket(const QXmppStanza &); void sendMessage(const QString &bareJid, const QString &message); private Q_SLOTS: void _q_elementReceived(const QDomElement &element, bool &handled); void _q_reconnect(); void _q_socketStateChanged(QAbstractSocket::SocketState state); void _q_streamConnected(); void _q_streamDisconnected(); void _q_streamError(QXmppClient::Error error); private: QXmppClientPrivate *const d; friend class QXmppInternalClientExtension; }; #endif // QXMPPCLIENT_H qxmpp-1.4.0/src/client/QXmppClientExtension.cpp000066400000000000000000000033311402370562100215330ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppClientExtension.h" #include class QXmppClientExtensionPrivate { public: QXmppClient *client; }; /// Constructs a QXmppClient extension. /// QXmppClientExtension::QXmppClientExtension() : d(new QXmppClientExtensionPrivate) { d->client = nullptr; } /// Destroys a QXmppClient extension. /// QXmppClientExtension::~QXmppClientExtension() { delete d; } /// Returns the discovery features to add to the client. /// QStringList QXmppClientExtension::discoveryFeatures() const { return QStringList(); } /// Returns the discovery identities to add to the client. /// QList QXmppClientExtension::discoveryIdentities() const { return QList(); } /// Returns the client which loaded this extension. /// QXmppClient *QXmppClientExtension::client() { return d->client; } /// Sets the client which loaded this extension. /// /// \param client void QXmppClientExtension::setClient(QXmppClient *client) { d->client = client; } qxmpp-1.4.0/src/client/QXmppClientExtension.h000066400000000000000000000040771402370562100212100ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPCLIENTEXTENSION_H #define QXMPPCLIENTEXTENSION_H #include "QXmppDiscoveryIq.h" #include "QXmppLogger.h" class QDomElement; class QXmppClient; class QXmppClientExtensionPrivate; class QXmppStream; /// \brief The QXmppClientExtension class is the base class for QXmppClient /// extensions. /// /// If you want to extend QXmppClient, for instance to support an IQ type /// which is not natively supported, you can subclass QXmppClientExtension /// and implement handleStanza(). You can then add your extension to the /// client instance using QXmppClient::addExtension(). /// /// \ingroup Core class QXMPP_EXPORT QXmppClientExtension : public QXmppLoggable { Q_OBJECT public: QXmppClientExtension(); ~QXmppClientExtension() override; virtual QStringList discoveryFeatures() const; virtual QList discoveryIdentities() const; /// \brief You need to implement this method to process incoming XMPP /// stanzas. /// /// You should return true if the stanza was handled and no further /// processing should occur, or false to let other extensions process /// the stanza. virtual bool handleStanza(const QDomElement &stanza) = 0; protected: QXmppClient *client(); virtual void setClient(QXmppClient *client); private: QXmppClientExtensionPrivate *const d; friend class QXmppClient; }; #endif qxmpp-1.4.0/src/client/QXmppClient_p.h000066400000000000000000000033431402370562100176250ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * Linus Jahn * * 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. * */ // // W A R N I N G // ------------- // // This file is not part of the QXmpp API. // // This header file may change from version to version without notice, // or even be removed. // // We mean it. // #ifndef QXMPPCLIENT_P_H #define QXMPPCLIENT_P_H #include "QXmppPresence.h" class QXmppClient; class QXmppClientExtension; class QXmppLogger; class QXmppOutgoingClient; class QTimer; class QXmppClientPrivate { public: QXmppClientPrivate(QXmppClient *qq); /// Current presence of the client QXmppPresence clientPresence; QList extensions; QXmppLogger *logger; /// Pointer to the XMPP stream QXmppOutgoingClient *stream; // reconnection bool receivedConflict; int reconnectionTries; QTimer *reconnectionTimer; // Client state indication bool isActive; void addProperCapability(QXmppPresence &presence); int getNextReconnectTime() const; static QStringList discoveryFeatures(); private: QXmppClient *q; }; #endif // QXMPPCLIENT_P_H qxmpp-1.4.0/src/client/QXmppConfiguration.cpp000066400000000000000000000341671402370562100212420ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 "QXmppConfiguration.h" #include "QXmppUtils.h" #include #include class QXmppConfigurationPrivate : public QSharedData { public: QXmppConfigurationPrivate(); QString host; int port; QString user; QString password; QString domain; QString resource; // Facebook QString facebookAccessToken; QString facebookAppId; // Google QString googleAccessToken; // Windows Live QString windowsLiveAccessToken; // default is false bool autoAcceptSubscriptions; // default is true bool sendIntialPresence; // default is true bool sendRosterRequest; // interval in seconds, if zero won't ping int keepAliveInterval; // interval in seconds, if zero won't timeout int keepAliveTimeout; // will keep reconnecting if disconnected, default is true bool autoReconnectionEnabled; // which authentication systems to use (if any) bool useSASLAuthentication; bool useNonSASLAuthentication; // default is false bool ignoreSslErrors; QXmppConfiguration::StreamSecurityMode streamSecurityMode; QXmppConfiguration::NonSASLAuthMechanism nonSASLAuthMechanism; QString saslAuthMechanism; QNetworkProxy networkProxy; QList caCertificates; }; QXmppConfigurationPrivate::QXmppConfigurationPrivate() : port(5222), resource("QXmpp"), autoAcceptSubscriptions(false), sendIntialPresence(true), sendRosterRequest(true), keepAliveInterval(60), keepAliveTimeout(20), autoReconnectionEnabled(true), useSASLAuthentication(true), useNonSASLAuthentication(true), ignoreSslErrors(false), streamSecurityMode(QXmppConfiguration::TLSEnabled), nonSASLAuthMechanism(QXmppConfiguration::NonSASLDigest) { } /// Creates a QXmppConfiguration object. QXmppConfiguration::QXmppConfiguration() : d(new QXmppConfigurationPrivate) { } /// Creates a copy of \a other. QXmppConfiguration::QXmppConfiguration(const QXmppConfiguration& other) : d(other.d) { } /// Destructor, destroys the QXmppConfiguration object. /// QXmppConfiguration::~QXmppConfiguration() { } /// Assigns \a other to this QXmppConfiguration. QXmppConfiguration& QXmppConfiguration::operator=(const QXmppConfiguration& other) { d = other.d; return *this; } /// Sets the host name. /// /// \param host host name of the XMPP server where connection has to be made /// (e.g. "jabber.org" and "talk.google.com"). It can also be an IP address in /// the form of a string (e.g. "192.168.1.25"). /// void QXmppConfiguration::setHost(const QString& host) { d->host = host; } /// Sets the domain name. /// /// \param domain Domain name e.g. "gmail.com" and "jabber.org". /// \note host name and domain name can be different for google /// domain name is gmail.com and host name is talk.google.com /// void QXmppConfiguration::setDomain(const QString& domain) { d->domain = domain; } /// Sets the port number. /// /// \param port Port number at which the XMPP server is listening. The default /// value is 5222. /// void QXmppConfiguration::setPort(int port) { d->port = port; } /// Sets the username. /// /// \param user Username of the account at the specified XMPP server. It should /// be the name without the domain name. E.g. "qxmpp.test1" and not /// "qxmpp.test1@gmail.com" /// void QXmppConfiguration::setUser(const QString& user) { d->user = user; } /// Sets the password. /// /// \param password Password for the specified username /// void QXmppConfiguration::setPassword(const QString& password) { d->password = password; } /// Sets the resource identifier. /// /// Multiple resources (e.g., devices or locations) may connect simultaneously /// to a server on behalf of each authorized client, with each resource /// differentiated by the resource identifier of an XMPP address /// (e.g. node\@domain/home vs. node\@domain/work) /// /// The default value is "QXmpp". /// /// \param resource Resource identifier of the client in connection. void QXmppConfiguration::setResource(const QString& resource) { d->resource = resource; } /// Sets the JID. If a full JID (i.e. one with a resource) is given, calling /// this method will update the username, domain and resource. Otherwise, only /// the username and the domain will be updated. /// /// \param jid void QXmppConfiguration::setJid(const QString& jid) { d->user = QXmppUtils::jidToUser(jid); d->domain = QXmppUtils::jidToDomain(jid); const QString resource = QXmppUtils::jidToResource(jid); if (!resource.isEmpty()) d->resource = resource; } /// Returns the host name. /// /// \return host name /// QString QXmppConfiguration::host() const { return d->host; } /// Returns the domain name. /// /// \return domain name /// QString QXmppConfiguration::domain() const { return d->domain; } /// Returns the port number. /// /// \return port number /// int QXmppConfiguration::port() const { return d->port; } /// Returns the username. /// /// \return username /// QString QXmppConfiguration::user() const { return d->user; } /// Returns the password. /// /// \return password /// QString QXmppConfiguration::password() const { return d->password; } /// Returns the resource identifier. /// /// \return resource identifier /// QString QXmppConfiguration::resource() const { return d->resource; } /// Returns the jabber id (jid). /// /// \return jabber id (jid) /// (e.g. "qxmpp.test1@gmail.com/resource" or qxmpptest@jabber.org/QXmpp156) /// QString QXmppConfiguration::jid() const { if (d->user.isEmpty()) return d->domain; else return jidBare() + "/" + d->resource; } /// Returns the bare jabber id (jid), without the resource identifier. /// /// \return bare jabber id (jid) /// (e.g. "qxmpp.test1@gmail.com" or qxmpptest@jabber.org) /// QString QXmppConfiguration::jidBare() const { if (d->user.isEmpty()) return d->domain; else return d->user + "@" + d->domain; } /// Returns the access token used for X-FACEBOOK-PLATFORM authentication. QString QXmppConfiguration::facebookAccessToken() const { return d->facebookAccessToken; } /// Sets the access token used for X-FACEBOOK-PLATFORM authentication. /// /// This token is returned by Facebook at the end of the OAuth authentication /// process. /// /// \param accessToken void QXmppConfiguration::setFacebookAccessToken(const QString& accessToken) { d->facebookAccessToken = accessToken; } /// Returns the application ID used for X-FACEBOOK-PLATFORM authentication. QString QXmppConfiguration::facebookAppId() const { return d->facebookAppId; } /// Sets the application ID used for X-FACEBOOK-PLATFORM authentication. /// /// \param appId void QXmppConfiguration::setFacebookAppId(const QString& appId) { d->facebookAppId = appId; } /// Returns the access token used for X-OAUTH2 authentication. QString QXmppConfiguration::googleAccessToken() const { return d->googleAccessToken; } /// Sets the access token used for X-OAUTH2 authentication. /// /// This token is returned by Google at the end of the OAuth authentication /// process. /// /// \param accessToken void QXmppConfiguration::setGoogleAccessToken(const QString& accessToken) { d->googleAccessToken = accessToken; } /// Returns the access token used for X-MESSENGER-OAUTH2 authentication. QString QXmppConfiguration::windowsLiveAccessToken() const { return d->windowsLiveAccessToken; } /// Sets the access token used for X-MESSENGER-OAUTH2 authentication. /// /// This token is returned by Windows Live at the end of the OAuth authentication /// process. /// /// \param accessToken void QXmppConfiguration::setWindowsLiveAccessToken(const QString& accessToken) { d->windowsLiveAccessToken = accessToken; } /// Returns the auto-accept-subscriptions-request configuration. /// /// \return boolean value /// true means that auto-accept-subscriptions-request is enabled else disabled for false /// bool QXmppConfiguration::autoAcceptSubscriptions() const { return d->autoAcceptSubscriptions; } /// Sets the auto-accept-subscriptions-request configuration. /// /// \param value boolean value /// true means that auto-accept-subscriptions-request is enabled else disabled for false /// void QXmppConfiguration::setAutoAcceptSubscriptions(bool value) { d->autoAcceptSubscriptions = value; } /// Returns the auto-reconnect-on-disconnection-on-error configuration. /// /// \return boolean value /// true means that auto-reconnect is enabled else disabled for false /// bool QXmppConfiguration::autoReconnectionEnabled() const { return d->autoReconnectionEnabled; } /// Sets the auto-reconnect-on-disconnection-on-error configuration. /// /// \param value boolean value /// true means that auto-reconnect is enabled else disabled for false /// void QXmppConfiguration::setAutoReconnectionEnabled(bool value) { d->autoReconnectionEnabled = value; } /// Returns whether SSL errors (such as certificate validation errors) /// are to be ignored when connecting to the XMPP server. bool QXmppConfiguration::ignoreSslErrors() const { return d->ignoreSslErrors; } /// Specifies whether SSL errors (such as certificate validation errors) /// are to be ignored when connecting to an XMPP server. void QXmppConfiguration::setIgnoreSslErrors(bool value) { d->ignoreSslErrors = value; } /// Returns whether to make use of SASL authentication. bool QXmppConfiguration::useSASLAuthentication() const { return d->useSASLAuthentication; } /// Sets whether to make use of SASL authentication. void QXmppConfiguration::setUseSASLAuthentication(bool useSASL) { d->useSASLAuthentication = useSASL; } /// Returns whether to make use of non-SASL authentication. bool QXmppConfiguration::useNonSASLAuthentication() const { return d->useNonSASLAuthentication; } /// Sets whether to make use of non-SASL authentication. void QXmppConfiguration::setUseNonSASLAuthentication(bool useNonSASL) { d->useNonSASLAuthentication = useNonSASL; } /// Returns the specified security mode for the stream. The default value is /// QXmppConfiguration::TLSEnabled. /// \return StreamSecurityMode QXmppConfiguration::StreamSecurityMode QXmppConfiguration::streamSecurityMode() const { return d->streamSecurityMode; } /// Specifies the specified security mode for the stream. The default value is /// QXmppConfiguration::TLSEnabled. /// \param mode StreamSecurityMode void QXmppConfiguration::setStreamSecurityMode( QXmppConfiguration::StreamSecurityMode mode) { d->streamSecurityMode = mode; } /// Returns the Non-SASL authentication mechanism configuration. /// /// \return QXmppConfiguration::NonSASLAuthMechanism /// QXmppConfiguration::NonSASLAuthMechanism QXmppConfiguration::nonSASLAuthMechanism() const { return d->nonSASLAuthMechanism; } /// Hints the library the Non-SASL authentication mechanism to be used for authentication. /// /// \param mech QXmppConfiguration::NonSASLAuthMechanism /// void QXmppConfiguration::setNonSASLAuthMechanism( QXmppConfiguration::NonSASLAuthMechanism mech) { d->nonSASLAuthMechanism = mech; } /// Returns the preferred SASL authentication mechanism. QString QXmppConfiguration::saslAuthMechanism() const { return d->saslAuthMechanism; } /// Sets the preferred SASL authentication \a mechanism. /// /// 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) { d->saslAuthMechanism = mechanism; } /// Specifies the network proxy used for the connection made by QXmppClient. /// The default value is QNetworkProxy::DefaultProxy that is the proxy is /// determined based on the application proxy set using /// QNetworkProxy::setApplicationProxy(). /// \param proxy QNetworkProxy void QXmppConfiguration::setNetworkProxy(const QNetworkProxy& proxy) { d->networkProxy = proxy; } /// Returns the specified network proxy. /// The default value is QNetworkProxy::DefaultProxy that is the proxy is /// determined based on the application proxy set using /// QNetworkProxy::setApplicationProxy(). /// \return QNetworkProxy QNetworkProxy QXmppConfiguration::networkProxy() const { return d->networkProxy; } /// Specifies the interval in seconds at which keep alive (ping) packets /// will be sent to the server. /// /// If set to zero, no keep alive packets will be sent. /// /// The default value is 60 seconds. void QXmppConfiguration::setKeepAliveInterval(int secs) { d->keepAliveInterval = secs; } /// Returns the keep alive interval in seconds. /// /// The default value is 60 seconds. int QXmppConfiguration::keepAliveInterval() const { return d->keepAliveInterval; } /// Specifies the maximum time in seconds to wait for a keep alive response /// from the server before considering we are disconnected. /// /// If set to zero or a value larger than the keep alive interval, /// no timeout will occur. /// /// The default value is 20 seconds. void QXmppConfiguration::setKeepAliveTimeout(int secs) { d->keepAliveTimeout = secs; } /// Returns the keep alive timeout in seconds. /// /// The default value is 20 seconds. int QXmppConfiguration::keepAliveTimeout() const { return d->keepAliveTimeout; } /// Specifies a list of trusted CA certificates. void QXmppConfiguration::setCaCertificates(const QList& caCertificates) { d->caCertificates = caCertificates; } /// Returns the a list of trusted CA certificates. QList QXmppConfiguration::caCertificates() const { return d->caCertificates; } qxmpp-1.4.0/src/client/QXmppConfiguration.h000066400000000000000000000114641402370562100207020ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 QXMPPCONFIGURATION_H #define QXMPPCONFIGURATION_H #include "QXmppGlobal.h" #include #include class QNetworkProxy; class QSslCertificate; class QXmppConfigurationPrivate; /// \brief The QXmppConfiguration class holds configuration options. /// /// It can be passed to QXmppClient to specify the options when connecting to /// an XMPP server. /// /// It is a container of all the settings, configuration required for /// connecting to an XMPP server. E.g. server name, username, port, type /// of authentication mechanism, type of security used by stream (encryption), /// etc.. /// class QXMPP_EXPORT QXmppConfiguration { public: /// An enumeration for type of the Security Mode that is stream is encrypted or not. /// The server may or may not have TLS feature. Server may force the encryption. /// Depending upon all this user can specify following options. enum StreamSecurityMode { TLSEnabled = 0, ///< Encryption is used if available (default). TLSDisabled, ///< No encryption even if the server offers it. TLSRequired, ///< Encryption must be available, otherwise the ///< connection will not be established. LegacySSL ///< Use only legacy SSL mode. }; /// An enumeration for various Non-SASL authentication mechanisms available. /// The server may or may not allow QXmppConfiguration::Plain mechanism. So /// specifying the mechanism is just a hint to the library. enum NonSASLAuthMechanism { NonSASLPlain = 0, ///< Plain NonSASLDigest ///< Digest (default) }; /// An enumeration for various SASL authentication mechanisms available. /// The server may or may not allow any particular mechanism. So depending /// upon the availability of mechanisms on the server the library will choose /// a mechanism. QXmppConfiguration(); QXmppConfiguration(const QXmppConfiguration &other); ~QXmppConfiguration(); QXmppConfiguration &operator=(const QXmppConfiguration &other); QString host() const; void setHost(const QString &); QString domain() const; void setDomain(const QString &); int port() const; void setPort(int); QString user() const; void setUser(const QString &); QString password() const; void setPassword(const QString &); QString resource() const; void setResource(const QString &); QString jid() const; void setJid(const QString &jid); QString jidBare() const; QString facebookAccessToken() const; void setFacebookAccessToken(const QString &); QString facebookAppId() const; void setFacebookAppId(const QString &); QString googleAccessToken() const; void setGoogleAccessToken(const QString &accessToken); QString windowsLiveAccessToken() const; void setWindowsLiveAccessToken(const QString &accessToken); bool autoAcceptSubscriptions() const; void setAutoAcceptSubscriptions(bool); bool autoReconnectionEnabled() const; void setAutoReconnectionEnabled(bool); bool useSASLAuthentication() const; void setUseSASLAuthentication(bool); bool useNonSASLAuthentication() const; void setUseNonSASLAuthentication(bool); bool ignoreSslErrors() const; void setIgnoreSslErrors(bool); QXmppConfiguration::StreamSecurityMode streamSecurityMode() const; void setStreamSecurityMode(QXmppConfiguration::StreamSecurityMode mode); QXmppConfiguration::NonSASLAuthMechanism nonSASLAuthMechanism() const; void setNonSASLAuthMechanism(QXmppConfiguration::NonSASLAuthMechanism); QString saslAuthMechanism() const; void setSaslAuthMechanism(const QString &mechanism); QNetworkProxy networkProxy() const; void setNetworkProxy(const QNetworkProxy &proxy); int keepAliveInterval() const; void setKeepAliveInterval(int secs); int keepAliveTimeout() const; void setKeepAliveTimeout(int secs); QList caCertificates() const; void setCaCertificates(const QList &); private: QSharedDataPointer d; }; #endif // QXMPPCONFIGURATION_H qxmpp-1.4.0/src/client/QXmppDiscoveryManager.cpp000066400000000000000000000175101402370562100216660ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 "QXmppDiscoveryManager.h" #include "QXmppClient.h" #include "QXmppClient_p.h" #include "QXmppConstants_p.h" #include "QXmppDataForm.h" #include "QXmppDiscoveryIq.h" #include "QXmppGlobal.h" #include "QXmppStream.h" #include #include class QXmppDiscoveryManagerPrivate { public: QString clientCapabilitiesNode; QString clientCategory; QString clientType; QString clientName; QXmppDataForm clientInfoForm; }; QXmppDiscoveryManager::QXmppDiscoveryManager() : d(new QXmppDiscoveryManagerPrivate) { d->clientCapabilitiesNode = "https://github.com/qxmpp-project/qxmpp"; d->clientCategory = "client"; #if defined Q_OS_ANDROID || defined Q_OS_BLACKBERRY || defined Q_OS_IOS || defined Q_OS_WP d->clientType = "phone"; #else d->clientType = "pc"; #endif if (qApp->applicationName().isEmpty() && qApp->applicationVersion().isEmpty()) d->clientName = QString("%1 %2").arg("Based on QXmpp", QXmppVersion()); else d->clientName = QString("%1 %2").arg(qApp->applicationName(), qApp->applicationVersion()); } QXmppDiscoveryManager::~QXmppDiscoveryManager() { delete d; } /// Requests information from the specified XMPP entity. /// /// \param jid The target entity's JID. /// \param node The target node (optional). QString QXmppDiscoveryManager::requestInfo(const QString& jid, const QString& node) { QXmppDiscoveryIq request; request.setType(QXmppIq::Get); request.setQueryType(QXmppDiscoveryIq::InfoQuery); request.setTo(jid); if (!node.isEmpty()) request.setQueryNode(node); if (client()->sendPacket(request)) return request.id(); else return QString(); } /// Requests items from the specified XMPP entity. /// /// \param jid The target entity's JID. /// \param node The target node (optional). QString QXmppDiscoveryManager::requestItems(const QString& jid, const QString& node) { QXmppDiscoveryIq request; request.setType(QXmppIq::Get); request.setQueryType(QXmppDiscoveryIq::ItemsQuery); request.setTo(jid); if (!node.isEmpty()) request.setQueryNode(node); if (client()->sendPacket(request)) return request.id(); else return QString(); } /// /// Returns the client's full capabilities. /// QXmppDiscoveryIq QXmppDiscoveryManager::capabilities() { QXmppDiscoveryIq iq; iq.setType(QXmppIq::Result); iq.setQueryType(QXmppDiscoveryIq::InfoQuery); // features QStringList features; // add base features of the client features << QXmppClientPrivate::discoveryFeatures(); // add features of all registered extensions const QList extensions = client()->extensions(); for (auto* extension : extensions) { if (extension) features << extension->discoveryFeatures(); } iq.setFeatures(features); // identities QList identities; QXmppDiscoveryIq::Identity identity; identity.setCategory(clientCategory()); identity.setType(clientType()); identity.setName(clientName()); identities << identity; for (auto* extension : client()->extensions()) { if (extension) identities << extension->discoveryIdentities(); } iq.setIdentities(identities); // extended information if (!d->clientInfoForm.isNull()) iq.setForm(d->clientInfoForm); return iq; } /// Sets the capabilities node of the local XMPP client. /// /// \param node void QXmppDiscoveryManager::setClientCapabilitiesNode(const QString& node) { d->clientCapabilitiesNode = node; } /// Sets the category of the local XMPP client. /// /// You can find a list of valid categories at: /// http://xmpp.org/registrar/disco-categories.html /// /// \param category void QXmppDiscoveryManager::setClientCategory(const QString& category) { d->clientCategory = category; } /// Sets the type of the local XMPP client. /// /// You can find a list of valid types at: /// http://xmpp.org/registrar/disco-categories.html /// /// \param type void QXmppDiscoveryManager::setClientType(const QString& type) { d->clientType = type; } /// Sets the name of the local XMPP client. /// /// \param name void QXmppDiscoveryManager::setClientName(const QString& name) { d->clientName = name; } /// Returns the capabilities node of the local XMPP client. /// /// By default this is "https://github.com/qxmpp-project/qxmpp". QString QXmppDiscoveryManager::clientCapabilitiesNode() const { return d->clientCapabilitiesNode; } /// Returns the category of the local XMPP client. /// /// By default this is "client". QString QXmppDiscoveryManager::clientCategory() const { return d->clientCategory; } /// Returns the type of the local XMPP client. /// /// With Qt builds for Android, Blackberry, iOS or Windows Phone this is set to /// "phone", otherwise it defaults to "pc". QString QXmppDiscoveryManager::clientType() const { return d->clientType; } /// Returns the name of the local XMPP client. /// /// By default this is "Based on QXmpp x.y.z". QString QXmppDiscoveryManager::clientName() const { return d->clientName; } /// Returns the client's extended information form, as defined /// by \xep{0128}: Service Discovery Extensions. QXmppDataForm QXmppDiscoveryManager::clientInfoForm() const { return d->clientInfoForm; } /// Sets the client's extended information form, as defined /// by \xep{0128}: Service Discovery Extensions. void QXmppDiscoveryManager::setClientInfoForm(const QXmppDataForm& form) { d->clientInfoForm = form; } /// \cond QStringList QXmppDiscoveryManager::discoveryFeatures() const { return QStringList() << ns_disco_info; } bool QXmppDiscoveryManager::handleStanza(const QDomElement& element) { if (element.tagName() == "iq" && QXmppDiscoveryIq::isDiscoveryIq(element)) { QXmppDiscoveryIq receivedIq; receivedIq.parse(element); switch (receivedIq.type()) { case QXmppIq::Get: if (receivedIq.queryType() == QXmppDiscoveryIq::InfoQuery && (receivedIq.queryNode().isEmpty() || receivedIq.queryNode().startsWith(d->clientCapabilitiesNode))) { // respond to info queries for the client itself QXmppDiscoveryIq qxmppFeatures = capabilities(); qxmppFeatures.setId(receivedIq.id()); qxmppFeatures.setTo(receivedIq.from()); qxmppFeatures.setQueryNode(receivedIq.queryNode()); client()->sendPacket(qxmppFeatures); return true; } else { // let other managers handle other queries return false; } case QXmppIq::Result: case QXmppIq::Error: // handle all replies if (receivedIq.queryType() == QXmppDiscoveryIq::InfoQuery) { emit infoReceived(receivedIq); } else if (receivedIq.queryType() == QXmppDiscoveryIq::ItemsQuery) { emit itemsReceived(receivedIq); } return true; case QXmppIq::Set: // let other manager handle "set" IQs return false; } } return false; } /// \endcond qxmpp-1.4.0/src/client/QXmppDiscoveryManager.h000066400000000000000000000045401402370562100213320ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 QXMPPDISCOVERYMANAGER_H #define QXMPPDISCOVERYMANAGER_H #include "QXmppClientExtension.h" class QXmppDataForm; class QXmppDiscoveryIq; class QXmppDiscoveryManagerPrivate; /// \brief The QXmppDiscoveryManager class makes it possible to discover information /// about other entities as defined by \xep{0030}: Service Discovery. /// /// \ingroup Managers class QXMPP_EXPORT QXmppDiscoveryManager : public QXmppClientExtension { Q_OBJECT public: QXmppDiscoveryManager(); ~QXmppDiscoveryManager() override; QXmppDiscoveryIq capabilities(); QString requestInfo(const QString& jid, const QString& node = QString()); QString requestItems(const QString& jid, const QString& node = QString()); QString clientCapabilitiesNode() const; void setClientCapabilitiesNode(const QString&); // http://xmpp.org/registrar/disco-categories.html#client QString clientCategory() const; void setClientCategory(const QString&); void setClientName(const QString&); QString clientName() const; QString clientType() const; void setClientType(const QString&); QXmppDataForm clientInfoForm() const; void setClientInfoForm(const QXmppDataForm& form); /// \cond QStringList discoveryFeatures() const override; bool handleStanza(const QDomElement& element) override; /// \endcond Q_SIGNALS: /// This signal is emitted when an information response is received. void infoReceived(const QXmppDiscoveryIq&); /// This signal is emitted when an items response is received. void itemsReceived(const QXmppDiscoveryIq&); private: QXmppDiscoveryManagerPrivate* d; }; #endif // QXMPPDISCOVERYMANAGER_H qxmpp-1.4.0/src/client/QXmppEntityTimeManager.cpp000066400000000000000000000043251402370562100220120ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 "QXmppEntityTimeManager.h" #include "QXmppClient.h" #include "QXmppConstants_p.h" #include "QXmppEntityTimeIq.h" #include "QXmppUtils.h" #include #include /// Request the time from an XMPP entity. /// /// \param jid QString QXmppEntityTimeManager::requestTime(const QString& jid) { QXmppEntityTimeIq request; request.setType(QXmppIq::Get); request.setTo(jid); if (client()->sendPacket(request)) return request.id(); else return QString(); } /// \cond QStringList QXmppEntityTimeManager::discoveryFeatures() const { return QStringList() << ns_entity_time; } bool QXmppEntityTimeManager::handleStanza(const QDomElement& element) { if (element.tagName() == "iq" && QXmppEntityTimeIq::isEntityTimeIq(element)) { QXmppEntityTimeIq entityTime; entityTime.parse(element); if (entityTime.type() == QXmppIq::Get) { // respond to query QXmppEntityTimeIq responseIq; responseIq.setType(QXmppIq::Result); responseIq.setId(entityTime.id()); responseIq.setTo(entityTime.from()); QDateTime currentTime = QDateTime::currentDateTime(); QDateTime utc = currentTime.toUTC(); responseIq.setUtc(utc); currentTime.setTimeSpec(Qt::UTC); responseIq.setTzo(utc.secsTo(currentTime)); client()->sendPacket(responseIq); } emit timeReceived(entityTime); return true; } return false; } /// \endcond qxmpp-1.4.0/src/client/QXmppEntityTimeManager.h000066400000000000000000000027031402370562100214550ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 QXMPPENTITYTIMEMANAGER_H #define QXMPPENTITYTIMEMANAGER_H #include "QXmppClientExtension.h" class QXmppEntityTimeIq; /// \brief The QXmppEntityTimeManager class provided the functionality to get /// the local time of an entity as defined by \xep{0202}: Entity Time. /// /// \ingroup Managers class QXMPP_EXPORT QXmppEntityTimeManager : public QXmppClientExtension { Q_OBJECT public: QString requestTime(const QString& jid); /// \cond QStringList discoveryFeatures() const override; bool handleStanza(const QDomElement& element) override; /// \endcond Q_SIGNALS: /// \brief This signal is emitted when a time response is received. void timeReceived(const QXmppEntityTimeIq&); }; #endif // QXMPPENTITYTIMEMANAGER_H qxmpp-1.4.0/src/client/QXmppInternalClientExtension.cpp000066400000000000000000000020021402370562100232220ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Linus Jahn * * 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 "QXmppClient.h" #include "QXmppClient_p.h" #include "QXmppInternalClientExtension_p.h" /// \cond QXmppInternalClientExtension::QXmppInternalClientExtension() : QXmppClientExtension() { } QXmppOutgoingClient *QXmppInternalClientExtension::clientStream() { return client()->d->stream; } /// \endcond qxmpp-1.4.0/src/client/QXmppInternalClientExtension_p.h000066400000000000000000000027741402370562100232260ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Linus Jahn * * 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. * */ // // W A R N I N G // ------------- // // This file is not part of the QXmpp API. // // This header file may change from version to version without notice, // or even be removed. // // We mean it. // #ifndef QXMPPINTERNALCLIENTEXTENSION_H #define QXMPPINTERNALCLIENTEXTENSION_H #include "QXmppClientExtension.h" class QXmppOutgoingClient; /// /// \brief The QXmppInternalClientExtension class is used to access private /// components of the QXmppClient. /// /// It is not exposed to the public API and is only used to split up internal /// parts of the QXmppClient, like TLS negotiation. /// class QXmppInternalClientExtension : public QXmppClientExtension { Q_OBJECT public: QXmppInternalClientExtension(); protected: QXmppOutgoingClient *clientStream(); }; #endif // QXMPPINTERNALCLIENTEXTENSION_H qxmpp-1.4.0/src/client/QXmppInvokable.cpp000066400000000000000000000101241402370562100203300ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Ian Reinhart Geiser * * 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 "QXmppInvokable.h" #include #include #include #include /// Constructs a QXmppInvokable with the specified \a parent. /// /// \param parent QXmppInvokable::QXmppInvokable(QObject *parent) : QObject(parent) { } /// Destroys a QXmppInvokable. QXmppInvokable::~QXmppInvokable() { } QVariant QXmppInvokable::dispatch(const QByteArray &method, const QList &args) { buildMethodHash(); if (!m_methodHash.contains(method)) return QVariant(); int idx = m_methodHash[method]; if (paramTypes(args) != metaObject()->method(idx).parameterTypes()) return QVariant(); const char *typeName = metaObject()->method(idx).typeName(); int resultType = QMetaType::type(typeName); void *result = QMetaType::create(resultType, nullptr); QGenericReturnArgument ret(typeName, result); QList genericArgs; QList::ConstIterator iter = args.begin(); while (iter != args.end()) { const void *data = iter->data(); const char *name = iter->typeName(); genericArgs << QGenericArgument(name, data); ++iter; } if (QMetaObject::invokeMethod(this, method.constData(), ret, genericArgs.value(0, QGenericArgument()), genericArgs.value(1, QGenericArgument()), genericArgs.value(2, QGenericArgument()), genericArgs.value(3, QGenericArgument()), genericArgs.value(4, QGenericArgument()), genericArgs.value(5, QGenericArgument()), genericArgs.value(6, QGenericArgument()), genericArgs.value(7, QGenericArgument()), genericArgs.value(8, QGenericArgument()), genericArgs.value(9, QGenericArgument()))) { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) QVariant returnValue(QMetaType(resultType), result); #else QVariant returnValue(resultType, result); #endif QMetaType::destroy(resultType, result); return returnValue; } else { qDebug("No such method '%s'", method.constData()); return QVariant(); } } QList QXmppInvokable::paramTypes(const QList ¶ms) { QList types; for (const auto &variant : qAsConst(params)) types << variant.typeName(); return types; } void QXmppInvokable::buildMethodHash() { QWriteLocker locker(&m_lock); if (m_methodHash.size() > 0) return; int methodCount = metaObject()->methodCount(); for (int idx = 0; idx < methodCount; ++idx) { QByteArray signature = metaObject()->method(idx).methodSignature(); m_methodHash[signature.left(signature.indexOf('('))] = idx; // qDebug() << metaObject()->method(idx).parameterTypes(); } } QStringList QXmppInvokable::interfaces() const { QStringList results; int methodCount = metaObject()->methodCount(); for (int idx = 0; idx < methodCount; ++idx) { if (metaObject()->method(idx).methodType() == QMetaMethod::Slot) { QByteArray signature = metaObject()->method(idx).methodSignature(); results << signature.left(signature.indexOf('(')); } } return results; } qxmpp-1.4.0/src/client/QXmppInvokable.h000066400000000000000000000046351402370562100200070ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Ian Reinhart Geiser * * 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 QXMPPINVOKABLE_H #define QXMPPINVOKABLE_H #include "QXmppGlobal.h" #include #include #include #include #include /** This is the base class for all objects that will be invokable via RPC. All public slots of objects derived from this class will be exposed to the RPC interface. As a note for all methods, they can only understand types that QVariant knows about. @author Ian Reinhart Geiser */ class QXMPP_EXPORT QXmppInvokable : public QObject { Q_OBJECT public: QXmppInvokable(QObject *parent = nullptr); ~QXmppInvokable() override; /** * Execute a method on an object. with a set of arguments. This method is reentrant, and the method * that is invoked will be done in a thread safe manner. It should be noted that while this method * is threadsafe and reentrant the side affects of the methods invoked may not be. */ QVariant dispatch(const QByteArray &method, const QList &args = QList()); /** * Utility method to convert a QList to a list of types for type * checking. */ static QList paramTypes(const QList ¶ms); /** * Reimplement this method to return a true if the invoking JID is allowed to execute the method. */ virtual bool isAuthorized(const QString &jid) const = 0; public Q_SLOTS: /** * This provides a list of interfaces for introspection of the presented interface. */ QStringList interfaces() const; private: void buildMethodHash(); QHash m_methodHash; QReadWriteLock m_lock; }; #endif qxmpp-1.4.0/src/client/QXmppMamManager.cpp000066400000000000000000000122421402370562100204260ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Niels Ole Salscheider * * 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 "QXmppMamManager.h" #include "QXmppClient.h" #include "QXmppConstants_p.h" #include "QXmppDataForm.h" #include "QXmppMamIq.h" #include "QXmppMessage.h" #include "QXmppUtils.h" #include /// \cond QStringList QXmppMamManager::discoveryFeatures() const { // XEP-0313: Message Archive Management return QStringList() << ns_mam; } bool QXmppMamManager::handleStanza(const QDomElement &element) { if (element.tagName() == "message") { QDomElement resultElement = element.firstChildElement("result"); if (!resultElement.isNull() && resultElement.namespaceURI() == ns_mam) { QDomElement forwardedElement = resultElement.firstChildElement("forwarded"); QString queryId = resultElement.attribute("queryid"); if (!forwardedElement.isNull() && forwardedElement.namespaceURI() == ns_forwarding) { QDomElement messageElement = forwardedElement.firstChildElement("message"); QDomElement delayElement = forwardedElement.firstChildElement("delay"); if (!messageElement.isNull()) { QXmppMessage message; message.parse(messageElement); if (!delayElement.isNull() && delayElement.namespaceURI() == ns_delayed_delivery) { const QString stamp = delayElement.attribute("stamp"); message.setStamp(QXmppUtils::datetimeFromString(stamp)); } emit archivedMessageReceived(queryId, message); } } return true; } } else if (QXmppMamResultIq::isMamResultIq(element)) { QXmppMamResultIq result; result.parse(element); emit resultsRecieved(result.id(), result.resultSetReply(), result.complete()); return true; } return false; } /// \endcond /// Retrieves archived messages. For each received message, the /// archiveMessageReceived() signal is emitted. Once all messages are received, /// the resultsRecieved() signal is emitted. It returns a result set that can /// be used to page through the results. /// The number of results may be limited by the server. /// /// \param to Optional entity that should be queried. Leave this empty to query /// the local archive. /// \param node Optional node that should be queried. This is used when querying /// a pubsub node. /// \param jid Optional JID to filter the results. /// \param start Optional start time to filter the results. /// \param end Optional end time to filter the results. /// \param resultSetQuery Optional Result Set Management query. This can be used /// to limit the number of results and to page through the /// archive. /// \return query id of the request. This can be used to associate the /// corresponding resultsRecieved signal. /// QString QXmppMamManager::retrieveArchivedMessages(const QString &to, const QString &node, const QString &jid, const QDateTime &start, const QDateTime &end, const QXmppResultSetQuery &resultSetQuery) { QList fields; QXmppDataForm::Field hiddenField(QXmppDataForm::Field::HiddenField); hiddenField.setKey("FORM_TYPE"); hiddenField.setValue(ns_mam); fields << hiddenField; if (!jid.isEmpty()) { QXmppDataForm::Field jidField; jidField.setKey("with"); jidField.setValue(jid); fields << jidField; } if (start.isValid()) { QXmppDataForm::Field startField; startField.setKey("start"); startField.setValue(QXmppUtils::datetimeToString(start)); fields << startField; } if (end.isValid()) { QXmppDataForm::Field endField; endField.setKey("end"); endField.setValue(QXmppUtils::datetimeToString(end)); fields << endField; } QXmppDataForm form; form.setType(QXmppDataForm::Submit); form.setFields(fields); QXmppMamQueryIq queryIq; QString queryId = queryIq.id(); /* reuse the IQ id as query id */ queryIq.setTo(to); queryIq.setNode(node); queryIq.setQueryId(queryId); queryIq.setForm(form); queryIq.setResultSetQuery(resultSetQuery); client()->sendPacket(queryIq); return queryId; } qxmpp-1.4.0/src/client/QXmppMamManager.h000066400000000000000000000046321402370562100200770ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Niels Ole Salscheider * * 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 QXMPPMAMMANAGER_H #define QXMPPMAMMANAGER_H #include "QXmppClientExtension.h" #include "QXmppResultSet.h" #include class QXmppMessage; /// /// \brief The QXmppMamManager class makes it possible to access message /// archives as defined by \xep{0313}: Message Archive Management. /// /// To make use of this manager, you need to instantiate it and load it into /// the QXmppClient instance as follows: /// /// \code /// QXmppMamManager *manager = new QXmppMamManager; /// client->addExtension(manager); /// \endcode /// /// \ingroup Managers /// /// \since QXmpp 1.0 /// class QXMPP_EXPORT QXmppMamManager : public QXmppClientExtension { Q_OBJECT public: QString retrieveArchivedMessages(const QString &to = QString(), const QString &node = QString(), const QString &jid = QString(), const QDateTime &start = QDateTime(), const QDateTime &end = QDateTime(), const QXmppResultSetQuery &resultSetQuery = QXmppResultSetQuery()); /// \cond QStringList discoveryFeatures() const override; bool handleStanza(const QDomElement &element) override; /// \endcond Q_SIGNALS: /// This signal is emitted when an archived message is received void archivedMessageReceived(const QString &queryId, const QXmppMessage &message); /// This signal is emitted when all results for a request have been received void resultsRecieved(const QString &queryId, const QXmppResultSetReply &resultSetReply, bool complete); }; #endif qxmpp-1.4.0/src/client/QXmppMessageReceiptManager.cpp000066400000000000000000000043701402370562100226170ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Georg Rudoy * Jeremy Lainé * * 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 "QXmppMessageReceiptManager.h" #include "QXmppClient.h" #include "QXmppConstants_p.h" #include "QXmppMessage.h" #include "QXmppUtils.h" #include /// Constructs a QXmppMessageReceiptManager to handle incoming and outgoing /// message delivery receipts. QXmppMessageReceiptManager::QXmppMessageReceiptManager() : QXmppClientExtension() { } /// \cond QStringList QXmppMessageReceiptManager::discoveryFeatures() const { return QStringList(ns_message_receipts); } bool QXmppMessageReceiptManager::handleStanza(const QDomElement &stanza) { if (stanza.tagName() != "message") return false; QXmppMessage message; message.parse(stanza); if (message.type() == QXmppMessage::Error) return false; // Handle receipts and cancel any further processing. if (!message.receiptId().isEmpty()) { // 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; } // If requested, send a receipt. if (message.isReceiptRequested() && !message.from().isEmpty() && !message.id().isEmpty()) { QXmppMessage receipt; receipt.setTo(message.from()); receipt.setReceiptId(message.id()); client()->sendPacket(receipt); } // Continue processing. return false; } /// \endcond qxmpp-1.4.0/src/client/QXmppMessageReceiptManager.h000066400000000000000000000031131402370562100222560ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Georg Rudoy * Jeremy Lainé * * 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 QXMPPMESSAGERECEIPTMANAGER_H #define QXMPPMESSAGERECEIPTMANAGER_H #include "QXmppClientExtension.h" /// \brief The QXmppMessageReceiptManager class makes it possible to /// send and receive message delivery receipts as defined in /// \xep{0184}: Message Delivery Receipts. /// /// \ingroup Managers class QXMPP_EXPORT QXmppMessageReceiptManager : public QXmppClientExtension { Q_OBJECT public: QXmppMessageReceiptManager(); /// \cond QStringList discoveryFeatures() const override; bool handleStanza(const QDomElement &stanza) override; /// \endcond Q_SIGNALS: /// This signal is emitted when receipt for the message with the /// given id is received. The id could be previously obtained by /// calling QXmppMessage::id(). void messageDelivered(const QString &jid, const QString &id); }; #endif // QXMPPMESSAGERECEIPTMANAGER_H qxmpp-1.4.0/src/client/QXmppMucManager.cpp000066400000000000000000000441751402370562100204520ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppMucManager.h" #include "QXmppClient.h" #include "QXmppConstants_p.h" #include "QXmppDiscoveryManager.h" #include "QXmppMessage.h" #include "QXmppMucIq.h" #include "QXmppUtils.h" #include #include class QXmppMucManagerPrivate { public: QMap rooms; }; class QXmppMucRoomPrivate { public: QString ownJid() const { return jid + "/" + nickName; } QXmppClient *client; QXmppDiscoveryManager *discoManager; QXmppMucRoom::Actions allowedActions; QString jid; QString name; QMap participants; QString password; QMap permissions; QSet permissionsQueue; QString nickName; QString subject; }; /// Constructs a new QXmppMucManager. QXmppMucManager::QXmppMucManager() { d = new QXmppMucManagerPrivate; } /// Destroys a QXmppMucManager. QXmppMucManager::~QXmppMucManager() { delete d; } /// Adds the given chat room to the set of managed rooms. /// /// \param roomJid QXmppMucRoom *QXmppMucManager::addRoom(const QString &roomJid) { QXmppMucRoom *room = d->rooms.value(roomJid); if (!room) { room = new QXmppMucRoom(client(), roomJid, this); d->rooms.insert(roomJid, room); connect(room, &QObject::destroyed, this, &QXmppMucManager::_q_roomDestroyed); // emit signal emit roomAdded(room); } return room; } QList QXmppMucManager::rooms() const { return d->rooms.values(); } /// \cond QStringList QXmppMucManager::discoveryFeatures() const { // XEP-0045: Multi-User Chat return QStringList() << ns_muc << ns_muc_admin << ns_muc_owner << ns_muc_user << ns_conference; } bool QXmppMucManager::handleStanza(const QDomElement &element) { if (element.tagName() == "iq") { if (QXmppMucAdminIq::isMucAdminIq(element)) { QXmppMucAdminIq iq; iq.parse(element); QXmppMucRoom *room = d->rooms.value(iq.from()); if (room && iq.type() == QXmppIq::Result && room->d->permissionsQueue.remove(iq.id())) { for (const auto &item : iq.items()) { const QString jid = item.jid(); if (!room->d->permissions.contains(jid)) room->d->permissions.insert(jid, item); } if (room->d->permissionsQueue.isEmpty()) { emit room->permissionsReceived(room->d->permissions.values()); } return true; } } else if (QXmppMucOwnerIq::isMucOwnerIq(element)) { QXmppMucOwnerIq iq; iq.parse(element); QXmppMucRoom *room = d->rooms.value(iq.from()); if (room && iq.type() == QXmppIq::Result && !iq.form().isNull()) { emit room->configurationReceived(iq.form()); return true; } } } return false; } void QXmppMucManager::setClient(QXmppClient *client) { QXmppClientExtension::setClient(client); connect(client, &QXmppClient::messageReceived, this, &QXmppMucManager::_q_messageReceived); } /// \endcond void QXmppMucManager::_q_messageReceived(const QXmppMessage &msg) { if (msg.type() != QXmppMessage::Normal) return; // process room invitations const QString roomJid = msg.mucInvitationJid(); if (!roomJid.isEmpty() && (!d->rooms.contains(roomJid) || !d->rooms.value(roomJid)->isJoined())) { emit invitationReceived(roomJid, msg.from(), msg.mucInvitationReason()); } } void QXmppMucManager::_q_roomDestroyed(QObject *object) { const QString key = d->rooms.key(static_cast(object)); d->rooms.remove(key); } /// Constructs a new QXmppMucRoom. /// /// \param parent QXmppMucRoom::QXmppMucRoom(QXmppClient *client, const QString &jid, QObject *parent) : QObject(parent) { d = new QXmppMucRoomPrivate; d->allowedActions = NoAction; d->client = client; d->discoManager = client->findExtension(); d->jid = jid; connect(d->client, &QXmppClient::disconnected, this, &QXmppMucRoom::_q_disconnected); connect(d->client, &QXmppClient::messageReceived, this, &QXmppMucRoom::_q_messageReceived); connect(d->client, &QXmppClient::presenceReceived, this, &QXmppMucRoom::_q_presenceReceived); if (d->discoManager) { connect(d->discoManager, &QXmppDiscoveryManager::infoReceived, this, &QXmppMucRoom::_q_discoveryInfoReceived); } // convenience signals for properties connect(this, &QXmppMucRoom::joined, this, &QXmppMucRoom::isJoinedChanged); connect(this, &QXmppMucRoom::left, this, &QXmppMucRoom::isJoinedChanged); } /// Destroys a QXmppMucRoom. QXmppMucRoom::~QXmppMucRoom() { delete d; } QXmppMucRoom::Actions QXmppMucRoom::allowedActions() const { return d->allowedActions; } /// Bans the specified user from the chat room. /// /// The specified \a jid is the Bare JID of the form "user@host". /// /// \return true if the request was sent, false otherwise bool QXmppMucRoom::ban(const QString &jid, const QString &reason) { if (!QXmppUtils::jidToResource(jid).isEmpty()) { qWarning("QXmppMucRoom::ban expects a bare JID"); return false; } QXmppMucItem item; item.setAffiliation(QXmppMucItem::OutcastAffiliation); item.setJid(jid); item.setReason(reason); QXmppMucAdminIq iq; iq.setType(QXmppIq::Set); iq.setTo(d->jid); iq.setItems(QList() << item); return d->client->sendPacket(iq); } bool QXmppMucRoom::isJoined() const { return d->participants.contains(d->ownJid()); } QString QXmppMucRoom::jid() const { return d->jid; } /// Joins the chat room. /// /// \return true if the request was sent, false otherwise bool QXmppMucRoom::join() { if (isJoined() || d->nickName.isEmpty()) return false; // reflect our current presence in the chat room QXmppPresence packet = d->client->clientPresence(); packet.setTo(d->ownJid()); packet.setType(QXmppPresence::Available); packet.setMucPassword(d->password); packet.setMucSupported(true); return d->client->sendPacket(packet); } /// Kicks the specified user from the chat room. /// /// The specified \a jid is the Occupant JID of the form "room@service/nick". /// /// \return true if the request was sent, false otherwise bool QXmppMucRoom::kick(const QString &jid, const QString &reason) { QXmppMucItem item; item.setNick(QXmppUtils::jidToResource(jid)); item.setRole(QXmppMucItem::NoRole); item.setReason(reason); QXmppMucAdminIq iq; iq.setType(QXmppIq::Set); iq.setTo(d->jid); iq.setItems(QList() << item); return d->client->sendPacket(iq); } /// Leaves the chat room. /// /// \param message An optional message. /// /// \return true if the request was sent, false otherwise bool QXmppMucRoom::leave(const QString &message) { QXmppPresence packet; packet.setTo(d->ownJid()); packet.setType(QXmppPresence::Unavailable); packet.setStatusText(message); return d->client->sendPacket(packet); } QString QXmppMucRoom::name() const { return d->name; } QString QXmppMucRoom::nickName() const { return d->nickName; } /// Invites a user to the chat room. /// /// \param jid /// \param reason /// /// \return true if the request was sent, false otherwise bool QXmppMucRoom::sendInvitation(const QString &jid, const QString &reason) { QXmppMessage message; message.setTo(jid); message.setType(QXmppMessage::Normal); message.setMucInvitationJid(d->jid); message.setMucInvitationReason(reason); return d->client->sendPacket(message); } /// Sends a message to the room. /// /// \return true if the request was sent, false otherwise bool QXmppMucRoom::sendMessage(const QString &text) { QXmppMessage msg; msg.setTo(d->jid); msg.setType(QXmppMessage::GroupChat); msg.setBody(text); return d->client->sendPacket(msg); } /// Sets your own nickname. /// /// You need to set your nickname before calling join(). /// /// \param nickName void QXmppMucRoom::setNickName(const QString &nickName) { if (nickName == d->nickName) return; // if we had already joined the room, request nickname change if (isJoined()) { QXmppPresence packet = d->client->clientPresence(); packet.setTo(d->jid + "/" + nickName); packet.setType(QXmppPresence::Available); d->client->sendPacket(packet); } else { d->nickName = nickName; emit nickNameChanged(nickName); } } /// Returns the "Full JID" of the given participant. /// /// The specified \a jid is the Occupant JID of the form "room@service/nick". QString QXmppMucRoom::participantFullJid(const QString &jid) const { if (d->participants.contains(jid)) return d->participants.value(jid).mucItem().jid(); else return QString(); } /// Returns the presence for the given participant. /// /// The specified \a jid is the Occupant JID of the form "room@service/nick". QXmppPresence QXmppMucRoom::participantPresence(const QString &jid) const { if (d->participants.contains(jid)) return d->participants.value(jid); QXmppPresence presence; presence.setFrom(jid); presence.setType(QXmppPresence::Unavailable); return presence; } QStringList QXmppMucRoom::participants() const { return d->participants.keys(); } QString QXmppMucRoom::password() const { return d->password; } /// Sets the chat room password. /// /// \param password void QXmppMucRoom::setPassword(const QString &password) { d->password = password; } QString QXmppMucRoom::subject() const { return d->subject; } /// Sets the chat room's subject. /// /// \param subject void QXmppMucRoom::setSubject(const QString &subject) { QXmppMessage msg; msg.setTo(d->jid); msg.setType(QXmppMessage::GroupChat); msg.setSubject(subject); d->client->sendPacket(msg); } /// Request the configuration form for the chat room. /// /// \return true if the request was sent, false otherwise /// /// \sa configurationReceived() bool QXmppMucRoom::requestConfiguration() { QXmppMucOwnerIq iq; iq.setTo(d->jid); return d->client->sendPacket(iq); } /// Send the configuration form for the chat room. /// /// \param form /// /// \return true if the request was sent, false otherwise bool QXmppMucRoom::setConfiguration(const QXmppDataForm &form) { QXmppMucOwnerIq iqPacket; iqPacket.setType(QXmppIq::Set); iqPacket.setTo(d->jid); iqPacket.setForm(form); return d->client->sendPacket(iqPacket); } /// Request the room's permissions. /// /// \return true if the request was sent, false otherwise /// /// \sa permissionsReceived() bool QXmppMucRoom::requestPermissions() { QList affiliations; affiliations << QXmppMucItem::OwnerAffiliation; affiliations << QXmppMucItem::AdminAffiliation; affiliations << QXmppMucItem::MemberAffiliation; affiliations << QXmppMucItem::OutcastAffiliation; d->permissions.clear(); d->permissionsQueue.clear(); for (const auto &affiliation : qAsConst(affiliations)) { QXmppMucItem item; item.setAffiliation(affiliation); QXmppMucAdminIq iq; iq.setTo(d->jid); iq.setItems(QList() << item); if (!d->client->sendPacket(iq)) return false; d->permissionsQueue += iq.id(); } return true; } /// Sets the room's permissions. /// /// \param permissions /// /// \return true if the request was sent, false otherwise bool QXmppMucRoom::setPermissions(const QList &permissions) { QList items; // Process changed members for (const auto &item : qAsConst(permissions)) { const QString jid = item.jid(); if (d->permissions.value(jid).affiliation() != item.affiliation()) items << item; d->permissions.remove(jid); } // Process deleted members const auto &jids = d->permissions.keys(); for (const auto &jid : jids) { QXmppMucItem item; item.setAffiliation(QXmppMucItem::NoAffiliation); item.setJid(jid); items << item; d->permissions.remove(jid); } // Don't send request if there are no changes if (items.isEmpty()) return false; QXmppMucAdminIq iq; iq.setTo(d->jid); iq.setType(QXmppIq::Set); iq.setItems(items); return d->client->sendPacket(iq); } void QXmppMucRoom::_q_disconnected() { const bool wasJoined = isJoined(); // clear chat room participants const QStringList removed = d->participants.keys(); d->participants.clear(); for (const auto &jid : removed) emit participantRemoved(jid); emit participantsChanged(); // update available actions if (d->allowedActions != NoAction) { d->allowedActions = NoAction; emit allowedActionsChanged(d->allowedActions); } // emit "left" signal if we had joined the room if (wasJoined) emit left(); } void QXmppMucRoom::_q_discoveryInfoReceived(const QXmppDiscoveryIq &iq) { if (iq.from() == d->jid) { QString name; const auto &identities = iq.identities(); for (const auto &identity : identities) { if (identity.category() == "conference") { name = identity.name(); break; } } if (name != d->name) { d->name = name; emit nameChanged(name); } } } void QXmppMucRoom::_q_messageReceived(const QXmppMessage &message) { if (QXmppUtils::jidToBareJid(message.from()) != d->jid) return; // handle message subject const QString subject = message.subject(); if (!subject.isEmpty()) { d->subject = subject; emit subjectChanged(subject); } emit messageReceived(message); } void QXmppMucRoom::_q_presenceReceived(const QXmppPresence &presence) { const QString jid = presence.from(); // if our own presence changes, reflect it in the chat room if (isJoined() && jid == d->client->configuration().jid()) { QXmppPresence packet = d->client->clientPresence(); packet.setTo(d->ownJid()); d->client->sendPacket(packet); } if (QXmppUtils::jidToBareJid(jid) != d->jid) return; if (presence.type() == QXmppPresence::Available) { const bool added = !d->participants.contains(jid); d->participants.insert(jid, presence); // refresh allowed actions if (jid == d->ownJid()) { QXmppMucItem mucItem = presence.mucItem(); Actions newActions = NoAction; // role if (mucItem.role() == QXmppMucItem::ModeratorRole) newActions |= (KickAction | SubjectAction); // affiliation if (mucItem.affiliation() == QXmppMucItem::OwnerAffiliation) newActions |= (ConfigurationAction | PermissionsAction | SubjectAction); else if (mucItem.affiliation() == QXmppMucItem::AdminAffiliation) newActions |= (PermissionsAction | SubjectAction); if (newActions != d->allowedActions) { d->allowedActions = newActions; emit allowedActionsChanged(d->allowedActions); } } if (added) { emit participantAdded(jid); emit participantsChanged(); if (jid == d->ownJid()) { // request room information if (d->discoManager) d->discoManager->requestInfo(d->jid); emit joined(); } } else { emit participantChanged(jid); } } else if (presence.type() == QXmppPresence::Unavailable) { if (d->participants.contains(jid)) { d->participants.insert(jid, presence); emit participantRemoved(jid); d->participants.remove(jid); emit participantsChanged(); // check whether this was our own presence if (jid == d->ownJid()) { const QString newNick = presence.mucItem().nick(); if (!newNick.isEmpty() && newNick != d->nickName) { d->nickName = newNick; emit nickNameChanged(newNick); return; } // check whether we were kicked if (presence.mucStatusCodes().contains(307)) { const QString actor = presence.mucItem().actor(); const QString reason = presence.mucItem().reason(); emit kicked(actor, reason); } // clear chat room participants const QStringList removed = d->participants.keys(); d->participants.clear(); for (const auto &jid : removed) emit participantRemoved(jid); emit participantsChanged(); // update available actions if (d->allowedActions != NoAction) { d->allowedActions = NoAction; emit allowedActionsChanged(d->allowedActions); } // notify user we left the room emit left(); } } } else if (presence.type() == QXmppPresence::Error) { if (presence.isMucSupported()) { // emit error emit error(presence.error()); // notify the user we left the room emit left(); } } } qxmpp-1.4.0/src/client/QXmppMucManager.h000066400000000000000000000212211402370562100201020ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPMUCMANAGER_H #define QXMPPMUCMANAGER_H #include "QXmppClientExtension.h" #include "QXmppMucIq.h" #include "QXmppPresence.h" class QXmppDataForm; class QXmppDiscoveryIq; class QXmppMessage; class QXmppMucManagerPrivate; class QXmppMucRoom; class QXmppMucRoomPrivate; /// \brief The QXmppMucManager class makes it possible to interact with /// multi-user chat rooms as defined by \xep{0045}: Multi-User Chat. /// /// To make use of this manager, you need to instantiate it and load it into /// the QXmppClient instance as follows: /// /// \code /// QXmppMucManager *manager = new QXmppMucManager; /// client->addExtension(manager); /// \endcode /// /// You can then join a room as follows: /// /// \code /// QXmppMucRoom *room = manager->addRoom("room@conference.example.com"); /// room->setNickName("mynick"); /// room->join(); /// \endcode /// /// \ingroup Managers class QXMPP_EXPORT QXmppMucManager : public QXmppClientExtension { Q_OBJECT Q_PROPERTY(QList rooms READ rooms NOTIFY roomAdded) public: QXmppMucManager(); ~QXmppMucManager() override; QXmppMucRoom *addRoom(const QString &roomJid); // documentation needs to be here, see https://stackoverflow.com/questions/49192523/ /// Returns the list of managed rooms. QList rooms() const; /// \cond QStringList discoveryFeatures() const override; bool handleStanza(const QDomElement &element) override; /// \endcond Q_SIGNALS: /// This signal is emitted when an invitation to a chat room is received. void invitationReceived(const QString &roomJid, const QString &inviter, const QString &reason); /// This signal is emitted when a new room is managed. void roomAdded(QXmppMucRoom *room); protected: /// \cond void setClient(QXmppClient *client) override; /// \endcond private Q_SLOTS: void _q_messageReceived(const QXmppMessage &message); void _q_roomDestroyed(QObject *object); private: QXmppMucManagerPrivate *d; }; /// \brief The QXmppMucRoom class represents a multi-user chat room /// as defined by \xep{0045}: Multi-User Chat. /// /// \sa QXmppMucManager class QXMPP_EXPORT QXmppMucRoom : public QObject { Q_OBJECT Q_FLAGS(Action Actions) /// The actions you are allowed to perform on the room Q_PROPERTY(QXmppMucRoom::Actions allowedActions READ allowedActions NOTIFY allowedActionsChanged) /// Whether you are currently in the room Q_PROPERTY(bool isJoined READ isJoined NOTIFY isJoinedChanged) /// The chat room's bare JID Q_PROPERTY(QString jid READ jid CONSTANT) /// The chat room's human-readable name Q_PROPERTY(QString name READ name NOTIFY nameChanged) /// Your own nickname Q_PROPERTY(QString nickName READ nickName WRITE setNickName NOTIFY nickNameChanged) /// The list of participant JIDs Q_PROPERTY(QStringList participants READ participants NOTIFY participantsChanged) /// The chat room password Q_PROPERTY(QString password READ password WRITE setPassword) /// The room's subject Q_PROPERTY(QString subject READ subject WRITE setSubject NOTIFY subjectChanged) public: /// This enum is used to describe chat room actions. enum Action { NoAction = 0, ///< no action SubjectAction = 1, ///< change the room's subject ConfigurationAction = 2, ///< change the room's configuration PermissionsAction = 4, ///< change the room's permissions KickAction = 8 ///< kick users from the room }; Q_DECLARE_FLAGS(Actions, Action) ~QXmppMucRoom() override; // documentation needs to be here, see https://stackoverflow.com/questions/49192523/ /// Returns the actions you are allowed to perform on the room. Actions allowedActions() const; // documentation needs to be here, see https://stackoverflow.com/questions/49192523/ /// Returns true if you are currently in the room. bool isJoined() const; // documentation needs to be here, see https://stackoverflow.com/questions/49192523/ /// Returns the chat room's bare JID. QString jid() const; // documentation needs to be here, see https://stackoverflow.com/questions/49192523/ /// /// Returns the chat room's human-readable name. /// /// This name will only be available after the room has been joined. /// QString name() const; // documentation needs to be here, see https://stackoverflow.com/questions/49192523/ /// Returns your own nickname. QString nickName() const; void setNickName(const QString &nickName); Q_INVOKABLE QString participantFullJid(const QString &jid) const; QXmppPresence participantPresence(const QString &jid) const; // documentation needs to be here, see https://stackoverflow.com/questions/49192523/ /// /// Returns the list of participant JIDs. /// /// These JIDs are Occupant JIDs of the form "room@service/nick". /// QStringList participants() const; // documentation needs to be here, see https://stackoverflow.com/questions/49192523/ /// Returns the chat room password. QString password() const; void setPassword(const QString &password); // documentation needs to be here, see https://stackoverflow.com/questions/49192523/ /// Returns the room's subject. QString subject() const; void setSubject(const QString &subject); Q_SIGNALS: /// This signal is emitted when the allowed actions change. void allowedActionsChanged(QXmppMucRoom::Actions actions) const; /// This signal is emitted when the configuration form for the room is received. void configurationReceived(const QXmppDataForm &configuration); /// This signal is emitted when an error is encountered. void error(const QXmppStanza::Error &error); /// This signal is emitted once you have joined the room. void joined(); /// This signal is emitted if you get kicked from the room. void kicked(const QString &jid, const QString &reason); /// \cond void isJoinedChanged(); /// \endcond /// This signal is emitted once you have left the room. void left(); /// This signal is emitted when a message is received. void messageReceived(const QXmppMessage &message); /// This signal is emitted when the room's human-readable name changes. void nameChanged(const QString &name); /// This signal is emitted when your own nick name changes. void nickNameChanged(const QString &nickName); /// This signal is emitted when a participant joins the room. void participantAdded(const QString &jid); /// This signal is emitted when a participant changes. void participantChanged(const QString &jid); /// This signal is emitted when a participant leaves the room. void participantRemoved(const QString &jid); /// \cond void participantsChanged(); /// \endcond /// This signal is emitted when the room's permissions are received. void permissionsReceived(const QList &permissions); /// This signal is emitted when the room's subject changes. void subjectChanged(const QString &subject); public Q_SLOTS: bool ban(const QString &jid, const QString &reason); bool join(); bool kick(const QString &jid, const QString &reason); bool leave(const QString &message = QString()); bool requestConfiguration(); bool requestPermissions(); bool setConfiguration(const QXmppDataForm &form); bool setPermissions(const QList &permissions); bool sendInvitation(const QString &jid, const QString &reason); bool sendMessage(const QString &text); private Q_SLOTS: void _q_disconnected(); void _q_discoveryInfoReceived(const QXmppDiscoveryIq &iq); void _q_messageReceived(const QXmppMessage &message); void _q_presenceReceived(const QXmppPresence &presence); private: QXmppMucRoom(QXmppClient *client, const QString &jid, QObject *parent); QXmppMucRoomPrivate *d; friend class QXmppMucManager; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QXmppMucRoom::Actions) #endif qxmpp-1.4.0/src/client/QXmppOutgoingClient.cpp000066400000000000000000000722401402370562100213570ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Manjeet Dahiya * Jeremy Lainé * * 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 "QXmppOutgoingClient.h" #include "QXmppConfiguration.h" #include "QXmppConstants_p.h" #include "QXmppIq.h" #include "QXmppLogger.h" #include "QXmppMessage.h" #include "QXmppNonSASLAuth.h" #include "QXmppPresence.h" #include "QXmppSasl_p.h" #include "QXmppStreamFeatures.h" #include "QXmppStreamManagement_p.h" #include "QXmppUtils.h" #include #include #include #include #include #include // IQ types #include "QXmppBindIq.h" #include "QXmppPingIq.h" #include "QXmppSessionIq.h" #include #include #include #include #include #include #include #include class QXmppOutgoingClientPrivate { public: QXmppOutgoingClientPrivate(QXmppOutgoingClient *q); void connectToHost(const QString &host, quint16 port); void connectToNextDNSHost(); void sendNonSASLAuth(bool plaintext); void sendNonSASLAuthQuery(); void sendBind(); void sendSessionStart(); void sendStreamManagementEnable(); // This object provides the configuration // required for connecting to the XMPP server. QXmppConfiguration config; QXmppStanza::Error::Condition xmppStreamError; // DNS QDnsLookup dns; int nextSrvRecordIdx; // Stream QString streamId; QString streamFrom; QString streamVersion; // Redirection QString redirectHost; quint16 redirectPort; // Session QString bindId; QString sessionId; bool bindModeAvailable; bool sessionAvailable; bool sessionStarted; // Authentication bool isAuthenticated; QString nonSASLAuthId; QXmppSaslClient *saslClient; // Stream Management bool streamManagementAvailable; QString smId; bool canResume; bool isResuming; QString resumeHost; quint16 resumePort; bool streamManagementEnabled; bool streamResumed; // Client State Indication bool clientStateIndicationEnabled; // Timers QTimer *pingTimer; QTimer *timeoutTimer; private: QXmppOutgoingClient *q; }; QXmppOutgoingClientPrivate::QXmppOutgoingClientPrivate(QXmppOutgoingClient *qq) : nextSrvRecordIdx(0), redirectPort(0), bindModeAvailable(false), sessionAvailable(false), sessionStarted(false), isAuthenticated(false), saslClient(nullptr), streamManagementAvailable(false), canResume(false), isResuming(false), resumePort(0), streamManagementEnabled(false), streamResumed(false), clientStateIndicationEnabled(false), pingTimer(nullptr), timeoutTimer(nullptr), q(qq) { } 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()) { QSslConfiguration newSslConfig; newSslConfig.setCaCertificates(config.caCertificates()); q->socket()->setSslConfiguration(newSslConfig); } // respect proxy q->socket()->setProxy(config.networkProxy()); // set the name the SSL certificate should match q->socket()->setPeerVerifyName(config.domain()); // connect to host const QXmppConfiguration::StreamSecurityMode localSecurity = q->configuration().streamSecurityMode(); if (localSecurity == QXmppConfiguration::LegacySSL) { if (!q->socket()->supportsSsl()) { q->warning("Not connecting as legacy SSL was requested, but SSL support is not available"); return; } q->socket()->connectToHostEncrypted(host, port); } else { q->socket()->connectToHost(host, port); } } void QXmppOutgoingClientPrivate::connectToNextDNSHost() { auto curIdx = nextSrvRecordIdx++; connectToHost( dns.serviceRecords().at(curIdx).target(), dns.serviceRecords().at(curIdx).port()); } /// Constructs an outgoing client stream. /// /// \param parent QXmppOutgoingClient::QXmppOutgoingClient(QObject *parent) : QXmppStream(parent), d(new QXmppOutgoingClientPrivate(this)) { // initialise socket auto *socket = new QSslSocket(this); setSocket(socket); connect(socket, &QAbstractSocket::disconnected, this, &QXmppOutgoingClient::_q_socketDisconnected); connect(socket, QOverload &>::of(&QSslSocket::sslErrors), this, &QXmppOutgoingClient::socketSslErrors); #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) connect(socket, &QSslSocket::errorOccurred, this, &QXmppOutgoingClient::socketError); #else connect(socket, QOverload::of(&QSslSocket::error), this, &QXmppOutgoingClient::socketError); #endif // DNS lookups connect(&d->dns, &QDnsLookup::finished, this, &QXmppOutgoingClient::_q_dnsLookupFinished); // XEP-0199: XMPP Ping d->pingTimer = new QTimer(this); connect(d->pingTimer, &QTimer::timeout, this, &QXmppOutgoingClient::pingSend); d->timeoutTimer = new QTimer(this); d->timeoutTimer->setSingleShot(true); connect(d->timeoutTimer, &QTimer::timeout, this, &QXmppOutgoingClient::pingTimeout); connect(this, &QXmppStream::connected, this, &QXmppOutgoingClient::pingStart); connect(this, &QXmppStream::disconnected, this, &QXmppOutgoingClient::pingStop); } /// Destroys an outgoing client stream. QXmppOutgoingClient::~QXmppOutgoingClient() { delete d; } /// Returns a reference to the stream's configuration. QXmppConfiguration &QXmppOutgoingClient::configuration() { return d->config; } /// Attempts to connect to the XMPP server. void QXmppOutgoingClient::connectToHost() { // if a host for resumption is available, connect to it if (d->canResume && !d->resumeHost.isEmpty() && d->resumePort) { d->connectToHost(d->resumeHost, d->resumePort); return; } // if an explicit host was provided, connect to it if (!d->config.host().isEmpty() && d->config.port()) { d->connectToHost(d->config.host(), d->config.port()); return; } // otherwise, lookup server const QString domain = configuration().domain(); debug(QString("Looking up server for domain %1").arg(domain)); d->dns.setName("_xmpp-client._tcp." + domain); d->dns.setType(QDnsLookup::SRV); d->dns.lookup(); d->nextSrvRecordIdx = 0; } /// /// Disconnects from the server and resets the stream management state. /// /// \since QXmpp 1.0 /// void QXmppOutgoingClient::disconnectFromHost() { d->canResume = false; QXmppStream::disconnectFromHost(); } void QXmppOutgoingClient::_q_dnsLookupFinished() { if (d->dns.error() == QDnsLookup::NoError && !d->dns.serviceRecords().isEmpty()) { // take the first returned record d->connectToNextDNSHost(); } else { // as a fallback, use domain as the host name warning(QString("Lookup for domain %1 failed: %2") .arg(d->dns.name(), d->dns.errorString())); d->connectToHost(d->config.domain(), d->config.port()); } } /// Returns true if authentication has succeeded. bool QXmppOutgoingClient::isAuthenticated() const { return d->isAuthenticated; } /// Returns true if the socket is connected and a session has been started. bool QXmppOutgoingClient::isConnected() const { return QXmppStream::isConnected() && d->sessionStarted; } /// /// Returns true if client state indication (xep-0352) is supported by the server /// /// \since QXmpp 1.0 /// bool QXmppOutgoingClient::isClientStateIndicationEnabled() const { return d->clientStateIndicationEnabled; } /// /// Returns whether Stream Management is currently enabled. /// /// \since QXmpp 1.4 /// bool QXmppOutgoingClient::isStreamManagementEnabled() const { return d->streamManagementEnabled; } /// /// Returns true if the current stream is a successful resumption of a previous /// stream. /// /// In case a stream has been resumed, some tasks like fetching the roster again /// are not required. /// /// \since QXmpp 1.4 /// bool QXmppOutgoingClient::isStreamResumed() const { return d->streamResumed; } void QXmppOutgoingClient::_q_socketDisconnected() { debug("Socket disconnected"); d->isAuthenticated = false; if (!d->redirectHost.isEmpty() && d->redirectPort > 0) { d->connectToHost(d->redirectHost, d->redirectPort); d->redirectHost = QString(); d->redirectPort = 0; } else { emit disconnected(); } } void QXmppOutgoingClient::socketSslErrors(const QList &errors) { // log errors warning("SSL errors"); for (int i = 0; i < errors.count(); ++i) warning(errors.at(i).errorString()); // relay signal emit sslErrors(errors); // if configured, ignore the errors if (configuration().ignoreSslErrors()) socket()->ignoreSslErrors(); } void QXmppOutgoingClient::socketError(QAbstractSocket::SocketError socketError) { Q_UNUSED(socketError); if (!d->sessionStarted && (d->dns.serviceRecords().count() > d->nextSrvRecordIdx)) { // some network error occurred during startup -> try next available SRV record server d->connectToNextDNSHost(); } else emit error(QXmppClient::SocketError); } /// \cond void QXmppOutgoingClient::handleStart() { QXmppStream::handleStart(); // reset stream information d->streamId.clear(); d->streamFrom.clear(); d->streamVersion.clear(); // reset authentication step if (d->saslClient) { delete d->saslClient; d->saslClient = nullptr; } // reset session information d->bindId.clear(); d->sessionId.clear(); d->sessionAvailable = false; d->sessionStarted = false; // reset stream management d->streamResumed = false; d->streamManagementEnabled = false; // start stream QByteArray data = ""); sendData(data); } void QXmppOutgoingClient::handleStream(const QDomElement &streamElement) { if (d->streamId.isEmpty()) d->streamId = streamElement.attribute("id"); if (d->streamFrom.isEmpty()) d->streamFrom = streamElement.attribute("from"); if (d->streamVersion.isEmpty()) { d->streamVersion = streamElement.attribute("version"); // no version specified, signals XMPP Version < 1.0. // switch to old auth mechanism if enabled if (d->streamVersion.isEmpty() && configuration().useNonSASLAuthentication()) { d->sendNonSASLAuthQuery(); } } } void QXmppOutgoingClient::handleStanza(const QDomElement &nodeRecv) { // if we receive any kind of data, stop the timeout timer d->timeoutTimer->stop(); const QString ns = nodeRecv.namespaceURI(); // give client opportunity to handle stanza bool handled = false; emit elementReceived(nodeRecv, handled); if (handled) return; if (QXmppStreamFeatures::isStreamFeatures(nodeRecv)) { QXmppStreamFeatures features; features.parse(nodeRecv); if (features.clientStateIndicationMode() == QXmppStreamFeatures::Enabled) d->clientStateIndicationEnabled = true; // handle authentication const bool nonSaslAvailable = features.nonSaslAuthMode() != QXmppStreamFeatures::Disabled; const bool saslAvailable = !features.authMechanisms().isEmpty(); if (saslAvailable && configuration().useSASLAuthentication()) { // supported and preferred SASL auth mechanisms 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()) supportedMechanisms.removeAll("X-MESSENGER-OAUTH2"); if (configuration().googleAccessToken().isEmpty()) supportedMechanisms.removeAll("X-OAUTH2"); // determine SASL Authentication mechanism to use QStringList commonMechanisms; QString usedMechanism; for (const auto &mechanism : qAsConst(supportedMechanisms)) { if (features.authMechanisms().contains(mechanism)) commonMechanisms << mechanism; } if (commonMechanisms.isEmpty()) { warning("No supported SASL Authentication mechanism available"); disconnectFromHost(); return; } else { usedMechanism = commonMechanisms.first(); } d->saslClient = QXmppSaslClient::create(usedMechanism, this); if (!d->saslClient) { warning("SASL mechanism negotiation failed"); disconnectFromHost(); return; } info(QString("SASL mechanism '%1' selected").arg(d->saslClient->mechanism())); d->saslClient->setHost(d->config.domain()); d->saslClient->setServiceType("xmpp"); if (d->saslClient->mechanism() == "X-FACEBOOK-PLATFORM") { d->saslClient->setUsername(configuration().facebookAppId()); d->saslClient->setPassword(configuration().facebookAccessToken()); } else if (d->saslClient->mechanism() == "X-MESSENGER-OAUTH2") { d->saslClient->setPassword(configuration().windowsLiveAccessToken()); } else if (d->saslClient->mechanism() == "X-OAUTH2") { d->saslClient->setUsername(configuration().user()); d->saslClient->setPassword(configuration().googleAccessToken()); } else { d->saslClient->setUsername(configuration().user()); d->saslClient->setPassword(configuration().password()); } // send SASL auth request QByteArray response; if (!d->saslClient->respond(QByteArray(), response)) { warning("SASL initial response failed"); disconnectFromHost(); return; } sendPacket(QXmppSaslAuth(d->saslClient->mechanism(), response)); return; } else if (nonSaslAvailable && configuration().useNonSASLAuthentication()) { d->sendNonSASLAuthQuery(); return; } // store which features are available d->sessionAvailable = (features.sessionMode() != QXmppStreamFeatures::Disabled); d->bindModeAvailable = (features.bindMode() != QXmppStreamFeatures::Disabled); d->streamManagementAvailable = (features.streamManagementMode() != QXmppStreamFeatures::Disabled); // chech whether the stream can be resumed if (d->streamManagementAvailable && d->canResume) { d->isResuming = true; QXmppStreamManagementResume streamManagementResume(lastIncomingSequenceNumber(), d->smId); QByteArray data; QXmlStreamWriter xmlStream(&data); streamManagementResume.toXml(&xmlStream); sendData(data); return; } // check whether bind is available if (d->bindModeAvailable) { d->sendBind(); return; } // check whether session is available if (d->sessionAvailable) { d->sendSessionStart(); return; } // otherwise we are done d->sessionStarted = true; emit connected(); } else if (ns == ns_stream && nodeRecv.tagName() == "error") { // handle redirects const auto otherHost = nodeRecv.firstChildElement("see-other-host"); if (!otherHost.isNull() && setResumeAddress(otherHost.text())) { QXmppStream::disconnectFromHost(); return; } if (!nodeRecv.firstChildElement("conflict").isNull()) d->xmppStreamError = QXmppStanza::Error::Conflict; else if (!nodeRecv.firstChildElement("not-authorized").isNull()) d->xmppStreamError = QXmppStanza::Error::NotAuthorized; else d->xmppStreamError = QXmppStanza::Error::UndefinedCondition; emit error(QXmppClient::XmppStreamError); } else if (ns == ns_sasl) { if (!d->saslClient) { warning("SASL stanza received, but no mechanism selected"); return; } if (nodeRecv.tagName() == "success") { debug("Authenticated"); d->isAuthenticated = true; handleStart(); } else if (nodeRecv.tagName() == "challenge") { QXmppSaslChallenge challenge; challenge.parse(nodeRecv); QByteArray response; if (d->saslClient->respond(challenge.value(), response)) { sendPacket(QXmppSaslResponse(response)); } else { warning("Could not respond to SASL challenge"); disconnectFromHost(); } } else if (nodeRecv.tagName() == "failure") { QXmppSaslFailure failure; failure.parse(nodeRecv); // RFC3920 defines the error condition as "not-authorized", but // some broken servers use "bad-auth" instead. We tolerate this // by remapping the error to "not-authorized". if (failure.condition() == "not-authorized" || failure.condition() == "bad-auth") d->xmppStreamError = QXmppStanza::Error::NotAuthorized; else d->xmppStreamError = QXmppStanza::Error::UndefinedCondition; emit error(QXmppClient::XmppStreamError); warning("Authentication failure"); disconnectFromHost(); } } else if (ns == ns_client) { if (nodeRecv.tagName() == "iq") { QDomElement element = nodeRecv.firstChildElement(); QString id = nodeRecv.attribute("id"); QString type = nodeRecv.attribute("type"); if (type.isEmpty()) warning("QXmppStream: iq type can't be empty"); if (id == d->sessionId) { QXmppSessionIq session; session.parse(nodeRecv); d->sessionStarted = true; if (d->streamManagementAvailable) { d->sendStreamManagementEnable(); } else { // we are connected now emit connected(); } } else if (QXmppBindIq::isBindIq(nodeRecv) && id == d->bindId) { QXmppBindIq bind; bind.parse(nodeRecv); // bind result if (bind.type() == QXmppIq::Result) { if (!bind.jid().isEmpty()) { static const QRegularExpression jidRegex("^([^@/]+)@([^@/]+)/(.+)$"); if (const auto match = jidRegex.match(bind.jid()); match.hasMatch()) { configuration().setUser(match.captured(1)); configuration().setDomain(match.captured(2)); configuration().setResource(match.captured(3)); } else { warning("Bind IQ received with invalid JID: " + bind.jid()); } } if (d->sessionAvailable) { d->sendSessionStart(); } else { d->sessionStarted = true; if (d->streamManagementAvailable) { d->sendStreamManagementEnable(); } else { // we are connected now emit connected(); } } } else if (bind.type() == QXmppIq::Error) { d->xmppStreamError = bind.error().condition(); emit error(QXmppClient::XmppStreamError); warning("Resource binding error received: " + bind.error().text()); disconnectFromHost(); } } // extensions // XEP-0078: Non-SASL Authentication else if (id == d->nonSASLAuthId && type == "result") { // successful Non-SASL Authentication debug("Authenticated (Non-SASL)"); d->isAuthenticated = true; // xmpp connection made d->sessionStarted = true; emit connected(); } else if (QXmppNonSASLAuthIq::isNonSASLAuthIq(nodeRecv)) { if (type == "result") { bool digest = !nodeRecv.firstChildElement("query").firstChildElement("digest").isNull(); bool plain = !nodeRecv.firstChildElement("query").firstChildElement("password").isNull(); bool plainText = false; if (plain && digest) { if (configuration().nonSASLAuthMechanism() == QXmppConfiguration::NonSASLDigest) plainText = false; else plainText = true; } else if (plain) plainText = true; else if (digest) plainText = false; else { warning("No supported Non-SASL Authentication mechanism available"); disconnectFromHost(); return; } d->sendNonSASLAuth(plainText); } } // XEP-0199: XMPP Ping else if (QXmppPingIq::isPingIq(nodeRecv)) { QXmppPingIq req; req.parse(nodeRecv); QXmppIq iq(QXmppIq::Result); iq.setId(req.id()); iq.setTo(req.from()); sendPacket(iq); } else { QXmppIq iqPacket; iqPacket.parse(nodeRecv); // if we didn't understant the iq, reply with error // except for "result" and "error" iqs if (type != "result" && type != "error") { QXmppIq iq(QXmppIq::Error); iq.setId(iqPacket.id()); iq.setTo(iqPacket.from()); QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::FeatureNotImplemented); iq.setError(error); sendPacket(iq); } else { emit iqReceived(iqPacket); } } } else if (nodeRecv.tagName() == "presence") { QXmppPresence presence; presence.parse(nodeRecv); // emit presence emit presenceReceived(presence); } else if (nodeRecv.tagName() == "message") { QXmppMessage message; message.parse(nodeRecv); // emit message emit messageReceived(message); } } else if (QXmppStreamManagementEnabled::isStreamManagementEnabled(nodeRecv)) { QXmppStreamManagementEnabled streamManagementEnabled; streamManagementEnabled.parse(nodeRecv); d->smId = streamManagementEnabled.id(); d->canResume = streamManagementEnabled.resume(); if (streamManagementEnabled.resume() && !streamManagementEnabled.location().isEmpty()) { setResumeAddress(streamManagementEnabled.location()); } d->streamManagementEnabled = true; enableStreamManagement(true); // we are connected now emit connected(); } else if (QXmppStreamManagementResumed::isStreamManagementResumed(nodeRecv)) { QXmppStreamManagementResumed streamManagementResumed; streamManagementResumed.parse(nodeRecv); setAcknowledgedSequenceNumber(streamManagementResumed.h()); d->isResuming = false; d->streamResumed = true; d->streamManagementEnabled = true; enableStreamManagement(false); // we are connected now // TODO: The stream was resumed. Therefore, we should not send presence information or request the roster. emit connected(); } else if (QXmppStreamManagementFailed::isStreamManagementFailed(nodeRecv)) { if (d->isResuming) { // resuming failed. We can try to bind a resource now. d->isResuming = false; // check whether bind is available if (d->bindModeAvailable) { d->sendBind(); return; } // check whether session is available if (d->sessionAvailable) { d->sendSessionStart(); return; } // otherwise we are done d->sessionStarted = true; emit connected(); } else { // we are connected now, but stream management is disabled emit connected(); } } } /// \endcond void QXmppOutgoingClient::pingStart() { const int interval = configuration().keepAliveInterval(); // start ping timer if (interval > 0) { d->pingTimer->setInterval(interval * 1000); d->pingTimer->start(); } } void QXmppOutgoingClient::pingStop() { // stop all timers d->pingTimer->stop(); d->timeoutTimer->stop(); } void QXmppOutgoingClient::pingSend() { // send ping packet QXmppPingIq ping; ping.setTo(configuration().domain()); sendPacket(ping); // start timeout timer const int timeout = configuration().keepAliveTimeout(); if (timeout > 0) { d->timeoutTimer->setInterval(timeout * 1000); d->timeoutTimer->start(); } } void QXmppOutgoingClient::pingTimeout() { warning("Ping timeout"); QXmppStream::disconnectFromHost(); emit error(QXmppClient::KeepAliveError); } bool QXmppOutgoingClient::setResumeAddress(const QString &address) { if (const auto location = parseHostAddress(address); !location.first.isEmpty()) { d->resumeHost = location.first; if (location.second > 0) { d->resumePort = location.second; } else { d->resumePort = 5222; } return true; } d->resumeHost.clear(); d->resumePort = 0; return false; } std::pair QXmppOutgoingClient::parseHostAddress(const QString &address) { QUrl url("//" + address); if (url.isValid() && !url.host().isEmpty()) { return { url.host(), url.port() }; } return { {}, -1 }; } void QXmppOutgoingClientPrivate::sendNonSASLAuth(bool plainText) { QXmppNonSASLAuthIq authQuery; authQuery.setType(QXmppIq::Set); authQuery.setUsername(q->configuration().user()); if (plainText) authQuery.setPassword(q->configuration().password()); else authQuery.setDigest(streamId, q->configuration().password()); authQuery.setResource(q->configuration().resource()); nonSASLAuthId = authQuery.id(); q->sendPacket(authQuery); } void QXmppOutgoingClientPrivate::sendNonSASLAuthQuery() { QXmppNonSASLAuthIq authQuery; authQuery.setType(QXmppIq::Get); authQuery.setTo(streamFrom); // FIXME : why are we setting the username, XEP-0078 states we should // not attempt to guess the required fields? authQuery.setUsername(q->configuration().user()); q->sendPacket(authQuery); } void QXmppOutgoingClientPrivate::sendBind() { QXmppBindIq bind; bind.setType(QXmppIq::Set); bind.setResource(q->configuration().resource()); bindId = bind.id(); q->sendPacket(bind); } void QXmppOutgoingClientPrivate::sendSessionStart() { QXmppSessionIq session; session.setType(QXmppIq::Set); session.setTo(q->configuration().domain()); sessionId = session.id(); q->sendPacket(session); } void QXmppOutgoingClientPrivate::sendStreamManagementEnable() { QXmppStreamManagementEnable streamManagementEnable(true); QByteArray data; QXmlStreamWriter xmlStream(&data); streamManagementEnable.toXml(&xmlStream); q->sendData(data); } /// Returns the type of the last XMPP stream error that occurred. QXmppStanza::Error::Condition QXmppOutgoingClient::xmppStreamError() { return d->xmppStreamError; } qxmpp-1.4.0/src/client/QXmppOutgoingClient.h000066400000000000000000000063441402370562100210260ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Manjeet Dahiya * Jeremy Lainé * * 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 QXMPPOUTGOINGCLIENT_H #define QXMPPOUTGOINGCLIENT_H #include "QXmppClient.h" #include "QXmppStanza.h" #include "QXmppStream.h" class QDomElement; class QSslError; class QXmppConfiguration; class QXmppPresence; class QXmppIq; class QXmppMessage; class QXmppOutgoingClientPrivate; /// \brief The QXmppOutgoingClient class represents an outgoing XMPP stream /// to an XMPP server. /// class QXMPP_EXPORT QXmppOutgoingClient : public QXmppStream { Q_OBJECT public: QXmppOutgoingClient(QObject *parent); ~QXmppOutgoingClient() override; void connectToHost(); bool isAuthenticated() const; bool isConnected() const override; bool isClientStateIndicationEnabled() const; bool isStreamManagementEnabled() const; bool isStreamResumed() const; QSslSocket *socket() const { return QXmppStream::socket(); }; QXmppStanza::Error::Condition xmppStreamError(); QXmppConfiguration &configuration(); Q_SIGNALS: /// This signal is emitted when an error is encountered. void error(QXmppClient::Error); /// This signal is emitted when an element is received. void elementReceived(const QDomElement &element, bool &handled); /// This signal is emitted when a presence is received. void presenceReceived(const QXmppPresence &); /// This signal is emitted when a message is received. void messageReceived(const QXmppMessage &); /// This signal is emitted when an IQ response (type result or error) has /// been received that was not handled by elementReceived(). void iqReceived(const QXmppIq &); /// This signal is emitted when SSL errors are encountered. void sslErrors(const QList &errors); protected: /// \cond // Overridable methods void handleStart() override; void handleStanza(const QDomElement &element) override; void handleStream(const QDomElement &element) override; /// \endcond public Q_SLOTS: void disconnectFromHost() override; private Q_SLOTS: void _q_dnsLookupFinished(); void _q_socketDisconnected(); void socketError(QAbstractSocket::SocketError); void socketSslErrors(const QList &); void pingStart(); void pingStop(); void pingSend(); void pingTimeout(); private: bool setResumeAddress(const QString &address); static std::pair parseHostAddress(const QString &address); friend class QXmppOutgoingClientPrivate; friend class tst_QXmppOutgoingClient; QXmppOutgoingClientPrivate *const d; }; #endif // QXMPPOUTGOINGCLIENT_H qxmpp-1.4.0/src/client/QXmppRegistrationManager.cpp000066400000000000000000000232121402370562100223650ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Melvin Keskin * Linus Jahn * * 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 "QXmppRegistrationManager.h" #include "QXmppClient.h" #include "QXmppConstants_p.h" #include "QXmppDiscoveryManager.h" #include "QXmppRegisterIq.h" #include "QXmppStreamFeatures.h" #include "QXmppUtils.h" #include class QXmppRegistrationManagerPrivate { public: QXmppRegistrationManagerPrivate(); // whether to block login and request the registration form on connect bool registerOnConnectEnabled; // whether the server supports registration (after login) bool supportedByServer; // caching QString changePasswordIqId; QString newPassword; QString deleteAccountIqId; QString registrationIqId; QXmppRegisterIq registrationFormToSend; }; QXmppRegistrationManagerPrivate::QXmppRegistrationManagerPrivate() : registerOnConnectEnabled(false), supportedByServer(false) { } /// /// Default constructor. /// QXmppRegistrationManager::QXmppRegistrationManager() : d(new QXmppRegistrationManagerPrivate) { } QXmppRegistrationManager::~QXmppRegistrationManager() = default; /// /// This adds the \c jabber:iq:register namespace to the features. /// QStringList QXmppRegistrationManager::discoveryFeatures() const { return QStringList { ns_register }; } /// /// Changes the password of the user's account. /// /// \note Be sure to only call this when any previous requests have finished. /// /// \param newPassword The new password to be set. This must not be empty. /// void QXmppRegistrationManager::changePassword(const QString &newPassword) { auto iq = QXmppRegisterIq::createChangePasswordRequest(client()->configuration().user(), newPassword); d->changePasswordIqId = iq.id(); d->newPassword = newPassword; client()->sendPacket(iq); } /// /// Cancels an existing registration on the server. /// /// \sa accountDeleted() /// \sa accountDeletionFailed() /// void QXmppRegistrationManager::deleteAccount() { auto iq = QXmppRegisterIq::createUnregistrationRequest(); d->deleteAccountIqId = iq.id(); client()->sendPacket(iq); } bool QXmppRegistrationManager::supportedByServer() const { return d->supportedByServer; } /// /// Requests the registration form for registering. /// /// \param service The service which the registration form should be requested /// from. If left empty, this will default to the local server. /// void QXmppRegistrationManager::requestRegistrationForm(const QString &service) { QXmppRegisterIq iq; iq.setType(QXmppIq::Get); iq.setTo(service); client()->sendPacket(iq); } /// /// Sets a registration form to be sent on the next connect with the server. /// \param iq The completed registration form. /// void QXmppRegistrationManager::setRegistrationFormToSend(const QXmppRegisterIq &iq) { d->registrationFormToSend = iq; } /// /// Sets a registration form to be sent on the next connect with the server. /// \param dataForm The completed data form for registration. /// void QXmppRegistrationManager::setRegistrationFormToSend(const QXmppDataForm &dataForm) { d->registrationFormToSend = QXmppRegisterIq(); d->registrationFormToSend.setForm(dataForm); } /// /// Sends a completed registration form that was previously set using /// setRegistrationFormToSend(). /// /// You usually only need to set the form and the manager will automatically /// send it when connected. More details can be found in the documentation of /// the QXmppRegistrationManager. /// void QXmppRegistrationManager::sendCachedRegistrationForm() { if (!d->registrationFormToSend.form().isNull()) d->registrationFormToSend.form().setType(QXmppDataForm::Submit); d->registrationFormToSend.setType(QXmppIq::Set); client()->sendPacket(d->registrationFormToSend); d->registrationIqId = d->registrationFormToSend.id(); // clear cache d->registrationFormToSend = QXmppRegisterIq(); } /// /// Returns whether to only request the registration form and not to connect /// with username/password. /// bool QXmppRegistrationManager::registerOnConnectEnabled() const { return d->registerOnConnectEnabled; } /// /// Sets whether to only request the registration form and not to connect with /// username/password. /// /// \param enabled true to register, false to connect normally. /// void QXmppRegistrationManager::setRegisterOnConnectEnabled(bool enabled) { d->registerOnConnectEnabled = enabled; } /// \cond bool QXmppRegistrationManager::handleStanza(const QDomElement &stanza) { if (d->registerOnConnectEnabled && QXmppStreamFeatures::isStreamFeatures(stanza)) { QXmppStreamFeatures features; features.parse(stanza); if (features.registerMode() == QXmppStreamFeatures::Disabled) { warning(QStringLiteral("Could not request the registration form, because the server does not advertise the register stream feature.")); client()->disconnectFromServer(); emit registrationFailed({ QXmppStanza::Error::Cancel, QXmppStanza::Error::FeatureNotImplemented, QStringLiteral("The server does not advertise the register stream feature.") }); return true; } if (!d->registrationFormToSend.form().isNull() || !d->registrationFormToSend.username().isNull()) { info(QStringLiteral("Sending completed form.")); sendCachedRegistrationForm(); return true; } info(QStringLiteral("Requesting registration form from server.")); requestRegistrationForm(); return true; } if (stanza.tagName() == "iq") { const QString &id = stanza.attribute(QStringLiteral("id")); if (!id.isEmpty() && id == d->registrationIqId) { QXmppIq iq; iq.parse(stanza); switch (iq.type()) { case QXmppIq::Result: info(QStringLiteral("Successfully registered with the service.")); emit registrationSucceeded(); break; case QXmppIq::Error: warning(QStringLiteral("Registering with the service failed: ").append(iq.error().text())); emit registrationFailed(iq.error()); break; default: break; // should never occur } d->registrationIqId.clear(); return true; } else if (!id.isEmpty() && id == d->changePasswordIqId) { QXmppIq iq; iq.parse(stanza); switch (iq.type()) { case QXmppIq::Result: info(QStringLiteral("Changed password successfully.")); client()->configuration().setPassword(d->newPassword); emit passwordChanged(d->newPassword); break; case QXmppIq::Error: warning(QStringLiteral("Failed to change password: ").append(iq.error().text())); emit passwordChangeFailed(iq.error()); break; default: break; // should never occur } d->changePasswordIqId.clear(); d->newPassword.clear(); return true; } else if (!id.isEmpty() && id == d->deleteAccountIqId) { QXmppIq iq; iq.parse(stanza); switch (iq.type()) { case QXmppIq::Result: info(QStringLiteral("Account deleted successfully.")); emit accountDeleted(); client()->disconnectFromServer(); break; case QXmppIq::Error: warning(QStringLiteral("Failed to delete account: ").append(iq.error().text())); emit accountDeletionFailed(iq.error()); break; default: break; // should never occur } d->deleteAccountIqId.clear(); return true; } else if (QXmppRegisterIq::isRegisterIq(stanza)) { QXmppRegisterIq iq; iq.parse(stanza); emit registrationFormReceived(iq); } } return false; } /// \endcond void QXmppRegistrationManager::setClient(QXmppClient *client) { QXmppClientExtension::setClient(client); // get service discovery manager auto *disco = client->findExtension(); if (disco) { connect(disco, &QXmppDiscoveryManager::infoReceived, this, &QXmppRegistrationManager::handleDiscoInfo); } connect(client, &QXmppClient::disconnected, [=]() { setSupportedByServer(false); }); } void QXmppRegistrationManager::handleDiscoInfo(const QXmppDiscoveryIq &iq) { // check features of own server if (iq.from().isEmpty() || iq.from() == client()->configuration().domain()) { if (iq.features().contains(ns_register)) setSupportedByServer(true); } } void QXmppRegistrationManager::setSupportedByServer(bool registrationSupported) { if (d->supportedByServer != registrationSupported) { d->supportedByServer = registrationSupported; emit supportedByServerChanged(); } } qxmpp-1.4.0/src/client/QXmppRegistrationManager.h000066400000000000000000000341421402370562100220360ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Melvin Keskin * Linus Jahn * * 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 QXMPPREGISTRATIONMANAGER_H #define QXMPPREGISTRATIONMANAGER_H #include "QXmppClientExtension.h" #include "QXmppRegisterIq.h" #include class QXmppRegistrationManagerPrivate; /// /// \brief The QXmppRegistrationManager class manages in-band registration and /// account management tasks like changing the password as defined in /// \xep{0077}: In-Band Registration. /// ///

Activating the manager

/// /// To make use of this manager, you need to instantiate it and load it into /// the QXmppClient instance as follows: /// /// \code /// auto *registrationManager = new QXmppRegistrationManager; /// client->addExtension(registrationManager); /// \endcode /// ///

Setting up service discovery correctly for this manager

/// /// This manager automatically recognizes whether the local server supports /// \xep{0077} (see supportedByServer()). You just need to request the service /// discovery information from the server on connect as below: /// /// \code /// connect(client, &QXmppClient::connected, [=]() { /// // The service discovery manager is added to the client by default. /// auto *discoManager = client->findExtension(); /// discoManager->requestInfo(client->configuration().server()); /// }); /// \endcode /// /// As soon as the result is retrieved, the supportedByServer() property should /// be correct and could be used to display the user whether account management /// tasks can be performed on this server. /// /// However, this is not relevant if you only want to /// register a new account on a server. /// ///

Changing the account's password

/// /// To change the password of the current account changePassword() can be used. /// Upon that either passwordChanged() or passwordChangeFailed() is emitted. /// /// If changing the password was successful, the new password is automatically /// set in the QXmppClient::configuration(), so reconnecting works properly. /// /// Example: /// \code /// auto *registrationManager = client->findExtension(); /// connect(registrationManager, &QXmppRegistrationManager::passwordChanged, [=](const QString &newPassword) { /// qDebug() << "Password changed to:" << newPassword; /// }); /// connect(registrationManager, &QXmppRegistrationManager::passwordChangeFailed, [=](QXmppStanza::Error error) { /// qDebug() << "Couldn't change the password:" << error.text(); /// }); /// /// registrationManager->changePassword(client->configuration().user(), "m1cr0$0ft"); /// \endcode /// ///

Unregistration with the server

/// /// If you want to delete your account on the server, you can do that using /// deleteAccount(). When the result is received either accountDeleted() or /// accountDeletionFailed() is emitted. In case it was successful the manager /// automatically disconnects from the client. /// /// \code /// auto *registrationManager = client->findExtension(); /// connect(registrationManager, &QXmppRegistrationManager::accountDeleted, [=]() { /// qDebug() << "Account deleted successfull, the client is disconnecting now"; /// }); /// connect(registrationManager, &QXmppRegistrationManager::accountDeletionFailed, [=](QXmppStanza::Error error) { /// qDebug() << "Couldn't delete account:" << error.text(); /// }); /// registrationManager->deleteAccount(); /// \endcode /// ///

Registering with a server

/// /// Registering with a server consists of multiple steps: /// -# Requesting the registration form from the server /// -# Filling out the registration form /// -# Sending the completed form to the server /// - On failure (e.g. because of a username conflict), the process /// continues at step 1 again. /// -# Connecting with the newly created account /// ///

Requesting the registration form from the server

/// /// First of all, you need to enable the registration process in the /// registration manager, which of course needs to be /// activated in the client. /// /// \code /// auto *registrationManager = client->findExtension(); /// registrationManager->setRegisterOnConnectEnabled(true); /// \endcode /// /// After that you can start to connect to the server you want to register /// with. No JID is set in the QXmppConfiguration for the client and instead /// only the server is set. /// /// \code /// QXmppConfiguration config; /// config.setDomain("example.org"); /// /// client->connectToServer(config); /// \endcode /// /// Alternatively, you can also provide a domain-only JID and no password to /// connectToServer(): /// /// \code /// client->connectToServer("example.org", QString()); /// \endcode /// /// Now as soon as (START)TLS was handled, the registration manager interrupts /// the normal connection process. The manager checks whether the server /// supports in-band registration and whether the server advertises this as a /// stream feature. /// /// If the server does not support in-band registration, the manager will abort /// the connection at this point and emit the registrationFailed() signal with /// a fixed QXmppStanza::Error of type QXmppStanza::Error::Cancel and with a /// condition of QXmppStanza::Error::FeatureNotImplemented. /// /// The manager will now request the registration form. This will either result /// in an error (reported by registrationFailed()) or, if everything went well, /// the registration form is reported by registrationFormReceived(). /// /// To handle everything correctly, you need to connect to both Q_SIGNALS: /// /// \code /// connect(registrationManager, &QXmppRegistrationManager::registrationFormReceived, [=](const QXmppRegisterIq &iq) { /// qDebug() << "Form received:" << iq.instructions(). /// // you now need to complete the form /// }); /// connect(registrationManager, &QXmppRegistrationManager::registrationFailed, [=](const QXmppStanza::Error &error) { /// qDebug() << "Requesting the registration form failed:" << error.text(); /// }); /// \endcode /// ///

Filling out the registration form

/// /// Now you need to fill out the registration form. If this requires user /// interaction, it is recommended that you disconnect from the server at this /// point now. This is required, because some servers will kick inactive, not /// authorized clients after a few seconds. /// /// If the returned IQ contains a data form, that can be displayed to a user or /// can be filled out in another way. /// /// If the server does not support data forms, you can check the standard /// fields of the QXmppRegisterIq. You need to search the fields for empty /// (non-null) strings. All fields that contain an empty string are required /// and can be filled out. You can just set values for those fields and send /// the form as described in the next step. /// /// \note QXmpp currently has only implemented the most important default /// fields in the QXmppRegisterIq. The other fields are not very widespread, /// because data forms are usually used for such purposes. /// ///

Sending the completed form to the server

/// /// Option A: If filling out the form goes very quick, you can set the /// filled out form directly using setRegistrationFormToSend() and then /// directly trigger the form to be sent using sendCachedRegistrationForm(). /// /// \code /// registrationManager->setRegistrationFormToSend(completedForm); /// registrationManager->sendCachedRegistrationForm(); /// \endcode /// /// Option B: If filling out the form takes longer, i.e. because user /// interaction is required, you should disconnect now. As soon as you have /// completed the form, you can set it using setRegistrationFormToSend(). After /// that you can reconnect to the server and the registration manager will /// automatically send the set form. /// /// \code /// client->disconnectFromServer(); /// // user fills out form ... /// registrationManager->setRegistrationFormToSend(completedForm); /// /// // As before, you only need to provide a domain to connectToServer() /// client->connectToServer(...); /// // the registration manager sends the form automatically /// \endcode /// /// The form is now sent to the server. As soon as the result is received, /// either registrationSucceeded() or registrationFailed() is emitted. /// /// In case there was a conflict or another error, you should request a new /// form and restart the process. This is especially important, if the form can /// only be used once as with most CAPTCHA implementations. /// ///

Connecting with the newly created account

/// /// You need to disconnect now. The user can then enter their credentials and /// connect as usually. /// /// It is also possible to extract username and password from the sent form, /// but that does not work always. There might also be forms that have no clear /// username or password fields. /// /// \ingroup Managers /// /// \since QXmpp 1.2 /// class QXMPP_EXPORT QXmppRegistrationManager : public QXmppClientExtension { Q_OBJECT /// Whether support of \xep{0077}: In-band Registration has been discovered on the server. Q_PROPERTY(bool supportedByServer READ supportedByServer NOTIFY supportedByServerChanged) public: QXmppRegistrationManager(); ~QXmppRegistrationManager(); QStringList discoveryFeatures() const override; void changePassword(const QString &newPassword); void deleteAccount(); // documentation needs to be here, see https://stackoverflow.com/questions/49192523/ /// /// Returns whether the server supports registration. /// /// By default this is set to false and only changes, if you request the /// service discovery info of the connected server using /// QXmppDiscoveryManager::requestInfo(). /// /// This is only relevant to actions that happen after authentication. /// /// \sa QXmppRegistrationManager::supportedByServerChanged() /// bool supportedByServer() const; void requestRegistrationForm(const QString &service = {}); void setRegistrationFormToSend(const QXmppRegisterIq &iq); void setRegistrationFormToSend(const QXmppDataForm &dataForm); void sendCachedRegistrationForm(); bool registerOnConnectEnabled() const; void setRegisterOnConnectEnabled(bool enabled); /// \cond bool handleStanza(const QDomElement &stanza) override; /// \endcond Q_SIGNALS: /// /// Emitted, when registrationSupported() changed. /// /// This can happen after the service discovery info of the server was /// retrieved using QXmppDiscoveryManager::requestInfo() or on disconnect. /// void supportedByServerChanged(); /// /// Emitted, when the password of the account was changed successfully. /// /// The new password is automatically set in QXmppClient::configuration(). /// /// \param newPassword The new password that was set on the server. /// void passwordChanged(const QString &newPassword); /// /// Emitted, when changing the password did not succeed. /// /// \param error Error returned from the service. /// void passwordChangeFailed(QXmppStanza::Error error); /// /// Emitted, when a registration form has been received. /// /// When registering an account on the server and user interaction is /// required now to complete the form, it is recommended to disconnect and /// sending the completed registration form on reconnect using /// QXmppRegistrationManager::setRegistrationFormToSend(). Some servers /// (i.e. ejabberd) kick their clients after a timeout when they are not /// active. This can be avoided this way. /// /// \param iq The received form. If it does not contain a valid data form /// (see QXmppRegisterIq::form()), the required fields should be marked by /// empty (but not null) strings in the QXmppRegisterIq (i.e. /// QXmppRegisterIq::password().isNull() => false). /// void registrationFormReceived(const QXmppRegisterIq &iq); /// /// Emitted, when the account was deleted successfully. /// void accountDeleted(); /// /// Emitted, when the account could not be deleted. /// void accountDeletionFailed(QXmppStanza::Error error); /// /// Emitted, when the registration with a service completed successfully. /// /// To connect with the account you still need to set the correct /// credentials in QXmppClient::configuration() and reconnect. /// void registrationSucceeded(); /// /// Emitted, when the registration failed. /// /// \param error The returned error from the service. The reported errors /// might be different from server to server, but the common ones are the /// following: /// \li type=Cancel and condition=Conflict: The username already exists. /// \li type=Cancel and condition=NotAllowed: The CAPTCHA verification /// failed. /// \li type=Modify and condition=NotAcceptable: Some required information /// was missing or the selected password was too weak. /// \li type=Modify and condition=JidMalformed: No username was provided or /// the username has an illegal format. /// void registrationFailed(const QXmppStanza::Error &error); protected: void setClient(QXmppClient *client) override; private Q_SLOTS: void handleDiscoInfo(const QXmppDiscoveryIq &iq); private: void setSupportedByServer(bool supportedByServer); QScopedPointer d; }; #endif // QXMPPREGISTRATIONMANAGER_H qxmpp-1.4.0/src/client/QXmppRemoteMethod.cpp000066400000000000000000000042451402370562100210210ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Ian Reinhart Geiser * * 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 "QXmppRemoteMethod.h" #include "QXmppClient.h" #include "QXmppUtils.h" #include #include #include QXmppRemoteMethod::QXmppRemoteMethod(const QString &jid, const QString &method, const QVariantList &args, QXmppClient *client) : QObject(client), m_client(client) { m_payload.setTo(jid); m_payload.setFrom(client->configuration().jid()); m_payload.setMethod(method); m_payload.setArguments(args); } QXmppRemoteMethodResult QXmppRemoteMethod::call() { // FIXME : spinning an event loop is a VERY bad idea, it can cause // us to lose incoming packets QEventLoop loop(this); connect(this, &QXmppRemoteMethod::callDone, &loop, &QEventLoop::quit); QTimer::singleShot(30000, &loop, &QEventLoop::quit); // Timeout in case the other end hangs... m_client->sendPacket(m_payload); loop.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents); return m_result; } void QXmppRemoteMethod::gotError(const QXmppRpcErrorIq &iq) { if (iq.id() == m_payload.id()) { m_result.hasError = true; m_result.errorMessage = iq.error().text(); m_result.code = iq.error().type(); emit callDone(); } } void QXmppRemoteMethod::gotResult(const QXmppRpcResponseIq &iq) { if (iq.id() == m_payload.id()) { m_result.hasError = false; // FIXME: we don't handle multiple responses m_result.result = iq.values().first(); emit callDone(); } } qxmpp-1.4.0/src/client/QXmppRemoteMethod.h000066400000000000000000000030221402370562100204560ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Ian Reinhart Geiser * * 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 QXMPPREMOTEMETHOD_H #define QXMPPREMOTEMETHOD_H #include "QXmppRpcIq.h" #include #include class QXmppClient; struct QXmppRemoteMethodResult { QXmppRemoteMethodResult() : hasError(false), code(0) { } bool hasError; int code; QString errorMessage; QVariant result; }; class QXMPP_EXPORT QXmppRemoteMethod : public QObject { Q_OBJECT public: QXmppRemoteMethod(const QString &jid, const QString &method, const QVariantList &args, QXmppClient *client); QXmppRemoteMethodResult call(); private Q_SLOTS: void gotError(const QXmppRpcErrorIq &iq); void gotResult(const QXmppRpcResponseIq &iq); Q_SIGNALS: void callDone(); private: QXmppRpcInvokeIq m_payload; QXmppClient *m_client; QXmppRemoteMethodResult m_result; }; #endif // QXMPPREMOTEMETHOD_H qxmpp-1.4.0/src/client/QXmppRosterManager.cpp000066400000000000000000000304601402370562100211740ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Manjeet Dahiya * Jeremy Lainé * * 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 "QXmppRosterManager.h" #include "QXmppClient.h" #include "QXmppPresence.h" #include "QXmppRosterIq.h" #include "QXmppUtils.h" #include class QXmppRosterManagerPrivate { public: QXmppRosterManagerPrivate(); void clear(); // map of bareJid and its rosterEntry QMap entries; // map of resources of the jid and map of resources and presences QMap> presences; // flag to store that the roster has been populated bool isRosterReceived; // id of the initial roster request QString rosterReqId; }; QXmppRosterManagerPrivate::QXmppRosterManagerPrivate() : isRosterReceived(false) { } void QXmppRosterManagerPrivate::clear() { entries.clear(); presences.clear(); rosterReqId.clear(); isRosterReceived = false; } /// /// Constructs a roster manager. /// QXmppRosterManager::QXmppRosterManager(QXmppClient *client) : d(new QXmppRosterManagerPrivate()) { connect(client, &QXmppClient::connected, this, &QXmppRosterManager::_q_connected); connect(client, &QXmppClient::disconnected, this, &QXmppRosterManager::_q_disconnected); connect(client, &QXmppClient::presenceReceived, this, &QXmppRosterManager::_q_presenceReceived); } QXmppRosterManager::~QXmppRosterManager() { delete d; } /// /// Accepts an existing subscription request or pre-approves future subscription /// requests. /// /// You can call this method in reply to the subscriptionRequest() signal or to /// create a pre-approved subscription. /// /// \note Pre-approving subscription requests is only allowed, if the server /// supports RFC6121 and advertises the 'urn:xmpp:features:pre-approval' stream /// feature. /// /// \sa QXmppStreamFeatures::preApprovedSubscriptionsSupported() /// bool QXmppRosterManager::acceptSubscription(const QString &bareJid, const QString &reason) { QXmppPresence presence; presence.setTo(bareJid); presence.setType(QXmppPresence::Subscribed); presence.setStatusText(reason); return client()->sendPacket(presence); } /// /// Upon XMPP connection, request the roster. /// void QXmppRosterManager::_q_connected() { // clear cache if stream has not been resumed if (client()->streamManagementState() != QXmppClient::ResumedStream) { d->clear(); } if (!d->isRosterReceived) { QXmppRosterIq roster; roster.setType(QXmppIq::Get); roster.setFrom(client()->configuration().jid()); // TODO: Request MIX annotations only when the server supports MIX-PAM. roster.setMixAnnotate(true); d->rosterReqId = roster.id(); if (client()->isAuthenticated()) client()->sendPacket(roster); } } void QXmppRosterManager::_q_disconnected() { // clear cache if stream cannot be resumed if (client()->streamManagementState() == QXmppClient::NoStreamManagement) { d->clear(); } } /// \cond bool QXmppRosterManager::handleStanza(const QDomElement &element) { if (element.tagName() != "iq" || !QXmppRosterIq::isRosterIq(element)) return false; // Security check: only server should send this iq // from() should be either empty or bareJid of the user const auto fromJid = element.attribute("from"); if (!fromJid.isEmpty() && QXmppUtils::jidToBareJid(fromJid) != client()->configuration().jidBare()) return false; QXmppRosterIq rosterIq; rosterIq.parse(element); bool isInitial = (d->rosterReqId == rosterIq.id()); if (isInitial) d->rosterReqId.clear(); switch (rosterIq.type()) { case QXmppIq::Set: { // send result iq QXmppIq returnIq(QXmppIq::Result); returnIq.setId(rosterIq.id()); client()->sendPacket(returnIq); // store updated entries and notify changes const auto items = rosterIq.items(); for (const auto &item : items) { const QString bareJid = item.bareJid(); if (item.subscriptionType() == QXmppRosterIq::Item::Remove) { if (d->entries.remove(bareJid)) { // notify the user that the item was removed emit itemRemoved(bareJid); } } else { const bool added = !d->entries.contains(bareJid); d->entries.insert(bareJid, item); if (added) { // notify the user that the item was added emit itemAdded(bareJid); } else { // notify the user that the item changed emit itemChanged(bareJid); } } } } break; case QXmppIq::Result: { const auto items = rosterIq.items(); for (const auto &item : items) { const auto bareJid = item.bareJid(); d->entries.insert(bareJid, item); } if (isInitial) { d->isRosterReceived = true; emit rosterReceived(); } break; } default: break; } return true; } /// \endcond void QXmppRosterManager::_q_presenceReceived(const QXmppPresence &presence) { const auto jid = presence.from(); const auto bareJid = QXmppUtils::jidToBareJid(jid); const auto resource = QXmppUtils::jidToResource(jid); if (bareJid.isEmpty()) return; switch (presence.type()) { case QXmppPresence::Available: d->presences[bareJid][resource] = presence; emit presenceChanged(bareJid, resource); break; case QXmppPresence::Unavailable: d->presences[bareJid].remove(resource); emit presenceChanged(bareJid, resource); break; case QXmppPresence::Subscribe: if (client()->configuration().autoAcceptSubscriptions()) { // accept subscription request acceptSubscription(bareJid); // ask for reciprocal subscription subscribe(bareJid); } else { emit subscriptionReceived(bareJid); } break; default: break; } } /// /// Refuses a subscription request. /// /// You can call this method in reply to the subscriptionRequest() signal. /// bool QXmppRosterManager::refuseSubscription(const QString &bareJid, const QString &reason) { QXmppPresence presence; presence.setTo(bareJid); presence.setType(QXmppPresence::Unsubscribed); presence.setStatusText(reason); return client()->sendPacket(presence); } /// /// Adds a new item to the roster without sending any subscription requests. /// /// As a result, the server will initiate a roster push, causing the /// itemAdded() or itemChanged() signal to be emitted. /// /// \param bareJid /// \param name Optional name for the item. /// \param groups Optional groups for the item. /// bool QXmppRosterManager::addItem(const QString &bareJid, const QString &name, const QSet &groups) { QXmppRosterIq::Item item; item.setBareJid(bareJid); item.setName(name); item.setGroups(groups); item.setSubscriptionType(QXmppRosterIq::Item::NotSet); QXmppRosterIq iq; iq.setType(QXmppIq::Set); iq.addItem(item); return client()->sendPacket(iq); } /// /// Removes a roster item and cancels subscriptions to and from the contact. /// /// As a result, the server will initiate a roster push, causing the /// itemRemoved() signal to be emitted. /// /// \param bareJid /// bool QXmppRosterManager::removeItem(const QString &bareJid) { QXmppRosterIq::Item item; item.setBareJid(bareJid); item.setSubscriptionType(QXmppRosterIq::Item::Remove); QXmppRosterIq iq; iq.setType(QXmppIq::Set); iq.addItem(item); return client()->sendPacket(iq); } /// /// Renames a roster item. /// /// As a result, the server will initiate a roster push, causing the /// itemChanged() signal to be emitted. /// /// \param bareJid /// \param name /// bool QXmppRosterManager::renameItem(const QString &bareJid, const QString &name) { if (!d->entries.contains(bareJid)) return false; auto item = d->entries.value(bareJid); item.setName(name); // If there is a pending subscription, do not include the corresponding attribute in the stanza. if (!item.subscriptionStatus().isEmpty()) item.setSubscriptionStatus({}); QXmppRosterIq iq; iq.setType(QXmppIq::Set); iq.addItem(item); return client()->sendPacket(iq); } /// /// Requests a subscription to the given contact. /// /// As a result, the server will initiate a roster push, causing the /// itemAdded() or itemChanged() signal to be emitted. /// bool QXmppRosterManager::subscribe(const QString &bareJid, const QString &reason) { QXmppPresence packet; packet.setTo(QXmppUtils::jidToBareJid(bareJid)); packet.setType(QXmppPresence::Subscribe); packet.setStatusText(reason); return client()->sendPacket(packet); } /// /// Removes a subscription to the given contact. /// /// As a result, the server will initiate a roster push, causing the /// itemChanged() signal to be emitted. /// bool QXmppRosterManager::unsubscribe(const QString &bareJid, const QString &reason) { QXmppPresence packet; packet.setTo(QXmppUtils::jidToBareJid(bareJid)); packet.setType(QXmppPresence::Unsubscribe); packet.setStatusText(reason); return client()->sendPacket(packet); } /// /// Function to get all the bareJids present in the roster. /// /// \return QStringList list of all the bareJids /// QStringList QXmppRosterManager::getRosterBareJids() const { return d->entries.keys(); } /// /// Returns the roster entry of the given bareJid. If the bareJid is not in the /// database and empty QXmppRosterIq::Item will be returned. /// /// \param bareJid as a QString /// QXmppRosterIq::Item QXmppRosterManager::getRosterEntry( const QString &bareJid) const { // will return blank entry if bareJid doesn't exist if (d->entries.contains(bareJid)) return d->entries.value(bareJid); return {}; } /// /// Get all the associated resources with the given bareJid. /// /// \param bareJid as a QString /// \return list of associated resources as a QStringList /// QStringList QXmppRosterManager::getResources(const QString &bareJid) const { if (d->presences.contains(bareJid)) return d->presences[bareJid].keys(); return {}; } /// /// Get all the presences of all the resources of the given bareJid. A bareJid /// can have multiple resources and each resource will have a presence /// associated with it. /// /// \param bareJid as a QString /// \return Map of resource and its respective presence QMap /// QMap QXmppRosterManager::getAllPresencesForBareJid( const QString &bareJid) const { if (d->presences.contains(bareJid)) return d->presences.value(bareJid); return {}; } /// /// Get the presence of the given resource of the given bareJid. /// /// \param bareJid as a QString /// \param resource as a QString /// \return QXmppPresence /// QXmppPresence QXmppRosterManager::getPresence(const QString &bareJid, const QString &resource) const { if (d->presences.contains(bareJid) && d->presences[bareJid].contains(resource)) { return d->presences[bareJid][resource]; } QXmppPresence presence; presence.setType(QXmppPresence::Unavailable); return presence; } /// /// Function to check whether the roster has been received or not. /// /// On disconnecting this is reset to false if no stream management is used by /// the client and so the stream cannot be resumed later. /// /// \return true if roster received else false /// bool QXmppRosterManager::isRosterReceived() const { return d->isRosterReceived; } qxmpp-1.4.0/src/client/QXmppRosterManager.h000066400000000000000000000123431402370562100206410ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Manjeet Dahiya * Jeremy Lainé * * 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 QXMPPROSTERMANAGER_H #define QXMPPROSTERMANAGER_H #include "QXmppClientExtension.h" #include "QXmppPresence.h" #include "QXmppRosterIq.h" #include #include #include class QXmppRosterManagerPrivate; /// /// \brief The QXmppRosterManager class provides access to a connected client's /// roster. /// /// \note Its object should not be created using its constructor. Instead /// \c QXmppClient::findExtension() should be used to get /// the instantiated object of this class. /// /// It stores all the Roster and Presence details of all the roster entries /// (that is all the bareJids) in the client's friend's list. It provides the /// functionality to get all the bareJids in the client's roster and Roster and /// Presence details of the same. /// /// After the QXmpp connected successfully to the XMPP server the signal /// \c QXmppClient::connected() is emitted and the roster is requested from the /// server. Once QXmpp receives the roster the signal /// \c QXmppRosterManager::rosterReceived() is emitted and after that the /// methods of this class can be used to get the roster entries. /// /// \c QXmppRosterManager::isRosterReceived() can be used to find out whether /// the roster has been received yet. /// /// The \c itemAdded(), \c itemChanged() and \c itemRemoved() signals are /// emitted whenever roster entries are added, changed or removed. /// /// The \c presenceChanged() signal is emitted whenever the presence for a /// roster item changes. /// /// \ingroup Managers /// class QXMPP_EXPORT QXmppRosterManager : public QXmppClientExtension { Q_OBJECT public: QXmppRosterManager(QXmppClient *stream); ~QXmppRosterManager() override; bool isRosterReceived() const; QStringList getRosterBareJids() const; QXmppRosterIq::Item getRosterEntry(const QString &bareJid) const; QStringList getResources(const QString &bareJid) const; QMap getAllPresencesForBareJid( const QString &bareJid) const; QXmppPresence getPresence(const QString &bareJid, const QString &resource) const; /// \cond bool handleStanza(const QDomElement &element) override; /// \endcond public Q_SLOTS: bool acceptSubscription(const QString &bareJid, const QString &reason = {}); bool refuseSubscription(const QString &bareJid, const QString &reason = {}); bool addItem(const QString &bareJid, const QString &name = {}, const QSet &groups = {}); bool removeItem(const QString &bareJid); bool renameItem(const QString &bareJid, const QString &name); bool subscribe(const QString &bareJid, const QString &reason = {}); bool unsubscribe(const QString &bareJid, const QString &reason = {}); Q_SIGNALS: /// This signal is emitted when the Roster IQ is received after a successful /// connection. That is the roster entries are empty before this signal is emitted. /// One should use getRosterBareJids() and getRosterEntry() only after /// this signal has been emitted. /// /// \note If the previous stream could be resumed, this signal is not /// emitted since QXmpp 1.4. Changes since the last connection are reported /// via the itemAdded(), itemChanged() and itemRemoved() signals. void rosterReceived(); /// This signal is emitted when the presence of a particular bareJid and resource changes. void presenceChanged(const QString &bareJid, const QString &resource); /// This signal is emitted when a contact asks to subscribe to your presence. /// /// You can either accept the request by calling acceptSubscription() or refuse it /// by calling refuseSubscription(). /// /// \note If you set QXmppConfiguration::autoAcceptSubscriptions() to true, this /// signal will not be emitted. void subscriptionReceived(const QString &bareJid); /// This signal is emitted when the roster entry of a particular bareJid is /// added as a result of roster push. void itemAdded(const QString &bareJid); /// This signal is emitted when the roster entry of a particular bareJid /// changes as a result of roster push. void itemChanged(const QString &bareJid); /// This signal is emitted when the roster entry of a particular bareJid is /// removed as a result of roster push. void itemRemoved(const QString &bareJid); private Q_SLOTS: void _q_connected(); void _q_disconnected(); void _q_presenceReceived(const QXmppPresence &); private: QXmppRosterManagerPrivate *d; }; #endif // QXMPPROSTER_H qxmpp-1.4.0/src/client/QXmppRpcManager.cpp000066400000000000000000000134451402370562100204460ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppRpcManager.h" #include "QXmppClient.h" #include "QXmppConstants_p.h" #include "QXmppInvokable.h" #include "QXmppRemoteMethod.h" #include "QXmppRpcIq.h" /// Constructs a QXmppRpcManager. QXmppRpcManager::QXmppRpcManager() { } /// Adds a local interface which can be queried using RPC. /// /// \param interface void QXmppRpcManager::addInvokableInterface(QXmppInvokable *interface) { m_interfaces[interface->metaObject()->className()] = interface; } /// Invokes a remote interface using RPC. /// /// \param iq void QXmppRpcManager::invokeInterfaceMethod(const QXmppRpcInvokeIq &iq) { QXmppStanza::Error error; const QStringList methodBits = iq.method().split('.'); if (methodBits.size() != 2) return; const QString interface = methodBits.first(); const QString method = methodBits.last(); QXmppInvokable *iface = m_interfaces.value(interface); if (iface) { if (iface->isAuthorized(iq.from())) { if (iface->interfaces().contains(method)) { QVariant result = iface->dispatch(method.toLatin1(), iq.arguments()); QXmppRpcResponseIq resultIq; resultIq.setId(iq.id()); resultIq.setTo(iq.from()); resultIq.setValues(QVariantList() << result); client()->sendPacket(resultIq); return; } else { error.setType(QXmppStanza::Error::Cancel); error.setCondition(QXmppStanza::Error::ItemNotFound); } } else { error.setType(QXmppStanza::Error::Auth); error.setCondition(QXmppStanza::Error::Forbidden); } } else { error.setType(QXmppStanza::Error::Cancel); error.setCondition(QXmppStanza::Error::ItemNotFound); } QXmppRpcErrorIq errorIq; errorIq.setId(iq.id()); errorIq.setTo(iq.from()); errorIq.setQuery(iq); errorIq.setError(error); client()->sendPacket(errorIq); } /// Calls a remote method using RPC with the specified arguments. /// /// \note This method blocks until the response is received, and it may /// cause XMPP stanzas to be lost! QXmppRemoteMethodResult QXmppRpcManager::callRemoteMethod(const QString &jid, const QString &interface, const QVariant &arg1, const QVariant &arg2, const QVariant &arg3, const QVariant &arg4, const QVariant &arg5, const QVariant &arg6, const QVariant &arg7, const QVariant &arg8, const QVariant &arg9, const QVariant &arg10) { QVariantList args; if (arg1.isValid()) args << arg1; if (arg2.isValid()) args << arg2; if (arg3.isValid()) args << arg3; if (arg4.isValid()) args << arg4; if (arg5.isValid()) args << arg5; if (arg6.isValid()) args << arg6; if (arg7.isValid()) args << arg7; if (arg8.isValid()) args << arg8; if (arg9.isValid()) args << arg9; if (arg10.isValid()) args << arg10; bool check; Q_UNUSED(check) QXmppRemoteMethod method(jid, interface, args, client()); check = connect(this, SIGNAL(rpcCallResponse(QXmppRpcResponseIq)), &method, SLOT(gotResult(QXmppRpcResponseIq))); Q_ASSERT(check); check = connect(this, SIGNAL(rpcCallError(QXmppRpcErrorIq)), &method, SLOT(gotError(QXmppRpcErrorIq))); Q_ASSERT(check); return method.call(); } /// \cond QStringList QXmppRpcManager::discoveryFeatures() const { // XEP-0009: Jabber-RPC return QStringList() << ns_rpc; } QList QXmppRpcManager::discoveryIdentities() const { QXmppDiscoveryIq::Identity identity; identity.setCategory("automation"); identity.setType("rpc"); return QList() << identity; } bool QXmppRpcManager::handleStanza(const QDomElement &element) { // XEP-0009: Jabber-RPC if (QXmppRpcInvokeIq::isRpcInvokeIq(element)) { QXmppRpcInvokeIq rpcIqPacket; rpcIqPacket.parse(element); invokeInterfaceMethod(rpcIqPacket); return true; } else if (QXmppRpcResponseIq::isRpcResponseIq(element)) { QXmppRpcResponseIq rpcResponseIq; rpcResponseIq.parse(element); emit rpcCallResponse(rpcResponseIq); return true; } else if (QXmppRpcErrorIq::isRpcErrorIq(element)) { QXmppRpcErrorIq rpcErrorIq; rpcErrorIq.parse(element); emit rpcCallError(rpcErrorIq); return true; } return false; } /// \endcond qxmpp-1.4.0/src/client/QXmppRpcManager.h000066400000000000000000000060071402370562100201070ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPRPCMANAGER_H #define QXMPPRPCMANAGER_H #include "QXmppClientExtension.h" #include "QXmppInvokable.h" #include "QXmppRemoteMethod.h" #include #include class QXmppRpcErrorIq; class QXmppRpcInvokeIq; class QXmppRpcResponseIq; /// \brief The QXmppRpcManager class make it possible to invoke remote methods /// and to expose local interfaces for remote procedure calls, as specified by /// \xep{0009}: Jabber-RPC. /// /// To make use of this manager, you need to instantiate it and load it into /// the QXmppClient instance as follows: /// /// \code /// QXmppRpcManager *manager = new QXmppRpcManager; /// client->addExtension(manager); /// \endcode /// /// \note THIS API IS NOT FINALIZED YET /// /// \ingroup Managers class QXMPP_EXPORT QXmppRpcManager : public QXmppClientExtension { Q_OBJECT public: QXmppRpcManager(); void addInvokableInterface(QXmppInvokable *interface); QXmppRemoteMethodResult callRemoteMethod(const QString &jid, const QString &interface, const QVariant &arg1 = QVariant(), const QVariant &arg2 = QVariant(), const QVariant &arg3 = QVariant(), const QVariant &arg4 = QVariant(), const QVariant &arg5 = QVariant(), const QVariant &arg6 = QVariant(), const QVariant &arg7 = QVariant(), const QVariant &arg8 = QVariant(), const QVariant &arg9 = QVariant(), const QVariant &arg10 = QVariant()); /// \cond QStringList discoveryFeatures() const override; QList discoveryIdentities() const override; bool handleStanza(const QDomElement &element) override; /// \endcond Q_SIGNALS: /// \cond void rpcCallResponse(const QXmppRpcResponseIq &result); void rpcCallError(const QXmppRpcErrorIq &err); /// \endcond private: void invokeInterfaceMethod(const QXmppRpcInvokeIq &iq); QMap m_interfaces; }; #endif qxmpp-1.4.0/src/client/QXmppTlsManager.cpp000066400000000000000000000052721402370562100204630ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Linus Jahn * * 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 "QXmppClient.h" #include "QXmppClient_p.h" #include "QXmppConstants_p.h" #include "QXmppOutgoingClient.h" #include "QXmppStartTlsPacket.h" #include "QXmppStreamFeatures.h" #include "QXmppTlsManager_p.h" #include #include /// \cond QXmppTlsManager::QXmppTlsManager() = default; bool QXmppTlsManager::handleStanza(const QDomElement &stanza) { if (QXmppStreamFeatures::isStreamFeatures(stanza) && !clientStream()->socket()->isEncrypted()) { QXmppStreamFeatures features; features.parse(stanza); // determine TLS mode to use const QXmppConfiguration::StreamSecurityMode localSecurity = client()->configuration().streamSecurityMode(); const QXmppStreamFeatures::Mode remoteSecurity = features.tlsMode(); if (!clientStream()->socket()->supportsSsl() && (localSecurity == QXmppConfiguration::TLSRequired || remoteSecurity == QXmppStreamFeatures::Required)) { warning("Disconnecting since TLS is required, but SSL support is not available"); client()->disconnectFromServer(); return true; } if (localSecurity == QXmppConfiguration::TLSRequired && remoteSecurity == QXmppStreamFeatures::Disabled) { warning("Disconnecting since TLS is required, but not supported by the server"); client()->disconnectFromServer(); return true; } if (clientStream()->socket()->supportsSsl() && localSecurity != QXmppConfiguration::TLSDisabled && remoteSecurity != QXmppStreamFeatures::Disabled) { // enable TLS since it is supported by both parties client()->sendPacket(QXmppStartTlsPacket()); return true; } } if (QXmppStartTlsPacket::isStartTlsPacket(stanza, QXmppStartTlsPacket::Proceed)) { debug("Starting encryption"); clientStream()->socket()->startClientEncryption(); return true; } return false; } /// \endcond qxmpp-1.4.0/src/client/QXmppTlsManager_p.h000066400000000000000000000026201402370562100204410ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Linus Jahn * * 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. * */ // // W A R N I N G // ------------- // // This file is not part of the QXmpp API. // // This header file may change from version to version without notice, // or even be removed. // // We mean it. // #ifndef QXMPPTLSMANAGER_H #define QXMPPTLSMANAGER_H #include "QXmppInternalClientExtension_p.h" /// /// \brief The QXmppTlsManager enables the QXmppClient to use STARTTLS. It is /// added to the client by default and can be configured using the /// \c QXmppConfiguration class. /// /// \ingroup Managers /// class QXmppTlsManager : public QXmppInternalClientExtension { Q_OBJECT public: QXmppTlsManager(); bool handleStanza(const QDomElement &stanza) override; }; #endif // QXMPPTLSMANAGER_H qxmpp-1.4.0/src/client/QXmppTransferManager.cpp000066400000000000000000001333071402370562100215060ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppTransferManager.h" #include "QXmppByteStreamIq.h" #include "QXmppClient.h" #include "QXmppConstants_p.h" #include "QXmppIbbIq.h" #include "QXmppSocks.h" #include "QXmppStreamInitiationIq_p.h" #include "QXmppStun.h" #include "QXmppTransferManager_p.h" #include "QXmppUtils.h" #include #include #include #include #include #include #include #include #include #include #include #include // time to try to connect to a SOCKS host (7 seconds) const int socksTimeout = 7000; static QString streamHash(const QString &sid, const QString &initiatorJid, const QString &targetJid) { QCryptographicHash hash(QCryptographicHash::Sha1); QString str = sid + initiatorJid + targetJid; hash.addData(str.toLatin1()); return hash.result().toHex(); } class QXmppTransferFileInfoPrivate : public QSharedData { public: QXmppTransferFileInfoPrivate(); QDateTime date; QByteArray hash; QString name; QString description; qint64 size; }; QXmppTransferFileInfoPrivate::QXmppTransferFileInfoPrivate() : size(0) { } QXmppTransferFileInfo::QXmppTransferFileInfo() : d(new QXmppTransferFileInfoPrivate) { } QXmppTransferFileInfo::QXmppTransferFileInfo(const QXmppTransferFileInfo &other) : d(other.d) { } QXmppTransferFileInfo::~QXmppTransferFileInfo() { } QDateTime QXmppTransferFileInfo::date() const { return d->date; } void QXmppTransferFileInfo::setDate(const QDateTime &date) { d->date = date; } QByteArray QXmppTransferFileInfo::hash() const { return d->hash; } void QXmppTransferFileInfo::setHash(const QByteArray &hash) { d->hash = hash; } QString QXmppTransferFileInfo::name() const { return d->name; } void QXmppTransferFileInfo::setName(const QString &name) { d->name = name; } QString QXmppTransferFileInfo::description() const { return d->description; } void QXmppTransferFileInfo::setDescription(const QString &description) { d->description = description; } qint64 QXmppTransferFileInfo::size() const { return d->size; } void QXmppTransferFileInfo::setSize(qint64 size) { d->size = size; } bool QXmppTransferFileInfo::isNull() const { return d->date.isNull() && d->description.isEmpty() && d->hash.isEmpty() && d->name.isEmpty() && d->size == 0; } QXmppTransferFileInfo &QXmppTransferFileInfo::operator=(const QXmppTransferFileInfo &other) { d = other.d; return *this; } bool QXmppTransferFileInfo::operator==(const QXmppTransferFileInfo &other) const { return other.d->size == d->size && other.d->hash == d->hash && other.d->name == d->name; } void QXmppTransferFileInfo::parse(const QDomElement &element) { d->date = QXmppUtils::datetimeFromString(element.attribute("date")); d->hash = QByteArray::fromHex(element.attribute("hash").toLatin1()); d->name = element.attribute("name"); d->size = element.attribute("size").toLongLong(); d->description = element.firstChildElement("desc").text(); } void QXmppTransferFileInfo::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement("file"); writer->writeDefaultNamespace(ns_stream_initiation_file_transfer); if (d->date.isValid()) writer->writeAttribute("date", QXmppUtils::datetimeToString(d->date)); if (!d->hash.isEmpty()) writer->writeAttribute("hash", d->hash.toHex()); if (!d->name.isEmpty()) writer->writeAttribute("name", d->name); if (d->size > 0) writer->writeAttribute("size", QString::number(d->size)); if (!d->description.isEmpty()) writer->writeTextElement("desc", d->description); writer->writeEndElement(); } class QXmppTransferJobPrivate { public: QXmppTransferJobPrivate(); int blockSize; QXmppClient *client; QXmppTransferJob::Direction direction; qint64 done; QXmppTransferJob::Error error; QCryptographicHash hash; QIODevice *iodevice; QString offerId; QString jid; QUrl localFileUrl; QString sid; QXmppTransferJob::Method method; QString mimeType; QString requestId; QXmppTransferJob::State state; QElapsedTimer transferStart; bool deviceIsOwn; // file meta-data QXmppTransferFileInfo fileInfo; // for in-band bytestreams int ibbSequence; // for socks5 bytestreams QTcpSocket *socksSocket; QXmppByteStreamIq::StreamHost socksProxy; }; QXmppTransferJobPrivate::QXmppTransferJobPrivate() : blockSize(16384), client(nullptr), direction(QXmppTransferJob::IncomingDirection), done(0), error(QXmppTransferJob::NoError), hash(QCryptographicHash::Md5), iodevice(nullptr), method(QXmppTransferJob::NoMethod), state(QXmppTransferJob::OfferState), deviceIsOwn(false), ibbSequence(0), socksSocket(nullptr) { } QXmppTransferJob::QXmppTransferJob(const QString &jid, QXmppTransferJob::Direction direction, QXmppClient *client, QObject *parent) : QXmppLoggable(parent), d(new QXmppTransferJobPrivate) { d->client = client; d->direction = direction; d->jid = jid; } QXmppTransferJob::~QXmppTransferJob() { delete d; } /// Call this method if you wish to abort on ongoing transfer job. /// void QXmppTransferJob::abort() { terminate(AbortError); } /// Call this method if you wish to accept an incoming transfer job. /// void QXmppTransferJob::accept(const QString &filePath) { if (d->direction == IncomingDirection && d->state == OfferState && !d->iodevice) { auto *file = new QFile(filePath, this); if (!file->open(QIODevice::WriteOnly)) { warning(QString("Could not write to %1").arg(filePath)); abort(); return; } d->iodevice = file; setLocalFileUrl(QUrl::fromLocalFile(filePath)); setState(QXmppTransferJob::StartState); } } /// Call this method if you wish to accept an incoming transfer job. /// void QXmppTransferJob::accept(QIODevice *iodevice) { if (d->direction == IncomingDirection && d->state == OfferState && !d->iodevice) { d->iodevice = iodevice; setState(QXmppTransferJob::StartState); } } QXmppTransferJob::Direction QXmppTransferJob::direction() const { return d->direction; } /// /// Returns the last error that was encountered. /// QXmppTransferJob::Error QXmppTransferJob::error() const { return d->error; } QString QXmppTransferJob::jid() const { return d->jid; } QUrl QXmppTransferJob::localFileUrl() const { return d->localFileUrl; } /// Sets the local file URL. /// /// \note You do not need to call this method if you called accept() /// with a file path. void QXmppTransferJob::setLocalFileUrl(const QUrl &localFileUrl) { if (localFileUrl != d->localFileUrl) { d->localFileUrl = localFileUrl; emit localFileUrlChanged(localFileUrl); } } /// Returns meta-data about the file being transferred. /// QXmppTransferFileInfo QXmppTransferJob::fileInfo() const { return d->fileInfo; } /// \cond QDateTime QXmppTransferJob::fileDate() const { return d->fileInfo.date(); } QByteArray QXmppTransferJob::fileHash() const { return d->fileInfo.hash(); } QString QXmppTransferJob::fileName() const { return d->fileInfo.name(); } qint64 QXmppTransferJob::fileSize() const { return d->fileInfo.size(); } /// \endcond QXmppTransferJob::Method QXmppTransferJob::method() const { return d->method; } /// Returns the job's session identifier. /// QString QXmppTransferJob::sid() const { return d->sid; } /// Returns the job's transfer speed in bytes per second. /// /// If the transfer has not started yet or is already finished, returns 0. /// qint64 QXmppTransferJob::speed() const { qint64 elapsed = d->transferStart.elapsed(); if (d->state != QXmppTransferJob::TransferState || !elapsed) return 0; return (d->done * 1000.0) / elapsed; } QXmppTransferJob::State QXmppTransferJob::state() const { return d->state; } void QXmppTransferJob::setState(QXmppTransferJob::State state) { if (d->state != state) { d->state = state; if (d->state == QXmppTransferJob::TransferState) d->transferStart.start(); emit stateChanged(d->state); } } void QXmppTransferJob::_q_terminated() { emit stateChanged(d->state); if (d->error != NoError) emit error(d->error); emit finished(); } void QXmppTransferJob::terminate(QXmppTransferJob::Error cause) { if (d->state == FinishedState) return; // change state d->error = cause; d->state = FinishedState; // close IO device if (d->iodevice && d->deviceIsOwn) d->iodevice->close(); // close socket if (d->socksSocket) { d->socksSocket->flush(); d->socksSocket->close(); } // emit signals later QTimer::singleShot(0, this, SLOT(_q_terminated())); } /// \cond QXmppTransferIncomingJob::QXmppTransferIncomingJob(const QString &jid, QXmppClient *client, QObject *parent) : QXmppTransferJob(jid, IncomingDirection, client, parent), m_candidateClient(nullptr), m_candidateTimer(nullptr) { } void QXmppTransferIncomingJob::checkData() { if ((d->fileInfo.size() && d->done != d->fileInfo.size()) || (!d->fileInfo.hash().isEmpty() && d->hash.result() != d->fileInfo.hash())) terminate(QXmppTransferJob::FileCorruptError); else terminate(QXmppTransferJob::NoError); } void QXmppTransferIncomingJob::connectToNextHost() { if (m_streamCandidates.isEmpty()) { // could not connect to any stream host QXmppByteStreamIq response; response.setId(m_streamOfferId); response.setTo(m_streamOfferFrom); QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::ItemNotFound); error.setCode(404); response.setType(QXmppIq::Error); response.setError(error); d->client->sendPacket(response); terminate(QXmppTransferJob::ProtocolError); return; } // try next host m_candidateHost = m_streamCandidates.takeFirst(); info(QString("Connecting to streamhost: %1 (%2 %3)").arg(m_candidateHost.jid(), m_candidateHost.host(), QString::number(m_candidateHost.port()))); const QString hostName = streamHash(d->sid, d->jid, d->client->configuration().jid()); // try to connect to stream host m_candidateClient = new QXmppSocksClient(m_candidateHost.host(), m_candidateHost.port(), this); m_candidateTimer = new QTimer(this); connect(m_candidateClient, &QAbstractSocket::disconnected, this, &QXmppTransferIncomingJob::_q_candidateDisconnected); connect(m_candidateClient, &QXmppSocksClient::ready, this, &QXmppTransferIncomingJob::_q_candidateReady); connect(m_candidateTimer, &QTimer::timeout, this, &QXmppTransferIncomingJob::_q_candidateDisconnected); m_candidateTimer->setSingleShot(true); m_candidateTimer->start(socksTimeout); m_candidateClient->connectToHost(hostName, 0); } void QXmppTransferIncomingJob::connectToHosts(const QXmppByteStreamIq &iq) { m_streamCandidates = iq.streamHosts(); m_streamOfferId = iq.id(); m_streamOfferFrom = iq.from(); connectToNextHost(); } bool QXmppTransferIncomingJob::writeData(const QByteArray &data) { const qint64 written = d->iodevice->write(data); if (written < 0) return false; d->done += written; if (!d->fileInfo.hash().isEmpty()) d->hash.addData(data); progress(d->done, d->fileInfo.size()); return true; } void QXmppTransferIncomingJob::_q_candidateReady() { if (!m_candidateClient) return; info(QString("Connected to streamhost: %1 (%2 %3)").arg(m_candidateHost.jid(), m_candidateHost.host(), QString::number(m_candidateHost.port()))); setState(QXmppTransferJob::TransferState); d->socksSocket = m_candidateClient; m_candidateClient = nullptr; m_candidateTimer->deleteLater(); m_candidateTimer = nullptr; connect(d->socksSocket, &QIODevice::readyRead, this, &QXmppTransferIncomingJob::_q_receiveData); connect(d->socksSocket, &QAbstractSocket::disconnected, this, &QXmppTransferIncomingJob::_q_disconnected); QXmppByteStreamIq ackIq; ackIq.setId(m_streamOfferId); ackIq.setTo(m_streamOfferFrom); ackIq.setType(QXmppIq::Result); ackIq.setSid(d->sid); ackIq.setStreamHostUsed(m_candidateHost.jid()); d->client->sendPacket(ackIq); } void QXmppTransferIncomingJob::_q_candidateDisconnected() { if (!m_candidateClient) return; warning(QString("Failed to connect to streamhost: %1 (%2 %3)").arg(m_candidateHost.jid(), m_candidateHost.host(), QString::number(m_candidateHost.port()))); m_candidateClient->deleteLater(); m_candidateClient = nullptr; m_candidateTimer->deleteLater(); m_candidateTimer = nullptr; // try next host connectToNextHost(); } void QXmppTransferIncomingJob::_q_disconnected() { if (d->state == QXmppTransferJob::FinishedState) return; checkData(); } void QXmppTransferIncomingJob::_q_receiveData() { if (d->state != QXmppTransferJob::TransferState) return; // receive data block if (d->direction == QXmppTransferJob::IncomingDirection) { writeData(d->socksSocket->readAll()); // if we have received all the data, stop here if (fileSize() && d->done >= fileSize()) checkData(); } } QXmppTransferOutgoingJob::QXmppTransferOutgoingJob(const QString &jid, QXmppClient *client, QObject *parent) : QXmppTransferJob(jid, OutgoingDirection, client, parent) { } void QXmppTransferOutgoingJob::connectToProxy() { info(QString("Connecting to proxy: %1 (%2 %3)").arg(d->socksProxy.jid(), d->socksProxy.host(), QString::number(d->socksProxy.port()))); const QString hostName = streamHash(d->sid, d->client->configuration().jid(), d->jid); QXmppSocksClient *socksClient = new QXmppSocksClient(d->socksProxy.host(), d->socksProxy.port(), this); connect(socksClient, &QAbstractSocket::disconnected, this, &QXmppTransferOutgoingJob::_q_disconnected); connect(socksClient, &QXmppSocksClient::ready, this, &QXmppTransferOutgoingJob::_q_proxyReady); d->socksSocket = socksClient; socksClient->connectToHost(hostName, 0); } void QXmppTransferOutgoingJob::startSending() { setState(QXmppTransferJob::TransferState); connect(d->socksSocket, &QIODevice::bytesWritten, this, &QXmppTransferOutgoingJob::_q_sendData); connect(d->iodevice, &QIODevice::readyRead, this, &QXmppTransferOutgoingJob::_q_sendData); _q_sendData(); } void QXmppTransferOutgoingJob::_q_disconnected() { if (d->state == QXmppTransferJob::FinishedState) return; if (fileSize() && d->done != fileSize()) terminate(QXmppTransferJob::ProtocolError); else terminate(QXmppTransferJob::NoError); } void QXmppTransferOutgoingJob::_q_proxyReady() { // activate stream QXmppByteStreamIq streamIq; streamIq.setType(QXmppIq::Set); streamIq.setFrom(d->client->configuration().jid()); streamIq.setTo(d->socksProxy.jid()); streamIq.setSid(d->sid); streamIq.setActivate(d->jid); d->requestId = streamIq.id(); d->client->sendPacket(streamIq); } void QXmppTransferOutgoingJob::_q_sendData() { if (d->state != QXmppTransferJob::TransferState) return; // don't saturate the outgoing socket if (d->socksSocket->bytesToWrite() > 2 * d->blockSize) return; // check whether we have written the whole file if (d->fileInfo.size() && d->done >= d->fileInfo.size()) { if (!d->socksSocket->bytesToWrite()) terminate(QXmppTransferJob::NoError); return; } char *buffer = new char[d->blockSize]; qint64 length = d->iodevice->read(buffer, d->blockSize); if (length < 0) { delete[] buffer; terminate(QXmppTransferJob::FileAccessError); return; } else { d->socksSocket->write(buffer, length); delete[] buffer; d->done += length; emit progress(d->done, fileSize()); } } /// \endcond class QXmppTransferManagerPrivate { public: QXmppTransferManagerPrivate(QXmppTransferManager *qq); QXmppTransferIncomingJob *getIncomingJobByRequestId(const QString &jid, const QString &id); QXmppTransferIncomingJob *getIncomingJobBySid(const QString &jid, const QString &sid); QXmppTransferOutgoingJob *getOutgoingJobByRequestId(const QString &jid, const QString &id); int ibbBlockSize; QList jobs; QString proxy; bool proxyOnly; QXmppSocksServer *socksServer; QXmppTransferJob::Methods supportedMethods; private: QXmppTransferJob *getJobByRequestId(QXmppTransferJob::Direction direction, const QString &jid, const QString &id); QXmppTransferManager *q; }; QXmppTransferManagerPrivate::QXmppTransferManagerPrivate(QXmppTransferManager *qq) : ibbBlockSize(4096), proxyOnly(false), socksServer(nullptr), supportedMethods(QXmppTransferJob::AnyMethod), q(qq) { } QXmppTransferJob *QXmppTransferManagerPrivate::getJobByRequestId(QXmppTransferJob::Direction direction, const QString &jid, const QString &id) { for (auto *job : jobs) { if (job->d->direction == direction && job->d->jid == jid && job->d->requestId == id) return job; } return nullptr; } QXmppTransferIncomingJob *QXmppTransferManagerPrivate::getIncomingJobByRequestId(const QString &jid, const QString &id) { return static_cast(getJobByRequestId(QXmppTransferJob::IncomingDirection, jid, id)); } QXmppTransferIncomingJob *QXmppTransferManagerPrivate::getIncomingJobBySid(const QString &jid, const QString &sid) { for (auto *job : jobs) { if (job->d->direction == QXmppTransferJob::IncomingDirection && job->d->jid == jid && job->d->sid == sid) return static_cast(job); } return nullptr; } QXmppTransferOutgoingJob *QXmppTransferManagerPrivate::getOutgoingJobByRequestId(const QString &jid, const QString &id) { return static_cast(getJobByRequestId(QXmppTransferJob::OutgoingDirection, jid, id)); } /// Constructs a QXmppTransferManager to handle incoming and outgoing /// file transfers. QXmppTransferManager::QXmppTransferManager() { d = new QXmppTransferManagerPrivate(this); // start SOCKS server d->socksServer = new QXmppSocksServer(this); connect(d->socksServer, &QXmppSocksServer::newConnection, this, &QXmppTransferManager::_q_socksServerConnected); if (!d->socksServer->listen()) { qWarning("QXmppSocksServer could not start listening"); } } QXmppTransferManager::~QXmppTransferManager() { delete d; } void QXmppTransferManager::byteStreamIqReceived(const QXmppByteStreamIq &iq) { // handle IQ from proxy for (auto *job : d->jobs) { if (job->d->socksProxy.jid() == iq.from() && job->d->requestId == iq.id()) { if (iq.type() == QXmppIq::Result && iq.streamHosts().size() > 0) { job->d->socksProxy = iq.streamHosts().first(); socksServerSendOffer(job); return; } } } if (iq.type() == QXmppIq::Result) byteStreamResultReceived(iq); else if (iq.type() == QXmppIq::Set) byteStreamSetReceived(iq); } /// Handle a response to a bystream set, i.e. after we informed the remote party /// that we connected to a stream host. void QXmppTransferManager::byteStreamResponseReceived(const QXmppIq &iq) { QXmppTransferJob *job = d->getIncomingJobByRequestId(iq.from(), iq.id()); if (!job || job->method() != QXmppTransferJob::SocksMethod || job->state() != QXmppTransferJob::StartState) return; if (iq.type() == QXmppIq::Error) job->terminate(QXmppTransferJob::ProtocolError); } /// Handle a bytestream result, i.e. after the remote party has connected to /// a stream host. void QXmppTransferManager::byteStreamResultReceived(const QXmppByteStreamIq &iq) { QXmppTransferOutgoingJob *job = d->getOutgoingJobByRequestId(iq.from(), iq.id()); if (!job || job->method() != QXmppTransferJob::SocksMethod || job->state() != QXmppTransferJob::StartState) return; // check the stream host if (iq.streamHostUsed() == job->d->socksProxy.jid()) { job->connectToProxy(); return; } // direction connection, start sending data if (!job->d->socksSocket) { warning("Client says they connected to our SOCKS server, but they did not"); job->terminate(QXmppTransferJob::ProtocolError); return; } connect(job->d->socksSocket, &QTcpSocket::disconnected, job, &QXmppTransferOutgoingJob::_q_disconnected); job->startSending(); } /// Handle a bytestream set, i.e. an invitation from the remote party to connect /// to a stream host. void QXmppTransferManager::byteStreamSetReceived(const QXmppByteStreamIq &iq) { QXmppIq response; response.setId(iq.id()); response.setTo(iq.from()); QXmppTransferIncomingJob *job = d->getIncomingJobBySid(iq.from(), iq.sid()); if (!job || job->method() != QXmppTransferJob::SocksMethod || job->state() != QXmppTransferJob::StartState) { // the stream is unknown QXmppStanza::Error error(QXmppStanza::Error::Auth, QXmppStanza::Error::NotAcceptable); error.setCode(406); response.setType(QXmppIq::Error); response.setError(error); client()->sendPacket(response); return; } job->connectToHosts(iq); } /// \cond QStringList QXmppTransferManager::discoveryFeatures() const { return QStringList() << ns_ibb // XEP-0047: In-Band Bytestreams << ns_bytestreams // XEP-0065: SOCKS5 Bytestreams << ns_stream_initiation // XEP-0095: Stream Initiation << ns_stream_initiation_file_transfer; // XEP-0096: SI File Transfer } bool QXmppTransferManager::handleStanza(const QDomElement &element) { if (element.tagName() != "iq") return false; // XEP-0047 In-Band Bytestreams if (QXmppIbbCloseIq::isIbbCloseIq(element)) { QXmppIbbCloseIq ibbCloseIq; ibbCloseIq.parse(element); ibbCloseIqReceived(ibbCloseIq); return true; } else if (QXmppIbbDataIq::isIbbDataIq(element)) { QXmppIbbDataIq ibbDataIq; ibbDataIq.parse(element); ibbDataIqReceived(ibbDataIq); return true; } else if (QXmppIbbOpenIq::isIbbOpenIq(element)) { QXmppIbbOpenIq ibbOpenIq; ibbOpenIq.parse(element); ibbOpenIqReceived(ibbOpenIq); return true; } // XEP-0065: SOCKS5 Bytestreams else if (QXmppByteStreamIq::isByteStreamIq(element)) { QXmppByteStreamIq byteStreamIq; byteStreamIq.parse(element); byteStreamIqReceived(byteStreamIq); return true; } // XEP-0095: Stream Initiation else if (QXmppStreamInitiationIq::isStreamInitiationIq(element)) { QXmppStreamInitiationIq siIq; siIq.parse(element); streamInitiationIqReceived(siIq); return true; } return false; } void QXmppTransferManager::setClient(QXmppClient *client) { QXmppClientExtension::setClient(client); // XEP-0047: In-Band Bytestreams connect(client, &QXmppClient::iqReceived, this, &QXmppTransferManager::_q_iqReceived); } /// \endcond void QXmppTransferManager::ibbCloseIqReceived(const QXmppIbbCloseIq &iq) { QXmppIq response; response.setTo(iq.from()); response.setId(iq.id()); QXmppTransferIncomingJob *job = d->getIncomingJobBySid(iq.from(), iq.sid()); if (!job || job->method() != QXmppTransferJob::InBandMethod) { // the job is unknown, cancel it QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::ItemNotFound); response.setType(QXmppIq::Error); response.setError(error); client()->sendPacket(response); return; } // acknowledge the packet response.setType(QXmppIq::Result); client()->sendPacket(response); // check received data job->checkData(); } void QXmppTransferManager::ibbDataIqReceived(const QXmppIbbDataIq &iq) { QXmppIq response; response.setTo(iq.from()); response.setId(iq.id()); QXmppTransferIncomingJob *job = d->getIncomingJobBySid(iq.from(), iq.sid()); if (!job || job->method() != QXmppTransferJob::InBandMethod || job->state() != QXmppTransferJob::TransferState) { // the job is unknown, cancel it QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::ItemNotFound); response.setType(QXmppIq::Error); response.setError(error); client()->sendPacket(response); return; } if (iq.sequence() != job->d->ibbSequence) { // the packet is out of sequence QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::UnexpectedRequest); response.setType(QXmppIq::Error); response.setError(error); client()->sendPacket(response); return; } // write data job->writeData(iq.payload()); job->d->ibbSequence++; // acknowledge the packet response.setType(QXmppIq::Result); client()->sendPacket(response); } void QXmppTransferManager::ibbOpenIqReceived(const QXmppIbbOpenIq &iq) { QXmppIq response; response.setTo(iq.from()); response.setId(iq.id()); QXmppTransferJob *job = d->getIncomingJobBySid(iq.from(), iq.sid()); if (!job || job->method() != QXmppTransferJob::InBandMethod) { // the job is unknown, cancel it QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::ItemNotFound); response.setType(QXmppIq::Error); response.setError(error); client()->sendPacket(response); return; } if (iq.blockSize() > d->ibbBlockSize) { // we prefer a smaller block size QXmppStanza::Error error(QXmppStanza::Error::Modify, QXmppStanza::Error::ResourceConstraint); response.setType(QXmppIq::Error); response.setError(error); client()->sendPacket(response); return; } job->d->blockSize = iq.blockSize(); job->setState(QXmppTransferJob::TransferState); // accept transfer response.setType(QXmppIq::Result); client()->sendPacket(response); } void QXmppTransferManager::ibbResponseReceived(const QXmppIq &iq) { QXmppTransferJob *job = d->getOutgoingJobByRequestId(iq.from(), iq.id()); if (!job || job->method() != QXmppTransferJob::InBandMethod || job->state() == QXmppTransferJob::FinishedState) return; // if the IO device is closed, do nothing if (!job->d->iodevice->isOpen()) return; if (iq.type() == QXmppIq::Result) { const QByteArray buffer = job->d->iodevice->read(job->d->blockSize); job->setState(QXmppTransferJob::TransferState); if (buffer.size()) { // send next data block QXmppIbbDataIq dataIq; dataIq.setTo(job->d->jid); dataIq.setSid(job->d->sid); dataIq.setSequence(job->d->ibbSequence++); dataIq.setPayload(buffer); job->d->requestId = dataIq.id(); client()->sendPacket(dataIq); job->d->done += buffer.size(); job->progress(job->d->done, job->fileSize()); } else { // close the bytestream QXmppIbbCloseIq closeIq; closeIq.setTo(job->d->jid); closeIq.setSid(job->d->sid); job->d->requestId = closeIq.id(); client()->sendPacket(closeIq); job->terminate(QXmppTransferJob::NoError); } } else if (iq.type() == QXmppIq::Error) { // close the bytestream QXmppIbbCloseIq closeIq; closeIq.setTo(job->d->jid); closeIq.setSid(job->d->sid); job->d->requestId = closeIq.id(); client()->sendPacket(closeIq); job->terminate(QXmppTransferJob::ProtocolError); } } void QXmppTransferManager::_q_iqReceived(const QXmppIq &iq) { for (auto *ptr : d->jobs) { // handle IQ from proxy if (ptr->direction() == QXmppTransferJob::OutgoingDirection && ptr->d->socksProxy.jid() == iq.from() && ptr->d->requestId == iq.id()) { auto *job = static_cast(ptr); if (job->d->socksSocket) { // proxy connection activation result if (iq.type() == QXmppIq::Result) { // proxy stream activated, start sending data job->startSending(); } else if (iq.type() == QXmppIq::Error) { // proxy stream not activated, terminate warning("Could not activate SOCKS5 proxy bytestream"); job->terminate(QXmppTransferJob::ProtocolError); } } else { // we could not get host/port from proxy, proceed without a proxy if (iq.type() == QXmppIq::Error) socksServerSendOffer(job); } return; } // handle IQ from peer else if (ptr->d->jid == iq.from() && ptr->d->requestId == iq.id()) { QXmppTransferJob *job = ptr; if (job->direction() == QXmppTransferJob::OutgoingDirection && job->method() == QXmppTransferJob::InBandMethod) { ibbResponseReceived(iq); return; } else if (job->direction() == QXmppTransferJob::IncomingDirection && job->method() == QXmppTransferJob::SocksMethod) { byteStreamResponseReceived(iq); return; } else if (job->direction() == QXmppTransferJob::OutgoingDirection && iq.type() == QXmppIq::Error) { // remote party cancelled stream initiation job->terminate(QXmppTransferJob::AbortError); return; } } } } void QXmppTransferManager::_q_jobDestroyed(QObject *object) { d->jobs.removeAll(static_cast(object)); } void QXmppTransferManager::_q_jobError(QXmppTransferJob::Error error) { auto *job = qobject_cast(sender()); if (!job || !d->jobs.contains(job)) return; if (job->direction() == QXmppTransferJob::OutgoingDirection && job->method() == QXmppTransferJob::InBandMethod && error == QXmppTransferJob::AbortError) { // close the bytestream QXmppIbbCloseIq closeIq; closeIq.setTo(job->d->jid); closeIq.setSid(job->d->sid); job->d->requestId = closeIq.id(); client()->sendPacket(closeIq); } } void QXmppTransferManager::_q_jobFinished() { auto *job = qobject_cast(sender()); if (!job || !d->jobs.contains(job)) return; emit jobFinished(job); } void QXmppTransferManager::_q_jobStateChanged(QXmppTransferJob::State state) { auto *job = qobject_cast(sender()); if (!job || !d->jobs.contains(job)) return; if (job->direction() != QXmppTransferJob::IncomingDirection) return; // disconnect from the signal disconnect(job, &QXmppTransferJob::stateChanged, this, &QXmppTransferManager::_q_jobStateChanged); // the job was refused by the local party if (state != QXmppTransferJob::StartState || !job->d->iodevice || !job->d->iodevice->isWritable()) { QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::Forbidden); error.setCode(403); QXmppIq response; response.setTo(job->jid()); response.setId(job->d->offerId); response.setType(QXmppIq::Error); response.setError(error); client()->sendPacket(response); job->terminate(QXmppTransferJob::AbortError); return; } // the job was accepted by the local party connect(job, QOverload::of(&QXmppTransferJob::error), this, &QXmppTransferManager::_q_jobError); QXmppDataForm form; form.setType(QXmppDataForm::Submit); QXmppDataForm::Field methodField(QXmppDataForm::Field::ListSingleField); methodField.setKey("stream-method"); if (job->method() == QXmppTransferJob::InBandMethod) methodField.setValue(ns_ibb); else if (job->method() == QXmppTransferJob::SocksMethod) methodField.setValue(ns_bytestreams); form.setFields(QList() << methodField); QXmppStreamInitiationIq response; response.setTo(job->jid()); response.setId(job->d->offerId); response.setType(QXmppIq::Result); response.setProfile(QXmppStreamInitiationIq::FileTransfer); response.setFeatureForm(form); client()->sendPacket(response); // notify user emit jobStarted(job); } /// Sends the file at \a filePath to a remote party. /// /// The remote party will be given the choice to accept or refuse the transfer. /// /// Returns 0 if the \a jid is not valid or if the file at \a filePath cannot be read. /// /// \note The recipient's \a jid must be a full JID with a resource, for instance "user@host/resource". /// QXmppTransferJob *QXmppTransferManager::sendFile(const QString &jid, const QString &filePath, const QString &description) { if (QXmppUtils::jidToResource(jid).isEmpty()) { warning("The file recipient's JID must be a full JID"); return nullptr; } QFileInfo info(filePath); QXmppTransferFileInfo fileInfo; fileInfo.setDate(info.lastModified()); fileInfo.setName(info.fileName()); fileInfo.setSize(info.size()); fileInfo.setDescription(description); // open file QIODevice *device = new QFile(filePath, this); if (!device->open(QIODevice::ReadOnly)) { warning(QString("Could not read from %1").arg(filePath)); delete device; device = nullptr; } // hash file if (device && !device->isSequential()) { QCryptographicHash hash(QCryptographicHash::Md5); QByteArray buffer; while (device->bytesAvailable()) { buffer = device->read(16384); hash.addData(buffer); } device->reset(); fileInfo.setHash(hash.result()); } // create job QXmppTransferJob *job = sendFile(jid, device, fileInfo); job->setLocalFileUrl(QUrl::fromLocalFile(filePath)); job->d->deviceIsOwn = true; return job; } /// Sends the file in \a device to a remote party. /// /// The remote party will be given the choice to accept or refuse the transfer. /// /// Returns 0 if the \a jid is not valid. /// /// \note The recipient's \a jid must be a full JID with a resource, for instance "user@host/resource". /// \note The ownership of the \a device should be managed by the caller. /// QXmppTransferJob *QXmppTransferManager::sendFile(const QString &jid, QIODevice *device, const QXmppTransferFileInfo &fileInfo, const QString &sid) { if (QXmppUtils::jidToResource(jid).isEmpty()) { warning("The file recipient's JID must be a full JID"); return nullptr; } auto *job = new QXmppTransferOutgoingJob(jid, client(), this); if (sid.isEmpty()) job->d->sid = QXmppUtils::generateStanzaHash(); else job->d->sid = sid; job->d->fileInfo = fileInfo; job->d->iodevice = device; // check file is open if (!device || !device->isReadable()) { job->terminate(QXmppTransferJob::FileAccessError); return job; } // check we support some methods if (!d->supportedMethods) { job->terminate(QXmppTransferJob::ProtocolError); return job; } // collect supported stream methods QXmppDataForm form; form.setType(QXmppDataForm::Form); QXmppDataForm::Field methodField(QXmppDataForm::Field::ListSingleField); methodField.setKey("stream-method"); if (d->supportedMethods & QXmppTransferJob::InBandMethod) methodField.setOptions(methodField.options() << qMakePair(QString(), QString::fromLatin1(ns_ibb))); if (d->supportedMethods & QXmppTransferJob::SocksMethod) methodField.setOptions(methodField.options() << qMakePair(QString(), QString::fromLatin1(ns_bytestreams))); form.setFields(QList() << methodField); // start job d->jobs.append(job); connect(job, &QObject::destroyed, this, &QXmppTransferManager::_q_jobDestroyed); connect(job, QOverload::of(&QXmppTransferJob::error), this, &QXmppTransferManager::_q_jobError); connect(job, &QXmppTransferJob::finished, this, &QXmppTransferManager::_q_jobFinished); QXmppStreamInitiationIq request; request.setType(QXmppIq::Set); request.setTo(jid); request.setProfile(QXmppStreamInitiationIq::FileTransfer); request.setFileInfo(job->d->fileInfo); request.setFeatureForm(form); request.setSiId(job->d->sid); job->d->requestId = request.id(); client()->sendPacket(request); // notify user emit jobStarted(job); return job; } void QXmppTransferManager::_q_socksServerConnected(QTcpSocket *socket, const QString &hostName, quint16 port) { const QString ownJid = client()->configuration().jid(); for (auto *job : d->jobs) { if (hostName == streamHash(job->d->sid, ownJid, job->jid()) && port == 0) { job->d->socksSocket = socket; return; } } warning("QXmppSocksServer got a connection for a unknown stream"); socket->close(); } void QXmppTransferManager::socksServerSendOffer(QXmppTransferJob *job) { const QString ownJid = client()->configuration().jid(); QList streamHosts; // discover local IPs if (!d->proxyOnly) { const auto &addresses = QXmppIceComponent::discoverAddresses(); for (const auto &address : addresses) { QXmppByteStreamIq::StreamHost streamHost; streamHost.setJid(ownJid); streamHost.setHost(address.toString()); streamHost.setPort(d->socksServer->serverPort()); streamHosts.append(streamHost); } } // add proxy if (!job->d->socksProxy.jid().isEmpty()) streamHosts.append(job->d->socksProxy); // check we have some stream hosts if (!streamHosts.size()) { warning("Could not determine local stream hosts"); job->terminate(QXmppTransferJob::ProtocolError); return; } // send offer QXmppByteStreamIq streamIq; streamIq.setType(QXmppIq::Set); streamIq.setTo(job->d->jid); streamIq.setSid(job->d->sid); streamIq.setStreamHosts(streamHosts); job->d->requestId = streamIq.id(); client()->sendPacket(streamIq); } void QXmppTransferManager::streamInitiationIqReceived(const QXmppStreamInitiationIq &iq) { if (iq.type() == QXmppIq::Result) streamInitiationResultReceived(iq); else if (iq.type() == QXmppIq::Set) streamInitiationSetReceived(iq); } // The remote party has accepted an outgoing transfer. void QXmppTransferManager::streamInitiationResultReceived(const QXmppStreamInitiationIq &iq) { QXmppTransferJob *job = d->getOutgoingJobByRequestId(iq.from(), iq.id()); if (!job || job->state() != QXmppTransferJob::OfferState) return; const auto &form = iq.featureForm(); const auto &fields = form.fields(); for (const auto &field : fields) { if (field.key() == "stream-method") { if ((field.value().toString() == ns_ibb) && (d->supportedMethods & QXmppTransferJob::InBandMethod)) job->d->method = QXmppTransferJob::InBandMethod; else if ((field.value().toString() == ns_bytestreams) && (d->supportedMethods & QXmppTransferJob::SocksMethod)) job->d->method = QXmppTransferJob::SocksMethod; } } // remote party accepted stream initiation job->setState(QXmppTransferJob::StartState); if (job->method() == QXmppTransferJob::InBandMethod) { // lower block size for IBB job->d->blockSize = d->ibbBlockSize; QXmppIbbOpenIq openIq; openIq.setTo(job->d->jid); openIq.setSid(job->d->sid); openIq.setBlockSize(job->d->blockSize); job->d->requestId = openIq.id(); client()->sendPacket(openIq); } else if (job->method() == QXmppTransferJob::SocksMethod) { if (!d->proxy.isEmpty()) { job->d->socksProxy.setJid(d->proxy); // query proxy QXmppByteStreamIq streamIq; streamIq.setType(QXmppIq::Get); streamIq.setTo(job->d->socksProxy.jid()); streamIq.setSid(job->d->sid); job->d->requestId = streamIq.id(); client()->sendPacket(streamIq); } else { socksServerSendOffer(job); } } else { warning("QXmppTransferManager received an unsupported method"); job->terminate(QXmppTransferJob::ProtocolError); } } void QXmppTransferManager::streamInitiationSetReceived(const QXmppStreamInitiationIq &iq) { QXmppIq response; response.setTo(iq.from()); response.setId(iq.id()); // check we support the profile if (iq.profile() != QXmppStreamInitiationIq::FileTransfer) { // FIXME : we should add: // QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::BadRequest); error.setCode(400); response.setType(QXmppIq::Error); response.setError(error); client()->sendPacket(response); return; } // check there is a receiver connected to the fileReceived() signal if (!isSignalConnected(QMetaMethod::fromSignal(&QXmppTransferManager::fileReceived))) { QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::Forbidden); error.setCode(403); response.setType(QXmppIq::Error); response.setError(error); client()->sendPacket(response); return; } // check the stream type QXmppTransferIncomingJob *job = new QXmppTransferIncomingJob(iq.from(), client(), this); int offeredMethods = QXmppTransferJob::NoMethod; job->d->offerId = iq.id(); job->d->sid = iq.siId(); job->d->mimeType = iq.mimeType(); job->d->fileInfo = iq.fileInfo(); const auto &form = iq.featureForm(); const auto &fields = form.fields(); for (const auto &field : fields) { if (field.key() == "stream-method") { QPair option; const auto &options = field.options(); for (const auto &option : options) { if (option.second == ns_ibb) offeredMethods = offeredMethods | QXmppTransferJob::InBandMethod; else if (option.second == ns_bytestreams) offeredMethods = offeredMethods | QXmppTransferJob::SocksMethod; } } } // select a method supported by both parties int sharedMethods = (offeredMethods & d->supportedMethods); if (sharedMethods & QXmppTransferJob::SocksMethod) job->d->method = QXmppTransferJob::SocksMethod; else if (sharedMethods & QXmppTransferJob::InBandMethod) job->d->method = QXmppTransferJob::InBandMethod; else { // FIXME : we should add: // QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::BadRequest); error.setCode(400); response.setType(QXmppIq::Error); response.setError(error); client()->sendPacket(response); delete job; return; } // register job d->jobs.append(job); connect(job, &QObject::destroyed, this, &QXmppTransferManager::_q_jobDestroyed); connect(job, &QXmppTransferJob::finished, this, &QXmppTransferManager::_q_jobFinished); connect(job, &QXmppTransferJob::stateChanged, this, &QXmppTransferManager::_q_jobStateChanged); // allow user to accept or decline the job emit fileReceived(job); } QString QXmppTransferManager::proxy() const { return d->proxy; } /// Set the JID of the SOCKS5 bytestream proxy to use for /// outgoing transfers. /// /// If you set a proxy, when you send a file the proxy will /// be offered to the recipient in addition to your own IP /// addresses. /// void QXmppTransferManager::setProxy(const QString &proxyJid) { d->proxy = proxyJid; } bool QXmppTransferManager::proxyOnly() const { return d->proxyOnly; } /// Set whether the proxy should systematically be used for /// outgoing SOCKS5 bytestream transfers. /// /// \note If you set this to true and do not provide a proxy /// using setProxy(), your outgoing transfers will fail! /// void QXmppTransferManager::setProxyOnly(bool proxyOnly) { d->proxyOnly = proxyOnly; } QXmppTransferJob::Methods QXmppTransferManager::supportedMethods() const { return d->supportedMethods; } /// Set the supported stream methods. This allows you to selectively /// enable or disable stream methods (In-Band or SOCKS5 bytestreams). /// /// The methods argument is a combination of zero or more /// QXmppTransferJob::Method. /// void QXmppTransferManager::setSupportedMethods(QXmppTransferJob::Methods methods) { d->supportedMethods = methods; } qxmpp-1.4.0/src/client/QXmppTransferManager.h000066400000000000000000000260031402370562100211450ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPTRANSFERMANAGER_H #define QXMPPTRANSFERMANAGER_H #include "QXmppClientExtension.h" #include #include #include #include class QTcpSocket; class QXmppByteStreamIq; class QXmppIbbCloseIq; class QXmppIbbDataIq; class QXmppIbbOpenIq; class QXmppIq; class QXmppStreamInitiationIq; class QXmppTransferFileInfoPrivate; class QXmppTransferJobPrivate; class QXmppTransferManager; class QXmppTransferManagerPrivate; class QXMPP_EXPORT QXmppTransferFileInfo { public: QXmppTransferFileInfo(); QXmppTransferFileInfo(const QXmppTransferFileInfo &other); ~QXmppTransferFileInfo(); QDateTime date() const; void setDate(const QDateTime &date); QByteArray hash() const; void setHash(const QByteArray &hash); QString name() const; void setName(const QString &name); QString description() const; void setDescription(const QString &description); qint64 size() const; void setSize(qint64 size); bool isNull() const; QXmppTransferFileInfo &operator=(const QXmppTransferFileInfo &other); bool operator==(const QXmppTransferFileInfo &other) const; /// \cond void parse(const QDomElement &element); void toXml(QXmlStreamWriter *writer) const; /// \endcond private: QSharedDataPointer d; }; /// \brief The QXmppTransferJob class represents a single file transfer job. /// /// \sa QXmppTransferManager /// class QXMPP_EXPORT QXmppTransferJob : public QXmppLoggable { Q_OBJECT Q_FLAGS(Method Methods) /// The job's transfer direction Q_PROPERTY(Direction direction READ direction CONSTANT) /// The local file URL Q_PROPERTY(QUrl localFileUrl READ localFileUrl WRITE setLocalFileUrl NOTIFY localFileUrlChanged) /// The remote party's JID Q_PROPERTY(QString jid READ jid CONSTANT) /// The job's transfer method Q_PROPERTY(Method method READ method CONSTANT) /// The job's state Q_PROPERTY(State state READ state NOTIFY stateChanged) /// The name of the file Q_PROPERTY(QString fileName READ fileName CONSTANT) /// The size of the file Q_PROPERTY(qint64 fileSize READ fileSize CONSTANT) public: /// This enum is used to describe the direction of a transfer job. enum Direction { IncomingDirection, ///< The file is being received. OutgoingDirection ///< The file is being sent. }; Q_ENUM(Direction) /// This enum is used to describe the type of error encountered by a transfer job. enum Error { NoError = 0, ///< No error occurred. AbortError, ///< The file transfer was aborted. FileAccessError, ///< An error was encountered trying to access a local file. FileCorruptError, ///< The file is corrupt: the file size or hash do not match. ProtocolError ///< An error was encountered in the file transfer protocol. }; Q_ENUM(Error) /// This enum is used to describe a transfer method. enum Method { NoMethod = 0, ///< No transfer method. InBandMethod = 1, ///< \xep{0047}: In-Band Bytestreams SocksMethod = 2, ///< \xep{0065}: SOCKS5 Bytestreams AnyMethod = 3 ///< Any supported transfer method. }; Q_ENUM(Method) Q_DECLARE_FLAGS(Methods, Method) /// This enum is used to describe the state of a transfer job. enum State { OfferState = 0, ///< The transfer is being offered to the remote party. StartState = 1, ///< The transfer is being connected. TransferState = 2, ///< The transfer is ongoing. FinishedState = 3 ///< The transfer is finished. }; Q_ENUM(State) ~QXmppTransferJob() override; // documentation needs to be here, see https://stackoverflow.com/questions/49192523/ /// Returns the job's transfer direction. QXmppTransferJob::Direction direction() const; /// Returns the remote party's JID. QString jid() const; /// Returns the job's transfer method. QXmppTransferJob::Method method() const; /// Returns the job's state. QXmppTransferJob::State state() const; QXmppTransferJob::Error error() const; QString sid() const; qint64 speed() const; // XEP-0096 : File transfer QXmppTransferFileInfo fileInfo() const; // documentation needs to be here, see https://stackoverflow.com/questions/49192523/ /// Returns the local file URL. QUrl localFileUrl() const; void setLocalFileUrl(const QUrl &localFileUrl); /// \cond QDateTime fileDate() const; QByteArray fileHash() const; QString fileName() const; qint64 fileSize() const; /// \endcond Q_SIGNALS: /// This signal is emitted when an error is encountered while /// processing the transfer job. void error(QXmppTransferJob::Error error); /// This signal is emitted when the transfer job is finished. /// /// You can determine if the job completed successfully by testing whether /// error() returns QXmppTransferJob::NoError. /// /// Note: Do not delete the job in the slot connected to this signal, /// instead use deleteLater(). void finished(); /// This signal is emitted when the local file URL changes. void localFileUrlChanged(const QUrl &localFileUrl); /// This signal is emitted to indicate the progress of this transfer job. void progress(qint64 done, qint64 total); /// This signal is emitted when the transfer job changes state. void stateChanged(QXmppTransferJob::State state); public Q_SLOTS: void abort(); void accept(const QString &filePath); void accept(QIODevice *output); private Q_SLOTS: void _q_terminated(); private: QXmppTransferJob(const QString &jid, QXmppTransferJob::Direction direction, QXmppClient *client, QObject *parent); void setState(QXmppTransferJob::State state); void terminate(QXmppTransferJob::Error error); QXmppTransferJobPrivate *const d; friend class QXmppTransferManager; friend class QXmppTransferManagerPrivate; friend class QXmppTransferIncomingJob; friend class QXmppTransferOutgoingJob; }; /// /// \brief The QXmppTransferManager class provides support for sending and /// receiving files. /// /// Stream initiation is performed as described in \xep{0095}: Stream Initiation /// and \xep{0096}: SI File Transfer. The actual file transfer is then performed /// using either \xep{0065}: SOCKS5 Bytestreams or \xep{0047}: In-Band Bytestreams. /// /// To make use of this manager, you need to instantiate it and load it into /// the QXmppClient instance as follows: /// /// \code /// QXmppTransferManager *manager = new QXmppTransferManager; /// client->addExtension(manager); /// \endcode /// /// \ingroup Managers /// class QXMPP_EXPORT QXmppTransferManager : public QXmppClientExtension { Q_OBJECT /// The JID of the bytestream proxy to use for outgoing transfers Q_PROPERTY(QString proxy READ proxy WRITE setProxy) /// Whether the proxy will systematically be used for outgoing SOCKS5 bytestream transfers Q_PROPERTY(bool proxyOnly READ proxyOnly WRITE setProxyOnly) /// The supported stream methods Q_PROPERTY(QXmppTransferJob::Methods supportedMethods READ supportedMethods WRITE setSupportedMethods) public: QXmppTransferManager(); ~QXmppTransferManager() override; // documentation needs to be here, see https://stackoverflow.com/questions/49192523/ /// Return the JID of the bytestream proxy to use for outgoing transfers. QString proxy() const; void setProxy(const QString &proxyJid); // documentation needs to be here, see https://stackoverflow.com/questions/49192523/ /// Return whether the proxy will systematically be used for outgoing /// SOCKS5 bytestream transfers. bool proxyOnly() const; void setProxyOnly(bool proxyOnly); // documentation needs to be here, see https://stackoverflow.com/questions/49192523/ /// Return the supported stream methods. /// /// The methods are a combination of zero or more QXmppTransferJob::Method. QXmppTransferJob::Methods supportedMethods() const; void setSupportedMethods(QXmppTransferJob::Methods methods); /// \cond QStringList discoveryFeatures() const override; bool handleStanza(const QDomElement &element) override; /// \endcond Q_SIGNALS: /// This signal is emitted when a new file transfer offer is received. /// /// To accept the transfer job, call the job's QXmppTransferJob::accept() method. /// To refuse the transfer job, call the job's QXmppTransferJob::abort() method. void fileReceived(QXmppTransferJob *job); /// This signal is emitted whenever a transfer job is started. void jobStarted(QXmppTransferJob *job); /// This signal is emitted whenever a transfer job is finished. /// /// \sa QXmppTransferJob::finished() void jobFinished(QXmppTransferJob *job); public Q_SLOTS: QXmppTransferJob *sendFile(const QString &jid, const QString &filePath, const QString &description = QString()); QXmppTransferJob *sendFile(const QString &jid, QIODevice *device, const QXmppTransferFileInfo &fileInfo, const QString &sid = QString()); protected: /// \cond void setClient(QXmppClient *client) override; /// \endcond private Q_SLOTS: void _q_iqReceived(const QXmppIq &); void _q_jobDestroyed(QObject *object); void _q_jobError(QXmppTransferJob::Error error); void _q_jobFinished(); void _q_jobStateChanged(QXmppTransferJob::State state); void _q_socksServerConnected(QTcpSocket *socket, const QString &hostName, quint16 port); private: QXmppTransferManagerPrivate *d; void byteStreamIqReceived(const QXmppByteStreamIq &); void byteStreamResponseReceived(const QXmppIq &); void byteStreamResultReceived(const QXmppByteStreamIq &); void byteStreamSetReceived(const QXmppByteStreamIq &); void ibbCloseIqReceived(const QXmppIbbCloseIq &); void ibbDataIqReceived(const QXmppIbbDataIq &); void ibbOpenIqReceived(const QXmppIbbOpenIq &); void ibbResponseReceived(const QXmppIq &); void streamInitiationIqReceived(const QXmppStreamInitiationIq &); void streamInitiationResultReceived(const QXmppStreamInitiationIq &); void streamInitiationSetReceived(const QXmppStreamInitiationIq &); void socksServerSendOffer(QXmppTransferJob *job); friend class QXmppTransferManagerPrivate; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QXmppTransferJob::Methods) #endif qxmpp-1.4.0/src/client/QXmppTransferManager_p.h000066400000000000000000000042461402370562100214710ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPTRANSFERMANAGER_P_H #define QXMPPTRANSFERMANAGER_P_H #include "QXmppByteStreamIq.h" #include "QXmppTransferManager.h" // // W A R N I N G // ------------- // // This file is not part of the QXmpp API. It exists for the convenience // of the QXmppTransferManager class. This header file may change from // version to version without notice, or even be removed. // // We mean it. // class QTimer; class QXmppSocksClient; class QXmppTransferIncomingJob : public QXmppTransferJob { Q_OBJECT public: QXmppTransferIncomingJob(const QString &jid, QXmppClient *client, QObject *parent); void checkData(); void connectToHosts(const QXmppByteStreamIq &iq); bool writeData(const QByteArray &data); private Q_SLOTS: void _q_candidateDisconnected(); void _q_candidateReady(); void _q_disconnected(); void _q_receiveData(); private: void connectToNextHost(); QXmppByteStreamIq::StreamHost m_candidateHost; QXmppSocksClient *m_candidateClient; QTimer *m_candidateTimer; QList m_streamCandidates; QString m_streamOfferId; QString m_streamOfferFrom; }; class QXmppTransferOutgoingJob : public QXmppTransferJob { Q_OBJECT public: QXmppTransferOutgoingJob(const QString &jid, QXmppClient *client, QObject *parent); void connectToProxy(); void startSending(); public Q_SLOTS: void _q_disconnected(); private Q_SLOTS: void _q_proxyReady(); void _q_sendData(); }; #endif qxmpp-1.4.0/src/client/QXmppUploadRequestManager.cpp000066400000000000000000000177711402370562100225250ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Linus Jahn * * 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 "QXmppUploadRequestManager.h" #include "QXmppClient.h" #include "QXmppConstants_p.h" #include "QXmppDataForm.h" #include "QXmppDiscoveryIq.h" #include "QXmppDiscoveryManager.h" #include "QXmppHttpUploadIq.h" #include #include class QXmppUploadServicePrivate : public QSharedData { public: QString jid; qint64 sizeLimit = -1; }; QXmppUploadService::QXmppUploadService() : d(new QXmppUploadServicePrivate) { } /// Copy constructor QXmppUploadService::QXmppUploadService(const QXmppUploadService &) = default; QXmppUploadService::~QXmppUploadService() = default; /// Equal operator QXmppUploadService &QXmppUploadService::operator=(const QXmppUploadService &) = default; /// Returns the JID of the HTTP File Upload service. QString QXmppUploadService::jid() const { return d->jid; } /// Sets the JID of the HTTP File Upload service. void QXmppUploadService::setJid(const QString &jid) { d->jid = jid; } /// Returns the size limit of files that can be uploaded to this upload /// service. /// /// A size limit of -1 means that there is no file size limit or it could not /// be determined. qint64 QXmppUploadService::sizeLimit() const { return d->sizeLimit; } /// Sets the size limit of files that can be uploaded to this upload service. void QXmppUploadService::setSizeLimit(qint64 sizeLimit) { d->sizeLimit = sizeLimit; } class QXmppUploadRequestManagerPrivate : public QSharedData { public: QVector uploadServices; }; QXmppUploadRequestManager::QXmppUploadRequestManager() : d(new QXmppUploadRequestManagerPrivate) { } QXmppUploadRequestManager::~QXmppUploadRequestManager() = default; /// Requests an upload slot from the server. /// /// \param file The info of the file to be uploaded. /// \param uploadService The HTTP File Upload service that is used to request /// the upload slot. If this is empty, the first /// discovered one is used. /// \return The id of the sent IQ. If sendPacket() isn't successful or no /// upload service has been discovered yet, an empty string will be /// returned. QString QXmppUploadRequestManager::requestUploadSlot(const QFileInfo &file, const QString &uploadService) { return requestUploadSlot(file, file.fileName(), uploadService); } /// Requests an upload slot from the server. /// /// \param file The info of the file to be uploaded. /// \param customFileName This name is used instead of the file's actual name /// for requesting the upload slot. /// \param uploadService The HTTP File Upload service that is used to request /// the upload slot. If this is empty, the first /// discovered one is used. /// \return The id of the sent IQ. If sendPacket() isn't successful or no /// upload service has been discovered yet, an empty string will be /// returned. QString QXmppUploadRequestManager::requestUploadSlot(const QFileInfo &file, const QString &customFileName, const QString &uploadService) { return requestUploadSlot(customFileName, file.size(), QMimeDatabase().mimeTypeForFile(file), uploadService); } /// Requests an upload slot from the server. /// /// \param fileName The name of the file to be uploaded. This may be used by /// the server to generate the URL. /// \param fileSize The size of the file to be uploaded. The server may reject /// too large files. /// \param mimeType The content-type of the file. This can be used by the /// server to set the HTTP MIME-type of the URL. /// \param uploadService The HTTP File Upload service that is used to request /// the upload slot. If this is empty, the first /// discovered one is used. /// \return The id of the sent IQ. If sendPacket() isn't successful or no /// upload service has been discovered yet, an empty string will be /// returned. QString QXmppUploadRequestManager::requestUploadSlot(const QString &fileName, qint64 fileSize, const QMimeType &mimeType, const QString &uploadService) { if (!serviceFound() && uploadService.isEmpty()) return {}; QXmppHttpUploadRequestIq iq; if (uploadService.isEmpty()) iq.setTo(d->uploadServices.first().jid()); else iq.setTo(uploadService); iq.setType(QXmppIq::Get); iq.setFileName(fileName); iq.setSize(fileSize); iq.setContentType(mimeType); if (client()->sendPacket(iq)) return iq.id(); return {}; } /// Returns true if an HTTP File Upload service has been discovered. bool QXmppUploadRequestManager::serviceFound() const { return !d->uploadServices.isEmpty(); } /// Returns all discovered HTTP File Upload services. QVector QXmppUploadRequestManager::uploadServices() const { return d->uploadServices; } bool QXmppUploadRequestManager::handleStanza(const QDomElement &element) { if (QXmppHttpUploadSlotIq::isHttpUploadSlotIq(element)) { QXmppHttpUploadSlotIq slot; slot.parse(element); emit slotReceived(slot); return true; } else if (QXmppHttpUploadRequestIq::isHttpUploadRequestIq(element)) { QXmppHttpUploadRequestIq requestError; requestError.parse(element); emit requestFailed(requestError); return true; } return false; } void QXmppUploadRequestManager::handleDiscoInfo(const QXmppDiscoveryIq &iq) { if (!iq.features().contains(ns_http_upload)) return; for (const QXmppDiscoveryIq::Identity &identity : iq.identities()) { if (identity.category() == QStringLiteral("store") && identity.type() == QStringLiteral("file")) { QXmppUploadService service; service.setJid(iq.from()); // get size limit bool isFormNsCorrect = false; for (const QXmppDataForm::Field &field : iq.form().fields()) { if (field.key() == QStringLiteral("FORM_TYPE")) { isFormNsCorrect = field.value() == ns_http_upload; } else if (isFormNsCorrect && field.key() == QStringLiteral("max-file-size")) { service.setSizeLimit(field.value().toLongLong()); } } d->uploadServices.append(service); emit serviceFoundChanged(); } } return; } void QXmppUploadRequestManager::setClient(QXmppClient *client) { QXmppClientExtension::setClient(client); // connect to service discovery manager auto *disco = client->findExtension(); if (disco) { // scan info of all entities for upload services connect(disco, &QXmppDiscoveryManager::infoReceived, this, &QXmppUploadRequestManager::handleDiscoInfo); // on client disconnect remove all upload services connect(client, &QXmppClient::disconnected, [=]() { d->uploadServices.clear(); emit serviceFoundChanged(); }); } } qxmpp-1.4.0/src/client/QXmppUploadRequestManager.h000066400000000000000000000106471402370562100221650ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Linus Jahn * * 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 QXMPPUPLOADREQUESTMANAGER_H #define QXMPPUPLOADREQUESTMANAGER_H #include #include class QFileInfo; class QMimeType; class QXmppHttpUploadRequestIq; class QXmppHttpUploadSlotIq; class QXmppUploadServicePrivate; class QXmppUploadRequestManagerPrivate; /// \brief QXmppUploadService represents an HTTP File Upload service. /// /// It is used to store the JID and maximum file size for uploads. class QXMPP_EXPORT QXmppUploadService { public: QXmppUploadService(); QXmppUploadService(const QXmppUploadService &); ~QXmppUploadService(); QXmppUploadService &operator=(const QXmppUploadService &); QString jid() const; void setJid(const QString &jid); qint64 sizeLimit() const; void setSizeLimit(qint64 sizeLimit); private: QSharedDataPointer d; }; /// /// \brief The QXmppUploadRequestManager implements the core of \xep{0369}: HTTP /// File Upload. /// /// It handles the discovery of QXmppUploadServices and can send upload /// requests and outputs the upload slots. It doesn't do the actual upload via. /// HTTP. /// /// To make use of this manager, you need to instantiate it and load it into /// the QXmppClient instance as follows: /// /// \code /// auto *manager = new QXmppUploadRequestManager; /// client->addExtension(manager); /// \endcode /// /// Apart from that, you also need to discover HTTP File Upload service(s) by /// requesting the Service Discovery info for each Service Discovery item of /// the server. The QXmppUploadManager will then automatically recognize upload /// services and add them to the list of discovered services /// \c uploadServices(). /// /// Keep in mind that theoretically any XMPP entity could promote to be an /// upload service and so is recognized by this manager. A potential attacker /// could exploit this vulnerability, so the client could be uploading files to /// the attacker (e.g. a normal user JID) instead of the own server. /// /// As soon as at least one upload service has been discovered, you can start /// to request upload slots by using \c requestUploadSlot(). Alternatively you /// can provide the JID of the upload service which should be used for /// uploading. /// /// \since QXmpp 1.1 /// /// \ingroup Managers /// class QXMPP_EXPORT QXmppUploadRequestManager : public QXmppClientExtension { Q_OBJECT public: QXmppUploadRequestManager(); ~QXmppUploadRequestManager(); QString requestUploadSlot(const QFileInfo &file, const QString &uploadService = QString()); QString requestUploadSlot(const QFileInfo &file, const QString &customFileName, const QString &uploadService = QString()); QString requestUploadSlot(const QString &fileName, qint64 fileSize, const QMimeType &mimeType, const QString &uploadService = QString()); bool serviceFound() const; QVector uploadServices() const; bool handleStanza(const QDomElement &stanza) override; Q_SIGNALS: /// Emitted when an upload slot was received. void slotReceived(const QXmppHttpUploadSlotIq &slot); /// Emitted when the slot request failed. /// /// \param request The sent IQ with an QXmppStanza::Error from the server. void requestFailed(const QXmppHttpUploadRequestIq &request); /// Emitted when the first upload service has been found. void serviceFoundChanged(); protected: void setClient(QXmppClient *client) override; private: void handleDiscoInfo(const QXmppDiscoveryIq &iq); QSharedDataPointer d; }; #endif // QXMPPUPLOADREQUESTMANAGER_H qxmpp-1.4.0/src/client/QXmppVCardManager.cpp000066400000000000000000000061601402370562100207150ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 "QXmppVCardManager.h" #include "QXmppClient.h" #include "QXmppConstants_p.h" #include "QXmppUtils.h" #include "QXmppVCardIq.h" class QXmppVCardManagerPrivate { public: QXmppVCardIq clientVCard; bool isClientVCardReceived; }; QXmppVCardManager::QXmppVCardManager() : d(new QXmppVCardManagerPrivate) { d->isClientVCardReceived = false; } QXmppVCardManager::~QXmppVCardManager() { delete d; } /// This function requests the server for vCard of the specified jid. /// Once received the signal vCardReceived() is emitted. /// /// \param jid Jid of the specific entry in the roster /// QString QXmppVCardManager::requestVCard(const QString& jid) { QXmppVCardIq request(jid); if (client()->sendPacket(request)) return request.id(); else return QString(); } /// Returns the vCard of the connected client. /// /// \return QXmppVCard /// const QXmppVCardIq& QXmppVCardManager::clientVCard() const { return d->clientVCard; } /// Sets the vCard of the connected client. /// /// \param clientVCard QXmppVCard /// void QXmppVCardManager::setClientVCard(const QXmppVCardIq& clientVCard) { d->clientVCard = clientVCard; d->clientVCard.setTo(""); d->clientVCard.setFrom(""); d->clientVCard.setType(QXmppIq::Set); client()->sendPacket(d->clientVCard); } /// This function requests the server for vCard of the connected user itself. /// Once received the signal clientVCardReceived() is emitted. Received vCard /// can be get using clientVCard(). QString QXmppVCardManager::requestClientVCard() { return requestVCard(); } /// Returns true if vCard of the connected client has been /// received else false. /// /// \return bool /// bool QXmppVCardManager::isClientVCardReceived() const { return d->isClientVCardReceived; } /// \cond QStringList QXmppVCardManager::discoveryFeatures() const { // XEP-0054: vcard-temp return QStringList() << ns_vcard; } bool QXmppVCardManager::handleStanza(const QDomElement& element) { if (element.tagName() == "iq" && QXmppVCardIq::isVCard(element)) { QXmppVCardIq vCardIq; vCardIq.parse(element); if (vCardIq.from().isEmpty() || vCardIq.from() == client()->configuration().jidBare()) { d->clientVCard = vCardIq; d->isClientVCardReceived = true; emit clientVCardReceived(); } emit vCardReceived(vCardIq); return true; } return false; } /// \endcond qxmpp-1.4.0/src/client/QXmppVCardManager.h000066400000000000000000000054131402370562100203620ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 QXMPPVCARDMANAGER_H #define QXMPPVCARDMANAGER_H #include "QXmppClientExtension.h" class QXmppVCardIq; class QXmppVCardManagerPrivate; /// /// \brief The QXmppVCardManager class gets/sets XMPP vCards. It is an /// implementation of \xep{0054}: vcard-temp. /// /// \note Its object should not be created using its constructor. Instead /// \c QXmppClient::findExtension() should be used to get /// the instantiated object of this class. /// /// Getting vCards of entries in Roster:
/// It doesn't store vCards of the JIDs in the roster of connected user. Instead /// client has to request for a particular vCard using requestVCard(). And connect to /// the signal vCardReceived() to get the requested vCard. /// /// Getting vCard of the connected client:
/// For getting the vCard of the connected user itself. Client can call requestClientVCard() /// and on the signal clientVCardReceived() it can get its vCard using clientVCard(). /// /// Setting vCard of the client:
/// Using setClientVCard() client can set its vCard. /// /// \note Client can't set/change vCards of roster entries. /// /// \ingroup Managers /// class QXMPP_EXPORT QXmppVCardManager : public QXmppClientExtension { Q_OBJECT public: QXmppVCardManager(); ~QXmppVCardManager() override; QString requestVCard(const QString& bareJid = QString()); const QXmppVCardIq& clientVCard() const; void setClientVCard(const QXmppVCardIq&); QString requestClientVCard(); bool isClientVCardReceived() const; /// \cond QStringList discoveryFeatures() const override; bool handleStanza(const QDomElement& element) override; /// \endcond Q_SIGNALS: /// This signal is emitted when the requested vCard is received /// after calling the requestVCard() function. void vCardReceived(const QXmppVCardIq&); /// This signal is emitted when the client's vCard is received /// after calling the requestClientVCard() function. void clientVCardReceived(); private: QXmppVCardManagerPrivate* d; }; #endif // QXMPPVCARDMANAGER_H qxmpp-1.4.0/src/client/QXmppVersionManager.cpp000066400000000000000000000100041402370562100213330ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 "QXmppVersionManager.h" #include "QXmppClient.h" #include "QXmppConstants_p.h" #include "QXmppGlobal.h" #include "QXmppVersionIq.h" #include #include #include class QXmppVersionManagerPrivate { public: QString clientName; QString clientVersion; QString clientOs; }; QXmppVersionManager::QXmppVersionManager() : d(new QXmppVersionManagerPrivate) { d->clientName = qApp->applicationName(); if (d->clientName.isEmpty()) d->clientName = "Based on QXmpp"; d->clientOs = QSysInfo::prettyProductName(); d->clientVersion = qApp->applicationVersion(); if (d->clientVersion.isEmpty()) d->clientVersion = QXmppVersion(); } QXmppVersionManager::~QXmppVersionManager() { delete d; } /// Request version information from the specified XMPP entity. /// /// \param jid QString QXmppVersionManager::requestVersion(const QString& jid) { QXmppVersionIq request; request.setType(QXmppIq::Get); request.setTo(jid); if (client()->sendPacket(request)) return request.id(); else return QString(); } /// Sets the local XMPP client's name. /// /// \param name void QXmppVersionManager::setClientName(const QString& name) { d->clientName = name; } /// Sets the local XMPP client's version. /// /// \param version void QXmppVersionManager::setClientVersion(const QString& version) { d->clientVersion = version; } /// Sets the local XMPP client's operating system. /// /// \param os void QXmppVersionManager::setClientOs(const QString& os) { d->clientOs = os; } /// Returns the local XMPP client's name. /// /// By default this is set to the QApplication::applicationName(), or /// "Based on QXmpp" if not specified. QString QXmppVersionManager::clientName() const { return d->clientName; } /// Returns the local XMPP client's version. /// /// By default this is set to QApplication::applicationVersion(), or /// QXmpp's version if not specified. QString QXmppVersionManager::clientVersion() const { return d->clientVersion; } /// Returns the local XMPP client's operating system. /// /// By default this equals to QSysInfo::prettyProductName() which contains the /// OS name and version (e.g. "Windows 8.1" or "Debian GNU/Linux buster"). QString QXmppVersionManager::clientOs() const { return d->clientOs; } /// \cond QStringList QXmppVersionManager::discoveryFeatures() const { // XEP-0092: Software Version return QStringList() << ns_version; } bool QXmppVersionManager::handleStanza(const QDomElement& element) { if (element.tagName() == "iq" && QXmppVersionIq::isVersionIq(element)) { QXmppVersionIq versionIq; versionIq.parse(element); if (versionIq.type() == QXmppIq::Get) { // respond to query QXmppVersionIq responseIq; responseIq.setType(QXmppIq::Result); responseIq.setId(versionIq.id()); responseIq.setTo(versionIq.from()); responseIq.setName(clientName()); responseIq.setVersion(clientVersion()); responseIq.setOs(clientOs()); client()->sendPacket(responseIq); } else if (versionIq.type() == QXmppIq::Result) { // emit response emit versionReceived(versionIq); } return true; } return false; } /// \endcond qxmpp-1.4.0/src/client/QXmppVersionManager.h000066400000000000000000000040001402370562100207770ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Manjeet Dahiya * * 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 QXMPPVERSIONMANAGER_H #define QXMPPVERSIONMANAGER_H #include "QXmppClientExtension.h" class QXmppVersionIq; class QXmppVersionManagerPrivate; /// /// \brief The QXmppVersionManager class makes it possible to request for /// the software version of an entity as defined by \xep{0092}: Software Version. /// /// \note Its object should not be created using its constructor. Instead /// \c QXmppClient::findExtension() should be used to get /// the instantiated object of this class. /// /// \ingroup Managers /// class QXMPP_EXPORT QXmppVersionManager : public QXmppClientExtension { Q_OBJECT public: QXmppVersionManager(); ~QXmppVersionManager() override; QString requestVersion(const QString &jid); void setClientName(const QString &); void setClientVersion(const QString &); void setClientOs(const QString &); QString clientName() const; QString clientVersion() const; QString clientOs() const; /// \cond QStringList discoveryFeatures() const override; bool handleStanza(const QDomElement &element) override; /// \endcond Q_SIGNALS: /// \brief This signal is emitted when a version response is received. void versionReceived(const QXmppVersionIq &); private: QXmppVersionManagerPrivate *d; }; #endif // QXMPPVERSIONMANAGER_H qxmpp-1.4.0/src/server/000077500000000000000000000000001402370562100147565ustar00rootroot00000000000000qxmpp-1.4.0/src/server/QXmppDialback.cpp000066400000000000000000000051371402370562100201500ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppDialback.h" #include "QXmppConstants_p.h" #include "QXmppUtils.h" #include /// Constructs a QXmppDialback. QXmppDialback::QXmppDialback() : m_command(Result) { } /// Returns the dialback command. QXmppDialback::Command QXmppDialback::command() const { return m_command; } /// Sets the dialback command. /// /// \param command void QXmppDialback::setCommand(QXmppDialback::Command command) { m_command = command; } /// Returns the dialback key. QString QXmppDialback::key() const { return m_key; } /// Sets the dialback key. /// /// \param key void QXmppDialback::setKey(const QString &key) { m_key = key; } /// Returns the dialback type. QString QXmppDialback::type() const { return m_type; } /// Sets the dialback type. /// /// \param type void QXmppDialback::setType(const QString &type) { m_type = type; } /// \cond bool QXmppDialback::isDialback(const QDomElement &element) { return element.namespaceURI() == ns_server_dialback && (element.tagName() == QLatin1String("result") || element.tagName() == QLatin1String("verify")); } void QXmppDialback::parse(const QDomElement &element) { QXmppStanza::parse(element); if (element.tagName() == QLatin1String("result")) m_command = Result; else m_command = Verify; m_type = element.attribute("type"); m_key = element.text(); } void QXmppDialback::toXml(QXmlStreamWriter *xmlWriter) const { if (m_command == Result) xmlWriter->writeStartElement("db:result"); else xmlWriter->writeStartElement("db:verify"); helperToXmlAddAttribute(xmlWriter, "id", id()); helperToXmlAddAttribute(xmlWriter, "to", to()); helperToXmlAddAttribute(xmlWriter, "from", from()); helperToXmlAddAttribute(xmlWriter, "type", m_type); if (!m_key.isEmpty()) xmlWriter->writeCharacters(m_key); xmlWriter->writeEndElement(); } /// \endcond qxmpp-1.4.0/src/server/QXmppDialback.h000066400000000000000000000034641402370562100176160ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPDIALBACK_H #define QXMPPDIALBACK_H #include "QXmppStanza.h" /// \brief The QXmppDialback class represents a stanza used for the Server /// Dialback protocol as specified by \xep{0220}: Server Dialback. /// /// \ingroup Stanzas class QXMPP_EXPORT QXmppDialback : public QXmppStanza { public: /// This enum is used to describe a dialback command. enum Command { Result, ///< A dialback command between the originating server ///< and the receiving server. Verify ///< A dialback command between the receiving server ///< and the authoritative server. }; QXmppDialback(); Command command() const; void setCommand(Command command); QString key() const; void setKey(const QString &key); QString type() const; void setType(const QString &type); /// \cond void parse(const QDomElement &element) override; void toXml(QXmlStreamWriter *writer) const override; static bool isDialback(const QDomElement &element); /// \endcond private: Command m_command; QString m_key; QString m_type; }; #endif qxmpp-1.4.0/src/server/QXmppIncomingClient.cpp000066400000000000000000000360161402370562100213600ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppIncomingClient.h" #include "QXmppBindIq.h" #include "QXmppConstants_p.h" #include "QXmppMessage.h" #include "QXmppPasswordChecker.h" #include "QXmppSasl_p.h" #include "QXmppSessionIq.h" #include "QXmppStartTlsPacket.h" #include "QXmppStreamFeatures.h" #include "QXmppUtils.h" #include #include #include #include #include class QXmppIncomingClientPrivate { public: QXmppIncomingClientPrivate(QXmppIncomingClient *qq); QTimer *idleTimer; QString domain; QString jid; QString resource; QXmppPasswordChecker *passwordChecker; QXmppSaslServer *saslServer; void checkCredentials(const QByteArray &response); QString origin() const; private: QXmppIncomingClient *q; }; QXmppIncomingClientPrivate::QXmppIncomingClientPrivate(QXmppIncomingClient *qq) : idleTimer(nullptr), passwordChecker(nullptr), saslServer(nullptr), q(qq) { } void QXmppIncomingClientPrivate::checkCredentials(const QByteArray &response) { QXmppPasswordRequest request; request.setDomain(domain); request.setUsername(saslServer->username()); if (saslServer->mechanism() == "PLAIN") { request.setPassword(saslServer->password()); QXmppPasswordReply *reply = passwordChecker->checkPassword(request); reply->setParent(q); reply->setProperty("__sasl_raw", response); QObject::connect(reply, &QXmppPasswordReply::finished, q, &QXmppIncomingClient::onPasswordReply); } else if (saslServer->mechanism() == "DIGEST-MD5") { QXmppPasswordReply *reply = passwordChecker->getDigest(request); reply->setParent(q); reply->setProperty("__sasl_raw", response); QObject::connect(reply, &QXmppPasswordReply::finished, q, &QXmppIncomingClient::onDigestReply); } } QString QXmppIncomingClientPrivate::origin() const { QSslSocket *socket = q->socket(); if (socket) return socket->peerAddress().toString() + " " + QString::number(socket->peerPort()); else return ""; } /// Constructs a new incoming client stream. /// /// \param socket The socket for the XMPP stream. /// \param domain The local domain. /// \param parent The parent QObject for the stream (optional). /// QXmppIncomingClient::QXmppIncomingClient(QSslSocket *socket, const QString &domain, QObject *parent) : QXmppStream(parent) { d = new QXmppIncomingClientPrivate(this); d->domain = domain; if (socket) { connect(socket, &QAbstractSocket::disconnected, this, &QXmppIncomingClient::onSocketDisconnected); setSocket(socket); } info(QString("Incoming client connection from %1").arg(d->origin())); // create inactivity timer d->idleTimer = new QTimer(this); d->idleTimer->setSingleShot(true); connect(d->idleTimer, &QTimer::timeout, this, &QXmppIncomingClient::onTimeout); } /// Destroys the current stream. /// QXmppIncomingClient::~QXmppIncomingClient() { delete d; } /// Returns true if the socket is connected, the client is authenticated /// and a resource is bound. /// bool QXmppIncomingClient::isConnected() const { return QXmppStream::isConnected() && !d->jid.isEmpty() && !d->resource.isEmpty(); } /// Returns the client's JID. /// QString QXmppIncomingClient::jid() const { return d->jid; } /// Sets the number of seconds after which a client will be disconnected /// for inactivity. void QXmppIncomingClient::setInactivityTimeout(int secs) { d->idleTimer->stop(); d->idleTimer->setInterval(secs * 1000); if (d->idleTimer->interval()) d->idleTimer->start(); } /// Sets the password checker used to verify client credentials. /// /// \param checker /// void QXmppIncomingClient::setPasswordChecker(QXmppPasswordChecker *checker) { d->passwordChecker = checker; } /// \cond void QXmppIncomingClient::handleStream(const QDomElement &streamElement) { if (d->idleTimer->interval()) d->idleTimer->start(); if (d->saslServer != nullptr) { delete d->saslServer; d->saslServer = nullptr; } // start stream const QByteArray sessionId = QXmppUtils::generateStanzaHash().toLatin1(); QString response = QString("") .arg( ns_client, ns_stream, sessionId, d->domain.toLatin1()); sendData(response.toUtf8()); // check requested domain if (streamElement.attribute("to") != d->domain) { QString response = QString("" "" "" "This server does not serve %1" "" "") .arg(streamElement.attribute("to")); sendData(response.toUtf8()); disconnectFromHost(); return; } // send stream features QXmppStreamFeatures features; if (socket() && !socket()->isEncrypted() && !socket()->localCertificate().isNull() && !socket()->privateKey().isNull()) features.setTlsMode(QXmppStreamFeatures::Enabled); if (!d->jid.isEmpty()) { features.setBindMode(QXmppStreamFeatures::Required); features.setSessionMode(QXmppStreamFeatures::Enabled); } else if (d->passwordChecker) { QStringList mechanisms; mechanisms << "PLAIN"; if (d->passwordChecker->hasGetPassword()) mechanisms << "DIGEST-MD5"; features.setAuthMechanisms(mechanisms); } sendPacket(features); } void QXmppIncomingClient::handleStanza(const QDomElement &nodeRecv) { const QString ns = nodeRecv.namespaceURI(); if (d->idleTimer->interval()) d->idleTimer->start(); if (QXmppStartTlsPacket::isStartTlsPacket(nodeRecv, QXmppStartTlsPacket::StartTls)) { sendPacket(QXmppStartTlsPacket(QXmppStartTlsPacket::Proceed)); socket()->flush(); socket()->startServerEncryption(); return; } else if (ns == ns_sasl) { if (!d->passwordChecker) { warning("Cannot perform authentication, no password checker"); sendPacket(QXmppSaslFailure("temporary-auth-failure")); disconnectFromHost(); return; } if (nodeRecv.tagName() == QLatin1String("auth")) { QXmppSaslAuth auth; auth.parse(nodeRecv); d->saslServer = QXmppSaslServer::create(auth.mechanism(), this); if (!d->saslServer) { sendPacket(QXmppSaslFailure("invalid-mechanism")); disconnectFromHost(); return; } d->saslServer->setRealm(d->domain.toUtf8()); QByteArray challenge; QXmppSaslServer::Response result = d->saslServer->respond(auth.value(), challenge); if (result == QXmppSaslServer::InputNeeded) { // check credentials d->checkCredentials(auth.value()); } else if (result == QXmppSaslServer::Challenge) { sendPacket(QXmppSaslChallenge(challenge)); } else { // FIXME: what condition? sendPacket(QXmppSaslFailure()); disconnectFromHost(); return; } } else if (nodeRecv.tagName() == QLatin1String("response")) { QXmppSaslResponse response; response.parse(nodeRecv); if (!d->saslServer) { warning("SASL response received, but no mechanism selected"); sendPacket(QXmppSaslFailure()); disconnectFromHost(); return; } QByteArray challenge; QXmppSaslServer::Response result = d->saslServer->respond(response.value(), challenge); if (result == QXmppSaslServer::InputNeeded) { // check credentials d->checkCredentials(response.value()); } else if (result == QXmppSaslServer::Succeeded) { // authentication succeeded d->jid = QString("%1@%2").arg(d->saslServer->username(), d->domain); info(QString("Authentication succeeded for '%1' from %2").arg(d->jid, d->origin())); updateCounter("incoming-client.auth.success"); sendPacket(QXmppSaslSuccess()); handleStart(); } else { // FIXME: what condition? sendPacket(QXmppSaslFailure()); disconnectFromHost(); } } } else if (ns == ns_client) { if (nodeRecv.tagName() == QLatin1String("iq")) { const QString type = nodeRecv.attribute("type"); if (QXmppBindIq::isBindIq(nodeRecv) && type == QLatin1String("set")) { QXmppBindIq bindSet; bindSet.parse(nodeRecv); d->resource = bindSet.resource().trimmed(); if (d->resource.isEmpty()) d->resource = QXmppUtils::generateStanzaHash(); d->jid = QString("%1/%2").arg(QXmppUtils::jidToBareJid(d->jid), d->resource); QXmppBindIq bindResult; bindResult.setType(QXmppIq::Result); bindResult.setId(bindSet.id()); bindResult.setJid(d->jid); sendPacket(bindResult); // bound emit connected(); return; } else if (QXmppSessionIq::isSessionIq(nodeRecv) && type == QLatin1String("set")) { QXmppSessionIq sessionSet; sessionSet.parse(nodeRecv); QXmppIq sessionResult; sessionResult.setType(QXmppIq::Result); sessionResult.setId(sessionSet.id()); sessionResult.setTo(d->jid); sendPacket(sessionResult); return; } } // check the sender is legitimate const QString from = nodeRecv.attribute("from"); if (!from.isEmpty() && from != d->jid && from != QXmppUtils::jidToBareJid(d->jid)) { warning(QString("Received a stanza from unexpected JID %1").arg(from)); return; } // process unhandled stanzas if (nodeRecv.tagName() == QLatin1String("iq") || nodeRecv.tagName() == QLatin1String("message") || nodeRecv.tagName() == QLatin1String("presence")) { QDomElement nodeFull(nodeRecv); // if the sender is empty, set it to the appropriate JID if (nodeFull.attribute("from").isEmpty()) { if (nodeFull.tagName() == QLatin1String("presence") && (nodeFull.attribute("type") == QLatin1String("subscribe") || nodeFull.attribute("type") == QLatin1String("subscribed"))) nodeFull.setAttribute("from", QXmppUtils::jidToBareJid(d->jid)); else nodeFull.setAttribute("from", d->jid); } // if the recipient is empty, set it to the local domain if (nodeFull.attribute("to").isEmpty()) nodeFull.setAttribute("to", d->domain); // emit stanza for processing by server emit elementReceived(nodeFull); } } } /// \endcond void QXmppIncomingClient::onDigestReply() { auto *reply = qobject_cast(sender()); if (!reply) return; reply->deleteLater(); if (reply->error() == QXmppPasswordReply::TemporaryError) { warning(QString("Temporary authentication failure for '%1' from %2").arg(d->saslServer->username(), d->origin())); updateCounter("incoming-client.auth.temporary-auth-failure"); sendPacket(QXmppSaslFailure("temporary-auth-failure")); disconnectFromHost(); return; } QByteArray challenge; d->saslServer->setPasswordDigest(reply->digest()); QXmppSaslServer::Response result = d->saslServer->respond(reply->property("__sasl_raw").toByteArray(), challenge); if (result != QXmppSaslServer::Challenge) { warning(QString("Authentication failed for '%1' from %2").arg(d->saslServer->username(), d->origin())); updateCounter("incoming-client.auth.not-authorized"); sendPacket(QXmppSaslFailure("not-authorized")); disconnectFromHost(); return; } // send new challenge sendPacket(QXmppSaslChallenge(challenge)); } void QXmppIncomingClient::onPasswordReply() { auto *reply = qobject_cast(sender()); if (!reply) return; reply->deleteLater(); const QString jid = QString("%1@%2").arg(d->saslServer->username(), d->domain); switch (reply->error()) { case QXmppPasswordReply::NoError: d->jid = jid; info(QString("Authentication succeeded for '%1' from %2").arg(d->jid, d->origin())); updateCounter("incoming-client.auth.success"); sendPacket(QXmppSaslSuccess()); handleStart(); break; case QXmppPasswordReply::AuthorizationError: warning(QString("Authentication failed for '%1' from %2").arg(jid, d->origin())); updateCounter("incoming-client.auth.not-authorized"); sendPacket(QXmppSaslFailure("not-authorized")); disconnectFromHost(); break; case QXmppPasswordReply::TemporaryError: warning(QString("Temporary authentication failure for '%1' from %2").arg(jid, d->origin())); updateCounter("incoming-client.auth.temporary-auth-failure"); sendPacket(QXmppSaslFailure("temporary-auth-failure")); disconnectFromHost(); break; } } void QXmppIncomingClient::onSocketDisconnected() { info(QString("Socket disconnected for '%1' from %2").arg(d->jid, d->origin())); emit disconnected(); } void QXmppIncomingClient::onTimeout() { warning(QString("Idle timeout for '%1' from %2").arg(d->jid, d->origin())); disconnectFromHost(); // make sure disconnected() gets emitted no matter what QTimer::singleShot(30, this, &QXmppStream::disconnected); } qxmpp-1.4.0/src/server/QXmppIncomingClient.h000066400000000000000000000036451402370562100210270ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPINCOMINGCLIENT_H #define QXMPPINCOMINGCLIENT_H #include "QXmppStream.h" class QXmppIncomingClientPrivate; class QXmppPasswordChecker; /// \brief Interface for password checkers. /// /// \brief The QXmppIncomingClient class represents an incoming XMPP stream /// from an XMPP client. /// class QXMPP_EXPORT QXmppIncomingClient : public QXmppStream { Q_OBJECT public: QXmppIncomingClient(QSslSocket *socket, const QString &domain, QObject *parent = nullptr); ~QXmppIncomingClient() override; bool isConnected() const override; QString jid() const; void setInactivityTimeout(int secs); void setPasswordChecker(QXmppPasswordChecker *checker); Q_SIGNALS: /// This signal is emitted when an element is received. void elementReceived(const QDomElement &element); protected: /// \cond void handleStream(const QDomElement &element) override; void handleStanza(const QDomElement &element) override; /// \endcond private Q_SLOTS: void onDigestReply(); void onPasswordReply(); void onSocketDisconnected(); void onTimeout(); private: Q_DISABLE_COPY(QXmppIncomingClient) QXmppIncomingClientPrivate *d; friend class QXmppIncomingClientPrivate; }; #endif qxmpp-1.4.0/src/server/QXmppIncomingServer.cpp000066400000000000000000000157561402370562100214200ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppIncomingServer.h" #include "QXmppConstants_p.h" #include "QXmppDialback.h" #include "QXmppOutgoingServer.h" #include "QXmppStartTlsPacket.h" #include "QXmppStreamFeatures.h" #include "QXmppUtils.h" #include #include #include #include class QXmppIncomingServerPrivate { public: QXmppIncomingServerPrivate(QXmppIncomingServer *qq); QString origin() const; QSet authenticated; QString domain; QString localStreamId; private: QXmppIncomingServer *q; }; QXmppIncomingServerPrivate::QXmppIncomingServerPrivate(QXmppIncomingServer *qq) : q(qq) { } QString QXmppIncomingServerPrivate::origin() const { QSslSocket *socket = q->socket(); if (socket) return socket->peerAddress().toString() + " " + QString::number(socket->peerPort()); else return ""; } /// Constructs a new incoming server stream. /// /// \param socket The socket for the XMPP stream. /// \param domain The local domain. /// \param parent The parent QObject for the stream (optional). /// QXmppIncomingServer::QXmppIncomingServer(QSslSocket *socket, const QString &domain, QObject *parent) : QXmppStream(parent) { d = new QXmppIncomingServerPrivate(this); d->domain = domain; if (socket) { connect(socket, &QAbstractSocket::disconnected, this, &QXmppIncomingServer::slotSocketDisconnected); setSocket(socket); } info(QString("Incoming server connection from %1").arg(d->origin())); } /// Destroys the current stream. QXmppIncomingServer::~QXmppIncomingServer() { delete d; } /// Returns the stream's identifier. /// QString QXmppIncomingServer::localStreamId() const { return d->localStreamId; } /// \cond void QXmppIncomingServer::handleStream(const QDomElement &streamElement) { const QString from = streamElement.attribute("from"); if (!from.isEmpty()) info(QString("Incoming server stream from %1 on %2").arg(from, d->origin())); // start stream d->localStreamId = QXmppUtils::generateStanzaHash().toLatin1(); QString data = QString("") .arg( ns_server, ns_server_dialback, ns_stream, d->localStreamId); sendData(data.toUtf8()); // send stream features QXmppStreamFeatures features; if (!socket()->isEncrypted() && !socket()->localCertificate().isNull() && !socket()->privateKey().isNull()) features.setTlsMode(QXmppStreamFeatures::Enabled); sendPacket(features); } void QXmppIncomingServer::handleStanza(const QDomElement &stanza) { const QString ns = stanza.namespaceURI(); if (QXmppStartTlsPacket::isStartTlsPacket(stanza, QXmppStartTlsPacket::StartTls)) { sendPacket(QXmppStartTlsPacket(QXmppStartTlsPacket::Proceed)); socket()->flush(); socket()->startServerEncryption(); return; } else if (QXmppDialback::isDialback(stanza)) { QXmppDialback request; request.parse(stanza); // check the request is valid if (!request.type().isEmpty() || request.from().isEmpty() || request.to() != d->domain || request.key().isEmpty()) { warning(QString("Invalid dialback received on %1").arg(d->origin())); return; } const QString domain = request.from(); if (request.command() == QXmppDialback::Result) { debug(QString("Received a dialback result from '%1' on %2").arg(domain, d->origin())); // establish dialback connection auto *stream = new QXmppOutgoingServer(d->domain, this); connect(stream, &QXmppOutgoingServer::dialbackResponseReceived, this, &QXmppIncomingServer::slotDialbackResponseReceived); stream->setVerify(d->localStreamId, request.key()); stream->connectToHost(domain); } else if (request.command() == QXmppDialback::Verify) { debug(QString("Received a dialback verify from '%1' on %2").arg(domain, d->origin())); emit dialbackRequestReceived(request); } } else if (d->authenticated.contains(QXmppUtils::jidToDomain(stanza.attribute("from")))) { // relay stanza if the remote party is authenticated emit elementReceived(stanza); } else { warning(QString("Received an element from unverified domain '%1' on %2").arg(QXmppUtils::jidToDomain(stanza.attribute("from")), d->origin())); disconnectFromHost(); } } /// \endcond /// Returns true if the socket is connected and the remote server is /// authenticated. /// bool QXmppIncomingServer::isConnected() const { return QXmppStream::isConnected() && !d->authenticated.isEmpty(); } /// Handles a dialback response received from the authority server. /// /// \param response /// void QXmppIncomingServer::slotDialbackResponseReceived(const QXmppDialback &dialback) { auto *stream = qobject_cast(sender()); if (!stream || dialback.command() != QXmppDialback::Verify || dialback.id() != d->localStreamId || dialback.from() != stream->remoteDomain()) return; // relay verify response QXmppDialback response; response.setCommand(QXmppDialback::Result); response.setTo(dialback.from()); response.setFrom(d->domain); response.setType(dialback.type()); sendPacket(response); // check for success if (response.type() == QLatin1String("valid")) { info(QString("Verified incoming domain '%1' on %2").arg(dialback.from(), d->origin())); const bool wasConnected = !d->authenticated.isEmpty(); d->authenticated.insert(dialback.from()); if (!wasConnected) emit connected(); } else { warning(QString("Failed to verify incoming domain '%1' on %2").arg(dialback.from(), d->origin())); disconnectFromHost(); } // disconnect dialback stream->disconnectFromHost(); stream->deleteLater(); } void QXmppIncomingServer::slotSocketDisconnected() { info(QString("Socket disconnected from %1").arg(d->origin())); emit disconnected(); } qxmpp-1.4.0/src/server/QXmppIncomingServer.h000066400000000000000000000036651402370562100210610ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPINCOMINGSERVER_H #define QXMPPINCOMINGSERVER_H #include "QXmppStream.h" class QXmppDialback; class QXmppIncomingServerPrivate; class QXmppOutgoingServer; /// \brief The QXmppIncomingServer class represents an incoming XMPP stream /// from an XMPP server. /// class QXMPP_EXPORT QXmppIncomingServer : public QXmppStream { Q_OBJECT public: QXmppIncomingServer(QSslSocket *socket, const QString &domain, QObject *parent); ~QXmppIncomingServer() override; bool isConnected() const override; QString localStreamId() const; Q_SIGNALS: /// This signal is emitted when a dialback verify request is received. void dialbackRequestReceived(const QXmppDialback &result); /// This signal is emitted when an element is received. void elementReceived(const QDomElement &element); protected: /// \cond void handleStanza(const QDomElement &stanzaElement) override; void handleStream(const QDomElement &streamElement) override; /// \endcond private Q_SLOTS: void slotDialbackResponseReceived(const QXmppDialback &dialback); void slotSocketDisconnected(); private: Q_DISABLE_COPY(QXmppIncomingServer) QXmppIncomingServerPrivate *d; friend class QXmppIncomingServerPrivate; }; #endif qxmpp-1.4.0/src/server/QXmppOutgoingServer.cpp000066400000000000000000000221541402370562100214360ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppOutgoingServer.h" #include "QXmppConstants_p.h" #include "QXmppDialback.h" #include "QXmppStartTlsPacket.h" #include "QXmppStreamFeatures.h" #include "QXmppUtils.h" #include #include #include #include #include #include #include class QXmppOutgoingServerPrivate { public: QList dataQueue; QDnsLookup dns; QString localDomain; QString localStreamKey; QString remoteDomain; QString verifyId; QString verifyKey; QTimer *dialbackTimer; bool ready; }; /// Constructs a new outgoing server-to-server stream. /// /// \param domain the local domain /// \param parent the parent object /// QXmppOutgoingServer::QXmppOutgoingServer(const QString &domain, QObject *parent) : QXmppStream(parent), d(new QXmppOutgoingServerPrivate) { // socket initialisation auto *socket = new QSslSocket(this); setSocket(socket); connect(socket, &QAbstractSocket::disconnected, this, &QXmppOutgoingServer::_q_socketDisconnected); #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) connect(socket, &QSslSocket::errorOccurred, this, &QXmppOutgoingServer::socketError); #else connect(socket, QOverload::of(&QSslSocket::error), this, &QXmppOutgoingServer::socketError); #endif // DNS lookups connect(&d->dns, &QDnsLookup::finished, this, &QXmppOutgoingServer::_q_dnsLookupFinished); d->dialbackTimer = new QTimer(this); d->dialbackTimer->setInterval(5000); d->dialbackTimer->setSingleShot(true); connect(d->dialbackTimer, &QTimer::timeout, this, &QXmppOutgoingServer::sendDialback); d->localDomain = domain; d->ready = false; connect(socket, QOverload &>::of(&QSslSocket::sslErrors), this, &QXmppOutgoingServer::slotSslErrors); } /// Destroys the stream. /// QXmppOutgoingServer::~QXmppOutgoingServer() { delete d; } /// Attempts to connect to an XMPP server for the specified \a domain. /// /// \param domain void QXmppOutgoingServer::connectToHost(const QString &domain) { d->remoteDomain = domain; // lookup server for domain debug(QString("Looking up server for domain %1").arg(domain)); d->dns.setName("_xmpp-server._tcp." + domain); d->dns.setType(QDnsLookup::SRV); d->dns.lookup(); } void QXmppOutgoingServer::_q_dnsLookupFinished() { QString host; quint16 port; if (d->dns.error() == QDnsLookup::NoError && !d->dns.serviceRecords().isEmpty()) { // take the first returned record host = d->dns.serviceRecords().first().target(); port = d->dns.serviceRecords().first().port(); } else { // as a fallback, use domain as the host name warning(QString("Lookup for domain %1 failed: %2") .arg(d->dns.name(), d->dns.errorString())); host = d->remoteDomain; port = 5269; } // set the name the SSL certificate should match socket()->setPeerVerifyName(d->remoteDomain); // connect to server info(QString("Connecting to %1:%2").arg(host, QString::number(port))); socket()->connectToHost(host, port); } void QXmppOutgoingServer::_q_socketDisconnected() { debug("Socket disconnected"); emit disconnected(); } /// \cond void QXmppOutgoingServer::handleStart() { QXmppStream::handleStart(); QString data = QString("") .arg( ns_server, ns_server_dialback, ns_stream, d->localDomain, d->remoteDomain); sendData(data.toUtf8()); } void QXmppOutgoingServer::handleStream(const QDomElement &streamElement) { Q_UNUSED(streamElement); // gmail.com servers are broken: they never send , // so we schedule sending the dialback in a couple of seconds d->dialbackTimer->start(); } void QXmppOutgoingServer::handleStanza(const QDomElement &stanza) { const QString ns = stanza.namespaceURI(); if (QXmppStreamFeatures::isStreamFeatures(stanza)) { QXmppStreamFeatures features; features.parse(stanza); if (!socket()->isEncrypted()) { // check we can satisfy TLS constraints if (!socket()->supportsSsl() && features.tlsMode() == QXmppStreamFeatures::Required) { warning("Disconnecting as TLS is required, but SSL support is not available"); disconnectFromHost(); return; } // enable TLS if possible if (socket()->supportsSsl() && features.tlsMode() != QXmppStreamFeatures::Disabled) { sendPacket(QXmppStartTlsPacket(QXmppStartTlsPacket::StartTls)); return; } } // send dialback if needed d->dialbackTimer->stop(); sendDialback(); } else if (QXmppStartTlsPacket::isStartTlsPacket(stanza, QXmppStartTlsPacket::Proceed)) { debug("Starting encryption"); socket()->startClientEncryption(); return; } else if (QXmppDialback::isDialback(stanza)) { QXmppDialback response; response.parse(stanza); // check the request is valid if (response.from().isEmpty() || response.to() != d->localDomain || response.type().isEmpty()) { warning("Invalid dialback response received"); return; } if (response.command() == QXmppDialback::Result) { if (response.type() == QLatin1String("valid")) { info(QString("Outgoing server stream to %1 is ready").arg(response.from())); d->ready = true; // send queued data for (const auto &data : qAsConst(d->dataQueue)) sendData(data); d->dataQueue.clear(); // emit signal emit connected(); } } else if (response.command() == QXmppDialback::Verify) { emit dialbackResponseReceived(response); } } } /// \endcond /// Returns true if the socket is connected and authentication succeeded. /// bool QXmppOutgoingServer::isConnected() const { return QXmppStream::isConnected() && d->ready; } /// Returns the stream's local dialback key. QString QXmppOutgoingServer::localStreamKey() const { return d->localStreamKey; } /// Sets the stream's local dialback key. /// /// \param key void QXmppOutgoingServer::setLocalStreamKey(const QString &key) { d->localStreamKey = key; } /// Sets the stream's verification information. /// /// \param id /// \param key void QXmppOutgoingServer::setVerify(const QString &id, const QString &key) { d->verifyId = id; d->verifyKey = key; } /// Sends or queues data until connected. /// /// \param data void QXmppOutgoingServer::queueData(const QByteArray &data) { if (isConnected()) sendData(data); else d->dataQueue.append(data); } /// Returns the remote server's domain. QString QXmppOutgoingServer::remoteDomain() const { return d->remoteDomain; } void QXmppOutgoingServer::sendDialback() { if (!d->localStreamKey.isEmpty()) { // send dialback key debug(QString("Sending dialback result to %1").arg(d->remoteDomain)); QXmppDialback dialback; dialback.setCommand(QXmppDialback::Result); dialback.setFrom(d->localDomain); dialback.setTo(d->remoteDomain); dialback.setKey(d->localStreamKey); sendPacket(dialback); } else if (!d->verifyId.isEmpty() && !d->verifyKey.isEmpty()) { // send dialback verify debug(QString("Sending dialback verify to %1").arg(d->remoteDomain)); QXmppDialback verify; verify.setCommand(QXmppDialback::Verify); verify.setId(d->verifyId); verify.setFrom(d->localDomain); verify.setTo(d->remoteDomain); verify.setKey(d->verifyKey); sendPacket(verify); } } void QXmppOutgoingServer::slotSslErrors(const QList &errors) { warning("SSL errors"); for (int i = 0; i < errors.count(); ++i) warning(errors.at(i).errorString()); socket()->ignoreSslErrors(); } void QXmppOutgoingServer::socketError(QAbstractSocket::SocketError error) { Q_UNUSED(error); emit disconnected(); } qxmpp-1.4.0/src/server/QXmppOutgoingServer.h000066400000000000000000000042751402370562100211070ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPOUTGOINGSERVER_H #define QXMPPOUTGOINGSERVER_H #include "QXmppStream.h" #include class QSslError; class QXmppDialback; class QXmppOutgoingServer; class QXmppOutgoingServerPrivate; /// \brief The QXmppOutgoingServer class represents an outgoing XMPP stream /// to another XMPP server. /// class QXMPP_EXPORT QXmppOutgoingServer : public QXmppStream { Q_OBJECT public: QXmppOutgoingServer(const QString &domain, QObject *parent); ~QXmppOutgoingServer() override; bool isConnected() const override; QString localStreamKey() const; void setLocalStreamKey(const QString &key); void setVerify(const QString &id, const QString &key); QString remoteDomain() const; Q_SIGNALS: /// This signal is emitted when a dialback verify response is received. void dialbackResponseReceived(const QXmppDialback &response); protected: /// \cond void handleStart() override; void handleStream(const QDomElement &streamElement) override; void handleStanza(const QDomElement &stanzaElement) override; /// \endcond public Q_SLOTS: void connectToHost(const QString &domain); void queueData(const QByteArray &data); private Q_SLOTS: void _q_dnsLookupFinished(); void _q_socketDisconnected(); void sendDialback(); void slotSslErrors(const QList &errors); void socketError(QAbstractSocket::SocketError error); private: Q_DISABLE_COPY(QXmppOutgoingServer) QXmppOutgoingServerPrivate *const d; }; #endif qxmpp-1.4.0/src/server/QXmppPasswordChecker.cpp000066400000000000000000000116051402370562100215420ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppPasswordChecker.h" #include #include #include /// Returns the requested domain. QString QXmppPasswordRequest::domain() const { return m_domain; } /// Sets the requested \a domain. /// /// \param domain void QXmppPasswordRequest::setDomain(const QString &domain) { m_domain = domain; } /// Returns the given password. QString QXmppPasswordRequest::password() const { return m_password; } /// Sets the given \a password. void QXmppPasswordRequest::setPassword(const QString &password) { m_password = password; } /// Returns the requested username. QString QXmppPasswordRequest::username() const { return m_username; } /// Sets the requested \a username. /// /// \param username void QXmppPasswordRequest::setUsername(const QString &username) { m_username = username; } /// Constructs a new QXmppPasswordReply. /// /// \param parent QXmppPasswordReply::QXmppPasswordReply(QObject *parent) : QObject(parent), m_error(QXmppPasswordReply::NoError), m_isFinished(false) { } /// Returns the received MD5 digest. QByteArray QXmppPasswordReply::digest() const { return m_digest; } /// Sets the received MD5 digest. /// /// \param digest void QXmppPasswordReply::setDigest(const QByteArray &digest) { m_digest = digest; } /// Returns the error that was found during the processing of this request. /// /// If no error was found, returns NoError. QXmppPasswordReply::Error QXmppPasswordReply::error() const { return m_error; } /// Returns the error that was found during the processing of this request. /// void QXmppPasswordReply::setError(QXmppPasswordReply::Error error) { m_error = error; } /// Mark reply as finished. void QXmppPasswordReply::finish() { m_isFinished = true; emit finished(); } /// Delay marking reply as finished. void QXmppPasswordReply::finishLater() { QTimer::singleShot(0, this, &QXmppPasswordReply::finish); } /// Returns true when the reply has finished. bool QXmppPasswordReply::isFinished() const { return m_isFinished; } /// Returns the received password. QString QXmppPasswordReply::password() const { return m_password; } /// Sets the received password. /// /// \param password void QXmppPasswordReply::setPassword(const QString &password) { m_password = password; } /// Checks that the given credentials are valid. /// /// The base implementation requires that you reimplement getPassword(). /// /// \param request QXmppPasswordReply *QXmppPasswordChecker::checkPassword(const QXmppPasswordRequest &request) { auto *reply = new QXmppPasswordReply; QString secret; QXmppPasswordReply::Error error = getPassword(request, secret); if (error == QXmppPasswordReply::NoError) { if (request.password() != secret) reply->setError(QXmppPasswordReply::AuthorizationError); } else { reply->setError(error); } // reply is finished reply->finishLater(); return reply; } /// Retrieves the MD5 digest for the given username. /// /// Reimplement this method if your backend natively supports /// retrieving MD5 digests. /// /// \param request QXmppPasswordReply *QXmppPasswordChecker::getDigest(const QXmppPasswordRequest &request) { auto *reply = new QXmppPasswordReply; QString secret; QXmppPasswordReply::Error error = getPassword(request, secret); if (error == QXmppPasswordReply::NoError) { reply->setDigest(QCryptographicHash::hash( (request.username() + ":" + request.domain() + ":" + secret).toUtf8(), QCryptographicHash::Md5)); } else { reply->setError(error); } // reply is finished reply->finishLater(); return reply; } /// Retrieves the password for the given username. /// /// The simplest way to write a password checker is to reimplement this method. /// /// \param request /// \param password QXmppPasswordReply::Error QXmppPasswordChecker::getPassword(const QXmppPasswordRequest &request, QString &password) { Q_UNUSED(request); Q_UNUSED(password); return QXmppPasswordReply::TemporaryError; } /// Returns true if the getPassword() method is implemented. /// bool QXmppPasswordChecker::hasGetPassword() const { return false; } qxmpp-1.4.0/src/server/QXmppPasswordChecker.h000066400000000000000000000053061402370562100212100ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPPASSWORDCHECKER_H #define QXMPPPASSWORDCHECKER_H #include "QXmppGlobal.h" #include /// \brief The QXmppPasswordRequest class represents a password request. /// class QXMPP_EXPORT QXmppPasswordRequest { public: /// This enum is used to describe request types. enum Type { CheckPassword = 0 }; QString domain() const; void setDomain(const QString &domain); QString password() const; void setPassword(const QString &password); QString username() const; void setUsername(const QString &username); private: QString m_domain; QString m_password; QString m_username; }; /// \brief The QXmppPasswordReply class represents a password reply. /// class QXMPP_EXPORT QXmppPasswordReply : public QObject { Q_OBJECT public: /// This enum is used to describe authentication errors. enum Error { NoError = 0, AuthorizationError, TemporaryError }; QXmppPasswordReply(QObject *parent = nullptr); QByteArray digest() const; void setDigest(const QByteArray &digest); QString password() const; void setPassword(const QString &password); QXmppPasswordReply::Error error() const; void setError(QXmppPasswordReply::Error error); bool isFinished() const; public Q_SLOTS: void finish(); void finishLater(); Q_SIGNALS: /// This signal is emitted when the reply has finished. void finished(); private: QByteArray m_digest; QString m_password; QXmppPasswordReply::Error m_error; bool m_isFinished; }; /// \brief The QXmppPasswordChecker class represents an abstract password checker. /// class QXMPP_EXPORT QXmppPasswordChecker { public: virtual QXmppPasswordReply *checkPassword(const QXmppPasswordRequest &request); virtual QXmppPasswordReply *getDigest(const QXmppPasswordRequest &request); virtual bool hasGetPassword() const; protected: virtual QXmppPasswordReply::Error getPassword(const QXmppPasswordRequest &request, QString &password); }; #endif qxmpp-1.4.0/src/server/QXmppServer.cpp000066400000000000000000000622431402370562100177250ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppServer.h" #include "QXmppConstants_p.h" #include "QXmppDialback.h" #include "QXmppIncomingClient.h" #include "QXmppIncomingServer.h" #include "QXmppIq.h" #include "QXmppOutgoingServer.h" #include "QXmppPresence.h" #include "QXmppServerExtension.h" #include "QXmppServerPlugin.h" #include "QXmppUtils.h" #include #include #include #include #include #include #include #include static void helperToXmlAddDomElement(QXmlStreamWriter *stream, const QDomElement &element, const QStringList &omitNamespaces) { stream->writeStartElement(element.tagName()); /* attributes */ QString xmlns = element.namespaceURI(); if (!xmlns.isEmpty() && !omitNamespaces.contains(xmlns)) stream->writeDefaultNamespace(xmlns); QDomNamedNodeMap attrs = element.attributes(); for (int i = 0; i < attrs.size(); i++) { QDomAttr attr = attrs.item(i).toAttr(); stream->writeAttribute(attr.name(), attr.value()); } /* children */ QDomNode childNode = element.firstChild(); while (!childNode.isNull()) { if (childNode.isElement()) { helperToXmlAddDomElement(stream, childNode.toElement(), QStringList() << xmlns); } else if (childNode.isText()) { stream->writeCharacters(childNode.toText().data()); } childNode = childNode.nextSibling(); } stream->writeEndElement(); } class QXmppServerPrivate { public: QXmppServerPrivate(QXmppServer *qq); void loadExtensions(QXmppServer *server); bool routeData(const QString &to, const QByteArray &data); void startExtensions(); void stopExtensions(); void info(const QString &message); void warning(const QString &message); QString domain; QList extensions; QXmppLogger *logger; QXmppPasswordChecker *passwordChecker; // client-to-server QSet incomingClients; QHash incomingClientsByJid; QHash> incomingClientsByBareJid; QSet serversForClients; // server-to-server QSet incomingServers; QSet outgoingServers; QSet serversForServers; // ssl QList caCertificates; QSslCertificate localCertificate; QSslKey privateKey; private: bool loaded; bool started; QXmppServer *q; }; QXmppServerPrivate::QXmppServerPrivate(QXmppServer *qq) : logger(nullptr), passwordChecker(nullptr), loaded(false), started(false), q(qq) { } /// Routes XMPP data to the given recipient. /// /// \param to /// \param data /// bool QXmppServerPrivate::routeData(const QString &to, const QByteArray &data) { // refuse to route packets to empty destination, own domain or sub-domains const QString toDomain = QXmppUtils::jidToDomain(to); if (to.isEmpty() || to == domain || toDomain.endsWith("." + domain)) return false; if (toDomain == domain) { // look for a client connection QList found; if (QXmppUtils::jidToResource(to).isEmpty()) { const auto &connections = incomingClientsByBareJid.value(to); for (auto *conn : connections) found << conn; } else { QXmppIncomingClient *conn = incomingClientsByJid.value(to); if (conn) found << conn; } // send data for (auto *conn : found) QMetaObject::invokeMethod(conn, "sendData", Q_ARG(QByteArray, data)); return !found.isEmpty(); } else if (!serversForServers.isEmpty()) { // look for an outgoing S2S connection for (auto *conn : qAsConst(outgoingServers)) { if (conn->remoteDomain() == toDomain) { // send or queue data QMetaObject::invokeMethod(conn, "queueData", Q_ARG(QByteArray, data)); return true; } } // if we did not find an outgoing server, // we need to establish the S2S connection auto *conn = new QXmppOutgoingServer(domain, nullptr); conn->setLocalStreamKey(QXmppUtils::generateStanzaHash().toLatin1()); conn->moveToThread(q->thread()); conn->setParent(q); QObject::connect(conn, &QXmppStream::disconnected, q, &QXmppServer::_q_outgoingServerDisconnected); // add stream outgoingServers.insert(conn); q->setGauge("outgoing-server.count", outgoingServers.size()); // queue data and connect to remote server QMetaObject::invokeMethod(conn, "queueData", Q_ARG(QByteArray, data)); QMetaObject::invokeMethod(conn, "connectToHost", Q_ARG(QString, toDomain)); return true; } else { // S2S is disabled, failed to route data return false; } } /// Handles an incoming XML element. /// /// \param server /// \param stream /// \param element static void handleStanza(QXmppServer *server, const QDomElement &element) { // try extensions const auto &extensions = server->extensions(); for (auto *extension : extensions) if (extension->handleStanza(element)) return; // default handlers const QString domain = server->domain(); const QString to = element.attribute("to"); if (to == domain) { if (element.tagName() == QLatin1String("iq")) { // we do not support the given IQ QXmppIq request; request.parse(element); if (request.type() != QXmppIq::Error && request.type() != QXmppIq::Result) { QXmppIq response(QXmppIq::Error); response.setId(request.id()); response.setFrom(domain); response.setTo(request.from()); QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::FeatureNotImplemented); response.setError(error); server->sendPacket(response); } } } else { // route element or reply on behalf of missing peer if (!server->sendElement(element) && element.tagName() == QLatin1String("iq")) { QXmppIq request; request.parse(element); QXmppIq response(QXmppIq::Error); response.setId(request.id()); response.setFrom(request.to()); response.setTo(request.from()); QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::ServiceUnavailable); response.setError(error); server->sendPacket(response); } } } void QXmppServerPrivate::info(const QString &message) { if (logger) logger->log(QXmppLogger::InformationMessage, message); } void QXmppServerPrivate::warning(const QString &message) { if (logger) logger->log(QXmppLogger::WarningMessage, message); } /// Load the server's extensions. /// /// \param server void QXmppServerPrivate::loadExtensions(QXmppServer *server) { if (!loaded) { for (auto *object : QPluginLoader::staticInstances()) { auto *plugin = qobject_cast(object); if (!plugin) continue; const auto &keys = plugin->keys(); for (const auto &key : keys) server->addExtension(plugin->create(key)); } loaded = true; } } /// Start the server's extensions. void QXmppServerPrivate::startExtensions() { if (!started) { for (auto *extension : extensions) if (!extension->start()) warning(QString("Could not start extension %1").arg(extension->extensionName())); started = true; } } /// Stop the server's extensions (in reverse order). /// void QXmppServerPrivate::stopExtensions() { if (started) { for (int i = extensions.size() - 1; i >= 0; --i) extensions[i]->stop(); started = false; } } /// Constructs a new XMPP server instance. /// /// \param parent QXmppServer::QXmppServer(QObject *parent) : QXmppLoggable(parent), d(new QXmppServerPrivate(this)) { qRegisterMetaType("QDomElement"); } /// Destroys an XMPP server instance. /// QXmppServer::~QXmppServer() { close(); delete d; } /// Registers a new extension with the server. /// /// \param extension void QXmppServer::addExtension(QXmppServerExtension *extension) { if (!extension || d->extensions.contains(extension)) return; d->info(QString("Added extension %1").arg(extension->extensionName())); extension->setParent(this); extension->setServer(this); // keep extensions sorted by priority for (int i = 0; i < d->extensions.size(); ++i) { QXmppServerExtension *other = d->extensions[i]; if (other->extensionPriority() < extension->extensionPriority()) { d->extensions.insert(i, extension); return; } } d->extensions << extension; } /// Returns the list of loaded extensions. /// QList QXmppServer::extensions() { d->loadExtensions(this); return d->extensions; } /// Returns the server's domain. /// QString QXmppServer::domain() const { return d->domain; } /// Sets the server's domain. /// /// \param domain void QXmppServer::setDomain(const QString &domain) { d->domain = domain; } QXmppLogger *QXmppServer::logger() { return d->logger; } /// Sets the QXmppLogger associated with the server. /// /// \param logger void QXmppServer::setLogger(QXmppLogger *logger) { if (logger != d->logger) { if (d->logger) { disconnect(this, &QXmppLoggable::logMessage, d->logger, &QXmppLogger::log); disconnect(this, &QXmppLoggable::setGauge, d->logger, &QXmppLogger::setGauge); disconnect(this, &QXmppLoggable::updateCounter, d->logger, &QXmppLogger::updateCounter); } d->logger = logger; if (d->logger) { connect(this, &QXmppLoggable::logMessage, d->logger, &QXmppLogger::log); connect(this, &QXmppLoggable::setGauge, d->logger, &QXmppLogger::setGauge); connect(this, &QXmppLoggable::updateCounter, d->logger, &QXmppLogger::updateCounter); } emit loggerChanged(d->logger); } } /// Returns the password checker used to verify client credentials. /// QXmppPasswordChecker *QXmppServer::passwordChecker() { return d->passwordChecker; } /// Sets the password checker used to verify client credentials. /// /// \param checker /// void QXmppServer::setPasswordChecker(QXmppPasswordChecker *checker) { d->passwordChecker = checker; } /// Returns the statistics for the server. QVariantMap QXmppServer::statistics() const { QVariantMap stats; stats["version"] = qApp->applicationVersion(); stats["incoming-clients"] = d->incomingClients.size(); stats["incoming-servers"] = d->incomingServers.size(); stats["outgoing-servers"] = d->outgoingServers.size(); return stats; } /// Sets the path for additional SSL CA certificates. /// /// \param path void QXmppServer::addCaCertificates(const QString &path) { // load certificates if (path.isEmpty()) { d->caCertificates = QList(); } else if (QFileInfo(path).isReadable()) { d->caCertificates = QSslCertificate::fromPath(path); } else { d->warning(QString("SSL CA certificates are not readable %1").arg(path)); d->caCertificates = QList(); } // reconfigure servers for (auto *server : d->serversForClients + d->serversForServers) server->addCaCertificates(d->caCertificates); } /// Sets the path for the local SSL certificate. /// /// \param path void QXmppServer::setLocalCertificate(const QString &path) { // load certificate QSslCertificate certificate; QFile file(path); if (path.isEmpty()) { d->localCertificate = QSslCertificate(); } else if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { d->localCertificate = QSslCertificate(file.readAll()); } else { d->warning(QString("SSL certificate is not readable %1").arg(path)); d->localCertificate = QSslCertificate(); } // reconfigure servers for (auto *server : d->serversForClients + d->serversForServers) server->setLocalCertificate(d->localCertificate); } /// /// Sets the local SSL certificate /// /// \param certificate /// /// \since QXmpp 0.9 /// void QXmppServer::setLocalCertificate(const QSslCertificate &certificate) { d->localCertificate = certificate; // reconfigure servers for (auto *server : d->serversForClients + d->serversForServers) server->setLocalCertificate(d->localCertificate); } /// Sets the path for the local SSL private key. /// /// \param path void QXmppServer::setPrivateKey(const QString &path) { // load key QSslKey key; QFile file(path); if (path.isEmpty()) { d->privateKey = QSslKey(); } else if (file.open(QIODevice::ReadOnly)) { d->privateKey = QSslKey(file.readAll(), QSsl::Rsa); } else { d->warning(QString("SSL key is not readable %1").arg(path)); d->privateKey = QSslKey(); } // reconfigure servers for (auto *server : d->serversForClients + d->serversForServers) server->setPrivateKey(d->privateKey); } /// /// Sets the local SSL private key. /// /// \param key /// /// \since QXmpp 0.9 /// void QXmppServer::setPrivateKey(const QSslKey &key) { d->privateKey = key; // reconfigure servers for (auto *server : d->serversForClients + d->serversForServers) server->setPrivateKey(d->privateKey); } /// Listen for incoming XMPP client connections. /// /// \param address /// \param port bool QXmppServer::listenForClients(const QHostAddress &address, quint16 port) { bool check; Q_UNUSED(check) if (d->domain.isEmpty()) { d->warning("No domain was specified!"); return false; } // create new server auto *server = new QXmppSslServer(this); server->addCaCertificates(d->caCertificates); server->setLocalCertificate(d->localCertificate); server->setPrivateKey(d->privateKey); check = connect(server, SIGNAL(newConnection(QSslSocket *)), this, SLOT(_q_clientConnection(QSslSocket *))); Q_ASSERT(check); if (!server->listen(address, port)) { d->warning(QString("Could not start listening for C2S on %1 %2").arg(address.toString(), QString::number(port))); delete server; return false; } d->serversForClients.insert(server); // start extensions d->loadExtensions(this); d->startExtensions(); return true; } /// Closes the server. /// void QXmppServer::close() { // prevent new connections for (auto *server : d->serversForClients + d->serversForServers) { server->close(); delete server; } d->serversForClients.clear(); d->serversForServers.clear(); // stop extensions d->stopExtensions(); // close XMPP streams QSetIterator itr(d->incomingClients); while (itr.hasNext()) itr.next()->disconnectFromHost(); for (auto *stream : d->incomingServers) stream->disconnectFromHost(); for (auto *stream : d->outgoingServers) stream->disconnectFromHost(); } /// Listen for incoming XMPP server connections. /// /// \param address /// \param port bool QXmppServer::listenForServers(const QHostAddress &address, quint16 port) { bool check; Q_UNUSED(check) if (d->domain.isEmpty()) { d->warning("No domain was specified!"); return false; } // create new server auto *server = new QXmppSslServer(this); server->addCaCertificates(d->caCertificates); server->setLocalCertificate(d->localCertificate); server->setPrivateKey(d->privateKey); check = connect(server, SIGNAL(newConnection(QSslSocket *)), this, SLOT(_q_serverConnection(QSslSocket *))); Q_ASSERT(check); if (!server->listen(address, port)) { d->warning(QString("Could not start listening for S2S on %1 %2").arg(address.toString(), QString::number(port))); delete server; return false; } d->serversForServers.insert(server); // start extensions d->loadExtensions(this); d->startExtensions(); return true; } /// Route an XMPP stanza. /// /// \param element bool QXmppServer::sendElement(const QDomElement &element) { // serialize data QByteArray data; QXmlStreamWriter xmlStream(&data); const QStringList omitNamespaces = QStringList() << ns_client << ns_server; helperToXmlAddDomElement(&xmlStream, element, omitNamespaces); // route data return d->routeData(element.attribute("to"), data); } /// Route an XMPP packet. /// /// \param packet bool QXmppServer::sendPacket(const QXmppStanza &packet) { // serialize data QByteArray data; QXmlStreamWriter xmlStream(&data); packet.toXml(&xmlStream); // route data return d->routeData(packet.to(), data); } /// Add a new incoming client \a stream. /// /// This method can be used for instance to implement BOSH support /// as a server extension. void QXmppServer::addIncomingClient(QXmppIncomingClient *stream) { stream->setPasswordChecker(d->passwordChecker); connect(stream, &QXmppStream::connected, this, &QXmppServer::_q_clientConnected); connect(stream, &QXmppStream::disconnected, this, &QXmppServer::_q_clientDisconnected); connect(stream, &QXmppIncomingClient::elementReceived, this, &QXmppServer::handleElement); // add stream d->incomingClients.insert(stream); setGauge("incoming-client.count", d->incomingClients.size()); } /// Handle a new incoming TCP connection from a client. /// /// \param socket void QXmppServer::_q_clientConnection(QSslSocket *socket) { // check the socket didn't die since the signal was emitted if (socket->state() != QAbstractSocket::ConnectedState) { delete socket; return; } auto *stream = new QXmppIncomingClient(socket, d->domain, this); stream->setInactivityTimeout(120); socket->setParent(stream); addIncomingClient(stream); } /// Handle a successful stream connection for a client. /// void QXmppServer::_q_clientConnected() { auto *client = qobject_cast(sender()); if (!client) return; // FIXME: at this point the JID must contain a resource, assert it? const QString jid = client->jid(); // check whether the connection conflicts with another one QXmppIncomingClient *old = d->incomingClientsByJid.value(jid); if (old && old != client) { old->sendData("Replaced by new connection"); old->disconnectFromHost(); } d->incomingClientsByJid.insert(jid, client); d->incomingClientsByBareJid[QXmppUtils::jidToBareJid(jid)].insert(client); // emit signal emit clientConnected(jid); } /// Handle a stream disconnection for a client. void QXmppServer::_q_clientDisconnected() { auto *client = qobject_cast(sender()); if (!client) return; if (d->incomingClients.remove(client)) { // remove stream from routing tables const QString jid = client->jid(); if (!jid.isEmpty()) { if (d->incomingClientsByJid.value(jid) == client) d->incomingClientsByJid.remove(jid); const QString bareJid = QXmppUtils::jidToBareJid(jid); if (d->incomingClientsByBareJid.contains(bareJid)) { d->incomingClientsByBareJid[bareJid].remove(client); if (d->incomingClientsByBareJid[bareJid].isEmpty()) d->incomingClientsByBareJid.remove(bareJid); } } // destroy client client->deleteLater(); // emit signal if (!jid.isEmpty()) emit clientDisconnected(jid); // update counter setGauge("incoming-client.count", d->incomingClients.size()); } } void QXmppServer::_q_dialbackRequestReceived(const QXmppDialback &dialback) { auto *stream = qobject_cast(sender()); if (!stream) return; if (dialback.command() == QXmppDialback::Verify) { // handle a verify request for (auto *out : qAsConst(d->outgoingServers)) { if (out->remoteDomain() != dialback.from()) continue; bool isValid = dialback.key() == out->localStreamKey(); QXmppDialback verify; verify.setCommand(QXmppDialback::Verify); verify.setId(dialback.id()); verify.setTo(dialback.from()); verify.setFrom(d->domain); verify.setType(isValid ? "valid" : "invalid"); stream->sendPacket(verify); return; } } } /// Handle an incoming XML element. void QXmppServer::handleElement(const QDomElement &element) { handleStanza(this, element); } /// Handle a stream disconnection for an outgoing server. void QXmppServer::_q_outgoingServerDisconnected() { auto *outgoing = qobject_cast(sender()); if (!outgoing) return; if (d->outgoingServers.remove(outgoing)) { outgoing->deleteLater(); setGauge("outgoing-server.count", d->outgoingServers.size()); } } /// Handle a new incoming TCP connection from a server. /// /// \param socket void QXmppServer::_q_serverConnection(QSslSocket *socket) { // check the socket didn't die since the signal was emitted if (socket->state() != QAbstractSocket::ConnectedState) { delete socket; return; } auto *stream = new QXmppIncomingServer(socket, d->domain, this); socket->setParent(stream); connect(stream, &QXmppStream::disconnected, this, &QXmppServer::_q_serverDisconnected); connect(stream, &QXmppIncomingServer::dialbackRequestReceived, this, &QXmppServer::_q_dialbackRequestReceived); connect(stream, &QXmppIncomingServer::elementReceived, this, &QXmppServer::handleElement); // add stream d->incomingServers.insert(stream); setGauge("incoming-server.count", d->incomingServers.size()); } /// Handle a stream disconnection for an incoming server. void QXmppServer::_q_serverDisconnected() { auto *incoming = qobject_cast(sender()); if (!incoming) return; if (d->incomingServers.remove(incoming)) { incoming->deleteLater(); setGauge("incoming-server.count", d->incomingServers.size()); } } class QXmppSslServerPrivate { public: QList caCertificates; QSslCertificate localCertificate; QSslKey privateKey; }; /// Constructs a new SSL server instance. /// /// \param parent QXmppSslServer::QXmppSslServer(QObject *parent) : QTcpServer(parent), d(new QXmppSslServerPrivate) { } /// Destroys an SSL server instance. /// QXmppSslServer::~QXmppSslServer() { delete d; } void QXmppSslServer::incomingConnection(qintptr socketDescriptor) { auto *socket = new QSslSocket; if (!socket->setSocketDescriptor(socketDescriptor)) { delete socket; return; } if (!d->localCertificate.isNull() && !d->privateKey.isNull()) { auto sslConfig = socket->sslConfiguration(); sslConfig.setCaCertificates(sslConfig.caCertificates() + d->caCertificates); socket->setSslConfiguration(sslConfig); socket->setProtocol(QSsl::AnyProtocol); socket->setLocalCertificate(d->localCertificate); socket->setPrivateKey(d->privateKey); } emit newConnection(socket); } /// Adds the given certificates to the CA certificate database to be used /// for incoming connections. /// /// \param certificates void QXmppSslServer::addCaCertificates(const QList &certificates) { d->caCertificates += certificates; } /// Sets the local certificate to be used for incoming connections. /// /// \param certificate void QXmppSslServer::setLocalCertificate(const QSslCertificate &certificate) { d->localCertificate = certificate; } /// Sets the local private key to be used for incoming connections. /// /// \param key void QXmppSslServer::setPrivateKey(const QSslKey &key) { d->privateKey = key; } qxmpp-1.4.0/src/server/QXmppServer.h000066400000000000000000000107041402370562100173650ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPSERVER_H #define QXMPPSERVER_H #include "QXmppLogger.h" #include #include class QDomElement; class QSslCertificate; class QSslKey; class QSslSocket; class QXmppDialback; class QXmppIncomingClient; class QXmppOutgoingServer; class QXmppPasswordChecker; class QXmppPresence; class QXmppServerExtension; class QXmppServerPrivate; class QXmppSslServer; class QXmppStanza; class QXmppStream; /// \brief The QXmppServer class represents an XMPP server. /// /// It provides support for both client-to-server and server-to-server /// communications, SSL encryption and logging facilities. /// /// QXmppServer comes with a number of modules for service discovery, /// XMPP ping, statistics and file transfer proxy support. You can write /// your own extensions for QXmppServer by subclassing QXmppServerExtension. /// /// \ingroup Core class QXMPP_EXPORT QXmppServer : public QXmppLoggable { Q_OBJECT /// The QXmppLogger associated with the server Q_PROPERTY(QXmppLogger *logger READ logger WRITE setLogger NOTIFY loggerChanged) public: QXmppServer(QObject *parent = nullptr); ~QXmppServer() override; void addExtension(QXmppServerExtension *extension); QList extensions(); QString domain() const; void setDomain(const QString &domain); // documentation needs to be here, see https://stackoverflow.com/questions/49192523/ /// Returns the QXmppLogger associated with the server. QXmppLogger *logger(); void setLogger(QXmppLogger *logger); QXmppPasswordChecker *passwordChecker(); void setPasswordChecker(QXmppPasswordChecker *checker); QVariantMap statistics() const; void addCaCertificates(const QString &caCertificates); void setLocalCertificate(const QString &path); void setLocalCertificate(const QSslCertificate &certificate); void setPrivateKey(const QString &path); void setPrivateKey(const QSslKey &key); void close(); bool listenForClients(const QHostAddress &address = QHostAddress::Any, quint16 port = 5222); bool listenForServers(const QHostAddress &address = QHostAddress::Any, quint16 port = 5269); bool sendElement(const QDomElement &element); bool sendPacket(const QXmppStanza &stanza); void addIncomingClient(QXmppIncomingClient *stream); Q_SIGNALS: /// This signal is emitted when a client has connected. void clientConnected(const QString &jid); /// This signal is emitted when a client has disconnected. void clientDisconnected(const QString &jid); /// This signal is emitted when the logger changes. void loggerChanged(QXmppLogger *logger); public Q_SLOTS: void handleElement(const QDomElement &element); private Q_SLOTS: void _q_clientConnection(QSslSocket *socket); void _q_clientConnected(); void _q_clientDisconnected(); void _q_dialbackRequestReceived(const QXmppDialback &dialback); void _q_outgoingServerDisconnected(); void _q_serverConnection(QSslSocket *socket); void _q_serverDisconnected(); private: friend class QXmppServerPrivate; QXmppServerPrivate *d; }; class QXmppSslServerPrivate; /// \brief The QXmppSslServer class represents an SSL-enabled TCP server. /// class QXMPP_EXPORT QXmppSslServer : public QTcpServer { Q_OBJECT public: QXmppSslServer(QObject *parent = nullptr); ~QXmppSslServer() override; void addCaCertificates(const QList &certificates); void setLocalCertificate(const QSslCertificate &certificate); void setPrivateKey(const QSslKey &key); Q_SIGNALS: /// This signal is emitted when a new connection is established. void newConnection(QSslSocket *socket); private: void incomingConnection(qintptr socketDescriptor) override; QXmppSslServerPrivate *const d; }; #endif qxmpp-1.4.0/src/server/QXmppServerExtension.cpp000066400000000000000000000061271402370562100216210ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppServerExtension.h" #include "QXmppLogger.h" #include "QXmppServer.h" #include #include #include class QXmppServerExtensionPrivate { public: QXmppServer *server; }; QXmppServerExtension::QXmppServerExtension() : d(new QXmppServerExtensionPrivate) { d->server = nullptr; } QXmppServerExtension::~QXmppServerExtension() { delete d; } /// Returns the discovery features to add to the server. /// QStringList QXmppServerExtension::discoveryFeatures() const { return QStringList(); } /// Returns the discovery items to add to the server. /// QStringList QXmppServerExtension::discoveryItems() const { return QStringList(); } /// Returns the extension's name. /// QString QXmppServerExtension::extensionName() const { int index = metaObject()->indexOfClassInfo("ExtensionName"); if (index < 0) return QString(); const char *name = metaObject()->classInfo(index).value(); return QString::fromLatin1(name); } /// Returns the extension's priority. /// /// Higher priority extensions are called first when handling /// incoming stanzas. /// /// The default implementation returns 0. int QXmppServerExtension::extensionPriority() const { return 0; } /// Handles an incoming XMPP stanza. /// /// Return true if no further processing should occur, false otherwise. /// /// \param stanza The received stanza. bool QXmppServerExtension::handleStanza(const QDomElement &stanza) { Q_UNUSED(stanza); return false; } /// Returns the list of subscribers for the given JID. /// /// \param jid QSet QXmppServerExtension::presenceSubscribers(const QString &jid) { Q_UNUSED(jid); return QSet(); } /// Returns the list of subscriptions for the given JID. /// /// \param jid QSet QXmppServerExtension::presenceSubscriptions(const QString &jid) { Q_UNUSED(jid); return QSet(); } /// Starts the extension. /// /// Return true if the extension was started, false otherwise. bool QXmppServerExtension::start() { return true; } /// Stops the extension. void QXmppServerExtension::stop() { } /// Returns the server which loaded this extension. QXmppServer *QXmppServerExtension::server() const { return d->server; } /// Sets the server which loaded this extension. /// /// \param server void QXmppServerExtension::setServer(QXmppServer *server) { d->server = server; } qxmpp-1.4.0/src/server/QXmppServerExtension.h000066400000000000000000000040351402370562100212620ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPSERVEREXTENSION_H #define QXMPPSERVEREXTENSION_H #include "QXmppLogger.h" #include class QDomElement; class QXmppServer; class QXmppServerExtensionPrivate; class QXmppStream; /// \brief The QXmppServerExtension class is the base class for QXmppServer /// extensions. /// /// If you want to extend QXmppServer, for instance to support an IQ type /// which is not natively supported, you can subclass QXmppServerExtension /// and implement handleStanza(). You can then add your extension to the /// client instance using QXmppServer::addExtension(). /// /// \ingroup Core class QXMPP_EXPORT QXmppServerExtension : public QXmppLoggable { Q_OBJECT public: QXmppServerExtension(); ~QXmppServerExtension() override; virtual QString extensionName() const; virtual int extensionPriority() const; virtual QStringList discoveryFeatures() const; virtual QStringList discoveryItems() const; virtual bool handleStanza(const QDomElement &stanza); virtual QSet presenceSubscribers(const QString &jid); virtual QSet presenceSubscriptions(const QString &jid); virtual bool start(); virtual void stop(); protected: QXmppServer *server() const; private: void setServer(QXmppServer *server); QXmppServerExtensionPrivate *const d; friend class QXmppServer; }; #endif qxmpp-1.4.0/src/server/QXmppServerPlugin.cpp000066400000000000000000000013571402370562100211030ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Niels Ole Salscheider * * 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 "QXmppServerPlugin.h" qxmpp-1.4.0/src/server/QXmppServerPlugin.h000066400000000000000000000034011402370562100205400ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 QXMPPSERVERPLUGIN_H #define QXMPPSERVERPLUGIN_H #include "QXmppGlobal.h" #include class QXmppServer; class QXmppServerExtension; class QXMPP_EXPORT QXmppServerPluginInterface { public: /// Creates the server extension identified by \a key. virtual QXmppServerExtension *create(const QString &key) = 0; /// Returns a list of valid extension keys. virtual QStringList keys() const = 0; }; Q_DECLARE_INTERFACE(QXmppServerPluginInterface, "com.googlecode.qxmpp.ServerPlugin/1.0") /// \brief The QXmppServerPlugin class is the base class for QXmppServer plugins. /// class QXMPP_EXPORT QXmppServerPlugin : public QObject, public QXmppServerPluginInterface { Q_OBJECT Q_INTERFACES(QXmppServerPluginInterface) public: /// Creates and returns the specified QXmppServerExtension. /// /// \param key The key for the QXmppServerExtension. QXmppServerExtension *create(const QString &key) override = 0; /// Returns the list of keys supported by this plugin. /// QStringList keys() const override = 0; }; #endif qxmpp-1.4.0/tests/000077500000000000000000000000001402370562100140235ustar00rootroot00000000000000qxmpp-1.4.0/tests/CMakeLists.txt000066400000000000000000000043451402370562100165710ustar00rootroot00000000000000include_directories(.) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Test) macro(add_simple_test TEST_NAME) add_executable(tst_${TEST_NAME} ${TEST_NAME}/tst_${TEST_NAME}.cpp) add_test(tst_${TEST_NAME} tst_${TEST_NAME}) target_link_libraries(tst_${TEST_NAME} Qt${QT_VERSION_MAJOR}::Test qxmpp) endmacro() include_directories(${PROJECT_SOURCE_DIR}/src/base) include_directories(${PROJECT_SOURCE_DIR}/src/client) include_directories(${PROJECT_SOURCE_DIR}/src/server) include_directories(${PROJECT_BINARY_DIR}/src/base) include_directories(${CMAKE_CURRENT_BINARY_DIR}) add_simple_test(qxmpparchiveiq) add_simple_test(qxmppattentionmanager) add_simple_test(qxmppbindiq) add_simple_test(qxmppbitsofbinarycontentid) add_simple_test(qxmppbitsofbinaryiq) add_simple_test(qxmppcarbonmanager) add_simple_test(qxmppclient) 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(qxmppmixinvitation) add_simple_test(qxmppmixitem) add_simple_test(qxmppmessage) add_simple_test(qxmppmessagereceiptmanager) add_simple_test(qxmppmixiq) add_simple_test(qxmppnonsaslauthiq) add_simple_test(qxmppoutgoingclient) add_simple_test(qxmpppushenableiq) add_simple_test(qxmpppresence) add_simple_test(qxmpppubsubiq) add_simple_test(qxmppregisteriq) add_simple_test(qxmppregistrationmanager) add_simple_test(qxmppresultset) add_simple_test(qxmpprosteriq) add_simple_test(qxmpprostermanager) add_simple_test(qxmpprpciq) add_simple_test(qxmppserver) add_simple_test(qxmppsessioniq) add_simple_test(qxmppsocks) add_simple_test(qxmppstanza) add_simple_test(qxmppstarttlspacket) add_simple_test(qxmppstream) add_simple_test(qxmppstreamfeatures) add_simple_test(qxmppstunmessage) add_simple_test(qxmppvcardiq) add_simple_test(qxmppvcardmanager) add_simple_test(qxmppversioniq) if(WITH_GSTREAMER) add_simple_test(qxmppcallmanager) endif() if(BUILD_INTERNAL_TESTS) add_simple_test(qxmppsasl) add_simple_test(qxmppstreaminitiationiq) endif() add_subdirectory(qxmpptransfermanager) add_subdirectory(qxmpputils) add_subdirectory(qxmppuploadrequestmanager) qxmpp-1.4.0/tests/IntegrationTesting.h000066400000000000000000000043611402370562100200210ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Linus Jahn * * 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 INTEGRATIONTESTING_H #define INTEGRATIONTESTING_H #include #include #include "QXmppConfiguration.h" #define ENV_ENABLED "QXMPP_TESTS_INTEGRATION_ENABLED" #define ENV_JID "QXMPP_TESTS_JID" #define ENV_PASSWORD "QXMPP_TESTS_PASSWORD" #define SKIP_IF_INTEGRATION_TESTS_DISABLED() \ if (!IntegrationTests::enabled()) { \ QSKIP("Export 'QXMPP_TESTS_INTEGRATION_ENABLED=1' to enable."); \ } else if (!IntegrationTests::credentialsAvailable()) { \ QFAIL("No credentials for integration tests provided! " \ "Export 'QXMPP_TESTS_JID' and 'QXMPP_TESTS_PASSWORD'."); \ } class IntegrationTests { public: static QString environmentVariable(const char *varName, const QString &defaultValue = {}) { #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) return qEnvironmentVariable(varName, defaultValue); #else return qEnvironmentVariableIsSet(varName) ? QString::fromLocal8Bit(qgetenv(varName)) : QString(); #endif } static bool enabled() { return environmentVariable(ENV_ENABLED, "0") == "1"; } static bool credentialsAvailable() { return !qEnvironmentVariableIsEmpty(ENV_JID) && !qEnvironmentVariableIsEmpty(ENV_PASSWORD); } static QXmppConfiguration clientConfiguration() { QXmppConfiguration config; config.setJid(environmentVariable(ENV_JID)); config.setPassword(environmentVariable(ENV_PASSWORD)); return config; } }; #undef ENV_ENABLED #undef ENV_JID #undef ENV_PASSWORD #endif // INTEGRATIONTESTING_H qxmpp-1.4.0/tests/qxmpparchiveiq/000077500000000000000000000000001402370562100170645ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmpparchiveiq/tst_qxmpparchiveiq.cpp000066400000000000000000000206171402370562100235310ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Jeremy Lainé * Manjeet Dahiya * * 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 "QXmppArchiveIq.h" #include "util.h" class tst_QXmppArchiveIq : public QObject { Q_OBJECT private slots: void testArchiveList_data(); void testArchiveList(); void testArchiveChat_data(); void testArchiveChat(); void testArchiveRemove(); void testArchiveRetrieve_data(); void testArchiveRetrieve(); }; void tst_QXmppArchiveIq::testArchiveList_data() { QTest::addColumn("xml"); QTest::addColumn("max"); QTest::newRow("no rsm") << QByteArray( "" "" "") << -1; QTest::newRow("with rsm") << QByteArray( "" "" "" "30" "" "" "") << 30; } void tst_QXmppArchiveIq::testArchiveList() { QFETCH(QByteArray, xml); QFETCH(int, max); QXmppArchiveListIq iq; parsePacket(iq, xml); QCOMPARE(iq.type(), QXmppIq::Get); QCOMPARE(iq.id(), QLatin1String("list_1")); QCOMPARE(iq.with(), QLatin1String("juliet@capulet.com")); QCOMPARE(iq.start(), QDateTime(QDate(1469, 7, 21), QTime(2, 0, 0), Qt::UTC)); QCOMPARE(iq.end(), QDateTime(QDate(1479, 7, 21), QTime(4, 0, 0), Qt::UTC)); QCOMPARE(iq.resultSetQuery().max(), max); serializePacket(iq, xml); } void tst_QXmppArchiveIq::testArchiveChat_data() { QTest::addColumn("xml"); QTest::addColumn("count"); QTest::newRow("no rsm") << QByteArray( "" "" "Art thou not Romeo, and a Montague?" "Neither, fair saint, if either thee dislike." "How cam'st thou hither, tell me, and wherefore?" "" "") << -1; QTest::newRow("with rsm") << QByteArray( "" "" "Art thou not Romeo, and a Montague?" "Neither, fair saint, if either thee dislike." "How cam'st thou hither, tell me, and wherefore?" "" "3" "" "" "") << 3; } void tst_QXmppArchiveIq::testArchiveChat() { QFETCH(QByteArray, xml); QFETCH(int, count); QXmppArchiveChatIq iq; parsePacket(iq, xml); QCOMPARE(iq.type(), QXmppIq::Result); QCOMPARE(iq.id(), QLatin1String("chat_1")); QCOMPARE(iq.chat().with(), QLatin1String("juliet@capulet.com")); QCOMPARE(iq.chat().messages().size(), 3); QCOMPARE(iq.chat().messages()[0].isReceived(), true); QCOMPARE(iq.chat().messages()[0].body(), QLatin1String("Art thou not Romeo, and a Montague?")); QCOMPARE(iq.chat().messages()[0].date(), QDateTime(QDate(1469, 7, 21), QTime(2, 56, 15), Qt::UTC)); QCOMPARE(iq.chat().messages()[1].isReceived(), false); QCOMPARE(iq.chat().messages()[1].date(), QDateTime(QDate(1469, 7, 21), QTime(2, 56, 26), Qt::UTC)); QCOMPARE(iq.chat().messages()[1].body(), QLatin1String("Neither, fair saint, if either thee dislike.")); QCOMPARE(iq.chat().messages()[2].isReceived(), true); QCOMPARE(iq.chat().messages()[2].date(), QDateTime(QDate(1469, 7, 21), QTime(2, 56, 33), Qt::UTC)); QCOMPARE(iq.chat().messages()[2].body(), QLatin1String("How cam'st thou hither, tell me, and wherefore?")); QCOMPARE(iq.resultSetReply().count(), count); serializePacket(iq, xml); } void tst_QXmppArchiveIq::testArchiveRemove() { const QByteArray xml( "" "" ""); QXmppArchiveRemoveIq iq; parsePacket(iq, xml); QCOMPARE(iq.type(), QXmppIq::Set); QCOMPARE(iq.id(), QLatin1String("remove_1")); QCOMPARE(iq.with(), QLatin1String("juliet@capulet.com")); QCOMPARE(iq.start(), QDateTime(QDate(1469, 7, 21), QTime(2, 0, 0), Qt::UTC)); QCOMPARE(iq.end(), QDateTime(QDate(1479, 7, 21), QTime(4, 0, 0), Qt::UTC)); serializePacket(iq, xml); } void tst_QXmppArchiveIq::testArchiveRetrieve_data() { QTest::addColumn("xml"); QTest::addColumn("max"); QTest::newRow("no rsm") << QByteArray( "" "" "") << -1; QTest::newRow("with rsm") << QByteArray( "" "" "" "30" "" "" "") << 30; } void tst_QXmppArchiveIq::testArchiveRetrieve() { QFETCH(QByteArray, xml); QFETCH(int, max); QXmppArchiveRetrieveIq iq; parsePacket(iq, xml); QCOMPARE(iq.type(), QXmppIq::Get); QCOMPARE(iq.id(), QLatin1String("retrieve_1")); QCOMPARE(iq.with(), QLatin1String("juliet@capulet.com")); QCOMPARE(iq.start(), QDateTime(QDate(1469, 7, 21), QTime(2, 0, 0), Qt::UTC)); QCOMPARE(iq.resultSetQuery().max(), max); serializePacket(iq, xml); } QTEST_MAIN(tst_QXmppArchiveIq) #include "tst_qxmpparchiveiq.moc" qxmpp-1.4.0/tests/qxmppattentionmanager/000077500000000000000000000000001402370562100204515ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppattentionmanager/tst_qxmppattentionmanager.cpp000066400000000000000000000143511402370562100265010ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Linus Jahn * * 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 "QXmppAttentionManager.h" #include "QXmppClient.h" #include "QXmppDiscoveryManager.h" #include "QXmppMessage.h" #include "QXmppRosterManager.h" #include "QXmppUtils.h" #include "util.h" class tst_QXmppAttentionManager : public QObject { Q_OBJECT private slots: void initTestCase(); void testDiscoFeatures(); void testReceived_data(); void testReceived(); void testRateLimiting(); void testSendRequest(); private: void setOwnJid(const QString &jid); void addToRoster(const QString &jid); QXmppClient client; QXmppLogger logger; QXmppAttentionManager *manager; }; void tst_QXmppAttentionManager::initTestCase() { logger.setLoggingType(QXmppLogger::SignalLogging); client.setLogger(&logger); manager = new QXmppAttentionManager(); client.addExtension(manager); } void tst_QXmppAttentionManager::testDiscoFeatures() { QCOMPARE(manager->discoveryFeatures(), QStringList() << "urn:xmpp:attention:0"); } void tst_QXmppAttentionManager::testReceived_data() { QTest::addColumn("msg"); QTest::addColumn("accepted"); QTest::addColumn("rateLimited"); auto createMessage = [](const QString &from, bool attention, const QDateTime &stamp = {}) -> QXmppMessage { QXmppMessage msg; msg.setBody("Moin moin"); msg.setFrom(from); msg.setAttentionRequested(attention); msg.setStamp(stamp); return msg; }; QTest::newRow("basic") << createMessage("other-user@qxmpp.org/Qlient", true) << true; QTest::newRow("no-attention-requested") << createMessage("other-user@qxmpp.org/Qlient", false) << false; QTest::newRow("with-stamp") << createMessage("other-user@qxmpp.org/Qlient", true, QDateTime::currentDateTimeUtc()) << false; QTest::newRow("own-account") << createMessage("me@qxmpp.org/Klient", true) << false; QTest::newRow("trusted") << createMessage("other-user@qxmpp.org/Klient", true) << true; } void tst_QXmppAttentionManager::testReceived() { QFETCH(QXmppMessage, msg); QFETCH(bool, accepted); QObject context; setOwnJid("me@qxmpp.org"); addToRoster("contact@qxmpp.org"); bool signalCalled = false; bool limitedCalled = false; connect(manager, &QXmppAttentionManager::attentionRequested, &context, [&](const QXmppMessage &receivedMsg, bool isTrusted) { signalCalled = true; QCOMPARE(isTrusted, QXmppUtils::jidToBareJid(receivedMsg.from()) == QStringLiteral("contact@qxmpp.org")); QCOMPARE(receivedMsg.body(), msg.body()); }); connect(manager, &QXmppAttentionManager::attentionRequestRateLimited, &context, [&](const QXmppMessage &) { limitedCalled = true; }); emit client.messageReceived(msg); QCOMPARE(signalCalled, accepted); QVERIFY(!limitedCalled); } void tst_QXmppAttentionManager::testRateLimiting() { int count = 1e3; int allowed = 3; client.removeExtension(manager); manager = new QXmppAttentionManager(allowed, QTime(0, 0, 1)); client.addExtension(manager); QObject context; setOwnJid("me@qxmpp.org"); int signalCalled = 0; int rateLimitedCalled = 0; connect(manager, &QXmppAttentionManager::attentionRequested, &context, [&](const QXmppMessage &, bool) { signalCalled++; }); connect(manager, &QXmppAttentionManager::attentionRequestRateLimited, &context, [&](const QXmppMessage &) { rateLimitedCalled++; }); QXmppMessage msg; msg.setAttentionRequested(true); for (int i = 0; i < count; i++) emit client.messageReceived(msg); QCOMPARE(signalCalled, allowed); QCOMPARE(rateLimitedCalled, count - allowed); // wait 1 s + 50 ms because of QTimer precision problems QThread::currentThread()->usleep(1000e3 + 50e3); QCoreApplication::processEvents(); for (int i = 0; i < count; i++) emit client.messageReceived(msg); QCOMPARE(signalCalled, allowed * 2); QCOMPARE(rateLimitedCalled, (count - allowed) * 2); } void tst_QXmppAttentionManager::testSendRequest() { QObject context; bool signalCalled = false; connect(&logger, &QXmppLogger::message, &context, [&](QXmppLogger::MessageType type, const QString &message) { if (type == QXmppLogger::SentMessage) { signalCalled = true; QXmppMessage msg; parsePacket(msg, message.toUtf8()); QCOMPARE(msg.type(), QXmppMessage::Chat); QCOMPARE(msg.id().size(), 36); QCOMPARE(msg.originId().size(), 36); QCOMPARE(msg.to(), QStringLiteral("account@qxmpp.org")); QCOMPARE(msg.body(), QStringLiteral("Hello")); QVERIFY(msg.isAttentionRequested()); } }); // the client is offline, so the message can't be sent and no id is returned QVERIFY(manager->requestAttention("account@qxmpp.org", "Hello").isEmpty()); QVERIFY(signalCalled); } void tst_QXmppAttentionManager::setOwnJid(const QString &jid) { client.connectToServer(jid, {}); client.disconnectFromServer(); } void tst_QXmppAttentionManager::addToRoster(const QString &jid) { auto *rosterManager = client.findExtension(); QXmppRosterIq::Item newItem; newItem.setBareJid(jid); newItem.setSubscriptionType(QXmppRosterIq::Item::Both); QXmppRosterIq iq; iq.setFrom("qxmpp.org"); iq.setType(QXmppIq::Set); iq.addItem(newItem); rosterManager->handleStanza(writePacketToDom(iq)); } QTEST_MAIN(tst_QXmppAttentionManager) #include "tst_qxmppattentionmanager.moc" qxmpp-1.4.0/tests/qxmppbindiq/000077500000000000000000000000001402370562100163575ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppbindiq/tst_qxmppbindiq.cpp000066400000000000000000000047041402370562100223160ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Jeremy Lainé * Manjeet Dahiya * * 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 "QXmppBindIq.h" #include "util.h" #include class tst_QXmppBindIq : public QObject { Q_OBJECT private slots: void testNoResource(); void testResource(); void testResult(); }; void tst_QXmppBindIq::testNoResource() { const QByteArray xml( "" "" ""); QXmppBindIq bind; parsePacket(bind, xml); QCOMPARE(bind.type(), QXmppIq::Set); QCOMPARE(bind.id(), QString("bind_1")); QCOMPARE(bind.jid(), QString()); QCOMPARE(bind.resource(), QString()); serializePacket(bind, xml); } void tst_QXmppBindIq::testResource() { const QByteArray xml( "" "" "someresource" "" ""); QXmppBindIq bind; parsePacket(bind, xml); QCOMPARE(bind.type(), QXmppIq::Set); QCOMPARE(bind.id(), QString("bind_2")); QCOMPARE(bind.jid(), QString()); QCOMPARE(bind.resource(), QString("someresource")); serializePacket(bind, xml); } void tst_QXmppBindIq::testResult() { const QByteArray xml( "" "" "somenode@example.com/someresource" "" ""); QXmppBindIq bind; parsePacket(bind, xml); QCOMPARE(bind.type(), QXmppIq::Result); QCOMPARE(bind.id(), QString("bind_2")); QCOMPARE(bind.jid(), QString("somenode@example.com/someresource")); QCOMPARE(bind.resource(), QString()); serializePacket(bind, xml); } QTEST_MAIN(tst_QXmppBindIq) #include "tst_qxmppbindiq.moc" qxmpp-1.4.0/tests/qxmppbitsofbinarycontentid/000077500000000000000000000000001402370562100215145ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppbitsofbinarycontentid/tst_qxmppbitsofbinarycontentid.cpp000066400000000000000000000175161402370562100306150ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Linus Jahn * * 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 "QXmppBitsOfBinaryContentId.h" #include "util.h" #include class tst_QXmppBitsOfBinaryContentId : public QObject { Q_OBJECT private slots: void testBasic(); void testFromContentId_data(); void testFromContentId(); void testFromCidUrl_data(); void testFromCidUrl(); void testEmpty(); void testIsValid_data(); void testIsValid(); void testIsBobContentId_data(); void testIsBobContentId(); void testUnsupportedAlgorithm(); }; void tst_QXmppBitsOfBinaryContentId::testBasic() { // test fromCidUrl() QXmppBitsOfBinaryContentId cid = QXmppBitsOfBinaryContentId::fromCidUrl(QStringLiteral( "cid:sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org")); QCOMPARE(cid.algorithm(), QCryptographicHash::Sha1); QCOMPARE(cid.hash().toHex(), QByteArrayLiteral("8f35fef110ffc5df08d579a50083ff9308fb6242")); QCOMPARE(cid.toCidUrl(), QStringLiteral("cid:sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org")); QCOMPARE(cid.toContentId(), QStringLiteral("sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org")); // test fromContentId() cid = QXmppBitsOfBinaryContentId::fromContentId(QStringLiteral( "sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org")); QCOMPARE(cid.algorithm(), QCryptographicHash::Sha1); QCOMPARE(cid.hash().toHex(), QByteArrayLiteral("8f35fef110ffc5df08d579a50083ff9308fb6242")); QCOMPARE(cid.toCidUrl(), QStringLiteral("cid:sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org")); QCOMPARE(cid.toContentId(), QStringLiteral("sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org")); // test setters cid = QXmppBitsOfBinaryContentId(); cid.setHash(QByteArray::fromHex(QByteArrayLiteral("8f35fef110ffc5df08d579a50083ff9308fb6242"))); cid.setAlgorithm(QCryptographicHash::Sha1); QCOMPARE(cid.algorithm(), QCryptographicHash::Sha1); QCOMPARE(cid.hash().toHex(), QByteArrayLiteral("8f35fef110ffc5df08d579a50083ff9308fb6242")); QCOMPARE(cid.toCidUrl(), QStringLiteral("cid:sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org")); QCOMPARE(cid.toContentId(), QStringLiteral("sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org")); } void tst_QXmppBitsOfBinaryContentId::testFromContentId_data() { QTest::addColumn("input"); QTest::addColumn("isValid"); #define ROW(NAME, INPUT, IS_VALID) \ QTest::newRow(NAME) << QStringLiteral(INPUT) << IS_VALID ROW("valid", "sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org", true); ROW("wrong-namespace", "sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob_222.xmpp.org", false); ROW("no-namespace", "sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@", false); ROW("url", "cid:sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org", false); ROW("url-and-wrong-namespace", "cid:sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob_222.xmpp.org", false); ROW("too-many-pluses", "sha1+sha256+sha3-256+blake2b256+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org", false); #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) ROW("wrong-hash-length", "cid:sha1+08d579a50083ff9308fb6242@bob.xmpp.org", false); #endif #undef ROW } void tst_QXmppBitsOfBinaryContentId::testFromContentId() { QFETCH(QString, input); QFETCH(bool, isValid); QCOMPARE(QXmppBitsOfBinaryContentId::fromContentId(input).isValid(), isValid); } void tst_QXmppBitsOfBinaryContentId::testFromCidUrl_data() { QTest::addColumn("input"); QTest::addColumn("isValid"); #define ROW(NAME, INPUT, IS_VALID) \ QTest::newRow(NAME) << QStringLiteral(INPUT) << IS_VALID ROW("valid", "cid:sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org", true); ROW("no-url", "sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org", false); ROW("wrong-namespace", "cid:sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@other", false); ROW("too-many-pluses", "cid:sha1+sha256+sha3-256+blake2b256+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org", false); #undef ROW } void tst_QXmppBitsOfBinaryContentId::testFromCidUrl() { QFETCH(QString, input); QFETCH(bool, isValid); QCOMPARE(QXmppBitsOfBinaryContentId::fromCidUrl(input).isValid(), isValid); } void tst_QXmppBitsOfBinaryContentId::testEmpty() { QXmppBitsOfBinaryContentId cid; QVERIFY(cid.toCidUrl().isEmpty()); QVERIFY(cid.toContentId().isEmpty()); } void tst_QXmppBitsOfBinaryContentId::testIsValid_data() { QTest::addColumn("hash"); QTest::addColumn("algorithm"); QTest::addColumn("isValid"); #define ROW(NAME, HASH, ALGORITHM, IS_VALID) \ QTest::newRow(NAME) << QByteArray::fromHex(HASH) << ALGORITHM << IS_VALID ROW("valid", "8f35fef110ffc5df08d579a50083ff9308fb6242", QCryptographicHash::Sha1, true); #ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 ROW("valid-sha256", "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b", QCryptographicHash::Sha256, true); #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) ROW("wrong-hash-length", "8f35fef110ffc5df08", QCryptographicHash::Sha1, false); #endif #undef ROW } void tst_QXmppBitsOfBinaryContentId::testIsValid() { QFETCH(QByteArray, hash); QFETCH(QCryptographicHash::Algorithm, algorithm); QFETCH(bool, isValid); QXmppBitsOfBinaryContentId contentId; contentId.setAlgorithm(algorithm); contentId.setHash(hash); QCOMPARE(contentId.isValid(), isValid); } void tst_QXmppBitsOfBinaryContentId::testIsBobContentId_data() { QTest::addColumn("input"); QTest::addColumn("checkIsUrl"); QTest::addColumn("isValid"); #define ROW(NAME, INPUT, CHECK_IS_URL, IS_VALID) \ QTest::newRow(NAME) << QStringLiteral(INPUT) << CHECK_IS_URL << IS_VALID ROW("valid-url-check-url", "cid:sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org", true, true); ROW("valid-url-no-check-url", "cid:sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org", false, true); ROW("valid-id-no-check-url", "sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org", false, true); ROW("not-an-url", "sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org", true, false); ROW("invalid-namespace-id", "sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org.org.org", false, false); ROW("invalid-namespace-url", "cid:sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org.org.org", true, false); ROW("no-hash-algorithm", "sha18f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org", false, false); #undef ROW } void tst_QXmppBitsOfBinaryContentId::testIsBobContentId() { QFETCH(QString, input); QFETCH(bool, checkIsUrl); QFETCH(bool, isValid); QCOMPARE(QXmppBitsOfBinaryContentId::isBitsOfBinaryContentId(input, checkIsUrl), isValid); } void tst_QXmppBitsOfBinaryContentId::testUnsupportedAlgorithm() { QCOMPARE( QXmppBitsOfBinaryContentId::fromContentId( QStringLiteral("blake2s160+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org")), QXmppBitsOfBinaryContentId()); } QTEST_MAIN(tst_QXmppBitsOfBinaryContentId) #include "tst_qxmppbitsofbinarycontentid.moc" qxmpp-1.4.0/tests/qxmppbitsofbinaryiq/000077500000000000000000000000001402370562100201365ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppbitsofbinaryiq/tst_qxmppbitsofbinaryiq.cpp000066400000000000000000000227051402370562100256550ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Linus Jahn * * 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 "QXmppBitsOfBinaryContentId.h" #include "QXmppBitsOfBinaryIq.h" #include "util.h" #include #include class tst_QXmppBitsOfBinaryIq : public QObject { Q_OBJECT private slots: void testBasic(); void testResult(); void testOtherSubelement(); void testIsBobIq(); }; void tst_QXmppBitsOfBinaryIq::testBasic() { const QByteArray xml( "" "" ""); QXmppBitsOfBinaryIq iq; parsePacket(iq, xml); QCOMPARE(iq.from(), QStringLiteral("doctor@shakespeare.lit/pda")); QCOMPARE(iq.id(), QStringLiteral("get-data-1")); QCOMPARE(iq.to(), QStringLiteral("ladymacbeth@shakespeare.lit/castle")); QCOMPARE(iq.type(), QXmppIq::Get); QCOMPARE(iq.cid().toContentId(), QStringLiteral("sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org")); QCOMPARE(iq.contentType(), QMimeType()); QCOMPARE(iq.data(), QByteArray()); QCOMPARE(iq.maxAge(), -1); serializePacket(iq, xml); iq = QXmppBitsOfBinaryIq(); iq.setFrom(QStringLiteral("doctor@shakespeare.lit/pda")); iq.setId(QStringLiteral("get-data-1")); iq.setTo(QStringLiteral("ladymacbeth@shakespeare.lit/castle")); iq.setType(QXmppIq::Get); iq.setCid(QXmppBitsOfBinaryContentId::fromContentId(QStringLiteral("sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org"))); serializePacket(iq, xml); } void tst_QXmppBitsOfBinaryIq::testResult() { const QByteArray xml = QByteArrayLiteral( "" "" "iVBORw0KGgoAAAANSUhEUgAAALQAAAA8BAMAAAA9AI20AAAAG1BMVEX///8AAADf39+" "/v79/f39fX1+fn58/Pz8fHx/8ACGJAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADS0lEQV" "RYhe2WS3MSQRCAYTf7OKY1kT0CxsRjHmh5BENIjqEk6pHVhFzdikqO7CGyP9t59Ox2z" "y6UeWBVqugLzM70Nz39mqnV1lIWgBWiYXV0BYfNZ0mvwypds1r62vH/gf76ZL/88Qlc" "41zeAnQrpx5H3z1Npfr5ovmHusa9SpRiNNIOcdrto6PJ5LLfb5bp9zM+VDq/vptxDEa" "a1sql9I3R5KhtfQsA5gNCWYyulV3TyTUDdfL56BvdDl4x7RiybDq9uBgxh1TTPUHDvA" "qNQb+LpT5sWehxJZKKcU2MZ6sDE7PMgW2mdlBGdy6ODe6fJFdMI+us95dNqftDMdwU6" "+MhpuTS9slcy5TFAcwq0Jt6qssJMTQGp4BGURlmSsNoo5oHL4kqc66NdkDO75mIfCxm" "RAlvHxMLdcb7JONavMJbttXXKoMSneYu3OQTlwkUh4mNayi6js55/2VcsZOQfXIYelz" "xLcntEGc3WVCsCORJVCc5r0ajAcq+EO1Q0oPm7n7+X/3jEReGdL6qT7Ml6FCjY+quJC" "r+D01f6BG0SaHG56ZG32DnY2jcEV1+pU0kxTaEwaGcekN7jyu50U/TV4q6YeieyiNTu" "klDKZLukyjKVNwotCUB3B0XO1WjHT3c0DHSO2zACwut8GOiljJIHaJsrlof/fpWNzGM" "os6TgIY0hZNpJshzSi4igOhy3cl4qK+YgnqHkAYcZEgdW6/HyrEK7afoY7RCFzArLl2" "LLDdrdmmHZfROajwIDfWj8yQG+rzwlA3WvdJiMHtjUekiNrp1oCbmyZDEyKROGjFVDr" "PRzlkR9UAfG/OErnPxrop5BwpoEpXQorq2zcGxbnBJndx8Bh0yljGiGv0B4E8+YP3Xp" "2rGydZNy4csW8W2pIvWhvijoujRJ0luXsoymV+8AXvE9HjII72+oReS6OfomHe3xWg/" "f2coSbDa1XZ1CvGMjy1nH9KBl83oPnQKi+vAXKLjCrRvvT2WCMkPmSFbquiVuTH1qjv" "p4j/u7CWyI5/Hn3KAaJJ90eP0Zp1Kjets4WPaElkxheF7cpBESzXuIdLwyFjSub07tB" "6JjxH3DGiu+zwHHimdtFsMvKqG/nBxm2TwbvyU6LWs5RnJX4dSldg3QhDLAAAAAElFT" "kSuQmCC" "" ""); const auto data = QByteArray::fromBase64(QByteArrayLiteral( "iVBORw0KGgoAAAANSUhEUgAAALQAAAA8BAMAAAA9AI20AAAAG1BMVEX///8AAADf39+" "/v79/f39fX1+fn58/Pz8fHx/8ACGJAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADS0lEQV" "RYhe2WS3MSQRCAYTf7OKY1kT0CxsRjHmh5BENIjqEk6pHVhFzdikqO7CGyP9t59Ox2z" "y6UeWBVqugLzM70Nz39mqnV1lIWgBWiYXV0BYfNZ0mvwypds1r62vH/gf76ZL/88Qlc" "41zeAnQrpx5H3z1Npfr5ovmHusa9SpRiNNIOcdrto6PJ5LLfb5bp9zM+VDq/vptxDEa" "a1sql9I3R5KhtfQsA5gNCWYyulV3TyTUDdfL56BvdDl4x7RiybDq9uBgxh1TTPUHDvA" "qNQb+LpT5sWehxJZKKcU2MZ6sDE7PMgW2mdlBGdy6ODe6fJFdMI+us95dNqftDMdwU6" "+MhpuTS9slcy5TFAcwq0Jt6qssJMTQGp4BGURlmSsNoo5oHL4kqc66NdkDO75mIfCxm" "RAlvHxMLdcb7JONavMJbttXXKoMSneYu3OQTlwkUh4mNayi6js55/2VcsZOQfXIYelz" "xLcntEGc3WVCsCORJVCc5r0ajAcq+EO1Q0oPm7n7+X/3jEReGdL6qT7Ml6FCjY+quJC" "r+D01f6BG0SaHG56ZG32DnY2jcEV1+pU0kxTaEwaGcekN7jyu50U/TV4q6YeieyiNTu" "klDKZLukyjKVNwotCUB3B0XO1WjHT3c0DHSO2zACwut8GOiljJIHaJsrlof/fpWNzGM" "os6TgIY0hZNpJshzSi4igOhy3cl4qK+YgnqHkAYcZEgdW6/HyrEK7afoY7RCFzArLl2" "LLDdrdmmHZfROajwIDfWj8yQG+rzwlA3WvdJiMHtjUekiNrp1oCbmyZDEyKROGjFVDr" "PRzlkR9UAfG/OErnPxrop5BwpoEpXQorq2zcGxbnBJndx8Bh0yljGiGv0B4E8+YP3Xp" "2rGydZNy4csW8W2pIvWhvijoujRJ0luXsoymV+8AXvE9HjII72+oReS6OfomHe3xWg/" "f2coSbDa1XZ1CvGMjy1nH9KBl83oPnQKi+vAXKLjCrRvvT2WCMkPmSFbquiVuTH1qjv" "p4j/u7CWyI5/Hn3KAaJJ90eP0Zp1Kjets4WPaElkxheF7cpBESzXuIdLwyFjSub07tB" "6JjxH3DGiu+zwHHimdtFsMvKqG/nBxm2TwbvyU6LWs5RnJX4dSldg3QhDLAAAAAElFT" "kSuQmCC")); QXmppBitsOfBinaryIq iq; parsePacket(iq, xml); QCOMPARE(iq.type(), QXmppIq::Result); QCOMPARE(iq.id(), QStringLiteral("data-result")); QCOMPARE(iq.cid().algorithm(), QCryptographicHash::Sha1); QCOMPARE(iq.cid().hash(), QByteArray::fromHex(QByteArrayLiteral("5a4c38d44fc64805cbb2d92d8b208be13ff40c0f"))); QCOMPARE(iq.contentType(), QMimeDatabase().mimeTypeForName(QStringLiteral("image/png"))); QCOMPARE(iq.maxAge(), 86400); QCOMPARE(iq.data(), data); serializePacket(iq, xml); iq = QXmppBitsOfBinaryIq(); iq.setId(QStringLiteral("data-result")); iq.setFrom(QStringLiteral("ladymacbeth@shakespeare.lit/castle")); iq.setTo(QStringLiteral("doctor@shakespeare.lit/pda")); iq.setType(QXmppIq::Result); iq.setCid(QXmppBitsOfBinaryContentId::fromContentId( QStringLiteral("sha1+5a4c38d44fc64805cbb2d92d8b208be13ff40c0f@bob.xmpp.org"))); iq.setContentType(QMimeDatabase().mimeTypeForName(QStringLiteral("image/png"))); iq.setMaxAge(86400); iq.setData(data); serializePacket(iq, xml); } void tst_QXmppBitsOfBinaryIq::testOtherSubelement() { const QByteArray xml( "" "" "" ""); QXmppBitsOfBinaryIq iq; parsePacket(iq, xml); QCOMPARE(iq.from(), QStringLiteral("doctor@shakespeare.lit/pda")); QCOMPARE(iq.id(), QStringLiteral("get-data-1")); QCOMPARE(iq.to(), QStringLiteral("ladymacbeth@shakespeare.lit/castle")); QCOMPARE(iq.type(), QXmppIq::Get); QCOMPARE(iq.cid().toContentId(), QStringLiteral("sha1+8f35fef110ffc5df08d579a50083ff9308fb6242@bob.xmpp.org")); QCOMPARE(iq.contentType(), QMimeType()); QCOMPARE(iq.data(), QByteArray()); QCOMPARE(iq.maxAge(), -1); } void tst_QXmppBitsOfBinaryIq::testIsBobIq() { QDomDocument doc; const QByteArray xmlSimple( "" "" ""); QCOMPARE(doc.setContent(xmlSimple, true), true); QCOMPARE(QXmppBitsOfBinaryIq::isBitsOfBinaryIq(doc.documentElement()), true); const QByteArray xmlMultipleElements( "" "" "" ""); QCOMPARE(doc.setContent(xmlMultipleElements, true), true); QCOMPARE(QXmppBitsOfBinaryIq::isBitsOfBinaryIq(doc.documentElement()), true); const QByteArray xmlWithoutBobData( "" "" ""); QCOMPARE(doc.setContent(xmlWithoutBobData, true), true); QCOMPARE(QXmppBitsOfBinaryIq::isBitsOfBinaryIq(doc.documentElement()), false); } QTEST_MAIN(tst_QXmppBitsOfBinaryIq) #include "tst_qxmppbitsofbinaryiq.moc" qxmpp-1.4.0/tests/qxmppcallmanager/000077500000000000000000000000001402370562100173575ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppcallmanager/tst_qxmppcallmanager.cpp000066400000000000000000000104201402370562100243060ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Jeremy Lainé * * 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 "QXmppCallManager.h" #include "QXmppClient.h" #include "QXmppServer.h" #include "util.h" #include #include class tst_QXmppCallManager : public QObject { Q_OBJECT private slots: void init(); void testCall(); void acceptCall(QXmppCall *call); private: QXmppCall *receiverCall; }; void tst_QXmppCallManager::init() { receiverCall = nullptr; } void tst_QXmppCallManager::acceptCall(QXmppCall *call) { receiverCall = call; call->accept(); } void tst_QXmppCallManager::testCall() { const QString testDomain("localhost"); const QHostAddress testHost(QHostAddress::LocalHost); const quint16 testPort = 12345; QXmppLogger logger; logger.setLoggingType(QXmppLogger::StdoutLogging); // prepare server TestPasswordChecker passwordChecker; passwordChecker.addCredentials("sender", "testpwd"); passwordChecker.addCredentials("receiver", "testpwd"); QXmppServer server; server.setDomain(testDomain); server.setPasswordChecker(&passwordChecker); server.listenForClients(testHost, testPort); // prepare sender QXmppClient sender; auto *senderManager = new QXmppCallManager; sender.addExtension(senderManager); sender.setLogger(&logger); QEventLoop senderLoop; connect(&sender, &QXmppClient::connected, &senderLoop, &QEventLoop::quit); connect(&sender, &QXmppClient::disconnected, &senderLoop, &QEventLoop::quit); QXmppConfiguration config; config.setDomain(testDomain); config.setHost(testHost.toString()); config.setPort(testPort); config.setUser("sender"); config.setPassword("testpwd"); sender.connectToServer(config); senderLoop.exec(); QCOMPARE(sender.isConnected(), true); // prepare receiver QXmppClient receiver; auto *receiverManager = new QXmppCallManager; connect(receiverManager, &QXmppCallManager::callReceived, this, &tst_QXmppCallManager::acceptCall); receiver.addExtension(receiverManager); receiver.setLogger(&logger); QEventLoop receiverLoop; connect(&receiver, &QXmppClient::connected, &receiverLoop, &QEventLoop::quit); connect(&receiver, &QXmppClient::disconnected, &receiverLoop, &QEventLoop::quit); config.setUser("receiver"); config.setPassword("testpwd"); receiver.connectToServer(config); receiverLoop.exec(); QCOMPARE(receiver.isConnected(), true); // connect call qDebug() << "======== CONNECT ========"; QEventLoop loop; QXmppCall *senderCall = senderManager->call("receiver@localhost/QXmpp"); QVERIFY(senderCall); connect(senderCall, &QXmppCall::connected, &loop, &QEventLoop::quit); loop.exec(); QVERIFY(receiverCall); QCOMPARE(senderCall->direction(), QXmppCall::OutgoingDirection); QCOMPARE(senderCall->state(), QXmppCall::ActiveState); QCOMPARE(receiverCall->direction(), QXmppCall::IncomingDirection); QCOMPARE(receiverCall->state(), QXmppCall::ActiveState); // exchange some media qDebug() << "======== TALK ========"; QTimer::singleShot(2000, &loop, &QEventLoop::quit); loop.exec(); // hangup call qDebug() << "======== HANGUP ========"; connect(senderCall, &QXmppCall::finished, &loop, &QEventLoop::quit); senderCall->hangup(); loop.exec(); QCOMPARE(senderCall->direction(), QXmppCall::OutgoingDirection); QCOMPARE(senderCall->state(), QXmppCall::FinishedState); QCOMPARE(receiverCall->direction(), QXmppCall::IncomingDirection); QCOMPARE(receiverCall->state(), QXmppCall::FinishedState); } QTEST_MAIN(tst_QXmppCallManager) #include "tst_qxmppcallmanager.moc" qxmpp-1.4.0/tests/qxmppcarbonmanager/000077500000000000000000000000001402370562100177105ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppcarbonmanager/tst_qxmppcarbonmanager.cpp000066400000000000000000000240271402370562100252000ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Jeremy Lainé * Manjeet Dahiya * * 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 "QXmppCarbonManager.h" #include "QXmppClient.h" #include "QXmppMessage.h" #include "util.h" #include class QXmppCarbonTestHelper : public QObject { Q_OBJECT public slots: void messageSent(const QXmppMessage& msg); void messageReceived(const QXmppMessage& msg); public: QXmppMessage m_expectedMessage; bool m_expectSent; bool m_signalTriggered; void compareMessages(const QXmppMessage& lhs, const QXmppMessage& rhs); }; class tst_QXmppCarbonManager : public QObject { Q_OBJECT private slots: void initTestCase(); void testHandleStanza_data(); void testHandleStanza(); private: QXmppCarbonTestHelper m_helper; QXmppCarbonManager *m_manager; QXmppClient client; }; void tst_QXmppCarbonManager::initTestCase() { m_manager = new QXmppCarbonManager(); connect(m_manager, &QXmppCarbonManager::messageSent, &m_helper, &QXmppCarbonTestHelper::messageSent); connect(m_manager, &QXmppCarbonManager::messageReceived, &m_helper, &QXmppCarbonTestHelper::messageReceived); client.connectToServer("romeo@montague.example", "a"); client.disconnectFromServer(); client.addExtension(m_manager); } void tst_QXmppCarbonManager::testHandleStanza_data() { QTest::addColumn("xml"); QTest::addColumn("accept"); QTest::addColumn("sent"); QTest::addColumn("forwardedxml"); QTest::newRow("received1") << QByteArray("" "" "" "" "What man art thou that, thus bescreen'd in night, so stumblest on my counsel?" "0e3141cd80894871a68e6fe6b1ec56fa" "" "" "" "") << true << false << QByteArray("" "What man art thou that, thus bescreen'd in night, so stumblest on my counsel?" "0e3141cd80894871a68e6fe6b1ec56fa" ""); QTest::newRow("sent1") << QByteArray("" "" "" "" "Neither, fair saint, if either thee dislike." "0e3141cd80894871a68e6fe6b1ec56fa" "" "" "" "") << true << true << QByteArray("" "Neither, fair saint, if either thee dislike." "0e3141cd80894871a68e6fe6b1ec56fa" ""); QTest::newRow("received-wrong-from") << QByteArray("" "" "" "" "What man art thou that, thus bescreen'd in night, so stumblest on my counsel?" "0e3141cd80894871a68e6fe6b1ec56fa" "" "" "" "") << false << false << QByteArray("" "What man art thou that, thus bescreen'd in night, so stumblest on my counsel?" "0e3141cd80894871a68e6fe6b1ec56fa" ""); QTest::newRow("sent-wrong-from") << QByteArray("" "" "" "" "Neither, fair saint, if either thee dislike." "0e3141cd80894871a68e6fe6b1ec56fa" "" "" "" "") << false << true << QByteArray("" "Neither, fair saint, if either thee dislike." "0e3141cd80894871a68e6fe6b1ec56fa" ""); QTest::newRow("forwarded_normal") << QByteArray("" "A most courteous exposition!" "" "" "" "Yet I should kill thee with much cherishing." "" "" "" "" "" "") << false << false << QByteArray(); } void tst_QXmppCarbonManager::testHandleStanza() { QFETCH(QByteArray, xml); QFETCH(bool, accept); QFETCH(bool, sent); QFETCH(QByteArray, forwardedxml); m_helper.m_expectedMessage = QXmppMessage(); if (!forwardedxml.isEmpty()) parsePacket(m_helper.m_expectedMessage, forwardedxml); m_helper.m_expectSent = sent; m_helper.m_signalTriggered = false; QDomDocument doc; QCOMPARE(doc.setContent(xml, true), true); QDomElement element = doc.documentElement(); bool accepted = m_manager->handleStanza(element); QCOMPARE(accepted, accept); QCOMPARE(m_helper.m_signalTriggered, accept); } void QXmppCarbonTestHelper::messageSent(const QXmppMessage& msg) { m_signalTriggered = true; QCOMPARE(m_expectSent, true); compareMessages(m_expectedMessage, msg); } void QXmppCarbonTestHelper::messageReceived(const QXmppMessage& msg) { m_signalTriggered = true; QCOMPARE(m_expectSent, false); compareMessages(m_expectedMessage, msg); } void QXmppCarbonTestHelper::compareMessages(const QXmppMessage& lhs, const QXmppMessage& rhs) { QCOMPARE(lhs.body(), rhs.body()); QCOMPARE(lhs.from(), rhs.from()); QCOMPARE(lhs.id(), rhs.id()); QCOMPARE(lhs.isAttentionRequested(), rhs.isAttentionRequested()); QCOMPARE(lhs.isMarkable(), rhs.isMarkable()); QCOMPARE(lhs.isPrivate(), rhs.isPrivate()); QCOMPARE(lhs.isReceiptRequested(), rhs.isReceiptRequested()); QCOMPARE(lhs.lang(), rhs.lang()); QCOMPARE(lhs.to(), rhs.to()); QCOMPARE(lhs.thread(), rhs.thread()); QCOMPARE(lhs.stamp(), rhs.stamp()); QCOMPARE(lhs.type(), rhs.type()); } QTEST_MAIN(tst_QXmppCarbonManager) #include "tst_qxmppcarbonmanager.moc" qxmpp-1.4.0/tests/qxmppclient/000077500000000000000000000000001402370562100163675ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppclient/tst_qxmppclient.cpp000066400000000000000000000054011402370562100223310ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Linus Jahn * Melvin Keskin * * 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 "QXmppClient.h" #include "QXmppLogger.h" #include "QXmppMessage.h" #include "QXmppRosterManager.h" #include "QXmppVCardManager.h" #include "QXmppVersionManager.h" #include "util.h" #include class tst_QXmppClient : public QObject { Q_OBJECT private slots: void initTestCase(); void handleMessageSent(QXmppLogger::MessageType type, const QString &text) const; void testSendMessage(); void testIndexOfExtension(); private: QXmppClient *client; }; void tst_QXmppClient::handleMessageSent(QXmppLogger::MessageType type, const QString &text) const { QCOMPARE(type, QXmppLogger::MessageType::SentMessage); QXmppMessage msg; parsePacket(msg, text.toUtf8()); QCOMPARE(msg.from(), QString()); QCOMPARE(msg.to(), QStringLiteral("support@qxmpp.org")); QCOMPARE(msg.body(), QStringLiteral("implement XEP-* plz")); } void tst_QXmppClient::initTestCase() { client = new QXmppClient(this); } void tst_QXmppClient::testSendMessage() { QXmppLogger logger; logger.setLoggingType(QXmppLogger::SignalLogging); client->setLogger(&logger); connect(&logger, &QXmppLogger::message, this, &tst_QXmppClient::handleMessageSent); client->sendMessage( QStringLiteral("support@qxmpp.org"), QStringLiteral("implement XEP-* plz")); // see handleMessageSent() client->setLogger(nullptr); } void tst_QXmppClient::testIndexOfExtension() { auto client = new QXmppClient; for (auto *ext : client->extensions()) { client->removeExtension(ext); } auto rosterManager = new QXmppRosterManager(client); auto vCardManager = new QXmppVCardManager; client->addExtension(rosterManager); client->addExtension(vCardManager); // This extension is not in the list. QCOMPARE(client->indexOfExtension(), -1); // These extensions are in the list. QCOMPARE(client->indexOfExtension(), 0); QCOMPARE(client->indexOfExtension(), 1); } QTEST_MAIN(tst_QXmppClient) #include "tst_qxmppclient.moc" qxmpp-1.4.0/tests/qxmppdataform/000077500000000000000000000000001402370562100167065ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppdataform/tst_qxmppdataform.cpp000066400000000000000000000161401402370562100231710ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Andrey Batyiev * Jeremy Lainé * Linus Jahn * * 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 #include // deprecated methods are also tested: this is used to avoid unnecessary warnings #undef QT_DEPRECATED_X #define QT_DEPRECATED_X(text) #include "QXmppDataForm.h" #include "util.h" class tst_QXmppDataForm : public QObject { Q_OBJECT private slots: void testSimple(); void testSubmit(); void testMedia(); void testMediaSource(); }; void tst_QXmppDataForm::testSimple() { const QByteArray xml( "" "Joggle Search" "Fill out this form to search for information!" "" "" "" ""); QXmppDataForm form; parsePacket(form, xml); QCOMPARE(form.isNull(), false); QCOMPARE(form.title(), QLatin1String("Joggle Search")); QCOMPARE(form.instructions(), QLatin1String("Fill out this form to search for information!")); QCOMPARE(form.fields().size(), 1); QCOMPARE(form.fields().at(0).type(), QXmppDataForm::Field::TextSingleField); QCOMPARE(form.fields().at(0).isRequired(), true); QCOMPARE(form.fields().at(0).key(), QString("search_request")); serializePacket(form, xml); } void tst_QXmppDataForm::testSubmit() { const QByteArray xml( "" "" "verona" "" ""); QXmppDataForm form; parsePacket(form, xml); QCOMPARE(form.isNull(), false); serializePacket(form, xml); } void tst_QXmppDataForm::testMedia() { const QByteArray xml = QByteArrayLiteral( "" "" "" "" "http://www.victim.com/challenges/ocr.jpeg?F3A6292C" "" "" "cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org" "" "" "" ""); // // test parsing // QXmppDataForm form; parsePacket(form, xml); QCOMPARE(form.isNull(), false); QCOMPARE(form.fields().size(), 1); QCOMPARE(form.fields().at(0).type(), QXmppDataForm::Field::TextSingleField); QCOMPARE(form.fields().at(0).isRequired(), false); QCOMPARE(form.fields().at(0).mediaSize(), QSize(290, 80)); QCOMPARE(form.fields().at(0).mediaSources().size(), 2); QCOMPARE( form.fields().at(0).mediaSources().at(0).uri().toString(), QStringLiteral("http://www.victim.com/challenges/ocr.jpeg?F3A6292C")); QCOMPARE( form.fields().at(0).mediaSources().at(0).contentType(), QMimeDatabase().mimeTypeForName(QStringLiteral("image/jpeg"))); QCOMPARE( form.fields().at(0).mediaSources().at(1).uri().toString(), QStringLiteral("cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org")); QCOMPARE( form.fields().at(0).mediaSources().at(1).contentType(), QMimeDatabase().mimeTypeForName(QStringLiteral("image/png"))); // deprecated QCOMPARE(form.fields().at(0).media().isNull(), false); QCOMPARE(form.fields().at(0).media().width(), 290); QCOMPARE(form.fields().at(0).media().height(), 80); QCOMPARE(form.fields().at(0).media().uris().size(), 2); QCOMPARE( form.fields().at(0).media().uris().at(0).first, QStringLiteral("image/jpeg")); QCOMPARE( form.fields().at(0).media().uris().at(0).second, QStringLiteral("http://www.victim.com/challenges/ocr.jpeg?F3A6292C")); QCOMPARE( form.fields().at(0).media().uris().at(1).first, QStringLiteral("image/png")); QCOMPARE( form.fields().at(0).media().uris().at(1).second, QStringLiteral("cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org")); serializePacket(form, xml); // // test non-const getters // QXmppDataForm::Field mediaField1; mediaField1.mediaSize().setWidth(290); mediaField1.mediaSize().setHeight(80); mediaField1.mediaSources() << QXmppDataForm::MediaSource( QUrl(QStringLiteral("http://www.victim.com/challenges/ocr.jpeg?F3A6292C")), QMimeDatabase().mimeTypeForName(QStringLiteral("image/jpeg"))); mediaField1.mediaSources() << QXmppDataForm::MediaSource( QUrl(QStringLiteral("cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org")), QMimeDatabase().mimeTypeForName(QStringLiteral("image/png"))); QXmppDataForm form2; form2.setType(QXmppDataForm::Form); form2.setFields(QList() << mediaField1); serializePacket(form2, xml); // // test setters // QXmppDataForm::Field mediaField2; mediaField2.setMediaSize(QSize(290, 80)); QVector sources; sources << QXmppDataForm::MediaSource( QUrl(QStringLiteral("http://www.victim.com/challenges/ocr.jpeg?F3A6292C")), QMimeDatabase().mimeTypeForName(QStringLiteral("image/jpeg"))); sources << QXmppDataForm::MediaSource( QUrl(QStringLiteral("cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org")), QMimeDatabase().mimeTypeForName(QStringLiteral("image/png"))); mediaField2.setMediaSources(sources); QXmppDataForm form3; form3.setType(QXmppDataForm::Form); form3.fields().append(mediaField2); serializePacket(form3, xml); // // test compatibility of deprecated methods // QXmppDataForm::Field mediaFieldBefore = mediaField1; mediaField1.setMedia(mediaField1.media()); QCOMPARE(mediaField1, mediaFieldBefore); QXmppDataForm::Field mediaField2Before = mediaField2; mediaField2.setMedia(mediaField2.media()); QCOMPARE(mediaField2, mediaField2Before); } void tst_QXmppDataForm::testMediaSource() { QXmppDataForm::MediaSource source; QCOMPARE(source.uri().toString(), QString()); QCOMPARE(source.contentType(), QMimeType()); source.setUri(QUrl("https://xmpp.org/index.html")); QCOMPARE(source.uri(), QUrl("https://xmpp.org/index.html")); source.setContentType(QMimeDatabase().mimeTypeForName("application/xml")); QCOMPARE(source.contentType(), QMimeDatabase().mimeTypeForName("application/xml")); } QTEST_MAIN(tst_QXmppDataForm) #include "tst_qxmppdataform.moc" qxmpp-1.4.0/tests/qxmppdiscoveryiq/000077500000000000000000000000001402370562100174525ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppdiscoveryiq/tst_qxmppdiscoveryiq.cpp000066400000000000000000000067031402370562100245050ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Jeremy Lainé * Manjeet Dahiya * * 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 "QXmppDiscoveryIq.h" #include "util.h" #include class tst_QXmppDiscoveryIq : public QObject { Q_OBJECT private slots: void testDiscovery(); void testDiscoveryWithForm(); }; void tst_QXmppDiscoveryIq::testDiscovery() { const QByteArray xml( "" "" "" "" "" "" "" "" ""); QXmppDiscoveryIq disco; parsePacket(disco, xml); QCOMPARE(disco.verificationString(), QByteArray::fromBase64("QgayPKawpkPSDYmwT/WM94uAlu0=")); serializePacket(disco, xml); } void tst_QXmppDiscoveryIq::testDiscoveryWithForm() { const QByteArray xml( "" "" "" "" "" "" "" "" "" "" "urn:xmpp:dataforms:softwareinfo" "" "" "ipv4" "ipv6" "" "" "Mac" "" "" "10.5.1" "" "" "Psi" "" "" "0.11" "" "" "" ""); QXmppDiscoveryIq disco; parsePacket(disco, xml); QCOMPARE(disco.verificationString(), QByteArray::fromBase64("q07IKJEyjvHSyhy//CH0CxmKi8w=")); serializePacket(disco, xml); } QTEST_MAIN(tst_QXmppDiscoveryIq) #include "tst_qxmppdiscoveryiq.moc" qxmpp-1.4.0/tests/qxmppentitytimeiq/000077500000000000000000000000001402370562100176365ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppentitytimeiq/tst_qxmppentitytimeiq.cpp000066400000000000000000000047731402370562100250620ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Jeremy Lainé * Manjeet Dahiya * * 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 "QXmppEntityTimeIq.h" #include "util.h" #include class tst_QXmppEntityTimeIq : public QObject { Q_OBJECT private slots: void testEntityTimeGet(); void testEntityTimeResult(); }; void tst_QXmppEntityTimeIq::testEntityTimeGet() { const QByteArray xml("" ""); QXmppEntityTimeIq entityTime; parsePacket(entityTime, xml); QCOMPARE(entityTime.id(), QLatin1String("time_1")); QCOMPARE(entityTime.to(), QLatin1String("juliet@capulet.com/balcony")); QCOMPARE(entityTime.from(), QLatin1String("romeo@montague.net/orchard")); QCOMPARE(entityTime.type(), QXmppIq::Get); serializePacket(entityTime, xml); } void tst_QXmppEntityTimeIq::testEntityTimeResult() { const QByteArray xml( "" "" ""); QXmppEntityTimeIq entityTime; parsePacket(entityTime, xml); QCOMPARE(entityTime.id(), QLatin1String("time_1")); QCOMPARE(entityTime.from(), QLatin1String("juliet@capulet.com/balcony")); QCOMPARE(entityTime.to(), QLatin1String("romeo@montague.net/orchard")); QCOMPARE(entityTime.type(), QXmppIq::Result); QCOMPARE(entityTime.tzo(), -21600); QCOMPARE(entityTime.utc(), QDateTime(QDate(2006, 12, 19), QTime(17, 58, 35), Qt::UTC)); serializePacket(entityTime, xml); } QTEST_MAIN(tst_QXmppEntityTimeIq) #include "tst_qxmppentitytimeiq.moc" qxmpp-1.4.0/tests/qxmpphttpuploadiq/000077500000000000000000000000001402370562100176275ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmpphttpuploadiq/tst_qxmpphttpuploadiq.cpp000066400000000000000000000124301402370562100250310ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Linus Jahn * * 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 "QXmppHttpUploadIq.h" #include "util.h" #include 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( "" "" ""); 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("xml"); QTest::addColumn("isRequest"); QTest::newRow("wrong-stanza") << QByteArray("") << false; QTest::newRow("empty-iq") << QByteArray("") << false; QTest::newRow("wrong-ns") << QByteArray("") << false; QTest::newRow("correct") << QByteArray("") << 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( "" "" "" "
Basic Base64String==
" "
foo=bar; user=romeo
" "
" "" "
" "
"); 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 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 emptyMap; iq.setPutHeaders(emptyMap); QCOMPARE(iq.putHeaders(), emptyMap); } void tst_QXmppHttpUploadIq::testIsSlot_data() { QTest::addColumn("xml"); QTest::addColumn("isSlot"); QTest::newRow("wrong-stanza") << QByteArray("") << false; QTest::newRow("empty-iq") << QByteArray("") << false; QTest::newRow("wrong-ns") << QByteArray("") << false; QTest::newRow("correct") << QByteArray("") << 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" qxmpp-1.4.0/tests/qxmppiceconnection/000077500000000000000000000000001402370562100177315ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppiceconnection/tst_qxmppiceconnection.cpp000066400000000000000000000124541402370562100252430ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppStun.h" #include "util.h" #include class tst_QXmppIceConnection : public QObject { Q_OBJECT private slots: void testBind(); void testBindStun(); void testConnect(); }; void tst_QXmppIceConnection::testBind() { const int componentId = 1024; QXmppLogger logger; logger.setLoggingType(QXmppLogger::StdoutLogging); QXmppIceConnection client; connect(&client, &QXmppLoggable::logMessage, &logger, &QXmppLogger::log); client.setIceControlling(true); client.addComponent(componentId); QXmppIceComponent *component = client.component(componentId); QVERIFY(component); QCOMPARE(client.gatheringState(), QXmppIceConnection::NewGatheringState); client.bind(QXmppIceComponent::discoverAddresses()); QCOMPARE(client.gatheringState(), QXmppIceConnection::CompleteGatheringState); QCOMPARE(client.localCandidates().size(), component->localCandidates().size()); QVERIFY(!client.localCandidates().isEmpty()); const auto &localCandidates = client.localCandidates(); for (const auto &c : localCandidates) { QCOMPARE(c.component(), componentId); QCOMPARE(c.type(), QXmppJingleCandidate::HostType); } } void tst_QXmppIceConnection::testBindStun() { const int componentId = 1024; QXmppLogger logger; logger.setLoggingType(QXmppLogger::StdoutLogging); QHostInfo stunInfo = QHostInfo::fromName("stun.l.google.com"); QVERIFY(!stunInfo.addresses().isEmpty()); QXmppIceConnection client; connect(&client, &QXmppLoggable::logMessage, &logger, &QXmppLogger::log); client.setIceControlling(true); QList> stunServers; for (auto &address : stunInfo.addresses()) { stunServers.push_back({address, 19302}); } client.setStunServers(stunServers); client.addComponent(componentId); QXmppIceComponent *component = client.component(componentId); QVERIFY(component); QCOMPARE(client.gatheringState(), QXmppIceConnection::NewGatheringState); client.bind(QXmppIceComponent::discoverAddresses()); QCOMPARE(client.gatheringState(), QXmppIceConnection::BusyGatheringState); QEventLoop loop; connect(&client, &QXmppIceConnection::gatheringStateChanged, &loop, &QEventLoop::quit); loop.exec(); bool foundReflexive = false; QCOMPARE(client.gatheringState(), QXmppIceConnection::CompleteGatheringState); QCOMPARE(client.localCandidates().size(), component->localCandidates().size()); QVERIFY(!client.localCandidates().isEmpty()); const auto &localCandidates = client.localCandidates(); for (const auto &c : localCandidates) { QCOMPARE(c.component(), componentId); if (c.type() == QXmppJingleCandidate::ServerReflexiveType) foundReflexive = true; else QCOMPARE(c.type(), QXmppJingleCandidate::HostType); } QVERIFY(foundReflexive); } void tst_QXmppIceConnection::testConnect() { const int componentId = 1024; QXmppLogger logger; logger.setLoggingType(QXmppLogger::StdoutLogging); QXmppIceConnection clientL; connect(&clientL, &QXmppLoggable::logMessage, &logger, &QXmppLogger::log); clientL.setIceControlling(true); clientL.addComponent(componentId); clientL.bind(QXmppIceComponent::discoverAddresses()); QXmppIceConnection clientR; connect(&clientR, &QXmppLoggable::logMessage, &logger, &QXmppLogger::log); clientR.setIceControlling(false); clientR.addComponent(componentId); clientR.bind(QXmppIceComponent::discoverAddresses()); // exchange credentials clientL.setRemoteUser(clientR.localUser()); clientL.setRemotePassword(clientR.localPassword()); clientR.setRemoteUser(clientL.localUser()); clientR.setRemotePassword(clientL.localPassword()); // exchange candidates const auto &rLocalCandidates = clientR.localCandidates(); for (const auto &candidate : rLocalCandidates) clientL.addRemoteCandidate(candidate); const auto &lLocalCandidates = clientL.localCandidates(); for (const auto &candidate : lLocalCandidates) clientR.addRemoteCandidate(candidate); // start ICE QEventLoop loop; connect(&clientL, &QXmppIceConnection::connected, &loop, &QEventLoop::quit); connect(&clientR, &QXmppIceConnection::connected, &loop, &QEventLoop::quit); clientL.connectToHost(); clientR.connectToHost(); // check both clients are connected loop.exec(); loop.exec(); QVERIFY(clientL.isConnected()); QVERIFY(clientR.isConnected()); } QTEST_MAIN(tst_QXmppIceConnection) #include "tst_qxmppiceconnection.moc" qxmpp-1.4.0/tests/qxmppiq/000077500000000000000000000000001402370562100155225ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppiq/tst_qxmppiq.cpp000066400000000000000000000037351402370562100206270ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppIq.h" #include "util.h" #include class tst_QXmppIq : public QObject { Q_OBJECT private slots: void testBasic_data(); void testBasic(); }; void tst_QXmppIq::testBasic_data() { QTest::addColumn("xml"); QTest::addColumn("type"); QTest::newRow("get") << QByteArray(R"()") << int(QXmppIq::Get); QTest::newRow("set") << QByteArray(R"()") << int(QXmppIq::Set); QTest::newRow("result") << QByteArray(R"()") << int(QXmppIq::Result); QTest::newRow("error") << QByteArray(R"()") << int(QXmppIq::Error); } void tst_QXmppIq::testBasic() { QFETCH(QByteArray, xml); QFETCH(int, type); QXmppIq iq; parsePacket(iq, xml); QCOMPARE(iq.to(), QString("foo@example.com/QXmpp")); QCOMPARE(iq.from(), QString("bar@example.com/QXmpp")); QCOMPARE(int(iq.type()), type); serializePacket(iq, xml); } QTEST_MAIN(tst_QXmppIq) #include "tst_qxmppiq.moc" qxmpp-1.4.0/tests/qxmppjingleiq/000077500000000000000000000000001402370562100167135ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp000066400000000000000000000505311402370562100232050ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppJingleIq.h" #include "util.h" #include class tst_QXmppJingleIq : public QObject { Q_OBJECT private slots: void testCandidate(); void testContent(); void testContentFingerprint(); void testContentSdp(); void testContentSdpReflexive(); void testContentSdpFingerprint(); void testContentSdpParameters(); void testSession(); void testTerminate(); void testAudioPayloadType(); void testVideoPayloadType(); void testRinging(); }; void tst_QXmppJingleIq::testCandidate() { const QByteArray xml( ""); QXmppJingleCandidate candidate; parsePacket(candidate, xml); QCOMPARE(candidate.foundation(), QLatin1String("1")); QCOMPARE(candidate.generation(), 0); QCOMPARE(candidate.id(), QLatin1String("el0747fg11")); QCOMPARE(candidate.host(), QHostAddress("10.0.1.1")); QCOMPARE(candidate.network(), 1); QCOMPARE(candidate.port(), quint16(8998)); QCOMPARE(candidate.priority(), 2130706431); QCOMPARE(candidate.protocol(), QLatin1String("udp")); QCOMPARE(candidate.type(), QXmppJingleCandidate::HostType); serializePacket(candidate, xml); }; void tst_QXmppJingleIq::testContent() { const QByteArray xml( "" "" "" "" "" "" "" "" "" "" "" "" "" ""); QXmppJingleIq::Content content; parsePacket(content, xml); QCOMPARE(content.creator(), QLatin1String("initiator")); QCOMPARE(content.name(), QLatin1String("voice")); QCOMPARE(content.descriptionMedia(), QLatin1String("audio")); QCOMPARE(content.descriptionSsrc(), quint32(0)); QCOMPARE(content.payloadTypes().size(), 6); QCOMPARE(content.payloadTypes()[0].id(), quint8(96)); QCOMPARE(content.payloadTypes()[1].id(), quint8(97)); QCOMPARE(content.payloadTypes()[2].id(), quint8(18)); QCOMPARE(content.payloadTypes()[3].id(), quint8(0)); QCOMPARE(content.payloadTypes()[4].id(), quint8(103)); QCOMPARE(content.payloadTypes()[5].id(), quint8(98)); QCOMPARE(content.transportCandidates().size(), 2); QCOMPARE(content.transportCandidates()[0].component(), 1); QCOMPARE(content.transportCandidates()[0].foundation(), QLatin1String("1")); QCOMPARE(content.transportCandidates()[0].host(), QHostAddress("10.0.1.1")); QCOMPARE(content.transportCandidates()[0].port(), quint16(8998)); QCOMPARE(content.transportCandidates()[0].priority(), 2130706431); QCOMPARE(content.transportCandidates()[0].protocol(), QLatin1String("udp")); QCOMPARE(content.transportCandidates()[0].type(), QXmppJingleCandidate::HostType); QCOMPARE(content.transportCandidates()[1].component(), 1); QCOMPARE(content.transportCandidates()[1].foundation(), QLatin1String("2")); QCOMPARE(content.transportCandidates()[1].host(), QHostAddress("192.0.2.3")); QCOMPARE(content.transportCandidates()[1].port(), quint16(45664)); QCOMPARE(content.transportCandidates()[1].priority(), 1694498815); QCOMPARE(content.transportCandidates()[1].protocol(), QLatin1String("udp")); QCOMPARE(content.transportCandidates()[1].type(), QXmppJingleCandidate::ServerReflexiveType); QCOMPARE(content.transportUser(), QLatin1String("8hhy")); QCOMPARE(content.transportPassword(), QLatin1String("asd88fgpdd777uzjYhagZg")); serializePacket(content, xml); } void tst_QXmppJingleIq::testContentFingerprint() { const QByteArray xml( "" "" "" "" "" "" "" "02:1A:CC:54:27:AB:EB:9C:53:3F:3E:4B:65:2E:7D:46:3F:54:42:CD:54:F1:7A:03:A2:7D:F9:B0:7F:46:19:B2" "" "" ""); QXmppJingleIq::Content content; parsePacket(content, xml); QCOMPARE(content.creator(), QLatin1String("initiator")); QCOMPARE(content.name(), QLatin1String("voice")); QCOMPARE(content.descriptionMedia(), QLatin1String("audio")); QCOMPARE(content.descriptionSsrc(), quint32(0)); QCOMPARE(content.payloadTypes().size(), 1); QCOMPARE(content.payloadTypes()[0].id(), quint8(0)); QCOMPARE(content.transportCandidates().size(), 1); QCOMPARE(content.transportCandidates()[0].component(), 1); QCOMPARE(content.transportCandidates()[0].foundation(), QLatin1String("1")); QCOMPARE(content.transportCandidates()[0].host(), QHostAddress("10.0.1.1")); QCOMPARE(content.transportCandidates()[0].port(), quint16(8998)); QCOMPARE(content.transportCandidates()[0].priority(), 2130706431); QCOMPARE(content.transportCandidates()[0].protocol(), QLatin1String("udp")); QCOMPARE(content.transportCandidates()[0].type(), QXmppJingleCandidate::HostType); QCOMPARE(content.transportUser(), QLatin1String("8hhy")); QCOMPARE(content.transportPassword(), QLatin1String("asd88fgpdd777uzjYhagZg")); QCOMPARE(content.transportFingerprint(), QByteArray::fromHex("021acc5427abeb9c533f3e4b652e7d463f5442cd54f17a03a27df9b07f4619b2")); QCOMPARE(content.transportFingerprintHash(), QLatin1String("sha-256")); QCOMPARE(content.transportFingerprintSetup(), QLatin1String("actpass")); serializePacket(content, xml); } void tst_QXmppJingleIq::testContentSdp() { const QString sdp( "m=audio 8998 RTP/AVP 96 97 18 0 103 98\r\n" "c=IN IP4 10.0.1.1\r\n" "a=rtpmap:96 speex/16000\r\n" "a=rtpmap:97 speex/8000\r\n" "a=rtpmap:18 G729/0\r\n" "a=rtpmap:0 PCMU/0\r\n" "a=rtpmap:103 L16/16000/2\r\n" "a=rtpmap:98 x-ISAC/8000\r\n" "a=candidate:1 1 udp 2130706431 10.0.1.1 8998 typ host generation 0\r\n" "a=candidate:2 1 udp 1694498815 192.0.2.3 45664 typ host generation 0\r\n" "a=ice-ufrag:8hhy\r\n" "a=ice-pwd:asd88fgpdd777uzjYhagZg\r\n"); QXmppJingleIq::Content content; QVERIFY(content.parseSdp(sdp)); QCOMPARE(content.descriptionMedia(), QLatin1String("audio")); QCOMPARE(content.descriptionSsrc(), quint32(0)); QCOMPARE(content.payloadTypes().size(), 6); QCOMPARE(content.payloadTypes()[0].id(), quint8(96)); QCOMPARE(content.payloadTypes()[1].id(), quint8(97)); QCOMPARE(content.payloadTypes()[2].id(), quint8(18)); QCOMPARE(content.payloadTypes()[3].id(), quint8(0)); QCOMPARE(content.payloadTypes()[4].id(), quint8(103)); QCOMPARE(content.payloadTypes()[5].id(), quint8(98)); QCOMPARE(content.transportCandidates().size(), 2); QCOMPARE(content.transportCandidates()[0].component(), 1); QCOMPARE(content.transportCandidates()[0].foundation(), QLatin1String("1")); QCOMPARE(content.transportCandidates()[0].host(), QHostAddress("10.0.1.1")); QCOMPARE(content.transportCandidates()[0].port(), quint16(8998)); QCOMPARE(content.transportCandidates()[0].priority(), 2130706431); QCOMPARE(content.transportCandidates()[0].protocol(), QLatin1String("udp")); QCOMPARE(content.transportCandidates()[0].type(), QXmppJingleCandidate::HostType); QCOMPARE(content.transportCandidates()[1].component(), 1); QCOMPARE(content.transportCandidates()[1].foundation(), QLatin1String("2")); QCOMPARE(content.transportCandidates()[1].host(), QHostAddress("192.0.2.3")); QCOMPARE(content.transportCandidates()[1].port(), quint16(45664)); QCOMPARE(content.transportCandidates()[1].priority(), 1694498815); QCOMPARE(content.transportCandidates()[1].protocol(), QLatin1String("udp")); QCOMPARE(content.transportCandidates()[1].type(), QXmppJingleCandidate::HostType); QCOMPARE(content.transportUser(), QLatin1String("8hhy")); QCOMPARE(content.transportPassword(), QLatin1String("asd88fgpdd777uzjYhagZg")); QCOMPARE(content.toSdp(), sdp); } void tst_QXmppJingleIq::testContentSdpReflexive() { const QString sdp( "m=audio 45664 RTP/AVP 96 97 18 0 103 98\r\n" "c=IN IP4 192.0.2.3\r\n" "a=rtpmap:96 speex/16000\r\n" "a=rtpmap:97 speex/8000\r\n" "a=rtpmap:18 G729/0\r\n" "a=rtpmap:0 PCMU/0\r\n" "a=rtpmap:103 L16/16000/2\r\n" "a=rtpmap:98 x-ISAC/8000\r\n" "a=candidate:1 1 udp 2130706431 10.0.1.1 8998 typ host generation 0\r\n" "a=candidate:2 1 udp 1694498815 192.0.2.3 45664 typ srflx generation 0\r\n" "a=ice-ufrag:8hhy\r\n" "a=ice-pwd:asd88fgpdd777uzjYhagZg\r\n"); QXmppJingleIq::Content content; QVERIFY(content.parseSdp(sdp)); QCOMPARE(content.descriptionMedia(), QLatin1String("audio")); QCOMPARE(content.descriptionSsrc(), quint32(0)); QCOMPARE(content.payloadTypes().size(), 6); QCOMPARE(content.payloadTypes()[0].id(), quint8(96)); QCOMPARE(content.payloadTypes()[1].id(), quint8(97)); QCOMPARE(content.payloadTypes()[2].id(), quint8(18)); QCOMPARE(content.payloadTypes()[3].id(), quint8(0)); QCOMPARE(content.payloadTypes()[4].id(), quint8(103)); QCOMPARE(content.payloadTypes()[5].id(), quint8(98)); QCOMPARE(content.transportCandidates().size(), 2); QCOMPARE(content.transportCandidates()[0].component(), 1); QCOMPARE(content.transportCandidates()[0].foundation(), QLatin1String("1")); QCOMPARE(content.transportCandidates()[0].host(), QHostAddress("10.0.1.1")); QCOMPARE(content.transportCandidates()[0].port(), quint16(8998)); QCOMPARE(content.transportCandidates()[0].priority(), 2130706431); QCOMPARE(content.transportCandidates()[0].protocol(), QLatin1String("udp")); QCOMPARE(content.transportCandidates()[0].type(), QXmppJingleCandidate::HostType); QCOMPARE(content.transportCandidates()[1].component(), 1); QCOMPARE(content.transportCandidates()[1].foundation(), QLatin1String("2")); QCOMPARE(content.transportCandidates()[1].host(), QHostAddress("192.0.2.3")); QCOMPARE(content.transportCandidates()[1].port(), quint16(45664)); QCOMPARE(content.transportCandidates()[1].priority(), 1694498815); QCOMPARE(content.transportCandidates()[1].protocol(), QLatin1String("udp")); QCOMPARE(content.transportCandidates()[1].type(), QXmppJingleCandidate::ServerReflexiveType); QCOMPARE(content.transportUser(), QLatin1String("8hhy")); QCOMPARE(content.transportPassword(), QLatin1String("asd88fgpdd777uzjYhagZg")); QCOMPARE(content.toSdp(), sdp); } void tst_QXmppJingleIq::testContentSdpFingerprint() { const QString sdp( "m=audio 8998 RTP/AVP 96 100\r\n" "c=IN IP4 10.0.1.1\r\n" "a=rtpmap:96 speex/16000\r\n" "a=fmtp:96 cng=on; vbr=on\r\n" "a=rtpmap:100 telephone-event/8000\r\n" "a=fmtp:100 0-15,66,70\r\n" "a=candidate:1 1 udp 2130706431 10.0.1.1 8998 typ host generation 0\r\n" "a=fingerprint:sha-256 02:1A:CC:54:27:AB:EB:9C:53:3F:3E:4B:65:2E:7D:46:3F:54:42:CD:54:F1:7A:03:A2:7D:F9:B0:7F:46:19:B2\r\n" "a=setup:actpass\r\n"); QXmppJingleIq::Content content; QVERIFY(content.parseSdp(sdp)); QCOMPARE(content.descriptionMedia(), QLatin1String("audio")); QCOMPARE(content.descriptionSsrc(), quint32(0)); QCOMPARE(content.payloadTypes().size(), 2); QCOMPARE(content.payloadTypes()[0].id(), quint8(96)); QCOMPARE(content.payloadTypes()[0].parameters().value("vbr"), QLatin1String("on")); QCOMPARE(content.payloadTypes()[0].parameters().value("cng"), QLatin1String("on")); QCOMPARE(content.payloadTypes()[1].id(), quint8(100)); QCOMPARE(content.payloadTypes()[1].parameters().value("events"), QLatin1String("0-15,66,70")); QCOMPARE(content.transportCandidates().size(), 1); QCOMPARE(content.transportCandidates()[0].component(), 1); QCOMPARE(content.transportCandidates()[0].foundation(), QLatin1String("1")); QCOMPARE(content.transportCandidates()[0].host(), QHostAddress("10.0.1.1")); QCOMPARE(content.transportCandidates()[0].port(), quint16(8998)); QCOMPARE(content.transportCandidates()[0].priority(), 2130706431); QCOMPARE(content.transportCandidates()[0].protocol(), QLatin1String("udp")); QCOMPARE(content.transportCandidates()[0].type(), QXmppJingleCandidate::HostType); QCOMPARE(content.transportFingerprint(), QByteArray::fromHex("021acc5427abeb9c533f3e4b652e7d463f5442cd54f17a03a27df9b07f4619b2")); QCOMPARE(content.transportFingerprintHash(), QLatin1String("sha-256")); QCOMPARE(content.transportFingerprintSetup(), QLatin1String("actpass")); QCOMPARE(content.toSdp(), sdp); } void tst_QXmppJingleIq::testContentSdpParameters() { const QString sdp( "m=audio 8998 RTP/AVP 96 100\r\n" "c=IN IP4 10.0.1.1\r\n" "a=rtpmap:96 speex/16000\r\n" "a=fmtp:96 cng=on; vbr=on\r\n" "a=rtpmap:100 telephone-event/8000\r\n" "a=fmtp:100 0-15,66,70\r\n" "a=candidate:1 1 udp 2130706431 10.0.1.1 8998 typ host generation 0\r\n"); QXmppJingleIq::Content content; QVERIFY(content.parseSdp(sdp)); QCOMPARE(content.descriptionMedia(), QLatin1String("audio")); QCOMPARE(content.descriptionSsrc(), quint32(0)); QCOMPARE(content.payloadTypes().size(), 2); QCOMPARE(content.payloadTypes()[0].id(), quint8(96)); QCOMPARE(content.payloadTypes()[0].parameters().value("vbr"), QLatin1String("on")); QCOMPARE(content.payloadTypes()[0].parameters().value("cng"), QLatin1String("on")); QCOMPARE(content.payloadTypes()[1].id(), quint8(100)); QCOMPARE(content.payloadTypes()[1].parameters().value("events"), QLatin1String("0-15,66,70")); QCOMPARE(content.transportCandidates().size(), 1); QCOMPARE(content.transportCandidates()[0].component(), 1); QCOMPARE(content.transportCandidates()[0].foundation(), QLatin1String("1")); QCOMPARE(content.transportCandidates()[0].host(), QHostAddress("10.0.1.1")); QCOMPARE(content.transportCandidates()[0].port(), quint16(8998)); QCOMPARE(content.transportCandidates()[0].priority(), 2130706431); QCOMPARE(content.transportCandidates()[0].protocol(), QLatin1String("udp")); QCOMPARE(content.transportCandidates()[0].type(), QXmppJingleCandidate::HostType); QCOMPARE(content.toSdp(), sdp); } void tst_QXmppJingleIq::testSession() { const QByteArray xml( "" "" "" "" "" "" "" ""); QXmppJingleIq session; parsePacket(session, xml); QCOMPARE(session.action(), QXmppJingleIq::SessionInitiate); QCOMPARE(session.initiator(), QLatin1String("romeo@montague.lit/orchard")); QCOMPARE(session.sid(), QLatin1String("a73sjjvkla37jfea")); QCOMPARE(session.contents().size(), 1); QCOMPARE(session.contents()[0].creator(), QLatin1String("initiator")); QCOMPARE(session.contents()[0].name(), QLatin1String("this-is-a-stub")); QCOMPARE(session.reason().text(), QString()); QCOMPARE(session.reason().type(), QXmppJingleIq::Reason::None); serializePacket(session, xml); } void tst_QXmppJingleIq::testTerminate() { const QByteArray xml( "" "" "" "" "" "" ""); QXmppJingleIq session; parsePacket(session, xml); QCOMPARE(session.action(), QXmppJingleIq::SessionTerminate); QCOMPARE(session.initiator(), QString()); QCOMPARE(session.sid(), QLatin1String("a73sjjvkla37jfea")); QCOMPARE(session.reason().text(), QString()); QCOMPARE(session.reason().type(), QXmppJingleIq::Reason::Success); serializePacket(session, xml); } void tst_QXmppJingleIq::testAudioPayloadType() { const QByteArray xml(R"()"); QXmppJinglePayloadType payload; parsePacket(payload, xml); QCOMPARE(payload.id(), static_cast(103)); QCOMPARE(payload.name(), QLatin1String("L16")); QCOMPARE(payload.channels(), static_cast(2)); QCOMPARE(payload.clockrate(), 16000u); serializePacket(payload, xml); } void tst_QXmppJingleIq::testVideoPayloadType() { const QByteArray xml( "" "" "" ""); QXmppJinglePayloadType payload; parsePacket(payload, xml); QCOMPARE(payload.id(), static_cast(98)); QCOMPARE(payload.name(), QLatin1String("theora")); QCOMPARE(payload.clockrate(), 90000u); QCOMPARE(payload.parameters().size(), 2); QCOMPARE(payload.parameters().value("height"), QLatin1String("768")); QCOMPARE(payload.parameters().value("width"), QLatin1String("1024")); serializePacket(payload, xml); } void tst_QXmppJingleIq::testRinging() { const QByteArray xml( "" "" "" "" ""); QXmppJingleIq iq; parsePacket(iq, xml); QCOMPARE(iq.ringing(), true); serializePacket(iq, xml); } QTEST_MAIN(tst_QXmppJingleIq) #include "tst_qxmppjingleiq.moc" qxmpp-1.4.0/tests/qxmppmammanager/000077500000000000000000000000001402370562100172165ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppmammanager/tst_qxmppmammanager.cpp000066400000000000000000000230731402370562100240140ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Niels Ole Salscheider * * 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 "QXmppMamManager.h" #include "QXmppMessage.h" #include "util.h" #include class QXmppMamTestHelper : public QObject { Q_OBJECT public slots: void archivedMessageReceived(const QString &queryId, const QXmppMessage &message); void resultsRecieved(const QString &queryId, const QXmppResultSetReply &resultSetReply, bool complete); public: QXmppMessage m_expectedMessage; QXmppResultSetReply m_expectedResultSetReply; QString m_expectedQueryId; bool m_expectedComplete; bool m_signalTriggered; void compareMessages(const QXmppMessage &lhs, const QXmppMessage &rhs) const; void compareResultSetReplys(const QXmppResultSetReply &lhs, const QXmppResultSetReply &rhs) const; }; class tst_QXmppMamManager : public QObject { Q_OBJECT private slots: void initTestCase(); void testHandleStanza_data(); void testHandleStanza(); void testHandleResultIq_data(); void testHandleResultIq(); private: QXmppMamTestHelper m_helper; QXmppMamManager m_manager; }; void tst_QXmppMamManager::initTestCase() { connect(&m_manager, &QXmppMamManager::archivedMessageReceived, &m_helper, &QXmppMamTestHelper::archivedMessageReceived); connect(&m_manager, &QXmppMamManager::resultsRecieved, &m_helper, &QXmppMamTestHelper::resultsRecieved); } void tst_QXmppMamManager::testHandleStanza_data() { QTest::addColumn("xml"); QTest::addColumn("accept"); QTest::addColumn("expectedMessage"); QTest::addColumn("expectedQueryId"); QTest::newRow("stanza1") << QByteArray("" "" "" "" "" "Call me but love, and I'll be new baptized; Henceforth I never will be Romeo." "" "" "" "") << true << QByteArray("" "" "Call me but love, and I'll be new baptized; Henceforth I never will be Romeo." "") << QString("f27"); QTest::newRow("stanza2") << QByteArray("" "" "" "" "" "What man art thou that thus bescreen'd in night so stumblest on my counsel?" "" "" "" "") << false << QByteArray() << QString(); QTest::newRow("stanza3") << QByteArray( "" "" "" "" "Call me but love, and I'll be new baptized; Henceforth I never will be Romeo." "" "" "") << false << QByteArray() << QString(); } void tst_QXmppMamManager::testHandleStanza() { QFETCH(QByteArray, xml); QFETCH(bool, accept); QFETCH(QByteArray, expectedMessage); QFETCH(QString, expectedQueryId); QDomDocument doc; QCOMPARE(doc.setContent(xml, true), true); QDomElement element = doc.documentElement(); m_helper.m_signalTriggered = false; m_helper.m_expectedMessage = QXmppMessage(); if (!expectedMessage.isEmpty()) { parsePacket(m_helper.m_expectedMessage, expectedMessage); } m_helper.m_expectedQueryId = expectedQueryId; bool accepted = m_manager.handleStanza(element); QCOMPARE(accepted, accept); QCOMPARE(m_helper.m_signalTriggered, accept); } void tst_QXmppMamManager::testHandleResultIq_data() { QTest::addColumn("xml"); QTest::addColumn("accept"); QTest::addColumn("expectedResultSetReply"); QTest::addColumn("expectedComplete"); QTest::newRow("stanza1") << QByteArray("" "" "" "28482-98726-73623" "09af3-cc343-b409f" "" "" "") << true << QByteArray("" "28482-98726-73623" "09af3-cc343-b409f" "") << false; QTest::newRow("stanza2") << QByteArray("" "" "" "28482-98726-73623" "09af3-cc343-b409f" "" "" "") << true << QByteArray("" "28482-98726-73623" "09af3-cc343-b409f" "") << true; } void tst_QXmppMamManager::testHandleResultIq() { QFETCH(QByteArray, xml); QFETCH(bool, accept); QFETCH(QByteArray, expectedResultSetReply); QFETCH(bool, expectedComplete); QDomDocument doc; QCOMPARE(doc.setContent(xml, true), true); QDomElement element = doc.documentElement(); m_helper.m_signalTriggered = false; m_helper.m_expectedResultSetReply = QXmppResultSetReply(); if (!expectedResultSetReply.isEmpty()) { parsePacket(m_helper.m_expectedResultSetReply, expectedResultSetReply); } m_helper.m_expectedComplete = expectedComplete; bool accepted = m_manager.handleStanza(element); QCOMPARE(accepted, accept); QCOMPARE(m_helper.m_signalTriggered, accept); } void QXmppMamTestHelper::archivedMessageReceived(const QString &queryId, const QXmppMessage &message) { m_signalTriggered = true; compareMessages(message, m_expectedMessage); QCOMPARE(queryId, m_expectedQueryId); } void QXmppMamTestHelper::resultsRecieved(const QString &queryId, const QXmppResultSetReply &resultSetReply, bool complete) { Q_UNUSED(queryId); m_signalTriggered = true; compareResultSetReplys(resultSetReply, m_expectedResultSetReply); QCOMPARE(complete, m_expectedComplete); } void QXmppMamTestHelper::compareMessages(const QXmppMessage &lhs, const QXmppMessage &rhs) const { QCOMPARE(lhs.body(), rhs.body()); QCOMPARE(lhs.from(), rhs.from()); QCOMPARE(lhs.id(), rhs.id()); QCOMPARE(lhs.isAttentionRequested(), rhs.isAttentionRequested()); QCOMPARE(lhs.isMarkable(), rhs.isMarkable()); QCOMPARE(lhs.isPrivate(), rhs.isPrivate()); QCOMPARE(lhs.isReceiptRequested(), rhs.isReceiptRequested()); QCOMPARE(lhs.lang(), rhs.lang()); QCOMPARE(lhs.to(), rhs.to()); QCOMPARE(lhs.thread(), rhs.thread()); QCOMPARE(lhs.stamp(), rhs.stamp()); QCOMPARE(lhs.type(), rhs.type()); } void QXmppMamTestHelper::compareResultSetReplys(const QXmppResultSetReply &lhs, const QXmppResultSetReply &rhs) const { QCOMPARE(lhs.first(), rhs.first()); QCOMPARE(lhs.last(), rhs.last()); QCOMPARE(lhs.count(), rhs.count()); QCOMPARE(lhs.index(), rhs.index()); QCOMPARE(lhs.isNull(), rhs.isNull()); } QTEST_MAIN(tst_QXmppMamManager) #include "tst_qxmppmammanager.moc" qxmpp-1.4.0/tests/qxmppmessage/000077500000000000000000000000001402370562100165355ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppmessage/tst_qxmppmessage.cpp000066400000000000000000001251731402370562100226560ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Jeremy Lainé * Manjeet Dahiya * * 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 "QXmppBitsOfBinaryContentId.h" #include "QXmppBitsOfBinaryDataList.h" #include "QXmppMessage.h" #include "QXmppMixInvitation.h" #include #include "util.h" #include class tst_QXmppMessage : public QObject { Q_OBJECT private slots: void testBasic_data(); void testBasic(); void testIsXmppStanza(); void testUnknownXExtension(); void testMessageAttention(); void testMessageReceipt(); void testDelay_data(); void testDelay(); void testDelayWithMultipleStamp(); void testExtendedAddresses(); void testMucInvitation(); void testState_data(); void testState(); void testXhtml(); void testSubextensions(); void testChatMarkers(); void testPrivateMessage(); void testOutOfBandUrl(); void testMessageCorrect(); void testMessageAttaching(); void testMix(); void testEme(); void testSpoiler(); void testProcessingHints(); void testBobData(); void testFallbackIndication(); void testStanzaIds(); void testSlashMe_data(); void testSlashMe(); void testMixInvitation(); }; void tst_QXmppMessage::testBasic_data() { QTest::addColumn("xml"); QTest::addColumn("type"); QTest::addColumn("body"); QTest::addColumn("subject"); QTest::addColumn("thread"); QTest::addColumn("parentThread"); QTest::newRow("error") << QByteArray(R"()") << int(QXmppMessage::Error) << QString() << QString() << QString() << QString(); QTest::newRow("normal") << QByteArray(R"()") << int(QXmppMessage::Normal) << QString() << QString() << QString() << QString(); QTest::newRow("chat") << QByteArray(R"()") << int(QXmppMessage::Chat) << QString() << QString() << QString() << QString(); QTest::newRow("groupchat") << QByteArray(R"()") << int(QXmppMessage::GroupChat) << QString() << QString() << QString() << QString(); QTest::newRow("headline") << QByteArray(R"()") << int(QXmppMessage::Headline) << QString() << QString() << QString() << QString(); QTest::newRow("no-parent-thread") << QByteArray("" "test subject" "test body & stuff" "0e3141cd80894871a68e6fe6b1ec56fa" "") << int(QXmppMessage::Normal) << "test body & stuff" << "test subject" << "0e3141cd80894871a68e6fe6b1ec56fa" << QString(); QTest::newRow("full") << QByteArray("" "test subject" "test body & stuff" "0e3141cd80894871a68e6fe6b1ec56fa" "") << int(QXmppMessage::Normal) << "test body & stuff" << "test subject" << "0e3141cd80894871a68e6fe6b1ec56fa" << "e0ffe42b28561960c6b12b944a092794b9683a38"; } void tst_QXmppMessage::testBasic() { QFETCH(QByteArray, xml); QFETCH(int, type); QFETCH(QString, body); QFETCH(QString, subject); QFETCH(QString, thread); QFETCH(QString, parentThread); QXmppMessage message; parsePacket(message, xml); QCOMPARE(message.to(), QString("foo@example.com/QXmpp")); QCOMPARE(message.from(), QString("bar@example.com/QXmpp")); QVERIFY(message.extendedAddresses().isEmpty()); QCOMPARE(int(message.type()), type); QCOMPARE(message.body(), body); QCOMPARE(message.subject(), subject); QCOMPARE(message.thread(), thread); QCOMPARE(message.parentThread(), parentThread); QCOMPARE(message.state(), QXmppMessage::None); QCOMPARE(message.isAttentionRequested(), false); 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)); QCOMPARE(message.bitsOfBinaryData(), QXmppBitsOfBinaryDataList()); QVERIFY(!message.isFallback()); QVERIFY(message.stanzaId().isNull()); QVERIFY(message.stanzaIdBy().isNull()); QVERIFY(message.originId().isNull()); message = QXmppMessage(); message.setTo(QStringLiteral("foo@example.com/QXmpp")); message.setFrom(QStringLiteral("bar@example.com/QXmpp")); message.setType(QXmppMessage::Type(type)); message.setBody(body); message.setSubject(subject); message.setThread(thread); message.setParentThread(parentThread); serializePacket(message, xml); } void tst_QXmppMessage::testIsXmppStanza() { QXmppMessage msg; QVERIFY(msg.isXmppStanza()); } void tst_QXmppMessage::testUnknownXExtension() { const QByteArray xml( "" "" ""); QXmppMessage message; parsePacket(message, xml); serializePacket(message, xml); } void tst_QXmppMessage::testMessageAttention() { const QByteArray xml( "" "" ""); QXmppMessage message; // parsing parsePacket(message, xml); QCOMPARE(message.to(), QString("foo@example.com/QXmpp")); QCOMPARE(message.from(), QString("bar@example.com/QXmpp")); QVERIFY(message.extendedAddresses().isEmpty()); QCOMPARE(message.type(), QXmppMessage::Normal); QCOMPARE(message.body(), QString()); QCOMPARE(message.isAttentionRequested(), true); QCOMPARE(message.isReceiptRequested(), false); QCOMPARE(message.receiptId(), QString()); // serialization message = QXmppMessage(QStringLiteral("bar@example.com/QXmpp"), QStringLiteral("foo@example.com/QXmpp")); message.setType(QXmppMessage::Normal); message.setAttentionRequested(true); serializePacket(message, xml); } void tst_QXmppMessage::testMessageReceipt() { const QByteArray xml( "" "My lord, dispatch; read o'er these articles." "" ""); QXmppMessage message; // parsing parsePacket(message, xml); QCOMPARE(message.id(), QString("richard2-4.1.247")); QCOMPARE(message.to(), QString("kingrichard@royalty.england.lit/throne")); QCOMPARE(message.from(), QString("northumberland@shakespeare.lit/westminster")); QVERIFY(message.extendedAddresses().isEmpty()); QCOMPARE(message.type(), QXmppMessage::Normal); QCOMPARE(message.body(), QString("My lord, dispatch; read o'er these articles.")); QCOMPARE(message.isAttentionRequested(), false); QCOMPARE(message.isReceiptRequested(), true); QCOMPARE(message.receiptId(), QString()); // serialization message = QXmppMessage(); message.setId(QStringLiteral("richard2-4.1.247")); message.setTo(QStringLiteral("kingrichard@royalty.england.lit/throne")); message.setFrom(QStringLiteral("northumberland@shakespeare.lit/westminster")); message.setType(QXmppMessage::Normal); message.setBody(QStringLiteral("My lord, dispatch; read o'er these articles.")); message.setReceiptRequested(true); serializePacket(message, xml); const QByteArray receiptXml( "" "" ""); QXmppMessage receipt; parsePacket(receipt, receiptXml); QCOMPARE(receipt.id(), QString("bi29sg183b4v")); QCOMPARE(receipt.to(), QString("northumberland@shakespeare.lit/westminster")); QCOMPARE(receipt.from(), QString("kingrichard@royalty.england.lit/throne")); QVERIFY(receipt.extendedAddresses().isEmpty()); QCOMPARE(receipt.type(), QXmppMessage::Normal); QCOMPARE(receipt.body(), QString()); QCOMPARE(receipt.isAttentionRequested(), false); QCOMPARE(receipt.isReceiptRequested(), false); QCOMPARE(receipt.receiptId(), QString("richard2-4.1.247")); receipt = QXmppMessage(); receipt.setId(QStringLiteral("bi29sg183b4v")); receipt.setTo(QStringLiteral("northumberland@shakespeare.lit/westminster")); receipt.setFrom(QStringLiteral("kingrichard@royalty.england.lit/throne")); receipt.setType(QXmppMessage::Normal); receipt.setReceiptId(QStringLiteral("richard2-4.1.247")); serializePacket(receipt, receiptXml); const QByteArray oldXml( "" "" ""); QXmppMessage old; parsePacket(old, oldXml); QCOMPARE(old.id(), QString("richard2-4.1.247")); QCOMPARE(old.to(), QString("northumberland@shakespeare.lit/westminster")); QCOMPARE(old.from(), QString("kingrichard@royalty.england.lit/throne")); QVERIFY(old.extendedAddresses().isEmpty()); QCOMPARE(old.type(), QXmppMessage::Normal); QCOMPARE(old.body(), QString()); QCOMPARE(old.isAttentionRequested(), false); QCOMPARE(old.isReceiptRequested(), false); QCOMPARE(old.receiptId(), QString("richard2-4.1.247")); // test that an ID is generated QXmppMessage message2; QVERIFY(message2.id().isNull()); message2.setReceiptRequested(true); QVERIFY(!message2.id().isEmpty()); } void tst_QXmppMessage::testDelay_data() { QTest::addColumn("xml"); QTest::addColumn("stamp"); QTest::newRow("delay") << QByteArray("" "" "") << QDateTime(QDate(2010, 06, 29), QTime(8, 23, 6), Qt::UTC); QTest::newRow("legacy") << QByteArray("" "" "") << QDateTime(QDate(2010, 06, 29), QTime(8, 23, 6), Qt::UTC); } void tst_QXmppMessage::testDelay() { QFETCH(QByteArray, xml); QFETCH(QDateTime, stamp); QXmppMessage message; parsePacket(message, xml); QCOMPARE(message.stamp(), stamp); serializePacket(message, xml); } void tst_QXmppMessage::testDelayWithMultipleStamp() { // the XEP-0203 should override XEP-0091's value since XEP-0091 was no more standard protocol QByteArray xml( "" "" "" ""); QByteArray resultXml( "" "" ""); QXmppMessage message; parsePacket(message, xml); qDebug() << message.stamp(); QCOMPARE(message.stamp(), QDateTime(QDate(2010, 06, 29), QTime(8, 23, 6, 123), Qt::UTC)); serializePacket(message, resultXml); } void tst_QXmppMessage::testExtendedAddresses() { QByteArray xml( "" "" "
" "
" "" ""); QXmppMessage message; parsePacket(message, xml); QCOMPARE(message.extendedAddresses().size(), 2); QCOMPARE(message.extendedAddresses()[0].description(), QLatin1String("Joe Hildebrand")); QCOMPARE(message.extendedAddresses()[0].jid(), QLatin1String("hildjj@jabber.org/Work")); QCOMPARE(message.extendedAddresses()[0].type(), QLatin1String("to")); QCOMPARE(message.extendedAddresses()[1].description(), QLatin1String("Jeremie Miller")); QCOMPARE(message.extendedAddresses()[1].jid(), QLatin1String("jer@jabber.org/Home")); QCOMPARE(message.extendedAddresses()[1].type(), QLatin1String("cc")); serializePacket(message, xml); } void tst_QXmppMessage::testMucInvitation() { QByteArray xml( "" "" ""); QXmppMessage message; parsePacket(message, xml); QCOMPARE(message.mucInvitationJid(), QLatin1String("darkcave@macbeth.shakespeare.lit")); QCOMPARE(message.mucInvitationPassword(), QLatin1String("cauldronburn")); QCOMPARE(message.mucInvitationReason(), QLatin1String("Hey Hecate, this is the place for all good witches!")); message = QXmppMessage(); message.setTo(QStringLiteral("hecate@shakespeare.lit")); message.setFrom(QStringLiteral("crone1@shakespeare.lit/desktop")); message.setType(QXmppMessage::Normal); message.setMucInvitationJid(QStringLiteral("darkcave@macbeth.shakespeare.lit")); message.setMucInvitationPassword(QStringLiteral("cauldronburn")); message.setMucInvitationReason(QStringLiteral("Hey Hecate, this is the place for all good witches!")); serializePacket(message, xml); } void tst_QXmppMessage::testState_data() { QTest::addColumn("xml"); QTest::addColumn("state"); QTest::newRow("none") << QByteArray("") << int(QXmppMessage::None); QTest::newRow("active") << QByteArray(R"()") << int(QXmppMessage::Active); QTest::newRow("inactive") << QByteArray(R"()") << int(QXmppMessage::Inactive); QTest::newRow("gone") << QByteArray(R"()") << int(QXmppMessage::Gone); QTest::newRow("composing") << QByteArray(R"()") << int(QXmppMessage::Composing); QTest::newRow("paused") << QByteArray(R"()") << int(QXmppMessage::Paused); } void tst_QXmppMessage::testState() { QFETCH(QByteArray, xml); QFETCH(int, state); QXmppMessage message; parsePacket(message, xml); QCOMPARE(int(message.state()), state); message = QXmppMessage(); message.setType(QXmppMessage::Normal); message.setState(QXmppMessage::State(state)); serializePacket(message, xml); } void tst_QXmppMessage::testXhtml() { const QByteArray xml("" "hi!" "" "" "

hi!

" "" "" "
"); QXmppMessage message; parsePacket(message, xml); QCOMPARE(message.xhtml(), QLatin1String("

hi!

")); message = QXmppMessage(); message.setBody(QStringLiteral("hi!")); message.setType(QXmppMessage::Normal); message.setXhtml(QStringLiteral("

hi!

")); serializePacket(message, xml); } void tst_QXmppMessage::testSubextensions() { const QByteArray xml = QByteArrayLiteral( "" "" "" "" "" "What man art thou that thus bescreen'd in night so stumblest on my counsel?" "" "" "" "" ""); QXmppMessage message; parsePacket(message, xml); QCOMPARE(message.extensions().size(), 2); QCOMPARE(message.extensions().first().tagName(), QLatin1String("result")); serializePacket(message, xml); } void tst_QXmppMessage::testChatMarkers() { const QByteArray markableXml( "" "sleeping" "My lord, dispatch; read o'er these articles." "" ""); QXmppMessage markableMessage; parsePacket(markableMessage, markableXml); QCOMPARE(markableMessage.isMarkable(), true); QCOMPARE(markableMessage.marker(), QXmppMessage::NoMarker); QCOMPARE(markableMessage.id(), QString("message-1")); QCOMPARE(markableMessage.markedId(), QString()); QCOMPARE(markableMessage.thread(), QString("sleeping")); QCOMPARE(markableMessage.markedThread(), QString()); const QByteArray receivedXml( "" "" ""); QXmppMessage receivedMessage; parsePacket(receivedMessage, receivedXml); QCOMPARE(receivedMessage.isMarkable(), false); QCOMPARE(receivedMessage.marker(), QXmppMessage::Received); QCOMPARE(receivedMessage.id(), QString("message-2")); QCOMPARE(receivedMessage.markedId(), QString("message-1")); QCOMPARE(receivedMessage.thread(), QString()); QCOMPARE(receivedMessage.markedThread(), QString("sleeping")); const QByteArray displayedXml( "" "" ""); QXmppMessage displayedMessage; parsePacket(displayedMessage, displayedXml); QCOMPARE(displayedMessage.isMarkable(), false); QCOMPARE(displayedMessage.marker(), QXmppMessage::Displayed); QCOMPARE(displayedMessage.id(), QString("message-2")); QCOMPARE(displayedMessage.markedId(), QString("message-1")); QCOMPARE(displayedMessage.thread(), QString()); QCOMPARE(displayedMessage.markedThread(), QString("sleeping")); const QByteArray acknowledgedXml( "" "" ""); QXmppMessage acknowledgedMessage; parsePacket(acknowledgedMessage, acknowledgedXml); QCOMPARE(acknowledgedMessage.isMarkable(), false); QCOMPARE(acknowledgedMessage.marker(), QXmppMessage::Acknowledged); QCOMPARE(acknowledgedMessage.id(), QString("message-2")); QCOMPARE(acknowledgedMessage.markedId(), QString("message-1")); QCOMPARE(acknowledgedMessage.thread(), QString()); QCOMPARE(acknowledgedMessage.markedThread(), QString("sleeping")); const QByteArray emptyThreadXml( "" "" ""); QXmppMessage emptyThreadMessage; parsePacket(emptyThreadMessage, emptyThreadXml); QCOMPARE(emptyThreadMessage.isMarkable(), false); QCOMPARE(emptyThreadMessage.marker(), QXmppMessage::Received); QCOMPARE(emptyThreadMessage.id(), QString("message-2")); QCOMPARE(emptyThreadMessage.markedId(), QString("message-1")); QCOMPARE(emptyThreadMessage.thread(), QString()); QCOMPARE(emptyThreadMessage.markedThread(), QString()); const QByteArray notMarkableSerialisation( ""); QXmppMessage serialisationMessage; serialisationMessage.setFrom("kingrichard@royalty.england.lit/throne"); serialisationMessage.setTo("northumberland@shakespeare.lit/westminster"); serialisationMessage.setId("message-3"); serialisationMessage.setMarkable(false); serializePacket(serialisationMessage, notMarkableSerialisation); const QByteArray markableSerialisation( "" "" ""); serialisationMessage.setMarkable(true); serializePacket(serialisationMessage, markableSerialisation); const QByteArray receivedSerialisation( "" "" ""); serialisationMessage.setMarkable(false); serialisationMessage.setMarker(QXmppMessage::Received); serialisationMessage.setMarkerId("message-2"); serializePacket(serialisationMessage, receivedSerialisation); const QByteArray receivedThreadSerialisation( "" "" ""); serialisationMessage.setMarker(QXmppMessage::Received); serialisationMessage.setMarkerId("message-2"); serialisationMessage.setMarkedThread("sleeping"); serializePacket(serialisationMessage, receivedThreadSerialisation); const QByteArray displayedThreadSerialisation( "" "" ""); serialisationMessage.setMarker(QXmppMessage::Displayed); serialisationMessage.setMarkerId("message-2"); serialisationMessage.setMarkedThread("sleeping"); serializePacket(serialisationMessage, displayedThreadSerialisation); const QByteArray acknowledgedThreadSerialisation( "" "" ""); serialisationMessage.setMarker(QXmppMessage::Acknowledged); serialisationMessage.setMarkerId("message-2"); serialisationMessage.setMarkedThread("sleeping"); serializePacket(serialisationMessage, acknowledgedThreadSerialisation); } void tst_QXmppMessage::testPrivateMessage() { const QByteArray xml = QByteArrayLiteral( "" "My lord, dispatch; read o'er these articles." "" ""); QXmppMessage message; parsePacket(message, xml); QCOMPARE(message.isPrivate(), true); message = QXmppMessage(); message.setBody(QStringLiteral("My lord, dispatch; read o'er these articles.")); message.setPrivate(true); serializePacket(message, xml); message.setPrivate(true); QCOMPARE(message.isPrivate(), true); message.setPrivate(false); QCOMPARE(message.isPrivate(), false); } void tst_QXmppMessage::testOutOfBandUrl() { const QByteArray oobXml( "" "Yeah, but do you have a license to Jabber?" "" "http://www.jabber.org/images/psa-license.jpg" "" ""); const QString firstUrl = "http://www.jabber.org/images/psa-license.jpg"; const QString newUrl = "https://xmpp.org/theme/images/xmpp-logo.svg"; QXmppMessage oobMessage; parsePacket(oobMessage, oobXml); QCOMPARE(oobMessage.outOfBandUrl(), firstUrl); oobMessage.setOutOfBandUrl(newUrl); QCOMPARE(oobMessage.outOfBandUrl(), newUrl); // set first url again oobMessage.setOutOfBandUrl(firstUrl); serializePacket(oobMessage, oobXml); } void tst_QXmppMessage::testMessageCorrect() { const QByteArray xml( "" "This is the corrected version." "" ""); QXmppMessage message; parsePacket(message, xml); QCOMPARE(message.replaceId(), QString("badmessage")); serializePacket(message, xml); message.setReplaceId("someotherid"); QCOMPARE(message.replaceId(), QString("someotherid")); } void tst_QXmppMessage::testMessageAttaching() { const QByteArray xml( "" "This is the corrected version." "" ""); 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( "" "Harpier cries: 'tis time, 'tis time." "" "hag66@shakespeare.example" "thirdwitch" "" ""); 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( "" "This message is encrypted with OMEMO, but your client doesn't seem to support that." "" ""); 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( "" "This message is encrypted with CustomCrypt, but your client doesn't seem to support that." "" ""); 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( "" "And at the end of the story, both of them die! It is so tragic!" "Love story end" ""); 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( "" "And at the end of the story, both of them die! It is so tragic!" "" ""); 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( "" "V unir avtug'f pybnx gb uvqr zr sebz gurve fvtug" "" "" "" "" ""); // 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)); } void tst_QXmppMessage::testBobData() { const QByteArray xml = QByteArrayLiteral( "" "" "iVBORw0KGgoAAAANSUhEUgAAALQAAAA8BAMAAAA9AI20AAAAG1BMVEX///8AAADf39+" "/v79/f39fX1+fn58/Pz8fHx/8ACGJAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADS0lEQV" "RYhe2WS3MSQRCAYTf7OKY1kT0CxsRjHmh5BENIjqEk6pHVhFzdikqO7CGyP9t59Ox2z" "y6UeWBVqugLzM70Nz39mqnV1lIWgBWiYXV0BYfNZ0mvwypds1r62vH/gf76ZL/88Qlc" "41zeAnQrpx5H3z1Npfr5ovmHusa9SpRiNNIOcdrto6PJ5LLfb5bp9zM+VDq/vptxDEa" "a1sql9I3R5KhtfQsA5gNCWYyulV3TyTUDdfL56BvdDl4x7RiybDq9uBgxh1TTPUHDvA" "qNQb+LpT5sWehxJZKKcU2MZ6sDE7PMgW2mdlBGdy6ODe6fJFdMI+us95dNqftDMdwU6" "+MhpuTS9slcy5TFAcwq0Jt6qssJMTQGp4BGURlmSsNoo5oHL4kqc66NdkDO75mIfCxm" "RAlvHxMLdcb7JONavMJbttXXKoMSneYu3OQTlwkUh4mNayi6js55/2VcsZOQfXIYelz" "xLcntEGc3WVCsCORJVCc5r0ajAcq+EO1Q0oPm7n7+X/3jEReGdL6qT7Ml6FCjY+quJC" "r+D01f6BG0SaHG56ZG32DnY2jcEV1+pU0kxTaEwaGcekN7jyu50U/TV4q6YeieyiNTu" "klDKZLukyjKVNwotCUB3B0XO1WjHT3c0DHSO2zACwut8GOiljJIHaJsrlof/fpWNzGM" "os6TgIY0hZNpJshzSi4igOhy3cl4qK+YgnqHkAYcZEgdW6/HyrEK7afoY7RCFzArLl2" "LLDdrdmmHZfROajwIDfWj8yQG+rzwlA3WvdJiMHtjUekiNrp1oCbmyZDEyKROGjFVDr" "PRzlkR9UAfG/OErnPxrop5BwpoEpXQorq2zcGxbnBJndx8Bh0yljGiGv0B4E8+YP3Xp" "2rGydZNy4csW8W2pIvWhvijoujRJ0luXsoymV+8AXvE9HjII72+oReS6OfomHe3xWg/" "f2coSbDa1XZ1CvGMjy1nH9KBl83oPnQKi+vAXKLjCrRvvT2WCMkPmSFbquiVuTH1qjv" "p4j/u7CWyI5/Hn3KAaJJ90eP0Zp1Kjets4WPaElkxheF7cpBESzXuIdLwyFjSub07tB" "6JjxH3DGiu+zwHHimdtFsMvKqG/nBxm2TwbvyU6LWs5RnJX4dSldg3QhDLAAAAAElFT" "kSuQmCC" "" ""); QXmppBitsOfBinaryData data; data.setCid(QXmppBitsOfBinaryContentId::fromContentId( QStringLiteral("sha1+5a4c38d44fc64805cbb2d92d8b208be13ff40c0f@bob.xmpp.org"))); data.setContentType(QMimeDatabase().mimeTypeForName(QStringLiteral("image/png"))); data.setData(QByteArray::fromBase64(QByteArrayLiteral( "iVBORw0KGgoAAAANSUhEUgAAALQAAAA8BAMAAAA9AI20AAAAG1BMVEX///8AAADf39+" "/v79/f39fX1+fn58/Pz8fHx/8ACGJAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADS0lEQV" "RYhe2WS3MSQRCAYTf7OKY1kT0CxsRjHmh5BENIjqEk6pHVhFzdikqO7CGyP9t59Ox2z" "y6UeWBVqugLzM70Nz39mqnV1lIWgBWiYXV0BYfNZ0mvwypds1r62vH/gf76ZL/88Qlc" "41zeAnQrpx5H3z1Npfr5ovmHusa9SpRiNNIOcdrto6PJ5LLfb5bp9zM+VDq/vptxDEa" "a1sql9I3R5KhtfQsA5gNCWYyulV3TyTUDdfL56BvdDl4x7RiybDq9uBgxh1TTPUHDvA" "qNQb+LpT5sWehxJZKKcU2MZ6sDE7PMgW2mdlBGdy6ODe6fJFdMI+us95dNqftDMdwU6" "+MhpuTS9slcy5TFAcwq0Jt6qssJMTQGp4BGURlmSsNoo5oHL4kqc66NdkDO75mIfCxm" "RAlvHxMLdcb7JONavMJbttXXKoMSneYu3OQTlwkUh4mNayi6js55/2VcsZOQfXIYelz" "xLcntEGc3WVCsCORJVCc5r0ajAcq+EO1Q0oPm7n7+X/3jEReGdL6qT7Ml6FCjY+quJC" "r+D01f6BG0SaHG56ZG32DnY2jcEV1+pU0kxTaEwaGcekN7jyu50U/TV4q6YeieyiNTu" "klDKZLukyjKVNwotCUB3B0XO1WjHT3c0DHSO2zACwut8GOiljJIHaJsrlof/fpWNzGM" "os6TgIY0hZNpJshzSi4igOhy3cl4qK+YgnqHkAYcZEgdW6/HyrEK7afoY7RCFzArLl2" "LLDdrdmmHZfROajwIDfWj8yQG+rzwlA3WvdJiMHtjUekiNrp1oCbmyZDEyKROGjFVDr" "PRzlkR9UAfG/OErnPxrop5BwpoEpXQorq2zcGxbnBJndx8Bh0yljGiGv0B4E8+YP3Xp" "2rGydZNy4csW8W2pIvWhvijoujRJ0luXsoymV+8AXvE9HjII72+oReS6OfomHe3xWg/" "f2coSbDa1XZ1CvGMjy1nH9KBl83oPnQKi+vAXKLjCrRvvT2WCMkPmSFbquiVuTH1qjv" "p4j/u7CWyI5/Hn3KAaJJ90eP0Zp1Kjets4WPaElkxheF7cpBESzXuIdLwyFjSub07tB" "6JjxH3DGiu+zwHHimdtFsMvKqG/nBxm2TwbvyU6LWs5RnJX4dSldg3QhDLAAAAAElFT" "kSuQmCC"))); data.setMaxAge(86400); QXmppMessage message; parsePacket(message, xml); QCOMPARE(message.type(), QXmppMessage::Chat); QCOMPARE(message.id(), QStringLiteral("")); QCOMPARE(message.bitsOfBinaryData().size(), 1); QCOMPARE(message.bitsOfBinaryData().first().cid().algorithm(), data.cid().algorithm()); QCOMPARE(message.bitsOfBinaryData().first().cid().hash(), data.cid().hash()); QCOMPARE(message.bitsOfBinaryData().first().cid(), data.cid()); QCOMPARE(message.bitsOfBinaryData().first().contentType(), data.contentType()); QCOMPARE(message.bitsOfBinaryData().first().maxAge(), data.maxAge()); QCOMPARE(message.bitsOfBinaryData().first().data(), data.data()); QCOMPARE(message.bitsOfBinaryData().first(), data); serializePacket(message, xml); QXmppMessage msg; msg.setType(QXmppMessage::Chat); msg.setId(QStringLiteral("")); QXmppBitsOfBinaryDataList bobDataList; bobDataList << data; msg.setBitsOfBinaryData(bobDataList); serializePacket(msg, xml); QXmppMessage msg2; msg2.setType(QXmppMessage::Chat); msg2.setId(QStringLiteral("")); msg2.bitsOfBinaryData() << data; serializePacket(msg2, xml); // test const getter const QXmppMessage constMessage = msg; QCOMPARE(constMessage.bitsOfBinaryData(), msg.bitsOfBinaryData()); } void tst_QXmppMessage::testFallbackIndication() { const QByteArray xml = QByteArrayLiteral( "" "" ""); QXmppMessage message; parsePacket(message, xml); QVERIFY(message.isFallback()); serializePacket(message, xml); QXmppMessage message2; message2.setIsFallback(true); serializePacket(message2, xml); } void tst_QXmppMessage::testStanzaIds() { const QByteArray xml = QByteArrayLiteral( "" "" "" ""); QXmppMessage msg; parsePacket(msg, xml); QCOMPARE(msg.stanzaId(), QStringLiteral("1236")); QCOMPARE(msg.stanzaIdBy(), QStringLiteral("server.tld")); QCOMPARE(msg.originId(), QStringLiteral("5678")); serializePacket(msg, xml); QXmppMessage msg2; msg2.setStanzaId(QStringLiteral("1236")); msg2.setStanzaIdBy(QStringLiteral("server.tld")); msg2.setOriginId(QStringLiteral("5678")); serializePacket(msg2, xml); } void tst_QXmppMessage::testSlashMe_data() { QTest::addColumn("body"); QTest::addColumn("actionText"); QTest::addColumn("expected"); #define POSTIIVE(name, body, actionText) \ QTest::newRow(QT_STRINGIFY(name)) << body << actionText << true #define NEGATIVE(name, body) \ QTest::newRow(QT_STRINGIFY(name)) << body << QString() << false POSTIIVE(correct, "/me is having lunch.", "is having lunch."); POSTIIVE(correct without text, "/me ", QString()); NEGATIVE(empty, ""); NEGATIVE(null, QString()); NEGATIVE(missing space, "/meshrugs in disgust"); NEGATIVE(with apostrophe, "/me's disgusted"); NEGATIVE(space at the beginning, " /me shrugs in disgust"); NEGATIVE(in quotation marks, R"("/me shrugs in disgust")"); NEGATIVE(display interpretation, "* Atlas shrugs in disgust"); NEGATIVE(quote, "Why did you say \"/me sleeps\"?"); #undef POSTIIVE #undef NEGATIVE } void tst_QXmppMessage::testSlashMe() { QFETCH(QString, body); QFETCH(QString, actionText); QFETCH(bool, expected); QCOMPARE(QXmppMessage::isSlashMeCommand(body), expected); QCOMPARE(QXmppMessage::slashMeCommandText(body), actionText); QXmppMessage msg; QVERIFY(!msg.isSlashMeCommand()); QVERIFY(msg.slashMeCommandText().isNull()); msg.setBody(body); QCOMPARE(msg.isSlashMeCommand(), expected); QCOMPARE(msg.slashMeCommandText(), actionText); } void tst_QXmppMessage::testMixInvitation() { const QByteArray xml( "" "Would you like to join the coven?" "" "hag66@shakespeare.example" "cat@shakespeare.example" "coven@mix.shakespeare.example" "ABCDEF" "" ""); QXmppMessage message; parsePacket(message, xml); QVERIFY(message.mixInvitation()); serializePacket(message, xml); } QTEST_MAIN(tst_QXmppMessage) #include "tst_qxmppmessage.moc" qxmpp-1.4.0/tests/qxmppmessagereceiptmanager/000077500000000000000000000000001402370562100214445ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppmessagereceiptmanager/tst_qxmppmessagereceiptmanager.cpp000066400000000000000000000155771402370562100305020ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Linus Jahn * * 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 "QXmppClient.h" #include "QXmppMessageReceiptManager.h" #include "util.h" #include 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; } void onLoggerMessage(QXmppLogger::MessageType type, const QString& text) { m_receiptSent = true; } private: QXmppMessageReceiptManager* m_manager; QXmppClient m_client; QXmppLogger* m_logger; bool m_messageDelivered = false; bool m_receiptSent = false; }; void tst_QXmppMessageReceiptManager::initTestCase() { m_manager = new QXmppMessageReceiptManager(); m_logger = new QXmppLogger(); m_client.addExtension(m_manager); m_logger->setLoggingType(QXmppLogger::SignalLogging); m_client.setLogger(m_logger); connect(m_logger, &QXmppLogger::message, this, &tst_QXmppMessageReceiptManager::onLoggerMessage); connect(m_manager, &QXmppMessageReceiptManager::messageDelivered, this, &tst_QXmppMessageReceiptManager::handleMessageDelivered); } void tst_QXmppMessageReceiptManager::testReceipt_data() { QTest::addColumn("xml"); QTest::addColumn("accept"); QTest::addColumn("sent"); QTest::addColumn("handled"); QTest::newRow("correct") << QByteArray( "" "" "") << true << false << true; QTest::newRow("from-to-equal") << QByteArray( "" "" "") << false << false << true; QTest::newRow("error-request") << QByteArray( " " " " " " " " " " " " " " "" "Your contact offline message queue is full. The message has been discarded." "" "" "1 " "") << false << false << false; QTest::newRow("error-receipt") << QByteArray( " " "" " " " " "" "Your contact offline message queue is full. The message has been discarded." "" "" "1 " "") << false << false << false; QTest::newRow("message with receipt request") << QByteArray( " " " " " " " " "1 " "") << false << true << false; QTest::newRow("message with no receipt request") << QByteArray( " " " " " " "1 " "") << false << false << false; } void tst_QXmppMessageReceiptManager::testReceipt() { m_messageDelivered = false; m_receiptSent = false; QFETCH(QByteArray, xml); QFETCH(bool, accept); QFETCH(bool, sent); QFETCH(bool, handled); QDomDocument doc; QCOMPARE(doc.setContent(xml, true), true); QDomElement element = doc.documentElement(); QCOMPARE(m_manager->handleStanza(element), handled); QCOMPARE(m_messageDelivered, accept); QCOMPARE(m_receiptSent, sent); } QTEST_MAIN(tst_QXmppMessageReceiptManager) #include "tst_qxmppmessagereceiptmanager.moc" qxmpp-1.4.0/tests/qxmppmixinvitation/000077500000000000000000000000001402370562100200135ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppmixinvitation/tst_qxmppmixinvitation.cpp000066400000000000000000000074711402370562100254120ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Melvin Keskin * * 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 "QXmppMixInvitation.h" #include "util.h" #include #include class tst_QXmppMixInvitation : public QObject { Q_OBJECT private slots: void testInvitation(); void testIsInvitation(); }; void tst_QXmppMixInvitation::testInvitation() { const QByteArray xml( "" "hag66@shakespeare.example" "cat@shakespeare.example" "coven@mix.shakespeare.example" "ABCDEF" ""); QXmppMixInvitation invitation; parsePacket(invitation, xml); QCOMPARE(invitation.inviterJid(), QStringLiteral("hag66@shakespeare.example")); QCOMPARE(invitation.inviteeJid(), QStringLiteral("cat@shakespeare.example")); QCOMPARE(invitation.channelJid(), QStringLiteral("coven@mix.shakespeare.example")); QCOMPARE(invitation.token(), QStringLiteral("ABCDEF")); serializePacket(invitation, xml); invitation.setInviterJid("hag66@shakespeare.example"); invitation.setInviteeJid("cat@shakespeare.example"); invitation.setChannelJid("coven@mix.shakespeare.example"); invitation.setToken("ABCDEF"); QCOMPARE(invitation.inviterJid(), QStringLiteral("hag66@shakespeare.example")); QCOMPARE(invitation.inviteeJid(), QStringLiteral("cat@shakespeare.example")); QCOMPARE(invitation.channelJid(), QStringLiteral("coven@mix.shakespeare.example")); QCOMPARE(invitation.token(), QStringLiteral("ABCDEF")); } void tst_QXmppMixInvitation::testIsInvitation() { QDomDocument doc; QDomElement element; const QByteArray correctInvitationWithNamespace( "" "hag66@shakespeare.example" "cat@shakespeare.example" "coven@mix.shakespeare.example" "ABCDEF" ""); QCOMPARE(doc.setContent(correctInvitationWithNamespace, true), true); element = doc.documentElement(); QVERIFY(QXmppMixInvitation::isMixInvitation(element)); const QByteArray invitationWithoutNamespace( "" "hag66@shakespeare.example" "cat@shakespeare.example" "coven@mix.shakespeare.example" "ABCDEF" ""); QCOMPARE(doc.setContent(invitationWithoutNamespace, true), true); element = doc.documentElement(); QVERIFY(!QXmppMixInvitation::isMixInvitation(element)); const QByteArray invitationWithIncorrectNamespace( "" "hag66@shakespeare.example" "cat@shakespeare.example" "coven@mix.shakespeare.example" "ABCDEF" ""); QCOMPARE(doc.setContent(invitationWithIncorrectNamespace, true), true); element = doc.documentElement(); QVERIFY(!QXmppMixInvitation::isMixInvitation(element)); } QTEST_MAIN(tst_QXmppMixInvitation) #include "tst_qxmppmixinvitation.moc" qxmpp-1.4.0/tests/qxmppmixiq/000077500000000000000000000000001402370562100162405ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppmixiq/tst_qxmppmixiq.cpp000066400000000000000000000336561402370562100220700ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Linus Jahn * * 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 "util.h" #include 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( "" "" "" "" "" "" "" "third witch" "" "" ""); QByteArray joinS2sSetXml( "" "" "" "" "" "" "stpeter" "" ""); QByteArray joinS2sResultXml( "" "" "" "" "" "" "third witch" "" ""); QByteArray joinC2sResultXml( "" "" "" "" "" "" "" "" "" ""); QByteArray leaveC2sSetXml( "" "" "" "" ""); QByteArray leaveS2sSetXml( "" "" ""); QByteArray leaveS2sResultXml( "" "" ""); QByteArray leaveC2sResultXml( "" "" "" "" ""); QByteArray updateSubscriptionSetXml( "" "" "" "" ""); QByteArray updateSubscriptionResultXml( "" "" "" "" ""); QByteArray setNickSetXml( "" "" "thirdwitch" "" ""); QByteArray setNickResultXml( "" "" "thirdwitch" "" ""); QByteArray createXml( "" "" ""); QByteArray createWithoutNameXml( "" "" ""); QByteArray destroyXml( "" "" ""); QByteArray emptyXml( ""); 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("xml"); QTest::addColumn("type"); QTest::addColumn("actionType"); QTest::addColumn("jid"); QTest::addColumn("channelName"); QTest::addColumn("nodes"); QTest::addColumn("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( "" "" ""); const QByteArray truePamXml( "" "" "" "" ""); const QByteArray falseXml( "" "" ""); 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" qxmpp-1.4.0/tests/qxmppmixitem/000077500000000000000000000000001402370562100165655ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppmixitem/tst_qxmppmixitem.cpp000066400000000000000000000114051402370562100227260ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Linus Jahn * * 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 "QXmppPubSubIq.h" #include "util.h" #include #include class tst_QXmppMixItem : public QObject { Q_OBJECT private slots: void testInfo(); void testIsInfoItem(); void testParticipant(); void testIsParticipantItem(); }; void tst_QXmppMixItem::testInfo() { const QByteArray xml( "" "" "urn:xmpp:mix:core:1" "" "" "Witches Coven" "" "" "A location not far from the blasted heath where the " "three witches meet" "" "" "greymalkin@shakespeare.example" "joan@shakespeare.example" "" ""); 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( "" "" "urn:xmpp:mix:core:1" "" ""); QCOMPARE(doc.setContent(xmlCorrect, true), true); element = doc.documentElement(); QVERIFY(QXmppMixInfoItem::isMixChannelInfo(element)); const QByteArray xmlWrong( "" "" "other:namespace" "" ""); QCOMPARE(doc.setContent(xmlWrong, true), true); element = doc.documentElement(); QVERIFY(!QXmppMixInfoItem::isMixChannelInfo(element)); } void tst_QXmppMixItem::testParticipant() { const QByteArray xml( "" "hag66@shakespeare.example" "thirdwitch" ""); 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( "" ""); QCOMPARE(doc.setContent(xmlCorrect, true), true); element = doc.documentElement(); QVERIFY(QXmppMixParticipantItem::isMixParticipantItem(element)); const QByteArray xmlWrong( "" ""); QCOMPARE(doc.setContent(xmlWrong, true), true); element = doc.documentElement(); QVERIFY(!QXmppMixParticipantItem::isMixParticipantItem(element)); } QTEST_MAIN(tst_QXmppMixItem) #include "tst_qxmppmixitem.moc" qxmpp-1.4.0/tests/qxmppnonsaslauthiq/000077500000000000000000000000001402370562100200025ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppnonsaslauthiq/tst_qxmppnonsaslauthiq.cpp000066400000000000000000000052541402370562100253650ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Jeremy Lainé * Manjeet Dahiya * * 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 "QXmppNonSASLAuth.h" #include "util.h" class tst_QXmppNonSASLAuthIq : public QObject { Q_OBJECT private slots: void testGet(); void testSetPlain(); void testSetDigest(); }; void tst_QXmppNonSASLAuthIq::testGet() { // Client requests authentication fields from server const QByteArray xml( "" "" ""); QXmppNonSASLAuthIq iq; parsePacket(iq, xml); serializePacket(iq, xml); } void tst_QXmppNonSASLAuthIq::testSetPlain() { // Client provides required information (plain) const QByteArray xml( "" "" "bill" "Calli0pe" "globe" "" ""); QXmppNonSASLAuthIq iq; parsePacket(iq, xml); QCOMPARE(iq.username(), QLatin1String("bill")); QCOMPARE(iq.digest(), QByteArray()); QCOMPARE(iq.password(), QLatin1String("Calli0pe")); QCOMPARE(iq.resource(), QLatin1String("globe")); serializePacket(iq, xml); } void tst_QXmppNonSASLAuthIq::testSetDigest() { // Client provides required information (digest) const QByteArray xml( "" "" "bill" "48fc78be9ec8f86d8ce1c39c320c97c21d62334d" "globe" "" ""); QXmppNonSASLAuthIq iq; parsePacket(iq, xml); QCOMPARE(iq.username(), QLatin1String("bill")); QCOMPARE(iq.digest(), QByteArray("\x48\xfc\x78\xbe\x9e\xc8\xf8\x6d\x8c\xe1\xc3\x9c\x32\x0c\x97\xc2\x1d\x62\x33\x4d")); QCOMPARE(iq.password(), QString()); QCOMPARE(iq.resource(), QLatin1String("globe")); serializePacket(iq, xml); } QTEST_MAIN(tst_QXmppNonSASLAuthIq) #include "tst_qxmppnonsaslauthiq.moc" qxmpp-1.4.0/tests/qxmppoutgoingclient/000077500000000000000000000000001402370562100201435ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppoutgoingclient/tst_qxmppoutgoingclient.cpp000066400000000000000000000044201402370562100256610ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Linus Jahn * * 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 "QXmppOutgoingClient.h" #include "util.h" class tst_QXmppOutgoingClient : public QObject { Q_OBJECT private: Q_SLOT void testParseHostAddress_data(); Q_SLOT void testParseHostAddress(); }; void tst_QXmppOutgoingClient::testParseHostAddress_data() { QTest::addColumn("input"); QTest::addColumn("resultHost"); QTest::addColumn("resultPort"); QTest::newRow("host-and-port") << QStringLiteral("qxmpp.org:443") << QStringLiteral("qxmpp.org") << 443; QTest::newRow("no-port") << QStringLiteral("qxmpp.org") << QStringLiteral("qxmpp.org") << -1; QTest::newRow("ipv4-with-port") << QStringLiteral("127.0.0.1:443") << QStringLiteral("127.0.0.1") << 443; QTest::newRow("ipv4-no-port") << QStringLiteral("127.0.0.1") << QStringLiteral("127.0.0.1") << -1; QTest::newRow("ipv6-with-port") << QStringLiteral("[2001:41D0:1:A49b::1]:9222") << QStringLiteral("2001:41d0:1:a49b::1") << 9222; QTest::newRow("ipv6-no-port") << QStringLiteral("[2001:41D0:1:A49b::1]") << QStringLiteral("2001:41d0:1:a49b::1") << -1; } void tst_QXmppOutgoingClient::testParseHostAddress() { QFETCH(QString, input); QFETCH(QString, resultHost); QFETCH(int, resultPort); const auto address = QXmppOutgoingClient::parseHostAddress(input); QCOMPARE(address.first, resultHost); QCOMPARE(address.second, resultPort); } QTEST_MAIN(tst_QXmppOutgoingClient) #include "tst_qxmppoutgoingclient.moc" qxmpp-1.4.0/tests/qxmpppresence/000077500000000000000000000000001402370562100167155ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmpppresence/tst_qxmpppresence.cpp000066400000000000000000000353461402370562100232200ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Olivier Goffart * Jeremy Lainé * * 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 "QXmppPresence.h" #include "util.h" #include #include class tst_QXmppPresence : public QObject { Q_OBJECT private slots: void testPresence(); void testPresence_data(); void testPresenceWithCapability(); void testPresenceWithExtendedAddresses(); void testPresenceWithMucItem(); void testPresenceWithMucPassword(); void testPresenceWithMucSupport(); void testPresenceWithLastUserInteraction(); void testPresenceWithMix(); void testPresenceWithVCard(); }; void tst_QXmppPresence::testPresence_data() { QXmppPresence foo; QTest::addColumn("xml"); QTest::addColumn("type"); QTest::addColumn("priority"); QTest::addColumn("statusType"); QTest::addColumn("statusText"); QTest::addColumn("vcardUpdate"); QTest::addColumn("photoHash"); // presence type QTest::newRow("available") << QByteArray("") << int(QXmppPresence::Available) << 0 << int(QXmppPresence::Online) << "" << int(QXmppPresence::VCardUpdateNone) << QByteArray(); QTest::newRow("unavailable") << QByteArray("") << int(QXmppPresence::Unavailable) << 0 << int(QXmppPresence::Online) << "" << int(QXmppPresence::VCardUpdateNone) << QByteArray(); QTest::newRow("error") << QByteArray("") << int(QXmppPresence::Error) << 0 << int(QXmppPresence::Online) << "" << int(QXmppPresence::VCardUpdateNone) << QByteArray(); QTest::newRow("subscribe") << QByteArray("") << int(QXmppPresence::Subscribe) << 0 << int(QXmppPresence::Online) << "" << int(QXmppPresence::VCardUpdateNone) << QByteArray(); QTest::newRow("unsubscribe") << QByteArray("") << int(QXmppPresence::Unsubscribe) << 0 << int(QXmppPresence::Online) << "" << int(QXmppPresence::VCardUpdateNone) << QByteArray(); QTest::newRow("subscribed") << QByteArray("") << int(QXmppPresence::Subscribed) << 0 << int(QXmppPresence::Online) << "" << int(QXmppPresence::VCardUpdateNone) << QByteArray(); QTest::newRow("unsubscribed") << QByteArray("") << int(QXmppPresence::Unsubscribed) << 0 << int(QXmppPresence::Online) << "" << int(QXmppPresence::VCardUpdateNone) << QByteArray(); QTest::newRow("probe") << QByteArray("") << int(QXmppPresence::Probe) << 0 << int(QXmppPresence::Online) << "" << int(QXmppPresence::VCardUpdateNone) << QByteArray(); // status text + priority QTest::newRow("full") << QByteArray("awayIn a meeting5") << int(QXmppPresence::Available) << 5 << int(QXmppPresence::Away) << "In a meeting" << int(QXmppPresence::VCardUpdateNone) << QByteArray(); // status type QTest::newRow("away") << QByteArray("away") << int(QXmppPresence::Available) << 0 << int(QXmppPresence::Away) << "" << int(QXmppPresence::VCardUpdateNone) << QByteArray(); QTest::newRow("dnd") << QByteArray("dnd") << int(QXmppPresence::Available) << 0 << int(QXmppPresence::DND) << "" << int(QXmppPresence::VCardUpdateNone) << QByteArray(); QTest::newRow("chat") << QByteArray("chat") << int(QXmppPresence::Available) << 0 << int(QXmppPresence::Chat) << "" << int(QXmppPresence::VCardUpdateNone) << QByteArray(); QTest::newRow("xa") << QByteArray("xa") << int(QXmppPresence::Available) << 0 << int(QXmppPresence::XA) << "" << int(QXmppPresence::VCardUpdateNone) << QByteArray(); QTest::newRow("invisible") << QByteArray("invisible") << int(QXmppPresence::Available) << 0 << int(QXmppPresence::Invisible) << "" << int(QXmppPresence::VCardUpdateNone) << QByteArray(); // photo QTest::newRow("vcard-photo") << QByteArray( "" "" "73b908bc" "" "") << int(QXmppPresence::Available) << 0 << int(QXmppPresence::Online) << "" << int(QXmppPresence::VCardUpdateValidPhoto) << QByteArray::fromHex("73b908bc"); QTest::newRow("vard-not-ready") << QByteArray( "" "" "") << int(QXmppPresence::Available) << 0 << int(QXmppPresence::Online) << "" << int(QXmppPresence::VCardUpdateNotReady) << QByteArray(); } void tst_QXmppPresence::testPresence() { QFETCH(QByteArray, xml); QFETCH(int, type); QFETCH(int, priority); QFETCH(int, statusType); QFETCH(QString, statusText); QFETCH(int, vcardUpdate); QFETCH(QByteArray, photoHash); // test parsing and serialization after parsing QXmppPresence parsedPresence; parsePacket(parsedPresence, xml); QCOMPARE(int(parsedPresence.type()), type); QCOMPARE(parsedPresence.priority(), priority); QCOMPARE(int(parsedPresence.availableStatusType()), statusType); QCOMPARE(parsedPresence.statusText(), statusText); QCOMPARE(int(parsedPresence.vCardUpdateType()), vcardUpdate); QCOMPARE(parsedPresence.photoHash(), photoHash); serializePacket(parsedPresence, xml); // test serialization from setters QXmppPresence presence; presence.setType(static_cast(type)); presence.setPriority(priority); presence.setAvailableStatusType(static_cast(statusType)); presence.setStatusText(statusText); presence.setVCardUpdateType(static_cast(vcardUpdate)); presence.setPhotoHash(photoHash); serializePacket(presence, xml); } void tst_QXmppPresence::testPresenceWithCapability() { const QByteArray xml( "" "away" "In a meeting" "5" "" "" "73b908bc" "" "" ""); // test parsing and serialization after parsing QXmppPresence presence; parsePacket(presence, xml); QCOMPARE(presence.to(), QString("foo@example.com/QXmpp")); QCOMPARE(presence.from(), QString("bar@example.com/QXmpp")); QCOMPARE(presence.availableStatusType(), QXmppPresence::Away); QCOMPARE(presence.statusText(), QString("In a meeting")); QCOMPARE(presence.priority(), 5); QCOMPARE(presence.photoHash(), QByteArray::fromHex("73b908bc")); QCOMPARE(presence.vCardUpdateType(), QXmppPresence::VCardUpdateValidPhoto); QCOMPARE(presence.capabilityHash(), QString("sha-1")); QCOMPARE(presence.capabilityNode(), QString("https://github.com/qxmpp-project/qxmpp")); QCOMPARE(presence.capabilityVer(), QByteArray::fromBase64("QgayPKawpkPSDYmwT/WM94uAlu0=")); QCOMPARE(presence.extensions().first().tagName(), QStringLiteral("x")); QCOMPARE(presence.extensions().first().attribute(QStringLiteral("xmlns")), QStringLiteral("urn:other:namespace")); serializePacket(presence, xml); // test serialization from setters QXmppPresence presence2; presence2.setTo(QStringLiteral("foo@example.com/QXmpp")); presence2.setFrom(QStringLiteral("bar@example.com/QXmpp")); presence2.setAvailableStatusType(QXmppPresence::Away); presence2.setStatusText(QStringLiteral("In a meeting")); presence2.setPriority(5); presence2.setPhotoHash(QByteArray::fromHex("73b908bc")); presence2.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto); presence2.setCapabilityHash(QStringLiteral("sha-1")); presence2.setCapabilityNode(QStringLiteral("https://github.com/qxmpp-project/qxmpp")); presence2.setCapabilityVer(QByteArray::fromBase64("QgayPKawpkPSDYmwT/WM94uAlu0=")); QXmppElement unknownExtension; unknownExtension.setTagName(QStringLiteral("x")); unknownExtension.setAttribute(QStringLiteral("xmlns"), QStringLiteral("urn:other:namespace")); presence2.setExtensions(QXmppElementList() << unknownExtension); serializePacket(presence2, xml); } void tst_QXmppPresence::testPresenceWithExtendedAddresses() { const QByteArray xml( "" "" "
" "
" "" ""); QXmppPresence presence; parsePacket(presence, xml); QCOMPARE(presence.extendedAddresses().size(), 2); QCOMPARE(presence.extendedAddresses()[0].description(), QString()); QCOMPARE(presence.extendedAddresses()[0].jid(), QLatin1String("temas@jabber.org")); QCOMPARE(presence.extendedAddresses()[0].type(), QLatin1String("bcc")); QCOMPARE(presence.extendedAddresses()[1].description(), QString()); QCOMPARE(presence.extendedAddresses()[1].jid(), QLatin1String("jer@jabber.org")); QCOMPARE(presence.extendedAddresses()[1].type(), QLatin1String("bcc")); serializePacket(presence, xml); } void tst_QXmppPresence::testPresenceWithMucItem() { const QByteArray xml( "" "" "" "" "Avaunt, you cullion!" "" "" "" ""); QXmppPresence presence; parsePacket(presence, xml); QCOMPARE(presence.to(), QLatin1String("pistol@shakespeare.lit/harfleur")); QCOMPARE(presence.from(), QLatin1String("harfleur@henryv.shakespeare.lit/pistol")); QCOMPARE(presence.type(), QXmppPresence::Unavailable); QCOMPARE(presence.mucItem().actor(), QLatin1String("fluellen@shakespeare.lit")); QCOMPARE(presence.mucItem().affiliation(), QXmppMucItem::NoAffiliation); QCOMPARE(presence.mucItem().jid(), QString()); QCOMPARE(presence.mucItem().reason(), QLatin1String("Avaunt, you cullion!")); QCOMPARE(presence.mucItem().role(), QXmppMucItem::NoRole); QCOMPARE(presence.mucStatusCodes(), QList() << 307); serializePacket(presence, xml); } void tst_QXmppPresence::testPresenceWithMucPassword() { const QByteArray xml( "" "" "pass" "" ""); QXmppPresence presence; parsePacket(presence, xml); QCOMPARE(presence.to(), QLatin1String("coven@chat.shakespeare.lit/thirdwitch")); QCOMPARE(presence.from(), QLatin1String("hag66@shakespeare.lit/pda")); QCOMPARE(presence.type(), QXmppPresence::Available); QCOMPARE(presence.isMucSupported(), true); QCOMPARE(presence.mucPassword(), QLatin1String("pass")); serializePacket(presence, xml); } void tst_QXmppPresence::testPresenceWithMucSupport() { const QByteArray xml( "" "" ""); QXmppPresence presence; parsePacket(presence, xml); QCOMPARE(presence.to(), QLatin1String("coven@chat.shakespeare.lit/thirdwitch")); QCOMPARE(presence.from(), QLatin1String("hag66@shakespeare.lit/pda")); QCOMPARE(presence.type(), QXmppPresence::Available); QCOMPARE(presence.isMucSupported(), true); QVERIFY(presence.mucPassword().isEmpty()); serializePacket(presence, xml); } void tst_QXmppPresence::testPresenceWithLastUserInteraction() { const QByteArray xml( "" "" ""); QXmppPresence presence; parsePacket(presence, xml); QVERIFY(!presence.lastUserInteraction().isNull()); QVERIFY(presence.lastUserInteraction().isValid()); QCOMPARE(presence.lastUserInteraction(), QDateTime(QDate(1969, 7, 21), QTime(2, 56, 15), Qt::UTC)); serializePacket(presence, xml); QDateTime another(QDate(2025, 2, 5), QTime(15, 32, 8), Qt::UTC); presence.setLastUserInteraction(another); QCOMPARE(presence.lastUserInteraction(), another); } void tst_QXmppPresence::testPresenceWithMix() { const QByteArray xml( "" "dnd" "Making a Brew" "" "hecate@shakespeare.example/UUID-x4r/2491" "thirdwitch" "" ""); 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")); } void tst_QXmppPresence::testPresenceWithVCard() { } QTEST_MAIN(tst_QXmppPresence) #include "tst_qxmpppresence.moc" qxmpp-1.4.0/tests/qxmpppubsubiq/000077500000000000000000000000001402370562100167435ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmpppubsubiq/tst_qxmpppubsubiq.cpp000066400000000000000000000273371402370562100232750ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppPubSubIq.h" #include "util.h" #include class tst_QXmppPubSubIq : public QObject { Q_OBJECT private slots: void testItems(); void testItemsResponse(); void testPublish(); void testRetractItem(); void testSubscribe(); void testSubscription(); void testSubscriptions(); void testIsPubSubIq_data(); void testIsPubSubIq(); }; void tst_QXmppPubSubIq::testItems() { const QByteArray xml( "" "" "" "" ""); QXmppPubSubIq iq; parsePacket(iq, xml); QCOMPARE(iq.id(), QLatin1String("items1")); QCOMPARE(iq.to(), QLatin1String("pubsub.shakespeare.lit")); QCOMPARE(iq.from(), QLatin1String("francisco@denmark.lit/barracks")); QCOMPARE(iq.type(), QXmppIq::Get); QCOMPARE(iq.queryType(), QXmppPubSubIq::ItemsQuery); QCOMPARE(iq.queryJid(), QString()); QCOMPARE(iq.queryNode(), QLatin1String("storage:bookmarks")); serializePacket(iq, xml); iq = QXmppPubSubIq(); iq.setId(QLatin1String("items1")); iq.setTo(QLatin1String("pubsub.shakespeare.lit")); iq.setFrom(QLatin1String("francisco@denmark.lit/barracks")); iq.setType(QXmppIq::Get); iq.setQueryType(QXmppPubSubIq::ItemsQuery); iq.setQueryJid({}); iq.setQueryNode(QLatin1String("storage:bookmarks")); serializePacket(iq, xml); } void tst_QXmppPubSubIq::testItemsResponse() { const QByteArray xml( "" "" "" "" "" "" "JC" "" "" "" "" "" ""); QXmppPubSubIq iq; parsePacket(iq, xml); QCOMPARE(iq.id(), QLatin1String("items1")); QCOMPARE(iq.to(), QLatin1String("francisco@denmark.lit/barracks")); QCOMPARE(iq.from(), QLatin1String("pubsub.shakespeare.lit")); QCOMPARE(iq.type(), QXmppIq::Result); QCOMPARE(iq.queryType(), QXmppPubSubIq::ItemsQuery); QCOMPARE(iq.queryJid(), QString()); QCOMPARE(iq.queryNode(), QLatin1String("storage:bookmarks")); serializePacket(iq, xml); } void tst_QXmppPubSubIq::testPublish() { const QByteArray xml( "" "" "" "" "" "" "JC" "" "" "" "" "" ""); QXmppPubSubIq iq; parsePacket(iq, xml); QCOMPARE(iq.id(), QLatin1String("items1")); QCOMPARE(iq.to(), QLatin1String("pubsub.shakespeare.lit")); QCOMPARE(iq.from(), QLatin1String("francisco@denmark.lit/barracks")); QCOMPARE(iq.type(), QXmppIq::Result); QCOMPARE(iq.queryType(), QXmppPubSubIq::PublishQuery); QCOMPARE(iq.queryJid(), QString()); QCOMPARE(iq.queryNode(), QLatin1String("storage:bookmarks")); QCOMPARE(iq.items().first().contents().tagName(), QLatin1String("storage")); serializePacket(iq, xml); // serialize using setters QXmppElement itemContentNick; itemContentNick.setTagName(QStringLiteral("nick")); itemContentNick.setValue(QStringLiteral("JC")); QXmppElement itemContentConference; itemContentConference.setTagName(QStringLiteral("conference")); itemContentConference.setAttribute(QStringLiteral("autojoin"), QStringLiteral("true")); itemContentConference.setAttribute(QStringLiteral("jid"), QStringLiteral("theplay@conference.shakespeare.lit")); itemContentConference.setAttribute(QStringLiteral("name"), QStringLiteral("The Play's the Thing")); itemContentConference.appendChild(itemContentNick); QXmppElement itemContent; itemContent.setTagName(QStringLiteral("storage")); itemContent.setAttribute(QStringLiteral("xmlns"), QStringLiteral("storage:bookmarks")); itemContent.appendChild(itemContentConference); QXmppPubSubItem item; item.setId(QStringLiteral("current")); item.setContents(itemContent); iq = QXmppPubSubIq(); iq.setId(QLatin1String("items1")); iq.setTo(QLatin1String("pubsub.shakespeare.lit")); iq.setFrom(QLatin1String("francisco@denmark.lit/barracks")); iq.setType(QXmppIq::Result); iq.setQueryType(QXmppPubSubIq::PublishQuery); iq.setQueryJid({}); iq.setQueryNode(QLatin1String("storage:bookmarks")); iq.setItems(QList() << item); serializePacket(iq, xml); } void tst_QXmppPubSubIq::testRetractItem() { const QByteArray xml( "" "" "" "" "" "" ""); QXmppPubSubIq iq; parsePacket(iq, xml); QCOMPARE(iq.id(), QString("retract1")); QCOMPARE(iq.to(), QLatin1String("pubsub.shakespeare.lit")); QCOMPARE(iq.from(), QLatin1String("hamlet@denmark.lit/elsinore")); QCOMPARE(iq.type(), QXmppIq::Set); QCOMPARE(iq.queryType(), QXmppPubSubIq::RetractQuery); QCOMPARE(iq.queryJid(), QString()); QCOMPARE(iq.queryNode(), QLatin1String("princely_musings")); QCOMPARE(iq.items().size(), 1); QCOMPARE(iq.items().first().id(), QStringLiteral("ae890ac52d0df67ed7cfdf51b644e901")); serializePacket(iq, xml); iq = QXmppPubSubIq(); iq.setId(QLatin1String("retract1")); iq.setTo(QLatin1String("pubsub.shakespeare.lit")); iq.setFrom(QLatin1String("hamlet@denmark.lit/elsinore")); iq.setType(QXmppIq::Set); iq.setQueryType(QXmppPubSubIq::RetractQuery); iq.setQueryJid({}); iq.setQueryNode(QLatin1String("princely_musings")); QXmppPubSubItem item; item.setId(QStringLiteral("ae890ac52d0df67ed7cfdf51b644e901")); iq.setItems(QList() << item); serializePacket(iq, xml); } void tst_QXmppPubSubIq::testSubscribe() { const QByteArray xml( "" "" "" "" ""); QXmppPubSubIq iq; parsePacket(iq, xml); QCOMPARE(iq.id(), QLatin1String("sub1")); QCOMPARE(iq.to(), QLatin1String("pubsub.shakespeare.lit")); QCOMPARE(iq.from(), QLatin1String("francisco@denmark.lit/barracks")); QCOMPARE(iq.type(), QXmppIq::Set); QCOMPARE(iq.queryType(), QXmppPubSubIq::SubscribeQuery); QCOMPARE(iq.queryJid(), QLatin1String("francisco@denmark.lit")); QCOMPARE(iq.queryNode(), QLatin1String("princely_musings")); serializePacket(iq, xml); } void tst_QXmppPubSubIq::testSubscription() { const QByteArray xml( "" "" "" "" ""); QXmppPubSubIq iq; parsePacket(iq, xml); QCOMPARE(iq.id(), QLatin1String("sub1")); QCOMPARE(iq.to(), QLatin1String("francisco@denmark.lit/barracks")); QCOMPARE(iq.from(), QLatin1String("pubsub.shakespeare.lit")); QCOMPARE(iq.type(), QXmppIq::Result); QCOMPARE(iq.queryType(), QXmppPubSubIq::SubscriptionQuery); QCOMPARE(iq.queryJid(), QLatin1String("francisco@denmark.lit")); QCOMPARE(iq.queryNode(), QLatin1String("princely_musings")); QCOMPARE(iq.subscriptionId(), QLatin1String("ba49252aaa4f5d320c24d3766f0bdcade78c78d3")); serializePacket(iq, xml); iq = QXmppPubSubIq(); iq.setId(QLatin1String("sub1")); iq.setTo(QLatin1String("francisco@denmark.lit/barracks")); iq.setFrom(QLatin1String("pubsub.shakespeare.lit")); iq.setType(QXmppIq::Result); iq.setQueryType(QXmppPubSubIq::SubscriptionQuery); iq.setQueryJid(QLatin1String("francisco@denmark.lit")); iq.setQueryNode(QLatin1String("princely_musings")); iq.setSubscriptionId(QLatin1String("ba49252aaa4f5d320c24d3766f0bdcade78c78d3")); serializePacket(iq, xml); } void tst_QXmppPubSubIq::testSubscriptions() { const QByteArray xml( "" "" "" "" ""); QXmppPubSubIq iq; parsePacket(iq, xml); QCOMPARE(iq.id(), QLatin1String("subscriptions1")); QCOMPARE(iq.to(), QLatin1String("pubsub.shakespeare.lit")); QCOMPARE(iq.from(), QLatin1String("francisco@denmark.lit/barracks")); QCOMPARE(iq.type(), QXmppIq::Get); QCOMPARE(iq.queryType(), QXmppPubSubIq::SubscriptionsQuery); QCOMPARE(iq.queryJid(), QString()); QCOMPARE(iq.queryNode(), QString()); serializePacket(iq, xml); } void tst_QXmppPubSubIq::testIsPubSubIq_data() { QTest::addColumn("xml"); QTest::addColumn("isValid"); QTest::newRow("valid-pubsub-iq") << QByteArrayLiteral("") << true; QTest::newRow("wrong-element") << QByteArrayLiteral("") << false; QTest::newRow("wrong-namespace") << QByteArrayLiteral("") << false; } void tst_QXmppPubSubIq::testIsPubSubIq() { QFETCH(QByteArray, xml); QFETCH(bool, isValid); QDomDocument doc; QCOMPARE(doc.setContent(xml, true), true); QDomElement element = doc.documentElement(); QCOMPARE(QXmppPubSubIq::isPubSubIq(element), isValid); } QTEST_MAIN(tst_QXmppPubSubIq) #include "tst_qxmpppubsubiq.moc" qxmpp-1.4.0/tests/qxmpppushenableiq/000077500000000000000000000000001402370562100175715ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmpppushenableiq/tst_qxmpppushenableiq.cpp000066400000000000000000000115301402370562100247350ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jonah Brüchert * * 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 #include "util.h" #include #include class tst_QXmppPushEnableIq : public QObject { Q_OBJECT private slots: void testPushEnable(); void testPushDisable(); void testXmlNs(); void testDataForm(); void testIsEnableIq(); }; void tst_QXmppPushEnableIq::testPushEnable() { const QByteArray xml( R"()" R"()" ""); QXmppPushEnableIq iq; parsePacket(iq, xml); QCOMPARE(iq.mode(), QXmppPushEnableIq::Enable); QCOMPARE(iq.jid(), QStringLiteral("push-5.client.example")); QCOMPARE(iq.node(), QStringLiteral("yxs32uqsflafdk3iuqo")); serializePacket(iq, xml); QXmppPushEnableIq sIq; sIq.setJid("push-5.client.example"); sIq.setMode(QXmppPushEnableIq::Enable); sIq.setNode("yxs32uqsflafdk3iuqo"); sIq.setType(QXmppIq::Set); sIq.setId("x42"); serializePacket(sIq, xml); } void tst_QXmppPushEnableIq::testPushDisable() { const QByteArray xml( R"()" R"()" ""); QXmppPushEnableIq iq; parsePacket(iq, xml); QCOMPARE(iq.mode(), QXmppPushEnableIq::Disable); QCOMPARE(iq.jid(), QStringLiteral("push-5.client.example")); serializePacket(iq, xml); QXmppPushEnableIq sIq; sIq.setJid("push-5.client.example"); sIq.setMode(QXmppPushEnableIq::Disable); sIq.setNode("yxs32uqsflafdk3iuqo"); sIq.setType(QXmppIq::Set); sIq.setId("x97"); serializePacket(sIq, xml); } void tst_QXmppPushEnableIq::testXmlNs() { const QByteArray xml( R"()" R"()" ""); QXmppPushEnableIq iq; parsePacket(iq, xml); QVERIFY(iq.jid().isEmpty()); } void tst_QXmppPushEnableIq::testDataForm() { const QByteArray xml( R"()" R"()" R"()" R"(http://jabber.org/protocol/pubsub#publish-options)" R"(eruio234vzxc2kla-91)" "" "" ""); QXmppPushEnableIq iq; parsePacket(iq, xml); QVERIFY(!iq.dataForm().isNull()); QCOMPARE(iq.dataForm().fields().size(), 2); serializePacket(iq, xml); QXmppPushEnableIq sIq; QXmppDataForm::Field field0; field0.setKey("FORM_TYPE"); field0.setType(QXmppDataForm::Field::HiddenField); field0.setValue("http://jabber.org/protocol/pubsub#publish-options"); QXmppDataForm::Field field1; field1.setKey("secret"); field1.setValue("eruio234vzxc2kla-91"); QXmppDataForm form; form.setType(QXmppDataForm::Submit); form.setFields({field0, field1}); sIq.setDataForm(form); sIq.setType(QXmppIq::Set); sIq.setMode(QXmppPushEnableIq::Enable); sIq.setId("x43"); sIq.setJid("push-5.client.example"); sIq.setNode("yxs32uqsflafdk3iuqo"); serializePacket(sIq, xml); } void tst_QXmppPushEnableIq::testIsEnableIq() { const QByteArray xml( R"()" R"()" ""); QDomDocument doc; doc.setContent(xml, true); bool isPushEnable = QXmppPushEnableIq::isPushEnableIq(doc.documentElement()); QCOMPARE(isPushEnable, true); // reset isPushEnable = false; const QByteArray xml2( R"()" R"()" ""); doc.setContent(xml2, true); isPushEnable = QXmppPushEnableIq::isPushEnableIq(doc.documentElement()); QCOMPARE(isPushEnable, true); } QTEST_MAIN(tst_QXmppPushEnableIq); #include "tst_qxmpppushenableiq.moc" qxmpp-1.4.0/tests/qxmppregisteriq/000077500000000000000000000000001402370562100172675ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppregisteriq/tst_qxmppregisteriq.cpp000066400000000000000000000374651402370562100241500ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * Linus Jahn * * 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 "QXmppBitsOfBinaryContentId.h" #include "QXmppBitsOfBinaryData.h" #include "QXmppBitsOfBinaryDataList.h" #include "QXmppRegisterIq.h" #include "util.h" #include class tst_QXmppRegisterIq : public QObject { Q_OBJECT private slots: void testGet(); void testResult(); void testResultWithForm(); void testSet(); void testSetWithForm(); void testBobData(); void testRegistered(); void testRemove(); void testChangePassword(); void testUnregistration(); }; void tst_QXmppRegisterIq::testGet() { const QByteArray xml( "" "" ""); QXmppRegisterIq iq; parsePacket(iq, xml); QCOMPARE(iq.id(), QLatin1String("reg1")); QCOMPARE(iq.to(), QLatin1String("shakespeare.lit")); QCOMPARE(iq.from(), QString()); QCOMPARE(iq.type(), QXmppIq::Get); QCOMPARE(iq.instructions(), QString()); QVERIFY(!iq.isRegistered()); QVERIFY(!iq.isRemove()); QVERIFY(iq.username().isNull()); QVERIFY(iq.password().isNull()); QVERIFY(iq.email().isNull()); QVERIFY(iq.form().isNull()); serializePacket(iq, xml); } void tst_QXmppRegisterIq::testResult() { const QByteArray xml( "" "" "Choose a username and password for use with this service. Please also provide your email address." "" "" "" "" ""); QXmppRegisterIq iq; parsePacket(iq, xml); QCOMPARE(iq.id(), QLatin1String("reg1")); QCOMPARE(iq.to(), QString()); QCOMPARE(iq.from(), QString()); QCOMPARE(iq.type(), QXmppIq::Result); QCOMPARE(iq.instructions(), QLatin1String("Choose a username and password for use with this service. Please also provide your email address.")); QVERIFY(!iq.username().isNull()); QVERIFY(iq.username().isEmpty()); QVERIFY(!iq.password().isNull()); QVERIFY(iq.password().isEmpty()); QVERIFY(!iq.email().isNull()); QVERIFY(iq.email().isEmpty()); QVERIFY(iq.form().isNull()); serializePacket(iq, xml); } void tst_QXmppRegisterIq::testResultWithForm() { const QByteArray xml( "" "" "Use the enclosed form to register. If your Jabber client does not support Data Forms, visit http://www.shakespeare.lit/contests.php" "" "Contest Registration" "" "Please provide the following information" "to sign up for our special contests!" "" "" "jabber:iq:register" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""); QXmppRegisterIq iq; parsePacket(iq, xml); QCOMPARE(iq.id(), QLatin1String("reg3")); QCOMPARE(iq.to(), QLatin1String("juliet@capulet.com/balcony")); QCOMPARE(iq.from(), QLatin1String("contests.shakespeare.lit")); QCOMPARE(iq.type(), QXmppIq::Result); QCOMPARE(iq.instructions(), QLatin1String("Use the enclosed form to register. If your Jabber client does not support Data Forms, visit http://www.shakespeare.lit/contests.php")); QVERIFY(iq.username().isNull()); QVERIFY(iq.password().isNull()); QVERIFY(iq.email().isNull()); QVERIFY(!iq.form().isNull()); QCOMPARE(iq.form().title(), QLatin1String("Contest Registration")); serializePacket(iq, xml); } void tst_QXmppRegisterIq::testSet() { const QByteArray xml( "" "" "bill" "Calliope" "bard@shakespeare.lit" "" ""); QXmppRegisterIq iq; parsePacket(iq, xml); QCOMPARE(iq.id(), QLatin1String("reg2")); QCOMPARE(iq.to(), QString()); QCOMPARE(iq.from(), QString()); QCOMPARE(iq.type(), QXmppIq::Set); QCOMPARE(iq.username(), QLatin1String("bill")); QCOMPARE(iq.password(), QLatin1String("Calliope")); QCOMPARE(iq.email(), QLatin1String("bard@shakespeare.lit")); QVERIFY(iq.form().isNull()); serializePacket(iq, xml); } void tst_QXmppRegisterIq::testSetWithForm() { const QByteArray xml( "" "" "" "" "jabber:iq:register" "" "" "Juliet" "" "" "Capulet" "" "" "juliet@capulet.com" "" "" "F" "" "" "" ""); QXmppRegisterIq iq; parsePacket(iq, xml); QCOMPARE(iq.id(), QLatin1String("reg4")); QCOMPARE(iq.to(), QLatin1String("contests.shakespeare.lit")); QCOMPARE(iq.from(), QLatin1String("juliet@capulet.com/balcony")); QCOMPARE(iq.type(), QXmppIq::Set); QVERIFY(iq.username().isNull()); QVERIFY(iq.password().isNull()); QVERIFY(iq.email().isNull()); QVERIFY(!iq.form().isNull()); serializePacket(iq, xml); QXmppRegisterIq sIq; sIq.setId(QLatin1String("reg4")); sIq.setTo(QLatin1String("contests.shakespeare.lit")); sIq.setFrom(QLatin1String("juliet@capulet.com/balcony")); sIq.setType(QXmppIq::Set); sIq.setForm(QXmppDataForm( QXmppDataForm::Submit, QList() << QXmppDataForm::Field( QXmppDataForm::Field::HiddenField, QStringLiteral("FORM_TYPE"), QStringLiteral("jabber:iq:register")) << QXmppDataForm::Field( QXmppDataForm::Field::TextSingleField, QStringLiteral("first"), QStringLiteral("Juliet"), false, QStringLiteral("Given Name")) << QXmppDataForm::Field( QXmppDataForm::Field::TextSingleField, QStringLiteral("last"), QStringLiteral("Capulet"), false, QStringLiteral("Family Name")) << QXmppDataForm::Field( QXmppDataForm::Field::TextSingleField, QStringLiteral("email"), QStringLiteral("juliet@capulet.com"), false, QStringLiteral("Email Address")) << QXmppDataForm::Field( QXmppDataForm::Field::ListSingleField, QStringLiteral("x-gender"), QStringLiteral("F"), false, QStringLiteral("Gender")))); serializePacket(sIq, xml); } void tst_QXmppRegisterIq::testBobData() { const QByteArray xml = QByteArrayLiteral( "" "" "" "iVBORw0KGgoAAAANSUhEUgAAALQAAAA8BAMAAAA9AI20AAAAG1BMVEX///8AAADf39+" "/v79/f39fX1+fn58/Pz8fHx/8ACGJAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADS0lEQV" "RYhe2WS3MSQRCAYTf7OKY1kT0CxsRjHmh5BENIjqEk6pHVhFzdikqO7CGyP9t59Ox2z" "y6UeWBVqugLzM70Nz39mqnV1lIWgBWiYXV0BYfNZ0mvwypds1r62vH/gf76ZL/88Qlc" "41zeAnQrpx5H3z1Npfr5ovmHusa9SpRiNNIOcdrto6PJ5LLfb5bp9zM+VDq/vptxDEa" "a1sql9I3R5KhtfQsA5gNCWYyulV3TyTUDdfL56BvdDl4x7RiybDq9uBgxh1TTPUHDvA" "qNQb+LpT5sWehxJZKKcU2MZ6sDE7PMgW2mdlBGdy6ODe6fJFdMI+us95dNqftDMdwU6" "+MhpuTS9slcy5TFAcwq0Jt6qssJMTQGp4BGURlmSsNoo5oHL4kqc66NdkDO75mIfCxm" "RAlvHxMLdcb7JONavMJbttXXKoMSneYu3OQTlwkUh4mNayi6js55/2VcsZOQfXIYelz" "xLcntEGc3WVCsCORJVCc5r0ajAcq+EO1Q0oPm7n7+X/3jEReGdL6qT7Ml6FCjY+quJC" "r+D01f6BG0SaHG56ZG32DnY2jcEV1+pU0kxTaEwaGcekN7jyu50U/TV4q6YeieyiNTu" "klDKZLukyjKVNwotCUB3B0XO1WjHT3c0DHSO2zACwut8GOiljJIHaJsrlof/fpWNzGM" "os6TgIY0hZNpJshzSi4igOhy3cl4qK+YgnqHkAYcZEgdW6/HyrEK7afoY7RCFzArLl2" "LLDdrdmmHZfROajwIDfWj8yQG+rzwlA3WvdJiMHtjUekiNrp1oCbmyZDEyKROGjFVDr" "PRzlkR9UAfG/OErnPxrop5BwpoEpXQorq2zcGxbnBJndx8Bh0yljGiGv0B4E8+YP3Xp" "2rGydZNy4csW8W2pIvWhvijoujRJ0luXsoymV+8AXvE9HjII72+oReS6OfomHe3xWg/" "f2coSbDa1XZ1CvGMjy1nH9KBl83oPnQKi+vAXKLjCrRvvT2WCMkPmSFbquiVuTH1qjv" "p4j/u7CWyI5/Hn3KAaJJ90eP0Zp1Kjets4WPaElkxheF7cpBESzXuIdLwyFjSub07tB" "6JjxH3DGiu+zwHHimdtFsMvKqG/nBxm2TwbvyU6LWs5RnJX4dSldg3QhDLAAAAAElFT" "kSuQmCC" "" "" ""); QXmppBitsOfBinaryData data; data.setCid(QXmppBitsOfBinaryContentId::fromContentId( QStringLiteral("sha1+5a4c38d44fc64805cbb2d92d8b208be13ff40c0f@bob.xmpp.org"))); data.setContentType(QMimeDatabase().mimeTypeForName(QStringLiteral("image/png"))); data.setData(QByteArray::fromBase64(QByteArrayLiteral( "iVBORw0KGgoAAAANSUhEUgAAALQAAAA8BAMAAAA9AI20AAAAG1BMVEX///8AAADf39+" "/v79/f39fX1+fn58/Pz8fHx/8ACGJAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADS0lEQV" "RYhe2WS3MSQRCAYTf7OKY1kT0CxsRjHmh5BENIjqEk6pHVhFzdikqO7CGyP9t59Ox2z" "y6UeWBVqugLzM70Nz39mqnV1lIWgBWiYXV0BYfNZ0mvwypds1r62vH/gf76ZL/88Qlc" "41zeAnQrpx5H3z1Npfr5ovmHusa9SpRiNNIOcdrto6PJ5LLfb5bp9zM+VDq/vptxDEa" "a1sql9I3R5KhtfQsA5gNCWYyulV3TyTUDdfL56BvdDl4x7RiybDq9uBgxh1TTPUHDvA" "qNQb+LpT5sWehxJZKKcU2MZ6sDE7PMgW2mdlBGdy6ODe6fJFdMI+us95dNqftDMdwU6" "+MhpuTS9slcy5TFAcwq0Jt6qssJMTQGp4BGURlmSsNoo5oHL4kqc66NdkDO75mIfCxm" "RAlvHxMLdcb7JONavMJbttXXKoMSneYu3OQTlwkUh4mNayi6js55/2VcsZOQfXIYelz" "xLcntEGc3WVCsCORJVCc5r0ajAcq+EO1Q0oPm7n7+X/3jEReGdL6qT7Ml6FCjY+quJC" "r+D01f6BG0SaHG56ZG32DnY2jcEV1+pU0kxTaEwaGcekN7jyu50U/TV4q6YeieyiNTu" "klDKZLukyjKVNwotCUB3B0XO1WjHT3c0DHSO2zACwut8GOiljJIHaJsrlof/fpWNzGM" "os6TgIY0hZNpJshzSi4igOhy3cl4qK+YgnqHkAYcZEgdW6/HyrEK7afoY7RCFzArLl2" "LLDdrdmmHZfROajwIDfWj8yQG+rzwlA3WvdJiMHtjUekiNrp1oCbmyZDEyKROGjFVDr" "PRzlkR9UAfG/OErnPxrop5BwpoEpXQorq2zcGxbnBJndx8Bh0yljGiGv0B4E8+YP3Xp" "2rGydZNy4csW8W2pIvWhvijoujRJ0luXsoymV+8AXvE9HjII72+oReS6OfomHe3xWg/" "f2coSbDa1XZ1CvGMjy1nH9KBl83oPnQKi+vAXKLjCrRvvT2WCMkPmSFbquiVuTH1qjv" "p4j/u7CWyI5/Hn3KAaJJ90eP0Zp1Kjets4WPaElkxheF7cpBESzXuIdLwyFjSub07tB" "6JjxH3DGiu+zwHHimdtFsMvKqG/nBxm2TwbvyU6LWs5RnJX4dSldg3QhDLAAAAAElFT" "kSuQmCC"))); QXmppRegisterIq parsedIq; parsePacket(parsedIq, xml); QCOMPARE(parsedIq.type(), QXmppIq::Result); QCOMPARE(parsedIq.id(), QStringLiteral("")); QCOMPARE(parsedIq.bitsOfBinaryData().size(), 1); QCOMPARE(parsedIq.bitsOfBinaryData().first().cid().algorithm(), data.cid().algorithm()); QCOMPARE(parsedIq.bitsOfBinaryData().first().cid().hash(), data.cid().hash()); QCOMPARE(parsedIq.bitsOfBinaryData().first().cid(), data.cid()); QCOMPARE(parsedIq.bitsOfBinaryData().first().contentType(), data.contentType()); QCOMPARE(parsedIq.bitsOfBinaryData().first().maxAge(), data.maxAge()); QCOMPARE(parsedIq.bitsOfBinaryData().first().data(), data.data()); QCOMPARE(parsedIq.bitsOfBinaryData().first(), data); serializePacket(parsedIq, xml); QXmppRegisterIq iq; iq.setType(QXmppIq::Result); iq.setId(QStringLiteral("")); QXmppBitsOfBinaryDataList bobDataList; bobDataList << data; iq.setBitsOfBinaryData(bobDataList); serializePacket(iq, xml); QXmppRegisterIq iq2; iq2.setType(QXmppIq::Result); iq2.setId(QStringLiteral("")); iq2.bitsOfBinaryData() << data; serializePacket(iq2, xml); // test const getter const QXmppRegisterIq constIq = iq; QCOMPARE(constIq.bitsOfBinaryData(), iq.bitsOfBinaryData()); } void tst_QXmppRegisterIq::testRegistered() { const QByteArray xml = QByteArrayLiteral( "" "" "" "juliet" "" ""); QXmppRegisterIq iq; parsePacket(iq, xml); QVERIFY(iq.isRegistered()); QCOMPARE(iq.username(), QStringLiteral("juliet")); serializePacket(iq, xml); iq = QXmppRegisterIq(); iq.setId(QStringLiteral("")); iq.setType(QXmppIq::Result); iq.setIsRegistered(true); iq.setUsername(QStringLiteral("juliet")); serializePacket(iq, xml); } void tst_QXmppRegisterIq::testRemove() { const QByteArray xml = QByteArrayLiteral( "" "" "" "juliet" "" ""); QXmppRegisterIq iq; parsePacket(iq, xml); QVERIFY(iq.isRemove()); QCOMPARE(iq.username(), QStringLiteral("juliet")); serializePacket(iq, xml); iq = QXmppRegisterIq(); iq.setId(QStringLiteral("")); iq.setType(QXmppIq::Result); iq.setIsRemove(true); iq.setUsername(QStringLiteral("juliet")); serializePacket(iq, xml); } void tst_QXmppRegisterIq::testChangePassword() { const QByteArray xml = QByteArrayLiteral( "" "" "bill" "m1cr0$0ft" "" ""); auto iq = QXmppRegisterIq::createChangePasswordRequest( QStringLiteral("bill"), QStringLiteral("m1cr0$0ft"), QStringLiteral("shakespeare.lit")); iq.setId(QStringLiteral("changePassword1")); serializePacket(iq, xml); } void tst_QXmppRegisterIq::testUnregistration() { const QByteArray xml = QByteArrayLiteral( "" "" "" "" ""); auto iq = QXmppRegisterIq::createUnregistrationRequest(QStringLiteral("shakespeare.lit")); iq.setId(QStringLiteral("unreg1")); serializePacket(iq, xml); } QTEST_MAIN(tst_QXmppRegisterIq) #include "tst_qxmppregisteriq.moc" qxmpp-1.4.0/tests/qxmppregistrationmanager/000077500000000000000000000000001402370562100211565ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppregistrationmanager/tst_qxmppregistrationmanager.cpp000066400000000000000000000370271402370562100277200ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Linus Jahn * * 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 "QXmppClient.h" #include "QXmppDiscoveryManager.h" #include "QXmppRegistrationManager.h" #include "QXmppStreamFeatures.h" #include "util.h" class tst_QXmppRegistrationManager : public QObject { Q_OBJECT private slots: void initTestCase(); void testDiscoFeatures(); void testChangePassword_data(); void testChangePassword(); void testDeleteAccount(); void testRequestRegistrationForm_data(); void testRequestRegistrationForm(); void testRegisterOnConnectGetSet(); void testServiceDiscovery(); void testSendCachedRegistrationForm_data(); void testSendCachedRegistrationForm(); void testStreamFeaturesCheck_data(); void testStreamFeaturesCheck(); void testRegistrationResult_data(); void testRegistrationResult(); void testChangePasswordResult_data(); void testChangePasswordResult(); void testDeleteAccountResult_data(); void testDeleteAccountResult(); void testRegistrationFormReceived(); void sendStreamFeaturesToManager(bool registrationEnabled = true); void setManagerConfig(const QString &username, const QString &server = QStringLiteral("example.org"), const QString &password = {}); private: QXmppClient client; QXmppLogger logger; QXmppRegistrationManager *manager; QString expectedXml; }; void tst_QXmppRegistrationManager::initTestCase() { manager = new QXmppRegistrationManager; client.addExtension(manager); logger.setLoggingType(QXmppLogger::SignalLogging); client.setLogger(&logger); } void tst_QXmppRegistrationManager::testDiscoFeatures() { QCOMPARE(manager->discoveryFeatures(), QStringList() << "jabber:iq:register"); } void tst_QXmppRegistrationManager::testChangePassword_data() { QTest::addColumn("username"); QTest::addColumn("password"); #define ROW(name, username, password) \ QTest::newRow(name) << QStringLiteral(username) << QStringLiteral(password) ROW("user-bill", "bill", "m1cr0$0ft"); ROW("user-alice", "alice", "bitten-apple"); #undef ROW } void tst_QXmppRegistrationManager::testChangePassword() { QFETCH(QString, username); QFETCH(QString, password); setManagerConfig(username, QStringLiteral("example.org"), password); QObject *context = new QObject(this); connect(&logger, &QXmppLogger::message, context, [=](QXmppLogger::MessageType type, const QString &text) { QCOMPARE(type, QXmppLogger::SentMessage); QXmppRegisterIq iq; parsePacket(iq, text.toUtf8()); QVERIFY(!iq.id().isEmpty()); QCOMPARE(iq.type(), QXmppIq::Set); QCOMPARE(iq.username(), username); QCOMPARE(iq.password(), password); delete context; // disconnects lambda }); manager->changePassword(password); } void tst_QXmppRegistrationManager::testDeleteAccount() { setManagerConfig(QStringLiteral("bob")); QObject *context = new QObject(this); connect(&logger, &QXmppLogger::message, context, [=](QXmppLogger::MessageType type, const QString &text) { QCOMPARE(type, QXmppLogger::SentMessage); QXmppRegisterIq iq; parsePacket(iq, text.toUtf8()); QVERIFY(!iq.id().isEmpty()); // to address must be the server or empty QVERIFY(iq.to() == QStringLiteral("example.org") || iq.to().isEmpty()); QCOMPARE(iq.type(), QXmppIq::Set); QVERIFY(iq.isRemove()); delete context; // disconnects lambda }); manager->deleteAccount(); } void tst_QXmppRegistrationManager::testRequestRegistrationForm_data() { QTest::addColumn("triggerManually"); #define ROW(name, enabled) \ QTest::newRow(name) << enabled ROW("trigger-manually", true); ROW("request-form-upon-stream-features", false); #undef ROW } void tst_QXmppRegistrationManager::testRequestRegistrationForm() { QFETCH(bool, triggerManually); setManagerConfig(QStringLiteral("bob")); manager->setRegistrationFormToSend(QXmppRegisterIq()); manager->setRegisterOnConnectEnabled(true); bool signalCalled = false; QObject *context = new QObject(this); connect(&logger, &QXmppLogger::message, context, [&](QXmppLogger::MessageType type, const QString &text) { if (type == QXmppLogger::SentMessage) { signalCalled = true; QVERIFY(text.contains(QStringLiteral(""))); QXmppRegisterIq iq; parsePacket(iq, text.toUtf8()); QVERIFY(!iq.id().isEmpty()); QCOMPARE(iq.type(), QXmppIq::Get); } }); if (triggerManually) manager->requestRegistrationForm(); else sendStreamFeaturesToManager(true); QVERIFY(signalCalled); delete context; manager->setRegisterOnConnectEnabled(false); } void tst_QXmppRegistrationManager::testRegisterOnConnectGetSet() { manager->setRegisterOnConnectEnabled(true); QVERIFY(manager->registerOnConnectEnabled()); manager->setRegisterOnConnectEnabled(false); QVERIFY(!manager->registerOnConnectEnabled()); } void tst_QXmppRegistrationManager::testServiceDiscovery() { setManagerConfig(QStringLiteral("bob")); bool signalEmitted = false; QObject *context = new QObject(this); connect(manager, &QXmppRegistrationManager::supportedByServerChanged, context, [&]() { signalEmitted = true; QCOMPARE(manager->supportedByServer(), true); }); QXmppDiscoveryIq iq; iq.setType(QXmppIq::Result); iq.setFrom(QStringLiteral("example.org")); iq.setTo(QStringLiteral("bob@example.org")); iq.setQueryType(QXmppDiscoveryIq::InfoQuery); iq.setFeatures(QStringList() << QStringLiteral("jabber:iq:register")); client.findExtension()->handleStanza(writePacketToDom(iq)); QVERIFY(signalEmitted); QVERIFY(manager->supportedByServer()); delete context; // on disconnect, supportedByServer needs to be reset emit client.disconnected(); QVERIFY(!manager->supportedByServer()); } void tst_QXmppRegistrationManager::testSendCachedRegistrationForm_data() { QTest::addColumn("triggerSendingManually"); #define ROW(name, enabled) \ QTest::newRow(name) << enabled ROW("manually-trigger-sending", true); ROW("sending-upon-correct-stream-features", false); #undef ROW } void tst_QXmppRegistrationManager::testSendCachedRegistrationForm() { QFETCH(bool, triggerSendingManually); setManagerConfig(QStringLiteral("bob")); QXmppRegisterIq iq; iq.setUsername(QStringLiteral("someone")); iq.setPassword(QStringLiteral("s3cr3t")); iq.setEmail(QStringLiteral("1234@example.org")); bool signalCalled = false; QObject *context = new QObject(this); connect(&logger, &QXmppLogger::message, context, [&](QXmppLogger::MessageType type, const QString &text) { if (type == QXmppLogger::SentMessage) { signalCalled = true; QXmppRegisterIq parsedIq; parsePacket(parsedIq, text.toUtf8()); QCOMPARE(parsedIq.id(), iq.id()); QCOMPARE(parsedIq.type(), QXmppIq::Set); QCOMPARE(parsedIq.username(), QStringLiteral("someone")); QCOMPARE(parsedIq.password(), QStringLiteral("s3cr3t")); QCOMPARE(parsedIq.email(), QStringLiteral("1234@example.org")); } }); manager->setRegistrationFormToSend(iq); if (triggerSendingManually) manager->sendCachedRegistrationForm(); else sendStreamFeaturesToManager(true); delete context; } void tst_QXmppRegistrationManager::testStreamFeaturesCheck_data() { QTest::addColumn("registrationEnabled"); #define ROW(name, enabled) \ QTest::newRow(name) << enabled ROW("registration-enabled", true); ROW("registration-disabled", false); #undef ROW } void tst_QXmppRegistrationManager::testStreamFeaturesCheck() { QFETCH(bool, registrationEnabled); bool signalEmitted = false; QObject *context = new QObject(this); connect(manager, &QXmppRegistrationManager::registrationFailed, context, [&](const QXmppStanza::Error &error) { signalEmitted = true; QCOMPARE(error.type(), QXmppStanza::Error::Cancel); QCOMPARE(error.condition(), QXmppStanza::Error::FeatureNotImplemented); }); manager->setRegisterOnConnectEnabled(true); sendStreamFeaturesToManager(registrationEnabled); QCOMPARE(signalEmitted, !registrationEnabled); delete context; manager->setRegisterOnConnectEnabled(false); } void tst_QXmppRegistrationManager::testRegistrationResult_data() { QTest::addColumn("isSuccess"); #define ROW(name, isSuccess) \ QTest::newRow(name) << isSuccess ROW("success", true); ROW("error", false); #undef ROW } void tst_QXmppRegistrationManager::testRegistrationResult() { QFETCH(bool, isSuccess); QXmppRegisterIq registrationRequestForm; registrationRequestForm.setUsername(QStringLiteral("someone")); registrationRequestForm.setPassword(QStringLiteral("s3cr3t")); registrationRequestForm.setEmail(QStringLiteral("1234@example.org")); registrationRequestForm.setId(QStringLiteral("register1")); bool succeededSignalCalled = false; bool failedSignalCalled = false; QObject *context = new QObject(this); connect(manager, &QXmppRegistrationManager::registrationSucceeded, context, [&]() { succeededSignalCalled = true; }); connect(manager, &QXmppRegistrationManager::registrationFailed, context, [&](const QXmppStanza::Error &) { failedSignalCalled = true; }); manager->setRegistrationFormToSend(registrationRequestForm); manager->sendCachedRegistrationForm(); QXmppIq serverResult(isSuccess ? QXmppIq::Result : QXmppIq::Error); serverResult.setId(registrationRequestForm.id()); manager->handleStanza(writePacketToDom(serverResult)); QCOMPARE(succeededSignalCalled, isSuccess); QCOMPARE(failedSignalCalled, !isSuccess); delete context; } void tst_QXmppRegistrationManager::testChangePasswordResult_data() { QTest::addColumn("isSuccess"); #define ROW(name, isSuccess) \ QTest::newRow(name) << isSuccess ROW("success", true); ROW("error", false); #undef ROW } void tst_QXmppRegistrationManager::testChangePasswordResult() { QFETCH(bool, isSuccess); QString changePasswordRequestIqId; bool requestSentSignalCalled = false; QObject *requestSentSignalContext = new QObject(this); connect(&logger, &QXmppLogger::message, requestSentSignalContext, [&](QXmppLogger::MessageType type, const QString &text) { if (type == QXmppLogger::SentMessage) { requestSentSignalCalled = true; QXmppIq parsedIq; parsePacket(parsedIq, text.toUtf8()); changePasswordRequestIqId = parsedIq.id(); } }); manager->changePassword({}); QVERIFY(requestSentSignalCalled); QVERIFY(!changePasswordRequestIqId.isEmpty()); delete requestSentSignalContext; bool resultSignalCalled = false; QObject *resultContext = new QObject(this); if (isSuccess) { connect(manager, &QXmppRegistrationManager::passwordChanged, resultContext, [&](const QString &) { resultSignalCalled = true; }); } else { connect(manager, &QXmppRegistrationManager::passwordChangeFailed, resultContext, [&](QXmppStanza::Error) { resultSignalCalled = true; }); } QXmppIq serverResult(isSuccess ? QXmppIq::Result : QXmppIq::Error); serverResult.setId(changePasswordRequestIqId); manager->handleStanza(writePacketToDom(serverResult)); QVERIFY(resultSignalCalled); delete resultContext; } void tst_QXmppRegistrationManager::testDeleteAccountResult_data() { QTest::addColumn("isSuccess"); #define ROW(name, isSuccess) \ QTest::newRow(name) << isSuccess ROW("success", true); ROW("error", false); #undef ROW } void tst_QXmppRegistrationManager::testDeleteAccountResult() { QFETCH(bool, isSuccess); QString deleteAccountRequestIqId; bool requestSentSignalCalled = false; QObject *requestSentSignalContext = new QObject(this); connect(&logger, &QXmppLogger::message, requestSentSignalContext, [&](QXmppLogger::MessageType type, const QString &text) { if (type == QXmppLogger::SentMessage) { requestSentSignalCalled = true; QXmppIq parsedIq; parsePacket(parsedIq, text.toUtf8()); deleteAccountRequestIqId = parsedIq.id(); } }); manager->deleteAccount(); QVERIFY(requestSentSignalCalled); QVERIFY(!deleteAccountRequestIqId.isEmpty()); delete requestSentSignalContext; bool resultSignalCalled = false; QObject *resultContext = new QObject(this); if (isSuccess) { connect(manager, &QXmppRegistrationManager::accountDeleted, resultContext, [&]() { resultSignalCalled = true; }); } else { connect(manager, &QXmppRegistrationManager::accountDeletionFailed, resultContext, [&](QXmppStanza::Error) { resultSignalCalled = true; }); } QXmppIq serverResult(isSuccess ? QXmppIq::Result : QXmppIq::Error); serverResult.setId(deleteAccountRequestIqId); manager->handleStanza(writePacketToDom(serverResult)); QVERIFY(resultSignalCalled); delete resultContext; } void tst_QXmppRegistrationManager::testRegistrationFormReceived() { QXmppRegisterIq iq; iq.setUsername(""); iq.setPassword(""); bool signalCalled = false; QObject *context = new QObject(this); connect(manager, &QXmppRegistrationManager::registrationFormReceived, context, [&](const QXmppRegisterIq &) { signalCalled = true; QCOMPARE(iq.username(), QStringLiteral("")); QCOMPARE(iq.password(), QStringLiteral("")); }); manager->handleStanza(writePacketToDom(iq)); delete context; } void tst_QXmppRegistrationManager::sendStreamFeaturesToManager(bool registrationEnabled) { QXmppStreamFeatures features; features.setBindMode(QXmppStreamFeatures::Enabled); if (registrationEnabled) features.setRegisterMode(QXmppStreamFeatures::Enabled); auto writeFeaturesToDom = [&]() -> QDomElement { QBuffer buffer; buffer.open(QIODevice::ReadWrite); QXmlStreamWriter writer(&buffer); features.toXml(&writer); // hacky hack to include stream namespace QByteArray manipulatedXml = buffer.data(); manipulatedXml.replace("stream:", QByteArray()); manipulatedXml.insert(9, QByteArrayLiteral(" xmlns=\"http://etherx.jabber.org/streams\"")); QDomDocument doc; doc.setContent(manipulatedXml, true); return doc.documentElement(); }; manager->handleStanza(writeFeaturesToDom()); } void tst_QXmppRegistrationManager::setManagerConfig(const QString &username, const QString &server, const QString &password) { client.connectToServer(username + u'@' + server, password); client.disconnectFromServer(); } QTEST_MAIN(tst_QXmppRegistrationManager) #include "tst_qxmppregistrationmanager.moc" qxmpp-1.4.0/tests/qxmppresultset/000077500000000000000000000000001402370562100171435ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppresultset/tst_qxmppresultset.cpp000066400000000000000000000142721402370562100236670ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Olivier Goffart * Jeremy Lainé * * 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 "QXmppResultSet.h" #include "util.h" #include class tst_QXmppResultSet : public QObject { Q_OBJECT private slots: void testQuery_data(); void testQuery(); void testReply_data(); void testReply(); }; void tst_QXmppResultSet::testQuery_data() { QTest::addColumn("xml"); QTest::addColumn("max"); QTest::addColumn("index"); QTest::addColumn("before"); QTest::addColumn("after"); QTest::newRow("Example 3") << QByteArray("" "10" "") << 10 << -1 << QString() << QString(); QTest::newRow("Example 5") << QByteArray("" "10" "peterpan@neverland.lit" "") << 10 << -1 << QString() << QString("peterpan@neverland.lit"); QTest::newRow("Example 5") << QByteArray("" "10" "peter@pixyland.org" "") << 10 << -1 << QString("peter@pixyland.org") << QString(); QTest::newRow("Example 11") << QByteArray("" "10" "" "") << 10 << -1 << QString("") << QString(); QTest::newRow("Example 12") << QByteArray("" "10" "371" "") << 10 << 371 << QString() << QString(); QTest::newRow("Example 15") << QByteArray("" "0" "") << 0 << -1 << QString() << QString(); } void tst_QXmppResultSet::testQuery() { QFETCH(QByteArray, xml); QFETCH(int, max); QFETCH(int, index); QFETCH(QString, before); QFETCH(QString, after); QXmppResultSetQuery iq; parsePacket(iq, xml); QCOMPARE(iq.max(), max); QCOMPARE(iq.index(), index); QCOMPARE(iq.before(), before); QCOMPARE(iq.before().isNull(), before.isNull()); QCOMPARE(iq.after(), after); QCOMPARE(iq.after().isNull(), after.isNull()); serializePacket(iq, xml); } void tst_QXmppResultSet::testReply_data() { QTest::addColumn("xml"); QTest::addColumn("count"); QTest::addColumn("index"); QTest::addColumn("first"); QTest::addColumn("last"); QTest::newRow("Example 4") << QByteArray("" "stpeter@jabber.org" "peterpan@neverland.lit" "800" "") << 800 << 0 << QString("stpeter@jabber.org") << QString("peterpan@neverland.lit"); QTest::newRow("Example 6") << QByteArray("" "stpeter@jabber.org" "peterpan@neverland.lit" "800" "") << 800 << 0 << QString("stpeter@jabber.org") << QString("peterpan@neverland.lit"); QTest::newRow("Example 4") << QByteArray("" "peter@pixyland.org" "peter@rabbit.lit" "800" "") << 800 << 10 << QString("peter@pixyland.org") << QString("peter@rabbit.lit"); QTest::newRow("Example 7") << QByteArray("" "790" "") << 790 << -1 << QString() << QString(); } void tst_QXmppResultSet::testReply() { QFETCH(QByteArray, xml); QFETCH(int, count); QFETCH(int, index); QFETCH(QString, first); QFETCH(QString, last); QXmppResultSetReply iq; parsePacket(iq, xml); QCOMPARE(iq.count(), count); QCOMPARE(iq.index(), index); QCOMPARE(iq.first(), first); QCOMPARE(iq.first().isNull(), first.isNull()); QCOMPARE(iq.last(), last); QCOMPARE(iq.last().isNull(), last.isNull()); serializePacket(iq, xml); } QTEST_MAIN(tst_QXmppResultSet) #include "tst_qxmppresultset.moc" qxmpp-1.4.0/tests/qxmpprosteriq/000077500000000000000000000000001402370562100167615ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmpprosteriq/tst_qxmpprosteriq.cpp000066400000000000000000000150271402370562100233220ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppRosterIq.h" #include "util.h" #include class tst_QXmppRosterIq : public QObject { Q_OBJECT private slots: void testItem_data(); void testItem(); void testApproved_data(); void testApproved(); void testVersion_data(); void testVersion(); void testMixAnnotate(); void testMixChannel(); }; void tst_QXmppRosterIq::testItem_data() { QTest::addColumn("xml"); QTest::addColumn("name"); QTest::addColumn("subscriptionStatus"); QTest::addColumn("subscriptionType"); QTest::addColumn("approved"); QTest::newRow("none") << QByteArray(R"()") << "" << "" << int(QXmppRosterIq::Item::None) << true; QTest::newRow("from") << QByteArray(R"()") << "" << "" << int(QXmppRosterIq::Item::From) << false; QTest::newRow("to") << QByteArray(R"()") << "" << "" << int(QXmppRosterIq::Item::To) << false; QTest::newRow("both") << QByteArray(R"()") << "" << "" << int(QXmppRosterIq::Item::Both) << false; QTest::newRow("remove") << QByteArray(R"()") << "" << "" << int(QXmppRosterIq::Item::Remove) << false; QTest::newRow("notset") << QByteArray("") << "" << "" << int(QXmppRosterIq::Item::NotSet) << false; QTest::newRow("ask-subscribe") << QByteArray("") << "" << "subscribe" << int(QXmppRosterIq::Item::NotSet) << false; QTest::newRow("ask-unsubscribe") << QByteArray("") << "" << "unsubscribe" << int(QXmppRosterIq::Item::NotSet) << false; QTest::newRow("name") << QByteArray(R"()") << "foo bar" << "" << int(QXmppRosterIq::Item::NotSet) << false; } void tst_QXmppRosterIq::testItem() { QFETCH(QByteArray, xml); QFETCH(QString, name); QFETCH(QString, subscriptionStatus); QFETCH(int, subscriptionType); QFETCH(bool, approved); QXmppRosterIq::Item item; parsePacket(item, xml); QCOMPARE(item.bareJid(), QLatin1String("foo@example.com")); QCOMPARE(item.groups(), QSet()); QCOMPARE(item.name(), name); QCOMPARE(item.subscriptionStatus(), subscriptionStatus); QCOMPARE(int(item.subscriptionType()), subscriptionType); QCOMPARE(item.isApproved(), approved); serializePacket(item, xml); item = QXmppRosterIq::Item(); item.setBareJid("foo@example.com"); item.setName(name); item.setSubscriptionStatus(subscriptionStatus); item.setSubscriptionType(QXmppRosterIq::Item::SubscriptionType(subscriptionType)); item.setIsApproved(approved); serializePacket(item, xml); } void tst_QXmppRosterIq::testApproved_data() { QTest::addColumn("xml"); QTest::addColumn("approved"); QTest::newRow("true") << QByteArray(R"()") << true; QTest::newRow("1") << QByteArray(R"()") << true; QTest::newRow("false") << QByteArray(R"()") << false; QTest::newRow("0") << QByteArray(R"()") << false; QTest::newRow("empty") << QByteArray(R"()") << false; } void tst_QXmppRosterIq::testApproved() { QFETCH(QByteArray, xml); QFETCH(bool, approved); QXmppRosterIq::Item item; parsePacket(item, xml); QCOMPARE(item.isApproved(), approved); } void tst_QXmppRosterIq::testVersion_data() { QTest::addColumn("xml"); QTest::addColumn("version"); QTest::newRow("noversion") << QByteArray(R"()") << ""; QTest::newRow("version") << QByteArray(R"()") << "3345678"; } void tst_QXmppRosterIq::testVersion() { QFETCH(QByteArray, xml); QFETCH(QString, version); QXmppRosterIq iq; parsePacket(iq, xml); QCOMPARE(iq.version(), version); serializePacket(iq, xml); } void tst_QXmppRosterIq::testMixAnnotate() { const QByteArray xml( "" "" "" "" "" ); 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( "" "" "" ); 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" qxmpp-1.4.0/tests/qxmpprostermanager/000077500000000000000000000000001402370562100177625ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmpprostermanager/tst_qxmpprostermanager.cpp000066400000000000000000000064661402370562100253330ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Linus Jahn * * 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 "QXmppClient.h" #include "QXmppDiscoveryManager.h" #include "QXmppRosterManager.h" #include "util.h" class tst_QXmppRosterManager : public QObject { Q_OBJECT private slots: void initTestCase(); void testDiscoFeatures(); void testRenameItem(); private: QXmppClient client; QXmppLogger logger; QXmppRosterManager *manager; }; void tst_QXmppRosterManager::initTestCase() { logger.setLoggingType(QXmppLogger::SignalLogging); client.setLogger(&logger); manager = client.findExtension(); } void tst_QXmppRosterManager::testDiscoFeatures() { QCOMPARE(manager->discoveryFeatures(), QStringList()); } void tst_QXmppRosterManager::testRenameItem() { // used to clean up lambda signal connections QObject context; auto createItem = [](const QString &jid, const QString &ask = {}) -> QXmppRosterIq::Item { QXmppRosterIq::Item item; item.setBareJid(jid); item.setSubscriptionStatus(ask); return item; }; // fill roster with initial contacts to rename QXmppRosterIq initialItems; initialItems.setType(QXmppIq::Result); initialItems.addItem(createItem("stpeter@jabber.org")); initialItems.addItem(createItem("bob@qxmpp.org")); QVERIFY(manager->handleStanza(writePacketToDom(initialItems))); // set a subscription state for bob (the subscription state MUST NOT be // sent when renaming an item, so we need to check that it's not) QXmppRosterIq bobAsk; bobAsk.setType(QXmppIq::Set); bobAsk.addItem(createItem("bob@qxmpp.org", "subscribe")); QVERIFY(manager->handleStanza(writePacketToDom(bobAsk))); QCOMPARE(manager->getRosterEntry("bob@qxmpp.org").subscriptionStatus(), QStringLiteral("subscribe")); // rename bob bool requestSent = false; connect(&logger, &QXmppLogger::message, &context, [&](QXmppLogger::MessageType type, const QString &text) { if (type == QXmppLogger::SentMessage) { requestSent = true; QXmppRosterIq renameRequest; parsePacket(renameRequest, text.toUtf8()); QCOMPARE(renameRequest.items().size(), 1); QCOMPARE(renameRequest.items().first().bareJid(), QStringLiteral("bob@qxmpp.org")); QCOMPARE(renameRequest.items().first().name(), QStringLiteral("Bob")); // check that subscription state ('ask') for bob is not included QVERIFY(renameRequest.items().first().subscriptionStatus().isNull()); } }); manager->renameItem("bob@qxmpp.org", "Bob"); QVERIFY(requestSent); } QTEST_MAIN(tst_QXmppRosterManager) #include "tst_qxmpprostermanager.moc" qxmpp-1.4.0/tests/qxmpprpciq/000077500000000000000000000000001402370562100162275ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmpprpciq/tst_qxmpprpciq.cpp000066400000000000000000000145021402370562100220330ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppRpcIq.h" #include "util.h" #include static void checkVariant(const QVariant &value, const QByteArray &xml) { // serialise QBuffer buffer; buffer.open(QIODevice::ReadWrite); QXmlStreamWriter writer(&buffer); QXmppRpcMarshaller::marshall(&writer, value); qDebug() << "expect " << xml; qDebug() << "writing" << buffer.data(); QCOMPARE(buffer.data(), xml); // parse QDomDocument doc; QCOMPARE(doc.setContent(xml, true), true); QDomElement element = doc.documentElement(); QStringList errors; QVariant test = QXmppRpcMarshaller::demarshall(element, errors); if (!errors.isEmpty()) qDebug() << errors; QCOMPARE(errors, QStringList()); QCOMPARE(test, value); } class tst_QXmppRpcIq : public QObject { Q_OBJECT private slots: void testBase64(); void testBool(); void testDateTime(); void testDouble(); void testInt(); void testNil(); void testString(); void testArray(); void testStruct(); void testInvoke(); void testResponse(); void testResponseFault(); }; void tst_QXmppRpcIq::testBase64() { checkVariant(QByteArray("\0\1\2\3", 4), QByteArray("AAECAw==")); } void tst_QXmppRpcIq::testBool() { checkVariant(false, QByteArray("0")); checkVariant(true, QByteArray("1")); } void tst_QXmppRpcIq::testDateTime() { checkVariant(QDateTime(QDate(1998, 7, 17), QTime(14, 8, 55)), QByteArray("1998-07-17T14:08:55")); } void tst_QXmppRpcIq::testDouble() { checkVariant(double(-12.214), QByteArray("-12.214")); } void tst_QXmppRpcIq::testInt() { checkVariant(int(-12), QByteArray("-12")); } void tst_QXmppRpcIq::testNil() { checkVariant(QVariant(), QByteArray("")); } void tst_QXmppRpcIq::testString() { checkVariant(QString("hello world"), QByteArray("hello world")); } void tst_QXmppRpcIq::testArray() { checkVariant(QVariantList() << QString("hello world") << double(-12.214), QByteArray("" "hello world" "-12.214" "")); } void tst_QXmppRpcIq::testStruct() { QMap map; map["bar"] = QString("hello world"); map["foo"] = double(-12.214); checkVariant(map, QByteArray("" "" "bar" "hello world" "" "" "foo" "-12.214" "" "")); } void tst_QXmppRpcIq::testInvoke() { const QByteArray xml( "" "" "" "examples.getStateName" "" "" "6" "" "" "" "" ""); QXmppRpcInvokeIq iq; parsePacket(iq, xml); QCOMPARE(iq.method(), QLatin1String("examples.getStateName")); QCOMPARE(iq.arguments(), QVariantList() << int(6)); serializePacket(iq, xml); } void tst_QXmppRpcIq::testResponse() { const QByteArray xml( "" "" "" "" "" "Colorado" "" "" "" "" ""); QXmppRpcResponseIq iq; parsePacket(iq, xml); QCOMPARE(iq.faultCode(), 0); QCOMPARE(iq.faultString(), QString()); QCOMPARE(iq.values(), QVariantList() << QString("Colorado")); serializePacket(iq, xml); } void tst_QXmppRpcIq::testResponseFault() { const QByteArray xml( "" "" "" "" "" "" "" "faultCode" "404" "" "" "faultString" "Not found" "" "" "" "" "" "" ""); QXmppRpcResponseIq iq; parsePacket(iq, xml); QCOMPARE(iq.faultCode(), 404); QCOMPARE(iq.faultString(), QLatin1String("Not found")); QCOMPARE(iq.values(), QVariantList()); serializePacket(iq, xml); } QTEST_MAIN(tst_QXmppRpcIq) #include "tst_qxmpprpciq.moc" qxmpp-1.4.0/tests/qxmppsasl/000077500000000000000000000000001402370562100160535ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppsasl/tst_qxmppsasl.cpp000066400000000000000000000417271402370562100215140ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppSasl_p.h" #include "util.h" #include class tst_QXmppSasl : public QObject { Q_OBJECT private slots: void testParsing(); void testAuth_data(); void testAuth(); void testChallenge_data(); void testChallenge(); void testFailure(); void testResponse_data(); void testResponse(); void testSuccess(); // client void testClientAvailableMechanisms(); void testClientBadMechanism(); void testClientAnonymous(); void testClientDigestMd5(); void testClientDigestMd5_data(); void testClientFacebook(); void testClientGoogle(); void testClientPlain(); void testClientScramSha1(); void testClientScramSha1_bad(); void testClientScramSha256(); void testClientWindowsLive(); // server void testServerBadMechanism(); void testServerAnonymous(); void testServerDigestMd5(); void testServerPlain(); void testServerPlainChallenge(); }; void tst_QXmppSasl::testParsing() { // empty QMap empty = QXmppSaslDigestMd5::parseMessage(QByteArray()); QCOMPARE(empty.size(), 0); QCOMPARE(QXmppSaslDigestMd5::serializeMessage(empty), QByteArray()); // non-empty const QByteArray bytes("number=12345,quoted_plain=\"quoted string\",quoted_quote=\"quoted\\\\slash\\\"quote\",string=string"); QMap map = QXmppSaslDigestMd5::parseMessage(bytes); QCOMPARE(map.size(), 4); QCOMPARE(map["number"], QByteArray("12345")); QCOMPARE(map["quoted_plain"], QByteArray("quoted string")); QCOMPARE(map["quoted_quote"], QByteArray("quoted\\slash\"quote")); QCOMPARE(map["string"], QByteArray("string")); QCOMPARE(QXmppSaslDigestMd5::serializeMessage(map), bytes); } void tst_QXmppSasl::testAuth_data() { QTest::addColumn("xml"); QTest::addColumn("mechanism"); QTest::addColumn("value"); QTest::newRow("plain") << QByteArray("AGZvbwBiYXI=") << "PLAIN" << QByteArray("\0foo\0bar", 8); QTest::newRow("digest-md5") << QByteArray("") << "DIGEST-MD5" << QByteArray(); } void tst_QXmppSasl::testAuth() { QFETCH(QByteArray, xml); QFETCH(QString, mechanism); QFETCH(QByteArray, value); // no condition QXmppSaslAuth auth; parsePacket(auth, xml); QCOMPARE(auth.mechanism(), mechanism); QCOMPARE(auth.value(), value); serializePacket(auth, xml); } void tst_QXmppSasl::testChallenge_data() { QTest::addColumn("xml"); QTest::addColumn("value"); QTest::newRow("empty") << QByteArray("") << QByteArray(); QTest::newRow("value") << QByteArray("AGZvbwBiYXI=") << QByteArray("\0foo\0bar", 8); } void tst_QXmppSasl::testChallenge() { QFETCH(QByteArray, xml); QFETCH(QByteArray, value); // no condition QXmppSaslChallenge challenge; parsePacket(challenge, xml); QCOMPARE(challenge.value(), value); serializePacket(challenge, xml); } void tst_QXmppSasl::testFailure() { // no condition const QByteArray xml = ""; QXmppSaslFailure failure; parsePacket(failure, xml); QCOMPARE(failure.condition(), QString()); serializePacket(failure, xml); // not authorized const QByteArray xml2 = ""; QXmppSaslFailure failure2; parsePacket(failure2, xml2); QCOMPARE(failure2.condition(), QLatin1String("not-authorized")); serializePacket(failure2, xml2); } void tst_QXmppSasl::testResponse_data() { QTest::addColumn("xml"); QTest::addColumn("value"); QTest::newRow("empty") << QByteArray("") << QByteArray(); QTest::newRow("value") << QByteArray("AGZvbwBiYXI=") << QByteArray("\0foo\0bar", 8); } void tst_QXmppSasl::testResponse() { QFETCH(QByteArray, xml); QFETCH(QByteArray, value); // no condition QXmppSaslResponse response; parsePacket(response, xml); QCOMPARE(response.value(), value); serializePacket(response, xml); } void tst_QXmppSasl::testSuccess() { const QByteArray xml = ""; QXmppSaslSuccess stanza; parsePacket(stanza, xml); serializePacket(stanza, xml); } void tst_QXmppSasl::testClientAvailableMechanisms() { const QStringList expectedMechanisms = { #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 2) "SCRAM-SHA3-512", #endif "SCRAM-SHA-512", "SCRAM-SHA-256", "SCRAM-SHA-1", "DIGEST-MD5", "PLAIN", "ANONYMOUS", "X-FACEBOOK-PLATFORM", "X-MESSENGER-OAUTH2", "X-OAUTH2" }; QCOMPARE(QXmppSaslClient::availableMechanisms(), expectedMechanisms); } void tst_QXmppSasl::testClientBadMechanism() { QXmppSaslClient *client = QXmppSaslClient::create("BAD-MECH"); QVERIFY(client == 0); } void tst_QXmppSasl::testClientAnonymous() { QXmppSaslClient *client = QXmppSaslClient::create("ANONYMOUS"); QVERIFY(client != 0); QCOMPARE(client->mechanism(), QLatin1String("ANONYMOUS")); // initial step returns nothing QByteArray response; QVERIFY(client->respond(QByteArray(), response)); QCOMPARE(response, QByteArray()); // any further step is an error QVERIFY(!client->respond(QByteArray(), response)); delete client; } void tst_QXmppSasl::testClientDigestMd5_data() { QTest::addColumn("qop"); QTest::newRow("qop-none") << QByteArray(); QTest::newRow("qop-auth") << QByteArray(",qop=\"auth\""); QTest::newRow("qop-multi") << QByteArray(",qop=\"auth,auth-int\""); } void tst_QXmppSasl::testClientDigestMd5() { QFETCH(QByteArray, qop); QXmppSaslDigestMd5::setNonce("AMzVG8Oibf+sVUCPPlWLR8lZQvbbJtJB9vJd+u3c6dw="); QXmppSaslClient *client = QXmppSaslClient::create("DIGEST-MD5"); QVERIFY(client != 0); QCOMPARE(client->mechanism(), QLatin1String("DIGEST-MD5")); client->setUsername("qxmpp1"); client->setPassword("qxmpp123"); client->setHost("jabber.ru"); client->setServiceType("xmpp"); // initial step returns nothing QByteArray response; QVERIFY(client->respond(QByteArray(), response)); QCOMPARE(response, QByteArray()); QVERIFY(client->respond(QByteArray("nonce=\"2530347127\"") + qop + QByteArray("charset=utf-8,algorithm=md5-sess"), response)); QCOMPARE(response, QByteArray("charset=utf-8,cnonce=\"AMzVG8Oibf+sVUCPPlWLR8lZQvbbJtJB9vJd+u3c6dw=\",digest-uri=\"xmpp/jabber.ru\",nc=00000001,nonce=2530347127,qop=auth,response=a61fbf4320577d74038b71a8546bc7ae,username=qxmpp1")); QVERIFY(client->respond(QByteArray("rspauth=d92bf7f4331700c24799cbab364a14b7"), response)); QCOMPARE(response, QByteArray()); // any further step is an error QVERIFY(!client->respond(QByteArray(), response)); delete client; } void tst_QXmppSasl::testClientFacebook() { QXmppSaslClient *client = QXmppSaslClient::create("X-FACEBOOK-PLATFORM"); QVERIFY(client != 0); QCOMPARE(client->mechanism(), QLatin1String("X-FACEBOOK-PLATFORM")); client->setUsername("123456789012345"); client->setPassword("abcdefghijlkmno"); // initial step returns nothing QByteArray response; QVERIFY(client->respond(QByteArray(), response)); QCOMPARE(response, QByteArray()); // challenge response QVERIFY(client->respond(QByteArray("version=1&method=auth.xmpp_login&nonce=AA4EFEE16F2AB64B131EEFFE6EACDDB8"), response)); QCOMPARE(response, QByteArray("access_token=abcdefghijlkmno&api_key=123456789012345&call_id&method=auth.xmpp_login&nonce=AA4EFEE16F2AB64B131EEFFE6EACDDB8&v=1.0")); // any further step is an error QVERIFY(!client->respond(QByteArray(), response)); delete client; } void tst_QXmppSasl::testClientGoogle() { QXmppSaslClient *client = QXmppSaslClient::create("X-OAUTH2"); QVERIFY(client != 0); QCOMPARE(client->mechanism(), QLatin1String("X-OAUTH2")); client->setUsername("foo"); client->setPassword("bar"); // initial step returns data QByteArray response; QVERIFY(client->respond(QByteArray(), response)); QCOMPARE(response, QByteArray("\0foo\0bar", 8)); // any further step is an error QVERIFY(!client->respond(QByteArray(), response)); delete client; } void tst_QXmppSasl::testClientPlain() { QXmppSaslClient *client = QXmppSaslClient::create("PLAIN"); QVERIFY(client != 0); QCOMPARE(client->mechanism(), QLatin1String("PLAIN")); client->setUsername("foo"); client->setPassword("bar"); // initial step returns data QByteArray response; QVERIFY(client->respond(QByteArray(), response)); QCOMPARE(response, QByteArray("\0foo\0bar", 8)); // any further step is an error QVERIFY(!client->respond(QByteArray(), response)); 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"); QVERIFY(client != 0); QCOMPARE(client->mechanism(), QLatin1String("X-MESSENGER-OAUTH2")); client->setPassword(QByteArray("footoken").toBase64()); // initial step returns data QByteArray response; QVERIFY(client->respond(QByteArray(), response)); QCOMPARE(response, QByteArray("footoken", 8)); // any further step is an error QVERIFY(!client->respond(QByteArray(), response)); delete client; } void tst_QXmppSasl::testServerBadMechanism() { QXmppSaslServer *server = QXmppSaslServer::create("BAD-MECH"); QVERIFY(server == 0); } void tst_QXmppSasl::testServerAnonymous() { QXmppSaslServer *server = QXmppSaslServer::create("ANONYMOUS"); QVERIFY(server != 0); QCOMPARE(server->mechanism(), QLatin1String("ANONYMOUS")); // initial step returns success QByteArray response; QCOMPARE(server->respond(QByteArray(), response), QXmppSaslServer::Succeeded); QCOMPARE(response, QByteArray()); // any further step is an error QCOMPARE(server->respond(QByteArray(), response), QXmppSaslServer::Failed); delete server; } void tst_QXmppSasl::testServerDigestMd5() { QXmppSaslDigestMd5::setNonce("OI08/m+QRm6Ma+fKOjuqVXtz40sR5u9/u5GN6sSW0rs="); QXmppSaslServer *server = QXmppSaslServer::create("DIGEST-MD5"); QVERIFY(server != 0); QCOMPARE(server->mechanism(), QLatin1String("DIGEST-MD5")); // initial step returns challenge QByteArray response; QCOMPARE(server->respond(QByteArray(), response), QXmppSaslServer::Challenge); QCOMPARE(response, QByteArray("algorithm=md5-sess,charset=utf-8,nonce=\"OI08/m+QRm6Ma+fKOjuqVXtz40sR5u9/u5GN6sSW0rs=\",qop=auth")); // password needed const QByteArray request = QByteArray("charset=utf-8,cnonce=\"AMzVG8Oibf+sVUCPPlWLR8lZQvbbJtJB9vJd+u3c6dw=\",digest-uri=\"xmpp/jabber.ru\",nc=00000001,nonce=\"OI08/m+QRm6Ma+fKOjuqVXtz40sR5u9/u5GN6sSW0rs=\",qop=auth,response=70e9063257ee2bf6bfd108975b917410,username=qxmpp1"); QCOMPARE(server->respond(request, response), QXmppSaslServer::InputNeeded); QCOMPARE(server->username(), QLatin1String("qxmpp1")); server->setPassword("qxmpp123"); // second challenge QCOMPARE(server->respond(request, response), QXmppSaslServer::Challenge); QCOMPARE(response, QByteArray("rspauth=2821a3add271b9ae02b813bed57ec878")); // success QCOMPARE(server->respond(QByteArray(), response), QXmppSaslServer::Succeeded); QCOMPARE(response, QByteArray()); // any further step is an error QCOMPARE(server->respond(QByteArray(), response), QXmppSaslServer::Failed); delete server; } void tst_QXmppSasl::testServerPlain() { QXmppSaslServer *server = QXmppSaslServer::create("PLAIN"); QVERIFY(server != 0); QCOMPARE(server->mechanism(), QLatin1String("PLAIN")); // initial step returns success QByteArray response; QCOMPARE(server->respond(QByteArray("\0foo\0bar", 8), response), QXmppSaslServer::InputNeeded); QCOMPARE(response, QByteArray()); QCOMPARE(server->username(), QLatin1String("foo")); QCOMPARE(server->password(), QLatin1String("bar")); // any further step is an error QCOMPARE(server->respond(QByteArray(), response), QXmppSaslServer::Failed); delete server; } void tst_QXmppSasl::testServerPlainChallenge() { QXmppSaslServer *server = QXmppSaslServer::create("PLAIN"); QVERIFY(server != 0); QCOMPARE(server->mechanism(), QLatin1String("PLAIN")); // initial step returns challenge QByteArray response; QCOMPARE(server->respond(QByteArray(), response), QXmppSaslServer::Challenge); QCOMPARE(response, QByteArray()); // initial step returns success QCOMPARE(server->respond(QByteArray("\0foo\0bar", 8), response), QXmppSaslServer::InputNeeded); QCOMPARE(response, QByteArray()); QCOMPARE(server->username(), QLatin1String("foo")); QCOMPARE(server->password(), QLatin1String("bar")); // any further step is an error QCOMPARE(server->respond(QByteArray(), response), QXmppSaslServer::Failed); delete server; } QTEST_MAIN(tst_QXmppSasl) #include "tst_qxmppsasl.moc" qxmpp-1.4.0/tests/qxmppserver/000077500000000000000000000000001402370562100164175ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppserver/tst_qxmppserver.cpp000066400000000000000000000066261402370562100224230ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppClient.h" #include "QXmppServer.h" #include "util.h" class tst_QXmppServer : public QObject { Q_OBJECT private slots: void testConnect_data(); void testConnect(); }; void tst_QXmppServer::testConnect_data() { QTest::addColumn("username"); QTest::addColumn("password"); QTest::addColumn("mechanism"); QTest::addColumn("connected"); QTest::newRow("plain-good") << "testuser" << "testpwd" << "PLAIN" << true; QTest::newRow("plain-bad-username") << "baduser" << "testpwd" << "PLAIN" << false; QTest::newRow("plain-bad-password") << "testuser" << "badpwd" << "PLAIN" << false; QTest::newRow("digest-good") << "testuser" << "testpwd" << "DIGEST-MD5" << true; QTest::newRow("digest-bad-username") << "baduser" << "testpwd" << "DIGEST-MD5" << false; QTest::newRow("digest-bad-password") << "testuser" << "badpwd" << "DIGEST-MD5" << false; } void tst_QXmppServer::testConnect() { QFETCH(QString, username); QFETCH(QString, password); QFETCH(QString, mechanism); QFETCH(bool, connected); const QString testDomain("localhost"); const QHostAddress testHost(QHostAddress::LocalHost); const quint16 testPort = 12345; QXmppLogger logger; //logger.setLoggingType(QXmppLogger::StdoutLogging); // prepare server TestPasswordChecker passwordChecker; passwordChecker.addCredentials("testuser", "testpwd"); QXmppServer server; server.setDomain(testDomain); server.setLogger(&logger); server.setPasswordChecker(&passwordChecker); server.listenForClients(testHost, testPort); // prepare client QXmppClient client; client.setLogger(&logger); QEventLoop loop; connect(&client, &QXmppClient::connected, &loop, &QEventLoop::quit); connect(&client, &QXmppClient::disconnected, &loop, &QEventLoop::quit); QXmppConfiguration config; config.setDomain(testDomain); config.setHost(testHost.toString()); config.setPort(testPort); config.setUser(username); config.setPassword(password); config.setSaslAuthMechanism(mechanism); client.connectToServer(config); loop.exec(); QCOMPARE(client.isConnected(), connected); } QTEST_MAIN(tst_QXmppServer) #include "tst_qxmppserver.moc" qxmpp-1.4.0/tests/qxmppsessioniq/000077500000000000000000000000001402370562100171265ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppsessioniq/tst_qxmppsessioniq.cpp000066400000000000000000000025511402370562100236320ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Jeremy Lainé * Manjeet Dahiya * * 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 "QXmppSessionIq.h" #include "util.h" class TestPackets : public QObject { Q_OBJECT private slots: void testSession(); }; void TestPackets::testSession() { const QByteArray xml( "" "" ""); QXmppSessionIq session; parsePacket(session, xml); QCOMPARE(session.id(), QString("session_1")); QCOMPARE(session.to(), QString("example.com")); QCOMPARE(session.type(), QXmppIq::Set); serializePacket(session, xml); } QTEST_MAIN(TestPackets) #include "tst_qxmppsessioniq.moc" qxmpp-1.4.0/tests/qxmppsocks/000077500000000000000000000000001402370562100162335ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppsocks/tst_qxmppsocks.cpp000066400000000000000000000220611402370562100220420ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppSocks.h" #include "util.h" #include #include class tst_QXmppSocks : public QObject { Q_OBJECT private slots: void init(); void newConnectionSlot(QTcpSocket *socket, QString hostName, quint16 port); void testClient_data(); void testClient(); void testClientAndServer(); void testServer_data(); void testServer(); private: QTcpSocket *m_connectionSocket; QString m_connectionHostName; quint16 m_connectionPort; }; void tst_QXmppSocks::init() { m_connectionSocket = nullptr; m_connectionHostName = QString(); m_connectionPort = 0; } void tst_QXmppSocks::newConnectionSlot(QTcpSocket *socket, QString hostName, quint16 port) { m_connectionSocket = socket; m_connectionHostName = hostName; m_connectionPort = port; } void tst_QXmppSocks::testClient_data() { QTest::addColumn("serverHandshake"); QTest::addColumn("serverHandshakeWorks"); QTest::addColumn("serverConnect"); QTest::addColumn("serverConnectWorks"); QTest::addColumn("clientReceivedData"); QTest::newRow("no authentication - good connect") << QByteArray::fromHex("0500") << true << QByteArray::fromHex("050000030e7777772e676f6f676c652e636f6d0050") << true << QByteArray(); QTest::newRow("no authentication - good connect and data") << QByteArray::fromHex("0500") << true << QByteArray::fromHex("050000030e7777772e676f6f676c652e636f6d0050001122") << true << QByteArray::fromHex("001122"); QTest::newRow("no authentication - bad connect") << QByteArray::fromHex("0500") << true << QByteArray::fromHex("0500") << false << QByteArray(); QTest::newRow("bad authentication") << QByteArray::fromHex("05ff") << false << QByteArray() << false << QByteArray(); } void tst_QXmppSocks::testClient() { QFETCH(QByteArray, serverHandshake); QFETCH(bool, serverHandshakeWorks); QFETCH(QByteArray, serverConnect); QFETCH(bool, serverConnectWorks); QFETCH(QByteArray, clientReceivedData); QTcpServer server; QVERIFY(server.listen()); QVERIFY(server.serverPort() != 0); QXmppSocksClient client("127.0.0.1", server.serverPort()); QEventLoop loop; connect(&server, &QTcpServer::newConnection, &loop, &QEventLoop::quit); client.connectToHost("www.google.com", 80); loop.exec(); // receive client handshake m_connectionSocket = server.nextPendingConnection(); QVERIFY(m_connectionSocket); connect(m_connectionSocket, &QAbstractSocket::disconnected, &loop, &QEventLoop::quit); connect(m_connectionSocket, &QIODevice::readyRead, &loop, &QEventLoop::quit); loop.exec(); QCOMPARE(client.state(), QAbstractSocket::ConnectedState); QCOMPARE(m_connectionSocket->state(), QAbstractSocket::ConnectedState); QCOMPARE(m_connectionSocket->readAll(), QByteArray::fromHex("050100")); // receive client connect m_connectionSocket->write(serverHandshake); loop.exec(); if (!serverHandshakeWorks) { QCOMPARE(client.state(), QAbstractSocket::UnconnectedState); QCOMPARE(m_connectionSocket->state(), QAbstractSocket::UnconnectedState); return; } QCOMPARE(client.state(), QAbstractSocket::ConnectedState); QCOMPARE(m_connectionSocket->state(), QAbstractSocket::ConnectedState); QCOMPARE(m_connectionSocket->readAll(), QByteArray::fromHex("050100030e7777772e676f6f676c652e636f6d0050")); // wait for client to be ready connect(&client, &QXmppSocksClient::ready, &loop, &QEventLoop::quit); m_connectionSocket->write(serverConnect); loop.exec(); if (!serverConnectWorks) { QCOMPARE(client.state(), QAbstractSocket::UnconnectedState); QCOMPARE(m_connectionSocket->state(), QAbstractSocket::UnconnectedState); return; } QCOMPARE(client.state(), QAbstractSocket::ConnectedState); QCOMPARE(m_connectionSocket->state(), QAbstractSocket::ConnectedState); QByteArray received = client.readAll(); QCOMPARE(received, clientReceivedData); // disconnect client.disconnectFromHost(); } void tst_QXmppSocks::testClientAndServer() { QXmppSocksServer server; QVERIFY(server.listen()); QVERIFY(server.serverPort() != 0); connect(&server, &QXmppSocksServer::newConnection, this, &tst_QXmppSocks::newConnectionSlot); QXmppSocksClient client("127.0.0.1", server.serverPort()); QEventLoop loop; connect(&client, &QXmppSocksClient::ready, &loop, &QEventLoop::quit); client.connectToHost("www.google.com", 80); loop.exec(); // check client QCOMPARE(client.state(), QAbstractSocket::ConnectedState); // check server QVERIFY(m_connectionSocket); QCOMPARE(m_connectionSocket->state(), QAbstractSocket::ConnectedState); QCOMPARE(m_connectionHostName, QLatin1String("www.google.com")); QCOMPARE(m_connectionPort, quint16(80)); // disconnect client.disconnectFromHost(); } void tst_QXmppSocks::testServer_data() { QTest::addColumn("clientHandshake"); QTest::addColumn("clientHandshakeWorks"); QTest::addColumn("clientConnect"); QTest::addColumn("clientConnectWorks"); QTest::newRow("no authentication - connect to www.google.com:80") << QByteArray::fromHex("050100") << true << QByteArray::fromHex("050100030e7777772e676f6f676c652e636f6d0050") << true; QTest::newRow("no authentication - bad connect") << QByteArray::fromHex("050100") << true << QByteArray::fromHex("0500") << false; QTest::newRow("no authentication or GSSAPI - connect to www.google.com:80") << QByteArray::fromHex("05020001") << true << QByteArray::fromHex("050100030e7777772e676f6f676c652e636f6d0050") << true; QTest::newRow("bad SOCKS version") << QByteArray::fromHex("060100") << false << QByteArray() << false; QTest::newRow("no methods") << QByteArray::fromHex("0500") << false << QByteArray() << false; QTest::newRow("GSSAPI only") << QByteArray::fromHex("050101") << false << QByteArray() << false; } void tst_QXmppSocks::testServer() { QFETCH(QByteArray, clientHandshake); QFETCH(bool, clientHandshakeWorks); QFETCH(QByteArray, clientConnect); QFETCH(bool, clientConnectWorks); QXmppSocksServer server; QVERIFY(server.listen()); QVERIFY(server.serverPort() != 0); connect(&server, &QXmppSocksServer::newConnection, this, &tst_QXmppSocks::newConnectionSlot); QTcpSocket client; client.connectToHost(QHostAddress::LocalHost, server.serverPort()); QVERIFY2(client.waitForConnected(), qPrintable(client.errorString())); QEventLoop loop; connect(&client, &QAbstractSocket::disconnected, &loop, &QEventLoop::quit); connect(&client, &QIODevice::readyRead, &loop, &QEventLoop::quit); // send client handshake client.write(clientHandshake); loop.exec(); if (!clientHandshakeWorks) { // consume any last data QByteArray data = client.readAll(); if (client.state() != QAbstractSocket::UnconnectedState) loop.exec(); QCOMPARE(client.state(), QAbstractSocket::UnconnectedState); QVERIFY(!m_connectionSocket); QVERIFY(m_connectionHostName.isNull()); QCOMPARE(m_connectionPort, quint16(0)); return; } QCOMPARE(client.state(), QAbstractSocket::ConnectedState); QCOMPARE(client.readAll(), QByteArray::fromHex("0500")); // request connect to www.google.com port 80 client.write(clientConnect); loop.exec(); if (!clientConnectWorks) { QCOMPARE(client.state(), QAbstractSocket::UnconnectedState); QVERIFY(!m_connectionSocket); QVERIFY(m_connectionHostName.isNull()); QCOMPARE(m_connectionPort, quint16(0)); return; } QCOMPARE(client.state(), QAbstractSocket::ConnectedState); QCOMPARE(client.readAll(), QByteArray::fromHex("050000030e7777772e676f6f676c652e636f6d0050")); QCOMPARE(client.state(), QAbstractSocket::ConnectedState); QVERIFY(m_connectionSocket); QCOMPARE(m_connectionSocket->state(), QAbstractSocket::ConnectedState); QCOMPARE(m_connectionHostName, QLatin1String("www.google.com")); QCOMPARE(m_connectionPort, quint16(80)); // disconnect client.disconnectFromHost(); } QTEST_MAIN(tst_QXmppSocks) #include "tst_qxmppsocks.moc" qxmpp-1.4.0/tests/qxmppstanza/000077500000000000000000000000001402370562100164115ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppstanza/tst_qxmppstanza.cpp000066400000000000000000000273051402370562100224040ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppStanza.h" #include "util.h" #include class tst_QXmppStanza : public QObject { Q_OBJECT private slots: void testExtendedAddress_data(); void testExtendedAddress(); void testErrorCases_data(); void testErrorCases(); void testErrorFileTooLarge(); void testErrorRetry(); }; void tst_QXmppStanza::testExtendedAddress_data() { QTest::addColumn("xml"); QTest::addColumn("delivered"); QTest::addColumn("description"); QTest::addColumn("jid"); QTest::addColumn("type"); QTest::newRow("simple") << QByteArray(R"(
)") << false << QString() << QString("foo@example.com/QXmpp") << QString("bcc"); QTest::newRow("full") << QByteArray(R"(
)") << true << QString("some description") << QString("foo@example.com/QXmpp") << QString("bcc"); } void tst_QXmppStanza::testExtendedAddress() { QFETCH(QByteArray, xml); QFETCH(bool, delivered); QFETCH(QString, description); QFETCH(QString, jid); QFETCH(QString, type); QXmppExtendedAddress address; parsePacket(address, xml); QCOMPARE(address.isDelivered(), delivered); QCOMPARE(address.description(), description); QCOMPARE(address.jid(), jid); QCOMPARE(address.type(), type); serializePacket(address, xml); } void tst_QXmppStanza::testErrorCases_data() { QTest::addColumn("xml"); QTest::addColumn("type"); QTest::addColumn("condition"); QTest::addColumn("text"); QTest::addColumn("redirectionUri"); QTest::addColumn("by"); #define ROW(name, xml, type, condition, text, redirect, by) \ QTest::newRow(QT_STRINGIFY(name)) \ << QByteArrayLiteral(xml) \ << QXmppStanza::Error::type \ << QXmppStanza::Error::condition \ << text \ << redirect \ << by #define BASIC(xml, type, condition) \ ROW(condition, xml, type, condition, QString(), QString(), QString()) ROW( empty-text, "" "" "", Modify, BadRequest, QString(), QString(), QString()); ROW( redirection-uri-gone, "" "" "xmpp:romeo@afterlife.example.net" "" "", Cancel, Gone, QString(), "xmpp:romeo@afterlife.example.net", "example.net"); ROW( redirection-uri-redirect, "" "" "xmpp:rms@afterlife.example.net" "" "", Cancel, Redirect, QString(), "xmpp:rms@afterlife.example.net", QString()); ROW( redirection-uri-empty, "" "" "", Cancel, Redirect, QString(), QString(), QString()); ROW( policy-violation-text, "" "" "The used words are not allowed on this server." "", Modify, PolicyViolation, "The used words are not allowed on this server.", QString(), "example.net"); ROW( jid-malformed-with-by, "" "" "", Modify, JidMalformed, QString(), QString(), "muc.example.com"); BASIC( "" "" "", Modify, BadRequest); BASIC( "" "" "", Cancel, Conflict); BASIC( "" "" "", Cancel, FeatureNotImplemented); BASIC( "" "" "", Auth, Forbidden); BASIC( "" "" "", Cancel, Gone); BASIC( "" "" "", Cancel, InternalServerError); BASIC( "" "" "", Cancel, ItemNotFound); BASIC( "" "" "", Modify, JidMalformed); BASIC( "" "" "", Modify, NotAcceptable); BASIC( "" "" "", Cancel, NotAllowed); BASIC( "" "" "", Auth, NotAuthorized); BASIC( "" "" "", Modify, PolicyViolation); BASIC( "" "" "", Wait, RecipientUnavailable); BASIC( "" "" "", Modify, Redirect); BASIC( "" "" "", Auth, RegistrationRequired); BASIC( "" "" "", Cancel, RemoteServerNotFound); BASIC( "" "" "", Wait, RemoteServerTimeout); BASIC( "" "" "", Wait, ResourceConstraint); BASIC( "" "" "", Cancel, ServiceUnavailable); BASIC( "" "" "", Auth, SubscriptionRequired); BASIC( "" "" "", Modify, UndefinedCondition); #undef BASIC #undef ROW } void tst_QXmppStanza::testErrorCases() { QFETCH(QByteArray, xml); QFETCH(QXmppStanza::Error::Type, type); QFETCH(QXmppStanza::Error::Condition, condition); QFETCH(QString, text); QFETCH(QString, redirectionUri); QFETCH(QString, by); // parsing QXmppStanza::Error error; parsePacket(error, xml); QCOMPARE(error.type(), type); QCOMPARE(error.condition(), condition); QCOMPARE(error.text(), text); QCOMPARE(error.redirectionUri(), redirectionUri); QCOMPARE(error.by(), by); // check parsed error results in the same xml serializePacket(error, xml); // serialization from setters error = QXmppStanza::Error(); error.setType(type); error.setCondition(condition); error.setText(text); error.setRedirectionUri(redirectionUri); error.setBy(by); serializePacket(error, xml); } void tst_QXmppStanza::testErrorFileTooLarge() { const QByteArray xml( "" "" "" "File too large. The maximum file size is 20000 bytes" "" "" "20000" "" ""); QXmppStanza::Error error; parsePacket(error, xml); QCOMPARE(error.type(), QXmppStanza::Error::Modify); QCOMPARE(error.text(), QString("File too large. The maximum file size is " "20000 bytes")); QCOMPARE(error.condition(), QXmppStanza::Error::NotAcceptable); QVERIFY(error.fileTooLarge()); QCOMPARE(error.maxFileSize(), 20000); serializePacket(error, xml); // test setters error.setMaxFileSize(60000); QCOMPARE(error.maxFileSize(), 60000); error.setFileTooLarge(false); QVERIFY(!error.fileTooLarge()); QXmppStanza::Error e2; e2.setMaxFileSize(123000); QVERIFY(e2.fileTooLarge()); } void tst_QXmppStanza::testErrorRetry() { const QByteArray xml( "" "" "" "Quota reached. You can only upload 5 files in 5 minutes" "" "" ""); QXmppStanza::Error error; parsePacket(error, xml); QCOMPARE(error.type(), QXmppStanza::Error::Wait); QCOMPARE(error.text(), QString("Quota reached. You can only upload 5 " "files in 5 minutes")); QCOMPARE(error.condition(), QXmppStanza::Error::ResourceConstraint); QCOMPARE(error.retryDate(), QDateTime(QDate(2017, 12, 03), QTime(23, 42, 05), Qt::UTC)); serializePacket(error, xml); // test setter error.setRetryDate(QDateTime(QDate(1985, 10, 26), QTime(1, 35))); QCOMPARE(error.retryDate(), QDateTime(QDate(1985, 10, 26), QTime(1, 35))); } QTEST_MAIN(tst_QXmppStanza) #include "tst_qxmppstanza.moc" qxmpp-1.4.0/tests/qxmppstarttlspacket/000077500000000000000000000000001402370562100201615ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppstarttlspacket/tst_qxmppstarttlspacket.cpp000066400000000000000000000055471402370562100257300ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Linus Jahn * * 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 "QXmppStartTlsPacket.h" #include "util.h" #include class tst_QXmppStartTlsPacket : public QObject { Q_OBJECT private slots: void testBasic_data(); void testBasic(); }; void tst_QXmppStartTlsPacket::testBasic_data() { QTest::addColumn("xml"); QTest::addColumn("valid"); QTest::addColumn("type"); #define ROW(name, xml, valid, type) \ QTest::newRow(name) \ << QByteArrayLiteral(xml) \ << valid \ << type ROW("starttls", R"()", true, QXmppStartTlsPacket::StartTls); ROW("proceed", R"()", true, QXmppStartTlsPacket::Proceed); ROW("failure", R"()", true, QXmppStartTlsPacket::Failure); ROW("invalid-tag", R"()", false, QXmppStartTlsPacket::StartTls); #undef ROW } void tst_QXmppStartTlsPacket::testBasic() { QFETCH(QByteArray, xml); QFETCH(bool, valid); QFETCH(QXmppStartTlsPacket::Type, type); QDomDocument doc; QCOMPARE(doc.setContent(xml, true), true); QCOMPARE(QXmppStartTlsPacket::isStartTlsPacket(doc.documentElement()), valid); QCOMPARE(QXmppStartTlsPacket::isStartTlsPacket(doc.documentElement(), type), valid); // test other types return false for (auto testValue : { QXmppStartTlsPacket::StartTls, QXmppStartTlsPacket::Proceed, QXmppStartTlsPacket::Failure }) { QCOMPARE(QXmppStartTlsPacket::isStartTlsPacket(doc.documentElement(), testValue), testValue == type && valid); } if (valid) { QXmppStartTlsPacket packet; parsePacket(packet, xml); QCOMPARE(packet.type(), type); serializePacket(packet, xml); QXmppStartTlsPacket packet2(type); serializePacket(packet2, xml); QXmppStartTlsPacket packet3; packet3.setType(type); serializePacket(packet2, xml); } } QTEST_MAIN(tst_QXmppStartTlsPacket) #include "tst_qxmppstarttlspacket.moc" qxmpp-1.4.0/tests/qxmppstream/000077500000000000000000000000001402370562100164045ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppstream/cd000066400000000000000000000000001402370562100167030ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppstream/tst_qxmppstream.cpp000066400000000000000000000103731402370562100223670ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Linus Jahn * * 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 "QXmppStream.h" #include "util.h" Q_DECLARE_METATYPE(QDomElement) class TestStream : public QXmppStream { Q_OBJECT public: TestStream(QObject *parent) : QXmppStream(parent) { } void handleStart() override { QXmppStream::handleStart(); emit started(); } void handleStream(const QDomElement &element) override { emit streamReceived(element); } void handleStanza(const QDomElement &element) override { emit stanzaReceived(element); } Q_SIGNAL void started(); Q_SIGNAL void streamReceived(const QDomElement &element); Q_SIGNAL void stanzaReceived(const QDomElement &element); }; class tst_QXmppStream : public QObject { Q_OBJECT private: Q_SLOT void initTestCase(); Q_SLOT void testProcessData(); }; void tst_QXmppStream::initTestCase() { qRegisterMetaType(); } void tst_QXmppStream::testProcessData() { TestStream stream(this); QSignalSpy onStarted(&stream, &TestStream::started); QSignalSpy onStreamReceived(&stream, &TestStream::streamReceived); QSignalSpy onStanzaReceived(&stream, &TestStream::stanzaReceived); stream.processData(R"()"); stream.processData(R"( )"); // check stream was found QCOMPARE(onStreamReceived.size(), 1); QCOMPARE(onStanzaReceived.size(), 0); QCOMPARE(onStarted.size(), 0); // check stream information const auto streamElement = onStreamReceived[0][0].value(); QCOMPARE(streamElement.tagName(), QStringLiteral("stream")); QCOMPARE(streamElement.namespaceURI(), QStringLiteral("http://etherx.jabber.org/streams")); QCOMPARE(streamElement.attribute("from"), QStringLiteral("juliet@im.example.com")); QCOMPARE(streamElement.attribute("to"), QStringLiteral("im.example.com")); QCOMPARE(streamElement.attribute("version"), QStringLiteral("1.0")); QCOMPARE(streamElement.attribute("lang"), QStringLiteral("en")); stream.processData(R"( )"); QCOMPARE(onStreamReceived.size(), 1); QCOMPARE(onStanzaReceived.size(), 1); QCOMPARE(onStarted.size(), 0); const auto features = onStanzaReceived[0][0].value(); QCOMPARE(features.tagName(), QStringLiteral("features")); QCOMPARE(features.namespaceURI(), QStringLiteral("http://etherx.jabber.org/streams")); // test partial data stream.processData(R"()"); stream.processData(R"(Moin)"); stream.processData(R"()"); QCOMPARE(onStreamReceived.size(), 1); QCOMPARE(onStanzaReceived.size(), 2); QCOMPARE(onStarted.size(), 0); const auto message = onStanzaReceived[1][0].value(); QCOMPARE(message.tagName(), QStringLiteral("message")); QCOMPARE(message.namespaceURI(), QStringLiteral("jabber:client")); stream.processData(R"()"); } QTEST_MAIN(tst_QXmppStream) #include "tst_qxmppstream.moc" qxmpp-1.4.0/tests/qxmppstreamfeatures/000077500000000000000000000000001402370562100201435ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppstreamfeatures/tst_qxmppstreamfeatures.cpp000066400000000000000000000146771402370562100257000ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Manjeet Dahiya * * 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 "QXmppStreamFeatures.h" #include "util.h" template static void parsePacketWithStream(T &packet, const QByteArray &xml) { QDomDocument doc; const auto wrappedXml = QByteArrayLiteral("") + xml + QByteArrayLiteral(""); QString err; bool parsingSuccess = doc.setContent(wrappedXml, true, &err); if (!err.isNull()) qDebug() << err; QVERIFY(parsingSuccess); packet.parse(doc.documentElement().firstChildElement()); } class tst_QXmppStreamFeatures : public QObject { Q_OBJECT private slots: void testEmpty(); void testRequired(); void testFull(); void testSetters(); }; void tst_QXmppStreamFeatures::testEmpty() { const QByteArray xml(""); QXmppStreamFeatures features; parsePacketWithStream(features, xml); QCOMPARE(features.bindMode(), QXmppStreamFeatures::Disabled); QCOMPARE(features.sessionMode(), QXmppStreamFeatures::Disabled); QCOMPARE(features.nonSaslAuthMode(), QXmppStreamFeatures::Disabled); QCOMPARE(features.tlsMode(), QXmppStreamFeatures::Disabled); QCOMPARE(features.clientStateIndicationMode(), QXmppStreamFeatures::Disabled); QCOMPARE(features.registerMode(), QXmppStreamFeatures::Disabled); QCOMPARE(features.preApprovedSubscriptionsSupported(), false); QCOMPARE(features.rosterVersioningSupported(), false); QCOMPARE(features.authMechanisms(), QStringList()); QCOMPARE(features.compressionMethods(), QStringList()); serializePacket(features, xml); } void tst_QXmppStreamFeatures::testRequired() { const QByteArray xml( "" "" "" "" ""); QXmppStreamFeatures features; parsePacketWithStream(features, xml); QCOMPARE(features.tlsMode(), QXmppStreamFeatures::Required); serializePacket(features, xml); } void tst_QXmppStreamFeatures::testFull() { const QByteArray xml("" "" "" "" "" "" "" "" "" "zlib" "PLAIN" ""); QXmppStreamFeatures features; parsePacketWithStream(features, xml); QCOMPARE(features.bindMode(), QXmppStreamFeatures::Enabled); QCOMPARE(features.sessionMode(), QXmppStreamFeatures::Enabled); QCOMPARE(features.nonSaslAuthMode(), QXmppStreamFeatures::Enabled); QCOMPARE(features.tlsMode(), QXmppStreamFeatures::Enabled); QCOMPARE(features.clientStateIndicationMode(), QXmppStreamFeatures::Enabled); QCOMPARE(features.registerMode(), QXmppStreamFeatures::Enabled); QCOMPARE(features.preApprovedSubscriptionsSupported(), true); QCOMPARE(features.authMechanisms(), QStringList() << "PLAIN"); QCOMPARE(features.compressionMethods(), QStringList() << "zlib"); serializePacket(features, xml); features = QXmppStreamFeatures(); features.setBindMode(QXmppStreamFeatures::Enabled); features.setSessionMode(QXmppStreamFeatures::Enabled); features.setNonSaslAuthMode(QXmppStreamFeatures::Enabled); features.setTlsMode(QXmppStreamFeatures::Enabled); features.setClientStateIndicationMode(QXmppStreamFeatures::Enabled); features.setRegisterMode(QXmppStreamFeatures::Enabled); features.setPreApprovedSubscriptionsSupported(true); features.setRosterVersioningSupported(true); features.setAuthMechanisms(QStringList { QStringLiteral("PLAIN") }); features.setCompressionMethods(QStringList { QStringLiteral("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" qxmpp-1.4.0/tests/qxmppstreaminitiationiq/000077500000000000000000000000001402370562100210265ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppstreaminitiationiq/tst_qxmppstreaminitiationiq.cpp000066400000000000000000000110141402370562100274240ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Olivier Goffart * Jeremy Lainé * * 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 "QXmppStreamInitiationIq_p.h" #include "QXmppTransferManager.h" #include "util.h" class tst_QXmppStreamInitiationIq : public QObject { Q_OBJECT private slots: void testFileInfo_data(); void testFileInfo(); void testOffer(); void testResult(); }; void tst_QXmppStreamInitiationIq::testFileInfo_data() { QTest::addColumn("xml"); QTest::addColumn("date"); QTest::addColumn("description"); QTest::addColumn("hash"); QTest::addColumn("name"); QTest::addColumn("size"); QTest::newRow("normal") << QByteArray("") << QDateTime() << QString() << QByteArray() << QString("test.txt") << qint64(1022); QTest::newRow("full") << QByteArray("" "This is a test. If this were a real file..." "") << QDateTime(QDate(1969, 7, 21), QTime(2, 56, 15), Qt::UTC) << QString("This is a test. If this were a real file...") << QByteArray::fromHex("552da749930852c69ae5d2141d3766b1") << QString("test.txt") << qint64(1022); } void tst_QXmppStreamInitiationIq::testFileInfo() { QFETCH(QByteArray, xml); QFETCH(QDateTime, date); QFETCH(QString, description); QFETCH(QByteArray, hash); QFETCH(QString, name); QFETCH(qint64, size); QXmppTransferFileInfo info; parsePacket(info, xml); QCOMPARE(info.date(), date); QCOMPARE(info.description(), description); QCOMPARE(info.hash(), hash); QCOMPARE(info.name(), name); QCOMPARE(info.size(), size); serializePacket(info, xml); } void tst_QXmppStreamInitiationIq::testOffer() { QByteArray xml( "" "" "" "" "" "" "" "" "" "" "" "" ""); QXmppStreamInitiationIq iq; parsePacket(iq, xml); QVERIFY(!iq.fileInfo().isNull()); QCOMPARE(iq.fileInfo().name(), QString("test.txt")); QCOMPARE(iq.fileInfo().size(), qint64(1022)); serializePacket(iq, xml); } void tst_QXmppStreamInitiationIq::testResult() { QByteArray xml( "" "" "" "" "" "http://jabber.org/protocol/bytestreams" "" "" "" "" ""); QXmppStreamInitiationIq iq; parsePacket(iq, xml); QVERIFY(iq.fileInfo().isNull()); serializePacket(iq, xml); } QTEST_MAIN(tst_QXmppStreamInitiationIq) #include "tst_qxmppstreaminitiationiq.moc" qxmpp-1.4.0/tests/qxmppstunmessage/000077500000000000000000000000001402370562100174475ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppstunmessage/tst_qxmppstunmessage.cpp000066400000000000000000000106131402370562100244720ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppStun.h" #include "util.h" #include class tst_QXmppStunMessage : public QObject { Q_OBJECT private slots: void testFingerprint(); void testIntegrity(); void testIPv4Address(); void testIPv6Address(); void testXorIPv4Address(); void testXorIPv6Address(); }; void tst_QXmppStunMessage::testFingerprint() { // without fingerprint QXmppStunMessage msg; msg.setType(0x0001); QCOMPARE(msg.encode(QByteArray(), false), QByteArray("\x00\x01\x00\x00\x21\x12\xA4\x42\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 20)); // with fingerprint QCOMPARE(msg.encode(QByteArray(), true), QByteArray("\x00\x01\x00\x08\x21\x12\xA4\x42\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x28\x00\x04\xB2\xAA\xF9\xF6", 28)); } void tst_QXmppStunMessage::testIntegrity() { QXmppStunMessage msg; msg.setType(0x0001); QCOMPARE(msg.encode(QByteArray("somesecret"), false), QByteArray("\x00\x01\x00\x18\x21\x12\xA4\x42\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x14\x96\x4B\x40\xD1\x84\x67\x6A\xFD\xB5\xE0\x7C\xC5\x1F\xFB\xBD\xA2\x61\xAF\xB1\x26", 44)); } void tst_QXmppStunMessage::testIPv4Address() { // encode QXmppStunMessage msg; msg.setType(0x0001); msg.mappedHost = QHostAddress("127.0.0.1"); msg.mappedPort = 12345; QByteArray packet = msg.encode(QByteArray(), false); QCOMPARE(packet, QByteArray("\x00\x01\x00\x0C\x21\x12\xA4\x42\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x08\x00\x01\x30\x39\x7F\x00\x00\x01", 32)); // decode QXmppStunMessage msg2; msg2.decode(packet); QCOMPARE(msg2.mappedHost, QHostAddress("127.0.0.1")); QCOMPARE(msg2.mappedPort, quint16(12345)); } void tst_QXmppStunMessage::testIPv6Address() { // encode QXmppStunMessage msg; msg.setType(0x0001); msg.mappedHost = QHostAddress("::1"); msg.mappedPort = 12345; const QByteArray packet = msg.encode(QByteArray(), false); QCOMPARE(packet, QByteArray("\x00\x01\x00\x18\x21\x12\xA4\x42\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x14\x00\x02\x30\x39\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 44)); // decode QXmppStunMessage msg2; msg2.decode(packet); QCOMPARE(msg2.mappedHost, QHostAddress("::1")); QCOMPARE(msg2.mappedPort, quint16(12345)); } void tst_QXmppStunMessage::testXorIPv4Address() { // encode QXmppStunMessage msg; msg.setType(0x0001); msg.xorMappedHost = QHostAddress("127.0.0.1"); msg.xorMappedPort = 12345; QByteArray packet = msg.encode(QByteArray(), false); QCOMPARE(packet, QByteArray("\x00\x01\x00\x0C\x21\x12\xA4\x42\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x00\x08\x00\x01\x11\x2B\x5E\x12\xA4\x43", 32)); // decode QXmppStunMessage msg2; msg2.decode(packet); QCOMPARE(msg2.xorMappedHost, QHostAddress("127.0.0.1")); QCOMPARE(msg2.xorMappedPort, quint16(12345)); } void tst_QXmppStunMessage::testXorIPv6Address() { // encode QXmppStunMessage msg; msg.setType(0x0001); msg.xorMappedHost = QHostAddress("::1"); msg.xorMappedPort = 12345; const QByteArray packet = msg.encode(QByteArray(), false); QCOMPARE(packet, QByteArray("\x00\x01\x00\x18\x21\x12\xA4\x42\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x00\x14\x00\x02\x11\x2B\x21\x12\xA4\x42\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 44)); // decode QXmppStunMessage msg2; msg2.decode(packet); QCOMPARE(msg2.xorMappedHost, QHostAddress("::1")); QCOMPARE(msg2.xorMappedPort, quint16(12345)); } QTEST_MAIN(tst_QXmppStunMessage) #include "tst_qxmppstunmessage.moc" qxmpp-1.4.0/tests/qxmpptransfermanager/000077500000000000000000000000001402370562100202705ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmpptransfermanager/CMakeLists.txt000066400000000000000000000004421402370562100230300ustar00rootroot00000000000000include_directories(${CMAKE_CURRENT_BINARY_DIR}) add_executable(tst_qxmpptransfermanager tst_qxmpptransfermanager.cpp tst_qxmpptransfermanager.qrc) add_test(tst_qxmpptransfermanager tst_qxmpptransfermanager) target_link_libraries(tst_qxmpptransfermanager Qt${QT_VERSION_MAJOR}::Test qxmpp) qxmpp-1.4.0/tests/qxmpptransfermanager/test.svg000066400000000000000000000043501402370562100217720ustar00rootroot00000000000000 image/svg+xml qxmpp-1.4.0/tests/qxmpptransfermanager/tst_qxmpptransfermanager.cpp000066400000000000000000000141541402370562100261400ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Jeremy Lainé * * 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 "QXmppClient.h" #include "QXmppServer.h" #include "QXmppTransferManager.h" #include "util.h" #include #include class tst_QXmppTransferManager : public QObject { Q_OBJECT private slots: void init(); void testSendFile_data(); void testSendFile(); void acceptFile(QXmppTransferJob *job); private: QBuffer receiverBuffer; QXmppTransferJob *receiverJob; }; void tst_QXmppTransferManager::init() { receiverBuffer.close(); receiverBuffer.setData(QByteArray()); receiverJob = nullptr; } void tst_QXmppTransferManager::acceptFile(QXmppTransferJob *job) { receiverJob = job; receiverBuffer.open(QIODevice::WriteOnly); job->accept(&receiverBuffer); } void tst_QXmppTransferManager::testSendFile_data() { QTest::addColumn("senderMethods"); QTest::addColumn("receiverMethods"); QTest::addColumn("works"); QTest::newRow("any - any") << QXmppTransferJob::AnyMethod << QXmppTransferJob::AnyMethod << true; QTest::newRow("any - inband") << QXmppTransferJob::AnyMethod << QXmppTransferJob::InBandMethod << true; QTest::newRow("any - socks") << QXmppTransferJob::AnyMethod << QXmppTransferJob::SocksMethod << true; QTest::newRow("inband - any") << QXmppTransferJob::InBandMethod << QXmppTransferJob::AnyMethod << true; QTest::newRow("inband - inband") << QXmppTransferJob::InBandMethod << QXmppTransferJob::InBandMethod << true; QTest::newRow("inband - socks") << QXmppTransferJob::InBandMethod << QXmppTransferJob::SocksMethod << false; QTest::newRow("socks - any") << QXmppTransferJob::SocksMethod << QXmppTransferJob::AnyMethod << true; QTest::newRow("socks - inband") << QXmppTransferJob::SocksMethod << QXmppTransferJob::InBandMethod << false; QTest::newRow("socks - socks") << QXmppTransferJob::SocksMethod << QXmppTransferJob::SocksMethod << true; } void tst_QXmppTransferManager::testSendFile() { QFETCH(QXmppTransferJob::Method, senderMethods); QFETCH(QXmppTransferJob::Method, receiverMethods); QFETCH(bool, works); const QString testDomain("localhost"); const QHostAddress testHost(QHostAddress::LocalHost); const quint16 testPort = 12345; QXmppLogger logger; //logger.setLoggingType(QXmppLogger::StdoutLogging); // prepare server TestPasswordChecker passwordChecker; passwordChecker.addCredentials("sender", "testpwd"); passwordChecker.addCredentials("receiver", "testpwd"); QXmppServer server; server.setDomain(testDomain); server.setLogger(&logger); server.setPasswordChecker(&passwordChecker); server.listenForClients(testHost, testPort); // prepare sender QXmppClient sender; auto *senderManager = new QXmppTransferManager; senderManager->setSupportedMethods(senderMethods); sender.addExtension(senderManager); sender.setLogger(&logger); QEventLoop senderLoop; connect(&sender, &QXmppClient::connected, &senderLoop, &QEventLoop::quit); connect(&sender, &QXmppClient::disconnected, &senderLoop, &QEventLoop::quit); QXmppConfiguration config; config.setDomain(testDomain); config.setHost(testHost.toString()); config.setPort(testPort); config.setUser("sender"); config.setPassword("testpwd"); sender.connectToServer(config); senderLoop.exec(); QCOMPARE(sender.isConnected(), true); // prepare receiver QXmppClient receiver; auto *receiverManager = new QXmppTransferManager; receiverManager->setSupportedMethods(receiverMethods); connect(receiverManager, &QXmppTransferManager::fileReceived, this, &tst_QXmppTransferManager::acceptFile); receiver.addExtension(receiverManager); receiver.setLogger(&logger); QEventLoop receiverLoop; connect(&receiver, &QXmppClient::connected, &receiverLoop, &QEventLoop::quit); connect(&receiver, &QXmppClient::disconnected, &receiverLoop, &QEventLoop::quit); config.setUser("receiver"); config.setPassword("testpwd"); receiver.connectToServer(config); receiverLoop.exec(); QCOMPARE(receiver.isConnected(), true); // send file QEventLoop loop; QXmppTransferJob *senderJob = senderManager->sendFile("receiver@localhost/QXmpp", ":/test.svg"); QVERIFY(senderJob); QCOMPARE(senderJob->localFileUrl(), QUrl::fromLocalFile(":/test.svg")); connect(senderJob, &QXmppTransferJob::finished, &loop, &QEventLoop::quit); loop.exec(); if (works) { QCOMPARE(senderJob->state(), QXmppTransferJob::FinishedState); QCOMPARE(senderJob->error(), QXmppTransferJob::NoError); // finish receiving file QVERIFY(receiverJob); connect(receiverJob, &QXmppTransferJob::finished, &loop, &QEventLoop::quit); loop.exec(); QCOMPARE(receiverJob->state(), QXmppTransferJob::FinishedState); QCOMPARE(receiverJob->error(), QXmppTransferJob::NoError); // check received file QFile expectedFile(":/test.svg"); QVERIFY(expectedFile.open(QIODevice::ReadOnly)); const QByteArray expectedData = expectedFile.readAll(); QCOMPARE(receiverBuffer.data(), expectedData); } else { QCOMPARE(senderJob->state(), QXmppTransferJob::FinishedState); QCOMPARE(senderJob->error(), QXmppTransferJob::AbortError); QVERIFY(!receiverJob); QCOMPARE(receiverBuffer.data(), QByteArray()); } } QTEST_MAIN(tst_QXmppTransferManager) #include "tst_qxmpptransfermanager.moc" qxmpp-1.4.0/tests/qxmpptransfermanager/tst_qxmpptransfermanager.qrc000066400000000000000000000001341402370562100261340ustar00rootroot00000000000000 test.svg qxmpp-1.4.0/tests/qxmppuploadrequestmanager/000077500000000000000000000000001402370562100213415ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppuploadrequestmanager/CMakeLists.txt000066400000000000000000000005001402370562100240740ustar00rootroot00000000000000include_directories(${CMAKE_CURRENT_BINARY_DIR}) add_executable(tst_qxmppuploadrequestmanager tst_qxmppuploadrequestmanager.cpp tst_qxmppuploadrequestmanager.qrc) add_test(tst_qxmppuploadrequestmanager tst_qxmppuploadrequestmanager) target_link_libraries(tst_qxmppuploadrequestmanager Qt${QT_VERSION_MAJOR}::Test qxmpp) qxmpp-1.4.0/tests/qxmppuploadrequestmanager/test.svg000066400000000000000000000043501402370562100230430ustar00rootroot00000000000000 image/svg+xml qxmpp-1.4.0/tests/qxmppuploadrequestmanager/tst_qxmppuploadrequestmanager.cpp000066400000000000000000000307351402370562100302650ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Yury Gubich * Linus Jahn * * 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 "QXmppClient.h" #include "QXmppDiscoveryManager.h" #include "QXmppHttpUploadIq.h" #include "QXmppLogger.h" #include "QXmppUploadRequestManager.h" #include "util.h" #include #include #include class TestHelper : public QObject { Q_OBJECT public: TestHelper(bool expectedEvent, bool expectedError); ~TestHelper(); public slots: void onSlotReceived(const QXmppHttpUploadSlotIq& slot); void onRequestFailed(const QXmppHttpUploadRequestIq& request); private: bool expectedEvent; bool expectedError; bool event; bool error; }; TestHelper::TestHelper(bool p_expectedEvent, bool p_expectedError) : QObject(), expectedEvent(p_expectedEvent), expectedError(p_expectedError), event(false), error(false) { } TestHelper::~TestHelper() { QCOMPARE(event, expectedEvent); QCOMPARE(error, expectedError); } void TestHelper::onRequestFailed(const QXmppHttpUploadRequestIq& request) { event = true; error = true; } void TestHelper::onSlotReceived(const QXmppHttpUploadSlotIq& slot) { event = true; error = false; } class tst_QXmppUploadRequestManager : public QObject { Q_OBJECT protected slots: void onLoggerMessage(QXmppLogger::MessageType type, const QString& text) const; private slots: void initTestCase(); void testDiscoveryService_data(); void testDiscoveryService(); void testHandleStanza_data(); void testHandleStanza(); void testSending_data(); void testSending(); void testUploadService(); private: QXmppUploadRequestManager* manager; QXmppClient client; QXmppDiscoveryManager* discovery; QString uploadServiceName; qint64 maxFileSize; QMimeType lastMimeType; QString lastFileName; qint64 lastFileSize; }; void tst_QXmppUploadRequestManager::onLoggerMessage(QXmppLogger::MessageType type, const QString& text) const { QCOMPARE(type, QXmppLogger::SentMessage); QDomDocument doc; QCOMPARE(doc.setContent(text, true), true); QDomElement element = doc.documentElement(); QXmppHttpUploadRequestIq iq; iq.parse(element); QCOMPARE(iq.type(), QXmppIq::Get); QCOMPARE(iq.to(), uploadServiceName); QCOMPARE(iq.fileName(), lastFileName); QCOMPARE(iq.size(), lastFileSize); QCOMPARE(iq.contentType(), lastMimeType); } void tst_QXmppUploadRequestManager::initTestCase() { uploadServiceName = "upload.montague.tld"; maxFileSize = 500UL * 1024UL * 1024UL; manager = new QXmppUploadRequestManager(); discovery = client.findExtension(); client.addExtension(manager); } void tst_QXmppUploadRequestManager::testHandleStanza_data() { QTest::addColumn("xml"); QTest::addColumn("accepted"); QTest::addColumn("event"); QTest::addColumn("error"); QTest::newRow("notAccepted") << QByteArray("" "" "" "" "What man art thou that, thus bescreen'd in night, so stumblest on my counsel?" "0e3141cd80894871a68e6fe6b1ec56fa" "" "" "" "") << false << false << false; QTest::newRow("slotReceived") << QByteArray("" "" "" "
Basic Base64String==
" "
foo=bar; user=romeo
" "
" "" "
" "
") << true << true << false; QTest::newRow("tooLargeError") << QByteArray("" "" "" "" "File too large. The maximum file size is 20000 bytes" "" "20000" "" "" "") << true << true << true; QTest::newRow("quotaReachedError") << QByteArray("" "" "" "" "Quota reached. You can only upload 5 files in 5 minutes" "" "" "") << true << true << true; } void tst_QXmppUploadRequestManager::testHandleStanza() { QFETCH(QByteArray, xml); QFETCH(bool, accepted); QFETCH(bool, event); QFETCH(bool, error); TestHelper helper(event, error); connect(manager, &QXmppUploadRequestManager::slotReceived, &helper, &TestHelper::onSlotReceived); connect(manager, &QXmppUploadRequestManager::requestFailed, &helper, &TestHelper::onRequestFailed); QDomDocument doc; QCOMPARE(doc.setContent(xml, true), true); QDomElement element = doc.documentElement(); bool realAccepted = manager->handleStanza(element); QCOMPARE(realAccepted, accepted); } void tst_QXmppUploadRequestManager::testDiscoveryService_data() { QTest::addColumn("xml"); QTest::addColumn("discovered"); QTest::newRow("mixDiscoveryStanzaIq") << QByteArray("" "" "" "" "" "" "") << false; QTest::newRow("HTTPUploadDiscoveryStanzaIq") << QByteArray("" "" "" "" "" "" "urn:xmpp:http:upload:0" "" "" "" + QByteArray::number(maxFileSize) + "" "" "" "" "") << true; } void tst_QXmppUploadRequestManager::testDiscoveryService() { QFETCH(QByteArray, xml); QFETCH(bool, discovered); QDomDocument doc; QCOMPARE(doc.setContent(xml, true), true); QDomElement element = doc.documentElement(); bool accepted = discovery->handleStanza(element); QCOMPARE(accepted, true); QCOMPARE(manager->serviceFound(), discovered); if (manager->serviceFound()) { QCOMPARE(manager->uploadServices().at(0).jid(), uploadServiceName); QCOMPARE(manager->uploadServices().at(0).sizeLimit(), maxFileSize); } } void tst_QXmppUploadRequestManager::testSending_data() { QTest::addColumn("fileInfo"); QTest::addColumn("fileName"); QTest::addColumn("fileSize"); QTest::addColumn("fileType"); QTest::newRow("fileInfo") << QFileInfo(":/test.svg") << "test.svg" << 2280LL << "image/svg+xml"; QTest::newRow("fileWithSizeBelowLimit") << QFileInfo() << "whatever.jpeg" << 698547LL << "image/jpeg"; QTest::newRow("fileWithSizeAboveLimit") << QFileInfo() << "some.pdf" << 65896498547LL << "application/pdf"; // there is no size above limit handling in request manager // there is also no code that selects an upload service with proper // size limit above requesting file size. // Is it something to worry about? } void tst_QXmppUploadRequestManager::testSending() { QFETCH(QFileInfo, fileInfo); QFETCH(QString, fileName); QFETCH(qint64, fileSize); QFETCH(QString, fileType); QXmppLogger* logger = new QXmppLogger(); logger->setLoggingType(QXmppLogger::SignalLogging); client.setLogger(logger); lastMimeType = QMimeDatabase().mimeTypeForName(fileType); connect(logger, &QXmppLogger::message, this, &tst_QXmppUploadRequestManager::onLoggerMessage); lastFileName = fileName; lastFileSize = fileSize; QString returnId; if (!fileInfo.baseName().isEmpty()) returnId = manager->requestUploadSlot(fileInfo); else returnId = manager->requestUploadSlot(fileName, fileSize, lastMimeType); // The client is not connected, so we never get an ID back (the packet was not sent). QVERIFY(returnId.isNull()); } void tst_QXmppUploadRequestManager::testUploadService() { QXmppUploadService service; QCOMPARE(service.sizeLimit(), -1LL); QVERIFY(service.jid().isNull()); service.setSizeLimit(256LL * 1024LL * 1024LL); QCOMPARE(service.sizeLimit(), 256LL * 1024LL * 1024LL); service.setJid(QStringLiteral("upload.shakespeare.lit")); QCOMPARE(service.jid(), QStringLiteral("upload.shakespeare.lit")); } QTEST_MAIN(tst_QXmppUploadRequestManager) #include "tst_qxmppuploadrequestmanager.moc" qxmpp-1.4.0/tests/qxmppuploadrequestmanager/tst_qxmppuploadrequestmanager.qrc000066400000000000000000000001341402370562100302560ustar00rootroot00000000000000 test.svg qxmpp-1.4.0/tests/qxmpputils/000077500000000000000000000000001402370562100162515ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmpputils/CMakeLists.txt000066400000000000000000000003461402370562100210140ustar00rootroot00000000000000include_directories(${CMAKE_CURRENT_BINARY_DIR}) add_executable(tst_qxmpputils tst_qxmpputils.cpp tst_qxmpputils.qrc) add_test(tst_qxmpputils tst_qxmpputils) target_link_libraries(tst_qxmpputils Qt${QT_VERSION_MAJOR}::Test qxmpp) qxmpp-1.4.0/tests/qxmpputils/test.bmp000066400000000000000000000002561402370562100177330ustar00rootroot00000000000000BM6(x  qxmpp-1.4.0/tests/qxmpputils/test.gif000066400000000000000000000000621402370562100177150ustar00rootroot00000000000000GIF87a, L$ʹ|N;qxmpp-1.4.0/tests/qxmpputils/test.jpg000066400000000000000000000005401402370562100177310ustar00rootroot00000000000000JFIFHHCreated with GIMP on a MacC  !"$"$C"656qt ?] image/svg+xml qxmpp-1.4.0/tests/qxmpputils/test.xpm000066400000000000000000000002341402370562100177550ustar00rootroot00000000000000/* XPM */ static char * test_xpm[] = { "6 6 3 1", " c #FF0000", ". c #00FF00", "+ c #0000FF", " ..++", " ..++", " ..++", " ..++", " ..++", " ..++"}; qxmpp-1.4.0/tests/qxmpputils/tst_qxmpputils.cpp000066400000000000000000000115421402370562100221000ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Jeremy Lainé * Manjeet Dahiya * * 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 "QXmppUtils.h" #include "util.h" #include class tst_QXmppUtils : public QObject { Q_OBJECT private slots: void testCrc32(); void testHmac(); void testJid(); void testMime(); void testTimezoneOffset(); void testStanzaHash(); }; void tst_QXmppUtils::testCrc32() { quint32 crc = QXmppUtils::generateCrc32(QByteArray()); QCOMPARE(crc, 0u); crc = QXmppUtils::generateCrc32(QByteArray("Hi There")); QCOMPARE(crc, 0xDB143BBEu); } void tst_QXmppUtils::testHmac() { QByteArray hmac = QXmppUtils::generateHmacMd5(QByteArray(16, '\x0b'), QByteArray("Hi There")); QCOMPARE(hmac, QByteArray::fromHex("9294727a3638bb1c13f48ef8158bfc9d")); hmac = QXmppUtils::generateHmacMd5(QByteArray("Jefe"), QByteArray("what do ya want for nothing?")); QCOMPARE(hmac, QByteArray::fromHex("750c783e6ab0b503eaa86e310a5db738")); hmac = QXmppUtils::generateHmacMd5(QByteArray(16, '\xaa'), QByteArray(50, '\xdd')); QCOMPARE(hmac, QByteArray::fromHex("56be34521d144c88dbb8c733f0e8b3f6")); } void tst_QXmppUtils::testJid() { QCOMPARE(QXmppUtils::jidToBareJid("foo@example.com/resource"), QLatin1String("foo@example.com")); QCOMPARE(QXmppUtils::jidToBareJid("foo@example.com"), QLatin1String("foo@example.com")); QCOMPARE(QXmppUtils::jidToBareJid("example.com"), QLatin1String("example.com")); QCOMPARE(QXmppUtils::jidToBareJid(QString()), QString()); QCOMPARE(QXmppUtils::jidToDomain("foo@example.com/resource"), QLatin1String("example.com")); QCOMPARE(QXmppUtils::jidToDomain("foo@example.com"), QLatin1String("example.com")); QCOMPARE(QXmppUtils::jidToDomain("example.com"), QLatin1String("example.com")); QCOMPARE(QXmppUtils::jidToDomain(QString()), QString()); QCOMPARE(QXmppUtils::jidToResource("foo@example.com/resource"), QLatin1String("resource")); QCOMPARE(QXmppUtils::jidToResource("foo@example.com"), QString()); QCOMPARE(QXmppUtils::jidToResource("example.com"), QString()); QCOMPARE(QXmppUtils::jidToResource(QString()), QString()); QCOMPARE(QXmppUtils::jidToUser("foo@example.com/resource"), QLatin1String("foo")); QCOMPARE(QXmppUtils::jidToUser("foo@example.com"), QLatin1String("foo")); QCOMPARE(QXmppUtils::jidToUser("example.com"), QString()); QCOMPARE(QXmppUtils::jidToUser(QString()), QString()); } // FIXME: how should we test MIME detection without expose getImageType? #if 0 QString getImageType(const QByteArray &contents); static void testMimeType(const QString &fileName, const QString fileType) { // load file from resources QFile file(":/" + fileName); QCOMPARE(file.open(QIODevice::ReadOnly), true); QCOMPARE(getImageType(file.readAll()), fileType); file.close(); } void tst_QXmppUtils::testMime() { testMimeType("test.bmp", "image/bmp"); testMimeType("test.gif", "image/gif"); testMimeType("test.jpg", "image/jpeg"); testMimeType("test.mng", "video/x-mng"); testMimeType("test.png", "image/png"); testMimeType("test.svg", "image/svg+xml"); testMimeType("test.xpm", "image/x-xpm"); } #else void tst_QXmppUtils::testMime() { } #endif void tst_QXmppUtils::testTimezoneOffset() { // parsing QCOMPARE(QXmppUtils::timezoneOffsetFromString("Z"), 0); QCOMPARE(QXmppUtils::timezoneOffsetFromString("+00:00"), 0); QCOMPARE(QXmppUtils::timezoneOffsetFromString("-00:00"), 0); QCOMPARE(QXmppUtils::timezoneOffsetFromString("+01:30"), 5400); QCOMPARE(QXmppUtils::timezoneOffsetFromString("-01:30"), -5400); // serialization QCOMPARE(QXmppUtils::timezoneOffsetToString(0), QLatin1String("Z")); QCOMPARE(QXmppUtils::timezoneOffsetToString(5400), QLatin1String("+01:30")); QCOMPARE(QXmppUtils::timezoneOffsetToString(-5400), QLatin1String("-01:30")); } void tst_QXmppUtils::testStanzaHash() { for (int i = 0; i < 100; i++) { const QString hash = QXmppUtils::generateStanzaHash(i); QCOMPARE(hash.size(), i); if (i == 36) { QCOMPARE(hash.count('-'), 4); } } const QString hash = QXmppUtils::generateStanzaUuid(); QCOMPARE(hash.size(), 36); QCOMPARE(hash.count('-'), 4); } QTEST_MAIN(tst_QXmppUtils) #include "tst_qxmpputils.moc" qxmpp-1.4.0/tests/qxmpputils/tst_qxmpputils.qrc000066400000000000000000000003701402370562100221000ustar00rootroot00000000000000 test.bmp test.gif test.jpg test.mng test.png test.svg test.xpm qxmpp-1.4.0/tests/qxmppvcardiq/000077500000000000000000000000001402370562100165425ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppvcardiq/tst_qxmppvcardiq.cpp000066400000000000000000000271401402370562100226630ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Jeremy Lainé * * 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 "QXmppVCardIq.h" #include "util.h" #include class tst_QXmppVCardIq : public QObject { Q_OBJECT private slots: void testAddress_data(); void testAddress(); void testEmail_data(); void testEmail(); void testPhone_data(); void testPhone(); void testVCard(); }; void tst_QXmppVCardIq::testAddress_data() { QTest::addColumn("xml"); QTest::addColumn("type"); QTest::addColumn("country"); QTest::addColumn("locality"); QTest::addColumn("postcode"); QTest::addColumn("region"); QTest::addColumn("street"); QTest::addColumn("equalsEmpty"); QTest::newRow("none") << QByteArray("") << int(QXmppVCardAddress::None) << "" << "" << "" << "" << "" << true; QTest::newRow("HOME") << QByteArray("") << int(QXmppVCardAddress::Home) << "" << "" << "" << "" << "" << false; QTest::newRow("WORK") << QByteArray("") << int(QXmppVCardAddress::Work) << "" << "" << "" << "" << "" << false; QTest::newRow("POSTAL") << QByteArray("") << int(QXmppVCardAddress::Postal) << "" << "" << "" << "" << "" << false; QTest::newRow("PREF") << QByteArray("") << int(QXmppVCardAddress::Preferred) << "" << "" << "" << "" << "" << false; QTest::newRow("country") << QByteArray("France") << int(QXmppVCardAddress::None) << "France" << "" << "" << "" << "" << false; QTest::newRow("locality") << QByteArray("Paris") << int(QXmppVCardAddress::None) << "" << "Paris" << "" << "" << "" << false; QTest::newRow("postcode") << QByteArray("75008") << int(QXmppVCardAddress::None) << "" << "" << "75008" << "" << "" << false; QTest::newRow("region") << QByteArray("Ile de France") << int(QXmppVCardAddress::None) << "" << "" << "" << "Ile de France" << "" << false; QTest::newRow("street") << QByteArray("55 rue du faubourg Saint-Honoré") << int(QXmppVCardAddress::None) << "" << "" << "" << "" << QString::fromUtf8("55 rue du faubourg Saint-Honoré") << false; } void tst_QXmppVCardIq::testAddress() { QFETCH(QByteArray, xml); QFETCH(int, type); QFETCH(QString, country); QFETCH(QString, locality); QFETCH(QString, postcode); QFETCH(QString, region); QFETCH(QString, street); QFETCH(bool, equalsEmpty); QXmppVCardAddress address; parsePacket(address, xml); QCOMPARE(int(address.type()), type); QCOMPARE(address.country(), country); QCOMPARE(address.locality(), locality); QCOMPARE(address.postcode(), postcode); QCOMPARE(address.region(), region); QCOMPARE(address.street(), street); serializePacket(address, xml); QXmppVCardAddress addressCopy = address; QVERIFY2(addressCopy == address, "QXmppVCardAddres::operator==() fails"); QVERIFY2(!(addressCopy != address), "QXmppVCardAddres::operator!=() fails"); QXmppVCardAddress emptyAddress; QCOMPARE(emptyAddress == address, equalsEmpty); QCOMPARE(emptyAddress != address, !equalsEmpty); } void tst_QXmppVCardIq::testEmail_data() { QTest::addColumn("xml"); QTest::addColumn("type"); QTest::newRow("none") << QByteArray("foo.bar@example.com") << int(QXmppVCardEmail::None); QTest::newRow("HOME") << QByteArray("foo.bar@example.com") << int(QXmppVCardEmail::Home); QTest::newRow("WORK") << QByteArray("foo.bar@example.com") << int(QXmppVCardEmail::Work); QTest::newRow("INTERNET") << QByteArray("foo.bar@example.com") << int(QXmppVCardEmail::Internet); QTest::newRow("X400") << QByteArray("foo.bar@example.com") << int(QXmppVCardEmail::X400); QTest::newRow("PREF") << QByteArray("foo.bar@example.com") << int(QXmppVCardEmail::Preferred); QTest::newRow("all") << QByteArray("foo.bar@example.com") << int(QXmppVCardEmail::Home | QXmppVCardEmail::Work | QXmppVCardEmail::Internet | QXmppVCardEmail::Preferred | QXmppVCardEmail::X400); } void tst_QXmppVCardIq::testEmail() { QFETCH(QByteArray, xml); QFETCH(int, type); QXmppVCardEmail email; parsePacket(email, xml); QCOMPARE(email.address(), QLatin1String("foo.bar@example.com")); QCOMPARE(int(email.type()), type); serializePacket(email, xml); } void tst_QXmppVCardIq::testPhone_data() { QTest::addColumn("xml"); QTest::addColumn("type"); QTest::newRow("none") << QByteArray("12345") << int(QXmppVCardPhone::None); QTest::newRow("HOME") << QByteArray("12345") << int(QXmppVCardPhone::Home); QTest::newRow("WORK") << QByteArray("12345") << int(QXmppVCardPhone::Work); QTest::newRow("VOICE") << QByteArray("12345") << int(QXmppVCardPhone::Voice); QTest::newRow("FAX") << QByteArray("12345") << int(QXmppVCardPhone::Fax); QTest::newRow("PAGER") << QByteArray("12345") << int(QXmppVCardPhone::Pager); QTest::newRow("MSG") << QByteArray("12345") << int(QXmppVCardPhone::Messaging); QTest::newRow("CELL") << QByteArray("12345") << int(QXmppVCardPhone::Cell); QTest::newRow("VIDEO") << QByteArray("") << int(QXmppVCardPhone::Video); QTest::newRow("BBS") << QByteArray("12345") << int(QXmppVCardPhone::BBS); QTest::newRow("MODEM") << QByteArray("12345") << int(QXmppVCardPhone::Modem); QTest::newRow("IDSN") << QByteArray("12345") << int(QXmppVCardPhone::ISDN); QTest::newRow("PCS") << QByteArray("12345") << int(QXmppVCardPhone::PCS); QTest::newRow("PREF") << QByteArray("12345") << int(QXmppVCardPhone::Preferred); } void tst_QXmppVCardIq::testPhone() { QFETCH(QByteArray, xml); QFETCH(int, type); QXmppVCardPhone phone; parsePacket(phone, xml); QCOMPARE(phone.number(), QLatin1String("12345")); QCOMPARE(int(phone.type()), type); serializePacket(phone, xml); } void tst_QXmppVCardIq::testVCard() { const QByteArray xml( "" "" "France" "1983-09-14" "I like XMPP." "foo.bar@example.com" "Foo Bar!" "FooBar" "FooWizBaz" "12345" "67890" "" "image/png" "" "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAAXNSR0IArs4c6QAAAAlwSFlzAAA" "UIgAAFCIBjw1HyAAAAAd0SU1FB9oIHQInNvuJovgAAAAiSURBVAjXY2TQ+s/AwMDAwPD/GiMDlP" "WfgYGBiQEHGJwSAK2BBQ1f3uvpAAAAAElFTkSuQmCC" "" "" "https://github.com/qxmpp-project/qxmpp/" "" "QXmpp foundation" "Main QXmpp dev unit" "" "Executive Director" "Patron Saint" "" ""); QXmppVCardIq vcard; parsePacket(vcard, xml); QCOMPARE(vcard.addresses().size(), 1); QCOMPARE(vcard.addresses()[0].country(), QLatin1String("France")); QCOMPARE(int(vcard.addresses()[0].type()), int(QXmppVCardEmail::None)); QCOMPARE(vcard.birthday(), QDate(1983, 9, 14)); QCOMPARE(vcard.description(), QLatin1String("I like XMPP.")); QCOMPARE(vcard.email(), QLatin1String("foo.bar@example.com")); QCOMPARE(vcard.emails().size(), 1); QCOMPARE(vcard.emails()[0].address(), QLatin1String("foo.bar@example.com")); QCOMPARE(int(vcard.emails()[0].type()), int(QXmppVCardEmail::Internet)); QCOMPARE(vcard.nickName(), QLatin1String("FooBar")); QCOMPARE(vcard.fullName(), QLatin1String("Foo Bar!")); QCOMPARE(vcard.firstName(), QLatin1String("Foo")); QCOMPARE(vcard.middleName(), QLatin1String("Baz")); QCOMPARE(vcard.lastName(), QLatin1String("Wiz")); QCOMPARE(vcard.phones().size(), 2); QCOMPARE(vcard.phones()[0].number(), QLatin1String("12345")); QCOMPARE(int(vcard.phones()[0].type()), int(QXmppVCardEmail::Home)); QCOMPARE(vcard.phones()[1].number(), QLatin1String("67890")); QCOMPARE(int(vcard.phones()[1].type()), int(QXmppVCardEmail::Work)); QCOMPARE(vcard.photo(), QByteArray::fromBase64("iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAAXNSR0IArs4c6QAAAAlwSFlzAAA" "UIgAAFCIBjw1HyAAAAAd0SU1FB9oIHQInNvuJovgAAAAiSURBVAjXY2TQ+s/AwMDAwPD/GiMDlP" "WfgYGBiQEHGJwSAK2BBQ1f3uvpAAAAAElFTkSuQmCC")); QCOMPARE(vcard.photoType(), QLatin1String("image/png")); QCOMPARE(vcard.url(), QLatin1String("https://github.com/qxmpp-project/qxmpp/")); const QXmppVCardOrganization &orgInfo = vcard.organization(); QCOMPARE(orgInfo.organization(), QLatin1String("QXmpp foundation")); QCOMPARE(orgInfo.unit(), QLatin1String("Main QXmpp dev unit")); QCOMPARE(orgInfo.title(), QLatin1String("Executive Director")); QCOMPARE(orgInfo.role(), QLatin1String("Patron Saint")); serializePacket(vcard, xml); } QTEST_MAIN(tst_QXmppVCardIq) #include "tst_qxmppvcardiq.moc" qxmpp-1.4.0/tests/qxmppvcardmanager/000077500000000000000000000000001402370562100175435ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppvcardmanager/tst_qxmppvcardmanager.cpp000066400000000000000000000117601402370562100246660ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Melvin Keskin * Linus Jahn * * 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 "QXmppClient.h" #include "QXmppVCardIq.h" #include "QXmppVCardManager.h" #include #include "IntegrationTesting.h" #include "util.h" #include Q_DECLARE_METATYPE(QXmppVCardIq); class tst_QXmppVCardManager : public QObject { Q_OBJECT private: Q_SLOT void testHandleStanza_data(); Q_SLOT void testHandleStanza(); // integration tests Q_SLOT void testSetClientVCard(); QXmppClient m_client; }; void tst_QXmppVCardManager::testHandleStanza_data() { QTest::addColumn("expectedIq"); QTest::addColumn("isClientVCard"); #define ROW(name, iq, clientVCard) \ QTest::newRow(QT_STRINGIFY(name)) << iq << clientVCard QXmppVCardIq iq; iq.setType(QXmppIq::Result); iq.setTo("stpeter@jabber.org/roundabout"); iq.setFullName("Jeremie Miller"); auto iqFromBare = iq; iqFromBare.setFrom("stpeter@jabber.org"); auto iqFromFull = iq; iqFromFull.setFrom("stpeter@jabber.org/roundabout"); ROW(client-vcard-from-empty, iq, true); ROW(client-vcard-from-bare, iqFromBare, true); ROW(client-vcard-from-full, iqFromFull, false); #undef ROW } void tst_QXmppVCardManager::testHandleStanza() { QFETCH(QXmppVCardIq, expectedIq); QFETCH(bool, isClientVCard); // initialize new manager to clear internal values QXmppVCardManager *manager = new QXmppVCardManager(); m_client.addExtension(manager); // sets own jid internally m_client.connectToServer("stpeter@jabber.org", {}); m_client.disconnectFromServer(); bool vCardReceived = false; bool clientVCardReceived = false; QObject context; connect(manager, &QXmppVCardManager::vCardReceived, &context, [&](QXmppVCardIq iq) { vCardReceived = true; QCOMPARE(iq, expectedIq); }); connect(manager, &QXmppVCardManager::clientVCardReceived, &context, [&]() { clientVCardReceived = true; QCOMPARE(manager->clientVCard(), expectedIq); }); bool accepted = manager->handleStanza(writePacketToDom(expectedIq)); QVERIFY(accepted); QVERIFY(vCardReceived); QCOMPARE(clientVCardReceived, isClientVCard); // clean up (client deletes manager) m_client.removeExtension(manager); } void tst_QXmppVCardManager::testSetClientVCard() { SKIP_IF_INTEGRATION_TESTS_DISABLED(); auto client = std::make_unique(); auto *vCardManager = client->findExtension(); auto config = IntegrationTests::clientConfiguration(); QSignalSpy connectSpy(client.get(), &QXmppClient::connected); QSignalSpy disconnectSpy(client.get(), &QXmppClient::disconnected); QSignalSpy vCardSpy(vCardManager, &QXmppVCardManager::clientVCardReceived); // connect to server client->connectToServer(config); QVERIFY2(connectSpy.wait(), "Could not connect to server!"); // request own vcard vCardManager->requestClientVCard(); QVERIFY(vCardSpy.wait()); // check our vcard has the correct address QCOMPARE(vCardManager->clientVCard().from(), client->configuration().jidBare()); // set a new vcard QXmppVCardIq newVCard; newVCard.setFirstName(QStringLiteral("Bob")); newVCard.setBirthday(QDate(1, 2, 2000)); newVCard.setEmail(QStringLiteral("bob@qxmpp.org")); vCardManager->setClientVCard(newVCard); // there's currently no signal to see whether the change was successful... QCoreApplication::processEvents(); // reconnect client->disconnectFromServer(); QVERIFY(disconnectSpy.wait()); client->connectToServer(config); QVERIFY2(connectSpy.wait(), "Could not connect to server!"); // request own vcard vCardManager->requestClientVCard(); QVERIFY(vCardSpy.wait()); // check our vcard has been changed successfully QCOMPARE(vCardManager->clientVCard().from(), client->configuration().jidBare()); QCOMPARE(vCardManager->clientVCard().firstName(), QStringLiteral("Bob")); QCOMPARE(vCardManager->clientVCard().birthday(), QDate(01, 02, 2000)); QCOMPARE(vCardManager->clientVCard().email(), QStringLiteral("bob@qxmpp.org")); // reset the vcard for future tests vCardManager->setClientVCard(QXmppVCardIq()); // disconnect client->disconnectFromServer(); QVERIFY(disconnectSpy.wait()); } QTEST_MAIN(tst_QXmppVCardManager) #include "tst_qxmppvcardmanager.moc" qxmpp-1.4.0/tests/qxmppversioniq/000077500000000000000000000000001402370562100171305ustar00rootroot00000000000000qxmpp-1.4.0/tests/qxmppversioniq/tst_qxmppversioniq.cpp000066400000000000000000000047501402370562100236410ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Jeremy Lainé * Manjeet Dahiya * * 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 "QXmppVersionIq.h" #include "util.h" #include class tst_QXmppVersionIq : public QObject { Q_OBJECT private slots: void testVersionGet(); void testVersionResult(); }; void tst_QXmppVersionIq::testVersionGet() { const QByteArray xmlGet( "" ""); QXmppVersionIq verIqGet; parsePacket(verIqGet, xmlGet); QCOMPARE(verIqGet.id(), QLatin1String("version_1")); QCOMPARE(verIqGet.to(), QLatin1String("juliet@capulet.com/balcony")); QCOMPARE(verIqGet.from(), QLatin1String("romeo@montague.net/orchard")); QCOMPARE(verIqGet.type(), QXmppIq::Get); serializePacket(verIqGet, xmlGet); } void tst_QXmppVersionIq::testVersionResult() { const QByteArray xmlResult( "" "" "qxmpp" "Windows-XP" "0.2.0" ""); QXmppVersionIq verIqResult; parsePacket(verIqResult, xmlResult); QCOMPARE(verIqResult.id(), QLatin1String("version_1")); QCOMPARE(verIqResult.to(), QLatin1String("romeo@montague.net/orchard")); QCOMPARE(verIqResult.from(), QLatin1String("juliet@capulet.com/balcony")); QCOMPARE(verIqResult.type(), QXmppIq::Result); QCOMPARE(verIqResult.name(), QString("qxmpp")); QCOMPARE(verIqResult.version(), QString("0.2.0")); QCOMPARE(verIqResult.os(), QString("Windows-XP")); serializePacket(verIqResult, xmlResult); } QTEST_MAIN(tst_QXmppVersionIq) #include "tst_qxmppversioniq.moc" qxmpp-1.4.0/tests/travis/000077500000000000000000000000001402370562100153335ustar00rootroot00000000000000qxmpp-1.4.0/tests/travis/build-and-test000077500000000000000000000012261402370562100200760ustar00rootroot00000000000000#!/bin/sh set -e HOST_SYSTEM=$(uname -s) CMAKE_ARGS="-DBUILD_TESTS:BOOL=True" case "$CONFIG" in full*) CMAKE_ARGS="-DBUILD_DOCUMENTATION:BOOL=True -DBUILD_EXAMPLES:BOOL=True -DWITH_GSTREAMER:BOOL=True" ;; esac case "$CONFIG" in *debug*) CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_BUILD_TYPE:STRING=Debug -DBUILD_INTERNAL_TESTS:BOOL=True" ;; esac # build with code coverage if [ "$CONFIG" = "full-debug" ]; then export CXXFLAGS="-fprofile-arcs -ftest-coverage" fi # compile mkdir build cd build cmake .. $CMAKE_ARGS if [ $HOST_SYSTEM = "Darwin" ]; then make -k -j$(sysctl -n hw.logicalcpu) else make -k -j$(nproc) fi # run tests make test qxmpp-1.4.0/tests/travis/install-build-depends000077500000000000000000000011161402370562100214430ustar00rootroot00000000000000#!/bin/sh set -e if [ "$(uname -s)" = "Darwin" ]; then brew update brew unlink python@3 brew install python@3 || true brew link --overwrite python@3 case "$CONFIG" in full*) brew install doxygen gstreamer gst-plugins-base gst-plugins-good gst-plugins-ugly ;; esac else sudo apt-get update -qq sudo apt-get install -qq clang cmake case "$CONFIG" in full*) sudo apt-get install -qq doxygen graphviz libgstreamer1.0-dev gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-ugly ;; esac fi qxmpp-1.4.0/tests/travis/push-documentation000077500000000000000000000020321402370562100211040ustar00rootroot00000000000000#!/bin/bash # Add ssh push key eval "$(ssh-agent -s)" echo $PUSH_KEY_REPO_DOC_QXMPP_ORG | base64 -d | unxz | ssh-add - # Set up git git config --global user.email "docsbot@qxmpp.org" git config --global user.name "QXmpp Docs Bot" # # Build documentation # echo '-------------------------------------------' echo 'Building documentation' echo '-------------------------------------------' mkdir build cd build cmake .. -DBUILD_DOCUMENTATION=ON make doc # # Push documentation # echo '-------------------------------------------' echo 'Cloning doc.qxmpp.org repository' echo '-------------------------------------------' git clone ssh://git@github.com/qxmpp-project/doc.qxmpp.org echo '-------------------------------------------' echo 'Commiting and pushing documentation' echo '-------------------------------------------' # delete old dev docs rm -rf doc.qxmpp.org/qxmpp-dev cp -r doc/html doc.qxmpp.org/qxmpp-dev git -C doc.qxmpp.org add . git -C doc.qxmpp.org commit -m "Add documentation for QXmpp master" git -C doc.qxmpp.org push qxmpp-1.4.0/tests/util.h000066400000000000000000000046661402370562100151650ustar00rootroot00000000000000/* * Copyright (C) 2008-2021 The QXmpp developers * * Authors: * Jeremy Lainé * Manjeet Dahiya * * 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 "QXmppPasswordChecker.h" #include #include template static void parsePacket(T &packet, const QByteArray &xml) { //qDebug() << "parsing" << xml; QDomDocument doc; QCOMPARE(doc.setContent(xml, true), true); QDomElement element = doc.documentElement(); packet.parse(element); } template static void serializePacket(T &packet, const QByteArray &xml) { QBuffer buffer; buffer.open(QIODevice::ReadWrite); QXmlStreamWriter writer(&buffer); packet.toXml(&writer); qDebug() << "expect " << xml; qDebug() << "writing" << buffer.data(); QCOMPARE(buffer.data(), xml); } template QDomElement writePacketToDom(T packet) { QBuffer buffer; buffer.open(QIODevice::ReadWrite); QXmlStreamWriter writer(&buffer); packet.toXml(&writer); QDomDocument doc; doc.setContent(buffer.data(), true); return doc.documentElement(); } class TestPasswordChecker : public QXmppPasswordChecker { public: void addCredentials(const QString &user, const QString &password) { m_credentials.insert(user, password); }; /// Retrieves the password for the given username. QXmppPasswordReply::Error getPassword(const QXmppPasswordRequest &request, QString &password) override { if (m_credentials.contains(request.username())) { password = m_credentials.value(request.username()); return QXmppPasswordReply::NoError; } else { return QXmppPasswordReply::AuthorizationError; } }; /// Returns whether getPassword() is enabled. bool hasGetPassword() const override { return true; }; private: QMap m_credentials; };