aboutsummaryrefslogtreecommitdiff
path: root/src/base
diff options
context:
space:
mode:
authorNiels Ole Salscheider <niels_ole@salscheider-online.de>2017-02-12 17:18:06 +0100
committerJeremy Lainé <jeremy.laine@m4x.org>2017-02-12 17:18:06 +0100
commit084eb01fb395488a0a3aee799be2b72ce11aa220 (patch)
tree6fec32386c116b0560b9c5d81412993361a2f453 /src/base
parent9deb86b248fee6bb9bcee14d595a933c8fdc4aa2 (diff)
downloadqxmpp-084eb01fb395488a0a3aee799be2b72ce11aa220.tar.gz
Implement XEP-0198: Stream Management (client only) (#99)
* Some features can be available with different namespaces (e.g. SM) * Provide static functions to convert between strings and stream errors Stream management will reuse this for <failed />. * [travis] test builds using clang * Implement XEP-0198: Stream Management (client only) * QXmppOutgoingClient: Move private methods to QXmppOutgoingClientPrivate
Diffstat (limited to 'src/base')
-rw-r--r--src/base/QXmppConstants.cpp2
-rw-r--r--src/base/QXmppConstants_p.h2
-rw-r--r--src/base/QXmppIq.cpp8
-rw-r--r--src/base/QXmppIq.h2
-rw-r--r--src/base/QXmppMessage.cpp8
-rw-r--r--src/base/QXmppMessage.h2
-rw-r--r--src/base/QXmppPresence.cpp8
-rw-r--r--src/base/QXmppPresence.h2
-rw-r--r--src/base/QXmppStanza.cpp106
-rw-r--r--src/base/QXmppStanza.h2
-rw-r--r--src/base/QXmppStanza_p.h145
-rw-r--r--src/base/QXmppStream.cpp122
-rw-r--r--src/base/QXmppStream.h24
-rw-r--r--src/base/QXmppStreamFeatures.cpp34
-rw-r--r--src/base/QXmppStreamFeatures.h10
-rw-r--r--src/base/QXmppStreamManagement.cpp325
-rw-r--r--src/base/QXmppStreamManagement_p.h194
-rw-r--r--src/base/base.pri2
18 files changed, 891 insertions, 107 deletions
diff --git a/src/base/QXmppConstants.cpp b/src/base/QXmppConstants.cpp
index a1a62de0..323b77f5 100644
--- a/src/base/QXmppConstants.cpp
+++ b/src/base/QXmppConstants.cpp
@@ -98,6 +98,8 @@ 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
diff --git a/src/base/QXmppConstants_p.h b/src/base/QXmppConstants_p.h
index 8b78c9ab..c4cddeb8 100644
--- a/src/base/QXmppConstants_p.h
+++ b/src/base/QXmppConstants_p.h
@@ -110,6 +110,8 @@ 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
diff --git a/src/base/QXmppIq.cpp b/src/base/QXmppIq.cpp
index dee0cb83..3e579726 100644
--- a/src/base/QXmppIq.cpp
+++ b/src/base/QXmppIq.cpp
@@ -91,6 +91,14 @@ void QXmppIq::setType(QXmppIq::Type type)
d->type = type;
}
+/// Indicates if the QXmppStanza is a stanza in the XMPP sence (i. e. a message,
+/// iq or presence)
+
+bool QXmppIq::isXmppStanza() const
+{
+ return true;
+}
+
/// \cond
void QXmppIq::parse(const QDomElement &element)
{
diff --git a/src/base/QXmppIq.h b/src/base/QXmppIq.h
index 5cfe11c4..9d75908b 100644
--- a/src/base/QXmppIq.h
+++ b/src/base/QXmppIq.h
@@ -60,6 +60,8 @@ public:
QXmppIq::Type type() const;
void setType(QXmppIq::Type);
+ bool isXmppStanza() const;
+
/// \cond
void parse(const QDomElement &element);
void toXml(QXmlStreamWriter *writer) const;
diff --git a/src/base/QXmppMessage.cpp b/src/base/QXmppMessage.cpp
index cdccba73..df6e6bd1 100644
--- a/src/base/QXmppMessage.cpp
+++ b/src/base/QXmppMessage.cpp
@@ -467,6 +467,14 @@ void QXmppMessage::setPrivate(const bool priv)
d->privatemsg = priv;
}
+/// Indicates if the QXmppStanza is a stanza in the XMPP sence (i. e. a message,
+/// iq or presence)
+
+bool QXmppMessage::isXmppStanza() const
+{
+ return true;
+}
+
/// \cond
void QXmppMessage::parse(const QDomElement &element)
{
diff --git a/src/base/QXmppMessage.h b/src/base/QXmppMessage.h
index cff04250..fbed97e0 100644
--- a/src/base/QXmppMessage.h
+++ b/src/base/QXmppMessage.h
@@ -133,6 +133,8 @@ public:
bool isPrivate() const;
void setPrivate(const bool);
+ bool isXmppStanza() const;
+
/// \cond
void parse(const QDomElement &element);
void toXml(QXmlStreamWriter *writer) const;
diff --git a/src/base/QXmppPresence.cpp b/src/base/QXmppPresence.cpp
index 07d0aa9e..259a2ea5 100644
--- a/src/base/QXmppPresence.cpp
+++ b/src/base/QXmppPresence.cpp
@@ -496,3 +496,11 @@ void QXmppPresence::setMucSupported(bool supported)
{
d->mucSupported = supported;
}
+
+/// Indicates if the QXmppStanza is a stanza in the XMPP sence (i. e. a message,
+/// iq or presence)
+
+bool QXmppPresence::isXmppStanza() const
+{
+ return true;
+}
diff --git a/src/base/QXmppPresence.h b/src/base/QXmppPresence.h
index 011549a9..f94c7c2a 100644
--- a/src/base/QXmppPresence.h
+++ b/src/base/QXmppPresence.h
@@ -128,6 +128,8 @@ public:
QStringList capabilityExt() const;
+ bool isXmppStanza() const;
+
private:
QSharedDataPointer<QXmppPresencePrivate> d;
};
diff --git a/src/base/QXmppStanza.cpp b/src/base/QXmppStanza.cpp
index 99b0692e..a2bb43be 100644
--- a/src/base/QXmppStanza.cpp
+++ b/src/base/QXmppStanza.cpp
@@ -25,6 +25,7 @@
#include "QXmppStanza.h"
+#include "QXmppStanza_p.h"
#include "QXmppUtils.h"
#include "QXmppConstants_p.h"
@@ -245,55 +246,7 @@ QString QXmppStanza::Error::getTypeStr() const
QString QXmppStanza::Error::getConditionStr() const
{
- switch(m_condition)
- {
- case BadRequest:
- return "bad-request";
- case Conflict:
- return "conflict";
- case FeatureNotImplemented:
- return "feature-not-implemented";
- case Forbidden:
- return "forbidden";
- case Gone:
- return "gone";
- case InternalServerError:
- return "internal-server-error";
- case ItemNotFound:
- return "item-not-found";
- case JidMalformed:
- return "jid-malformed";
- case NotAcceptable:
- return "not-acceptable";
- case NotAllowed:
- return "not-allowed";
- case NotAuthorized:
- return "not-authorized";
- case PaymentRequired:
- return "payment-required";
- case RecipientUnavailable:
- return "recipient-unavailable";
- case Redirect:
- return "redirect";
- case RegistrationRequired:
- return "registration-required";
- case RemoteServerNotFound:
- return "remote-server-not-found";
- case RemoteServerTimeout:
- return "remote-server-timeout";
- case ResourceConstraint:
- return "resource-constraint";
- case ServiceUnavailable:
- return "service-unavailable";
- case SubscriptionRequired:
- return "subscription-required";
- case UndefinedCondition:
- return "undefined-condition";
- case UnexpectedRequest:
- return "unexpected-request";
- default:
- return "";
- }
+ return strFromCondition(m_condition);
}
void QXmppStanza::Error::setTypeFromStr(const QString& type)
@@ -314,52 +267,7 @@ void QXmppStanza::Error::setTypeFromStr(const QString& type)
void QXmppStanza::Error::setConditionFromStr(const QString& type)
{
- if(type == "bad-request")
- setCondition(BadRequest);
- else if(type == "conflict")
- setCondition(Conflict);
- else if(type == "feature-not-implemented")
- setCondition(FeatureNotImplemented);
- else if(type == "forbidden")
- setCondition(Forbidden);
- else if(type == "gone")
- setCondition(Gone);
- else if(type == "internal-server-error")
- setCondition(InternalServerError);
- else if(type == "item-not-found")
- setCondition(ItemNotFound);
- else if(type == "jid-malformed")
- setCondition(JidMalformed);
- else if(type == "not-acceptable")
- setCondition(NotAcceptable);
- else if(type == "not-allowed")
- setCondition(NotAllowed);
- else if(type == "not-authorized")
- setCondition(NotAuthorized);
- else if(type == "payment-required")
- setCondition(PaymentRequired);
- else if(type == "recipient-unavailable")
- setCondition(RecipientUnavailable);
- else if(type == "redirect")
- setCondition(Redirect);
- else if(type == "registration-required")
- setCondition(RegistrationRequired);
- else if(type == "remote-server-not-found")
- setCondition(RemoteServerNotFound);
- else if(type == "remote-server-timeout")
- setCondition(RemoteServerTimeout);
- else if(type == "resource-constraint")
- setCondition(ResourceConstraint);
- else if(type == "service-unavailable")
- setCondition(ServiceUnavailable);
- else if(type == "subscription-required")
- setCondition(SubscriptionRequired);
- else if(type == "undefined-condition")
- setCondition(UndefinedCondition);
- else if(type == "unexpected-request")
- setCondition(UnexpectedRequest);
- else
- setCondition(static_cast<QXmppStanza::Error::Condition>(-1));
+ setCondition(conditionFromStr(type));
}
void QXmppStanza::Error::parse(const QDomElement &errorElement)
@@ -578,6 +486,14 @@ void QXmppStanza::setExtendedAddresses(const QList<QXmppExtendedAddress> &addres
d->extendedAddresses = addresses;
}
+/// Indicates if the QXmppStanza is a stanza in the XMPP sence (i. e. a message,
+/// iq or presence)
+
+bool QXmppStanza::isXmppStanza() const
+{
+ return false;
+}
+
/// \cond
void QXmppStanza::generateAndSetNextId()
{
diff --git a/src/base/QXmppStanza.h b/src/base/QXmppStanza.h
index 4583bcd3..1d378956 100644
--- a/src/base/QXmppStanza.h
+++ b/src/base/QXmppStanza.h
@@ -189,6 +189,8 @@ public:
QList<QXmppExtendedAddress> extendedAddresses() const;
void setExtendedAddresses(const QList<QXmppExtendedAddress> &extendedAddresses);
+ virtual bool isXmppStanza() const;
+
/// \cond
virtual void parse(const QDomElement &element);
virtual void toXml(QXmlStreamWriter *writer) const = 0;
diff --git a/src/base/QXmppStanza_p.h b/src/base/QXmppStanza_p.h
new file mode 100644
index 00000000..a35a44f2
--- /dev/null
+++ b/src/base/QXmppStanza_p.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2017 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"
+
+// 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 strFromCondition(const 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";
+ case QXmppStanza::Error::PaymentRequired:
+ return "payment-required";
+ 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";
+ default:
+ return "";
+ }
+}
+
+static QXmppStanza::Error::Condition conditionFromStr(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")
+ return QXmppStanza::Error::PaymentRequired;
+ 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;
+ else
+ return static_cast<QXmppStanza::Error::Condition>(-1);
+}
+
+#endif
+
diff --git a/src/base/QXmppStream.cpp b/src/base/QXmppStream.cpp
index 9ea20856..215bf86b 100644
--- a/src/base/QXmppStream.cpp
+++ b/src/base/QXmppStream.cpp
@@ -27,11 +27,13 @@
#include "QXmppLogger.h"
#include "QXmppStanza.h"
#include "QXmppStream.h"
+#include "QXmppStreamManagement_p.h"
#include "QXmppUtils.h"
#include <QBuffer>
#include <QDomDocument>
#include <QHostAddress>
+#include <QMap>
#include <QRegExp>
#include <QSslSocket>
#include <QStringList>
@@ -51,10 +53,15 @@ public:
// incoming stream state
QByteArray streamStart;
+
+ bool streamManagementEnabled;
+ QMap<unsigned, QByteArray> unacknowledgedStanzas;
+ unsigned lastOutgoingSequenceNumber;
+ unsigned lastIncomingSequenceNumber;
};
QXmppStreamPrivate::QXmppStreamPrivate()
- : socket(0)
+ : socket(0), streamManagementEnabled(false), lastOutgoingSequenceNumber(0), lastIncomingSequenceNumber(0)
{
}
@@ -86,6 +93,7 @@ QXmppStream::~QXmppStream()
void QXmppStream::disconnectFromHost()
{
+ d->streamManagementEnabled = false;
if (d->socket) {
if (d->socket->state() == QAbstractSocket::ConnectedState) {
sendData(streamRootElementEnd);
@@ -140,8 +148,15 @@ bool QXmppStream::sendPacket(const QXmppStanza &packet)
QXmlStreamWriter xmlStream(&data);
packet.toXml(&xmlStream);
+ bool isXmppStanza = packet.isXmppStanza();
+ if (isXmppStanza && d->streamManagementEnabled)
+ d->unacknowledgedStanzas[++d->lastOutgoingSequenceNumber] = data;
+
// send packet
- return sendData(data);
+ bool success = sendData(data);
+ if (isXmppStanza)
+ sendAcknowledgementRequest();
+ return success;
}
/// Returns the QSslSocket used for this stream.
@@ -253,7 +268,17 @@ void QXmppStream::_q_socketReadyRead()
// process stanzas
QDomElement nodeRecv = doc.documentElement().firstChildElement();
while (!nodeRecv.isNull()) {
- handleStanza(nodeRecv);
+ if (QXmppStreamManagementAck::isStreamManagementAck(nodeRecv))
+ handleAcknowledgement(nodeRecv);
+ else if (QXmppStreamManagementReq::isStreamManagementReq(nodeRecv))
+ sendAcknowledgement();
+ else {
+ handleStanza(nodeRecv);
+ if(nodeRecv.tagName() == QLatin1String("message") ||
+ nodeRecv.tagName() == QLatin1String("presence") ||
+ nodeRecv.tagName() == QLatin1String("iq"))
+ ++d->lastIncomingSequenceNumber;
+ }
nodeRecv = nodeRecv.nextSiblingElement();
}
@@ -262,4 +287,95 @@ void QXmppStream::_q_socketReadyRead()
disconnectFromHost();
}
+/// Enables Stream Management acks / reqs (XEP-0198).
+///
+/// \param resetSequenceNumber Indicates if the sequence numbers should be resetted.
+/// This must be done iff the stream is not resumed.
+void QXmppStream::enableStreamManagement(bool resetSequenceNumber)
+{
+ d->streamManagementEnabled = true;
+
+ if (resetSequenceNumber) {
+ d->lastOutgoingSequenceNumber = 0;
+ d->lastIncomingSequenceNumber = 0;
+
+ // resend unacked stanzas
+ if (!d->unacknowledgedStanzas.empty()) {
+ QMap<unsigned, QByteArray> oldUnackedStanzas = d->unacknowledgedStanzas;
+ d->unacknowledgedStanzas.clear();
+ for (QMap<unsigned, QByteArray>::iterator it = oldUnackedStanzas.begin(); it != oldUnackedStanzas.end(); ++it) {
+ d->unacknowledgedStanzas[++d->lastOutgoingSequenceNumber] = it.value();
+ sendData(it.value());
+ }
+ sendAcknowledgementRequest();
+ }
+ } else {
+ // resend unacked stanzas
+ if (!d->unacknowledgedStanzas.empty()) {
+ for (QMap<unsigned, QByteArray>::iterator it = d->unacknowledgedStanzas.begin(); it != d->unacknowledgedStanzas.end(); ++it)
+ sendData(it.value());
+ sendAcknowledgementRequest();
+ }
+ }
+}
+/// Returns the sequence number of the last incoming stanza (XEP-0198).
+unsigned QXmppStream::lastIncomingSequenceNumber() const
+{
+ return d->lastIncomingSequenceNumber;
+}
+
+/// Sets the last acknowledged sequence number for outgoing stanzas (XEP-0198).
+void QXmppStream::setAcknowledgedSequenceNumber(unsigned sequenceNumber)
+{
+ for (QMap<unsigned, QByteArray>::iterator it = d->unacknowledgedStanzas.begin(); it != d->unacknowledgedStanzas.end(); ) {
+ if (it.key() <= sequenceNumber)
+ it = d->unacknowledgedStanzas.erase(it);
+ else
+ ++it;
+ }
+}
+
+/// Handles an incoming acknowledgement from XEP-0198.
+///
+/// \param element
+void QXmppStream::handleAcknowledgement(QDomElement &element)
+{
+ if (!d->streamManagementEnabled)
+ return;
+
+ QXmppStreamManagementAck ack;
+ ack.parse(element);
+ setAcknowledgedSequenceNumber(ack.seqNo());
+}
+
+/// Sends an acknowledgement as defined in XEP-0198.
+void QXmppStream::sendAcknowledgement()
+{
+ if (!d->streamManagementEnabled)
+ return;
+
+ // prepare packet
+ QByteArray data;
+ QXmlStreamWriter xmlStream(&data);
+ QXmppStreamManagementAck ack(d->lastIncomingSequenceNumber);
+ ack.toXml(&xmlStream);
+
+ // send packet
+ sendData(data);
+}
+
+/// Sends an acknowledgement request as defined in XEP-0198.
+void QXmppStream::sendAcknowledgementRequest()
+{
+ if (!d->streamManagementEnabled)
+ return;
+
+ // prepare packet
+ QByteArray data;
+ QXmlStreamWriter xmlStream(&data);
+ QXmppStreamManagementReq::toXml(&xmlStream);
+
+ // send packet
+ sendData(data);
+}
diff --git a/src/base/QXmppStream.h b/src/base/QXmppStream.h
index 3411e2e9..c0ac1807 100644
--- a/src/base/QXmppStream.h
+++ b/src/base/QXmppStream.h
@@ -74,6 +74,30 @@ protected:
/// \param element
virtual void handleStream(const QDomElement &element) = 0;
+ /// Enables Stream Management acks / reqs (XEP-0198).
+ ///
+ /// \param resetSeqno Indicates if the sequence numbers should be resetted.
+ /// This must be done iff the stream is not resumed.
+ void enableStreamManagement(bool resetSequenceNumber);
+
+ /// Returns the sequence number of the last incoming stanza (XEP-0198).
+ unsigned lastIncomingSequenceNumber() const;
+
+ /// Sets the last acknowledged sequence number for outgoing stanzas (XEP-0198).
+ void setAcknowledgedSequenceNumber(unsigned sequenceNumber);
+
+private:
+ /// Handles an incoming acknowledgement from XEP-0198.
+ ///
+ /// \param element
+ void handleAcknowledgement(QDomElement &element);
+
+ /// Sends an acknowledgement as defined in XEP-0198.
+ void sendAcknowledgement();
+
+ /// Sends an acknowledgement request as defined in XEP-0198.
+ void sendAcknowledgementRequest();
+
public slots:
virtual void disconnectFromHost();
virtual bool sendData(const QByteArray&);
diff --git a/src/base/QXmppStreamFeatures.cpp b/src/base/QXmppStreamFeatures.cpp
index 322530b5..fd2832dc 100644
--- a/src/base/QXmppStreamFeatures.cpp
+++ b/src/base/QXmppStreamFeatures.cpp
@@ -30,7 +30,8 @@ QXmppStreamFeatures::QXmppStreamFeatures()
: m_bindMode(Disabled),
m_sessionMode(Disabled),
m_nonSaslAuthMode(Disabled),
- m_tlsMode(Disabled)
+ m_tlsMode(Disabled),
+ m_streamManagementMode(Disabled)
{
}
@@ -94,6 +95,16 @@ void QXmppStreamFeatures::setTlsMode(QXmppStreamFeatures::Mode mode)
m_tlsMode = mode;
}
+QXmppStreamFeatures::Mode QXmppStreamFeatures::streamManagementMode() const
+{
+ return m_streamManagementMode;
+}
+
+void QXmppStreamFeatures::setStreamManagementMode(QXmppStreamFeatures::Mode mode)
+{
+ m_streamManagementMode = mode;
+}
+
/// \cond
bool QXmppStreamFeatures::isStreamFeatures(const QDomElement &element)
{
@@ -104,15 +115,18 @@ bool QXmppStreamFeatures::isStreamFeatures(const QDomElement &element)
static QXmppStreamFeatures::Mode readFeature(const QDomElement &element, const char *tagName, const char *tagNs)
{
QDomElement subElement = element.firstChildElement(tagName);
- if (subElement.namespaceURI() == tagNs)
- {
- if (!subElement.firstChildElement("required").isNull())
- return QXmppStreamFeatures::Required;
- else
- return QXmppStreamFeatures::Enabled;
- } else {
- return QXmppStreamFeatures::Disabled;
+ QXmppStreamFeatures::Mode mode = QXmppStreamFeatures::Disabled;
+ while (!subElement.isNull()) {
+ if (subElement.namespaceURI() == tagNs)
+ {
+ if (!subElement.firstChildElement("required").isNull())
+ mode = QXmppStreamFeatures::Required;
+ else if (mode != QXmppStreamFeatures::Required)
+ mode = QXmppStreamFeatures::Enabled;
+ }
+ subElement = subElement.nextSiblingElement(tagName);
}
+ return mode;
}
void QXmppStreamFeatures::parse(const QDomElement &element)
@@ -121,6 +135,7 @@ void QXmppStreamFeatures::parse(const QDomElement &element)
m_sessionMode = readFeature(element, "session", ns_session);
m_nonSaslAuthMode = readFeature(element, "auth", ns_authFeature);
m_tlsMode = readFeature(element, "starttls", ns_tls);
+ m_streamManagementMode = readFeature(element, "sm", ns_stream_management);
// parse advertised compression methods
QDomElement compression = element.firstChildElement("compression");
@@ -165,6 +180,7 @@ void QXmppStreamFeatures::toXml(QXmlStreamWriter *writer) const
writeFeature(writer, "session", ns_session, m_sessionMode);
writeFeature(writer, "auth", ns_authFeature, m_nonSaslAuthMode);
writeFeature(writer, "starttls", ns_tls, m_tlsMode);
+ writeFeature(writer, "sm", ns_stream_management, m_streamManagementMode);
if (!m_compressionMethods.isEmpty())
{
diff --git a/src/base/QXmppStreamFeatures.h b/src/base/QXmppStreamFeatures.h
index 46a9fd7d..242f748c 100644
--- a/src/base/QXmppStreamFeatures.h
+++ b/src/base/QXmppStreamFeatures.h
@@ -56,6 +56,15 @@ public:
Mode tlsMode() const;
void setTlsMode(Mode mode);
+ /// Returns the mode (disabled, enabled or required) for XEP-0198: Stream
+ /// Management
+ Mode streamManagementMode() const;
+
+ /// Sets the mode for XEP-0198: Stream Management
+ ///
+ /// \pa mode The mode to set.
+ void setStreamManagementMode(Mode mode);
+
/// \cond
void parse(const QDomElement &element);
void toXml(QXmlStreamWriter *writer) const;
@@ -68,6 +77,7 @@ private:
Mode m_sessionMode;
Mode m_nonSaslAuthMode;
Mode m_tlsMode;
+ Mode m_streamManagementMode;
QStringList m_authMechanisms;
QStringList m_compressionMethods;
};
diff --git a/src/base/QXmppStreamManagement.cpp b/src/base/QXmppStreamManagement.cpp
new file mode 100644
index 00000000..02c70c5a
--- /dev/null
+++ b/src/base/QXmppStreamManagement.cpp
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2008-2013 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 "QXmppStreamManagement_p.h"
+#include "QXmppStanza_p.h"
+
+#include "QXmppConstants_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("resume");
+ m_resume = resume == QString("true") || resume == QString("1");
+ m_max = element.attribute("max").toUInt();
+}
+
+void QXmppStreamManagementEnable::toXml(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("enable");
+ writer->writeAttribute("xmlns", ns_stream_management);
+ if (m_resume)
+ writer->writeAttribute("resume", "true");
+ if (m_max > 0)
+ writer->writeAttribute("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("resume");
+ m_resume = resume == QString("true") || resume == QString("1");
+ m_max = element.attribute("max").toUInt();
+ m_location = element.attribute("location");
+}
+
+void QXmppStreamManagementEnabled::toXml(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("enable");
+ writer->writeAttribute("xmlns", ns_stream_management);
+ if (m_resume)
+ writer->writeAttribute("resume", "true");
+ if (m_max > 0)
+ writer->writeAttribute("max", QString::number(m_max));
+ if (!m_location.isEmpty())
+ writer->writeAttribute("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("h").toUInt();
+ m_previd = element.attribute("previd");
+}
+
+void QXmppStreamManagementResume::toXml(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("resume");
+ writer->writeAttribute("h", QString::number(m_h));
+ writer->writeAttribute("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("h").toUInt();
+ m_previd = element.attribute("previd");
+}
+
+void QXmppStreamManagementResumed::toXml(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("resumed");
+ writer->writeAttribute("h", QString::number(m_h));
+ writer->writeAttribute("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 = conditionFromStr(childElement.tagName());
+ }
+}
+
+void QXmppStreamManagementFailed::toXml(QXmlStreamWriter *writer) const
+{
+ QString errorString = strFromCondition(m_error);
+
+ writer->writeStartElement("failed");
+ writer->writeAttribute("xmlns", ns_stream_management);
+ writer->writeStartElement(errorString, 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("h").toUInt();
+}
+
+void QXmppStreamManagementAck::toXml(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement("a");
+ writer->writeAttribute("xmlns", ns_stream_management);
+ writer->writeAttribute("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->writeAttribute("xmlns", ns_stream_management);
+ writer->writeEndElement();
+}
diff --git a/src/base/QXmppStreamManagement_p.h b/src/base/QXmppStreamManagement_p.h
new file mode 100644
index 00000000..2cf53c81
--- /dev/null
+++ b/src/base/QXmppStreamManagement_p.h
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2008-2014 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 <QDomDocument>
+#include <QXmlStreamWriter>
+
+// 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
+};
+
+#endif
diff --git a/src/base/base.pri b/src/base/base.pri
index 3326d07d..670de9d6 100644
--- a/src/base/base.pri
+++ b/src/base/base.pri
@@ -40,6 +40,7 @@ HEADERS += \
base/QXmppCodec_p.h \
base/QXmppConstants_p.h \
base/QXmppSasl_p.h \
+ base/QXmppStanza_p.h \
base/QXmppStreamInitiationIq_p.h \
base/QXmppStun_p.h
@@ -80,6 +81,7 @@ SOURCES += \
base/QXmppStream.cpp \
base/QXmppStreamFeatures.cpp \
base/QXmppStreamInitiationIq.cpp \
+ base/QXmppStreamManagement.cpp \
base/QXmppStun.cpp \
base/QXmppUtils.cpp \
base/QXmppVCardIq.cpp \