From 931fc68242bdfc5e5165b58f21614d84a2b489ff Mon Sep 17 00:00:00 2001 From: Jeremy Lainé Date: Sun, 30 Aug 2015 08:13:28 +0200 Subject: Fix build error when VPX support is enabled (closes #71) --- src/base/QXmppCodec.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/base') diff --git a/src/base/QXmppCodec.cpp b/src/base/QXmppCodec.cpp index 328fd043..b2622619 100644 --- a/src/base/QXmppCodec.cpp +++ b/src/base/QXmppCodec.cpp @@ -1218,11 +1218,11 @@ QList QXmppVpxDecoder::handlePacket(const QXmppRtpPacket &packe if (frag_type == NoFragment) { // unfragmented packet if ((payload[1] & 0x1) == 0 // is key frame - || packet.sequence == sequence) { + || packet.sequence() == sequence) { if (d->decodeFrame(payload.mid(1), &frame)) frames << frame; - sequence = packet.sequence + 1; + sequence = packet.sequence() + 1; } d->packetBuffer.resize(0); @@ -1231,13 +1231,13 @@ QList QXmppVpxDecoder::handlePacket(const QXmppRtpPacket &packe if (frag_type == StartFragment) { // start fragment if ((payload[1] & 0x1) == 0 // is key frame - || packet.sequence == sequence) { + || packet.sequence() == sequence) { d->packetBuffer = payload.mid(1); - sequence = packet.sequence + 1; + sequence = packet.sequence() + 1; } } else { // continuation or end fragment - if (packet.sequence == sequence) { + if (packet.sequence() == sequence) { const int packetPos = d->packetBuffer.size(); d->packetBuffer.resize(packetPos + packetLength); stream.readRawData(d->packetBuffer.data() + packetPos, packetLength); -- cgit v1.2.3 From f4397521b3a57caf5889bfbbb620ce910df97cbf Mon Sep 17 00:00:00 2001 From: Jeremy Lainé Date: Sun, 30 Aug 2015 08:22:16 +0200 Subject: fix compiler warning --- src/base/QXmppCodec.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/base') diff --git a/src/base/QXmppCodec.cpp b/src/base/QXmppCodec.cpp index b2622619..98b6934a 100644 --- a/src/base/QXmppCodec.cpp +++ b/src/base/QXmppCodec.cpp @@ -1260,6 +1260,7 @@ QList QXmppVpxDecoder::handlePacket(const QXmppRtpPacket &packe bool QXmppVpxDecoder::setParameters(const QMap ¶meters) { + Q_UNUSED(parameters); return true; } -- cgit v1.2.3 From 74c459bdb6d77ef598bfb3b352b488730b31ca8a Mon Sep 17 00:00:00 2001 From: Jeremy Lainé Date: Sun, 30 Aug 2015 08:24:49 +0200 Subject: bump version to 0.9.1 --- CHANGELOG | 5 +++++ qxmpp.pri | 2 +- src/base/QXmppGlobal.h | 2 +- tests/qxmpputils/tst_qxmpputils.cpp | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) (limited to 'src/base') diff --git a/CHANGELOG b/CHANGELOG index f9856b80..6d5f4da8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +QXmpp 0.9.1 (Aug 30, 2015) +-------------------------- + + - Fix build error when VPX support is enabled (issue 71). + QXmpp 0.9.0 (Aug 28, 2015) -------------------------- diff --git a/qxmpp.pri b/qxmpp.pri index 95cd16ac..fc4438bd 100644 --- a/qxmpp.pri +++ b/qxmpp.pri @@ -1,7 +1,7 @@ # Common definitions QT += network xml -QXMPP_VERSION = 0.9.0 +QXMPP_VERSION = 0.9.1 QXMPP_INCLUDEPATH = $$PWD/src/base $$PWD/src/client $$PWD/src/server # Determine library name diff --git a/src/base/QXmppGlobal.h b/src/base/QXmppGlobal.h index 1d4047ba..f6373f08 100644 --- a/src/base/QXmppGlobal.h +++ b/src/base/QXmppGlobal.h @@ -52,7 +52,7 @@ /// available. /// -#define QXMPP_VERSION 0x000900 +#define QXMPP_VERSION 0x000901 QXMPP_EXPORT QString QXmppVersion(); diff --git a/tests/qxmpputils/tst_qxmpputils.cpp b/tests/qxmpputils/tst_qxmpputils.cpp index 61d2a5dc..f14eacc8 100644 --- a/tests/qxmpputils/tst_qxmpputils.cpp +++ b/tests/qxmpputils/tst_qxmpputils.cpp @@ -114,7 +114,7 @@ void tst_QXmppUtils::testMime() void tst_QXmppUtils::testLibVersion() { - QCOMPARE(QXmppVersion(), QString("0.9.0")); + QCOMPARE(QXmppVersion(), QString("0.9.1")); } void tst_QXmppUtils::testTimezoneOffset() -- cgit v1.2.3 From aa131bd436b528a68bdcea4fab180b551f2a0d81 Mon Sep 17 00:00:00 2001 From: Jeremy Lainé Date: Mon, 31 Aug 2015 13:54:28 +0200 Subject: hide QXmppJingleIq internals --- src/base/QXmppJingleIq.cpp | 135 +++++++++++++++++++++++++++++++-------------- src/base/QXmppJingleIq.h | 36 +++++------- 2 files changed, 108 insertions(+), 63 deletions(-) (limited to 'src/base') diff --git a/src/base/QXmppJingleIq.cpp b/src/base/QXmppJingleIq.cpp index 26799054..6d2304f1 100644 --- a/src/base/QXmppJingleIq.cpp +++ b/src/base/QXmppJingleIq.cpp @@ -652,10 +652,40 @@ void QXmppJingleIq::Reason::toXml(QXmlStreamWriter *writer) const } /// \endcond +class QXmppJingleIqPrivate : public QSharedData +{ +public: + QXmppJingleIqPrivate(); + + QXmppJingleIq::Action action; + QString initiator; + QString responder; + QString sid; + + QXmppJingleIq::Content content; + QXmppJingleIq::Reason reason; + bool ringing; +}; + +QXmppJingleIqPrivate::QXmppJingleIqPrivate() + : ringing(false) +{ +} + /// Constructs a QXmppJingleIq. QXmppJingleIq::QXmppJingleIq() - : m_ringing(false) + : d(new QXmppJingleIqPrivate()) +{ +} + +QXmppJingleIq::QXmppJingleIq(const QXmppJingleIq &other) + : QXmppIq(other) + , d(other.d) +{ +} + +QXmppJingleIq::~QXmppJingleIq() { } @@ -663,7 +693,7 @@ QXmppJingleIq::QXmppJingleIq() QXmppJingleIq::Action QXmppJingleIq::action() const { - return m_action; + return d->action; } /// Sets the Jingle IQ's action. @@ -672,14 +702,28 @@ QXmppJingleIq::Action QXmppJingleIq::action() const void QXmppJingleIq::setAction(QXmppJingleIq::Action action) { - m_action = action; + d->action = action; +} + +/// Returns a reference to the IQ's content element. + +QXmppJingleIq::Content& QXmppJingleIq::content() +{ + return d->content; +} + +/// Returns a const reference to the IQ's content element. + +const QXmppJingleIq::Content& QXmppJingleIq::content() const +{ + return d->content; } /// Returns the session initiator. QString QXmppJingleIq::initiator() const { - return m_initiator; + return d->initiator; } /// Sets the session initiator. @@ -688,14 +732,28 @@ QString QXmppJingleIq::initiator() const void QXmppJingleIq::setInitiator(const QString &initiator) { - m_initiator = 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 m_responder; + return d->responder; } /// Sets the session responder. @@ -704,39 +762,39 @@ QString QXmppJingleIq::responder() const void QXmppJingleIq::setResponder(const QString &responder) { - m_responder = responder; + d->responder = responder; } -/// Returns the session ID. +/// Returns true if the call is ringing. -QString QXmppJingleIq::sid() const +bool QXmppJingleIq::ringing() const { - return m_sid; + return d->ringing; } -/// Sets the session ID. +/// Set to true if the call is ringing. /// -/// \param sid +/// \param ringing -void QXmppJingleIq::setSid(const QString &sid) +void QXmppJingleIq::setRinging(bool ringing) { - m_sid = sid; + d->ringing = ringing; } -/// Returns true if the call is ringing. +/// Returns the session ID. -bool QXmppJingleIq::ringing() const +QString QXmppJingleIq::sid() const { - return m_ringing; + return d->sid; } -/// Set to true if the call is ringing. +/// Sets the session ID. /// -/// \param ringing +/// \param sid -void QXmppJingleIq::setRinging(bool ringing) +void QXmppJingleIq::setSid(const QString &sid) { - m_ringing = ringing; + d->sid = sid; } /// \cond @@ -750,43 +808,40 @@ void QXmppJingleIq::parseElementFromChild(const QDomElement &element) { QDomElement jingleElement = element.firstChildElement("jingle"); const QString action = jingleElement.attribute("action"); - for (int i = ContentAccept; i <= TransportReplace; i++) - { - if (action == jingle_actions[i]) - { - m_action = static_cast(i); + for (int i = ContentAccept; i <= TransportReplace; i++) { + if (action == jingle_actions[i]) { + d->action = static_cast(i); break; } } - m_initiator = jingleElement.attribute("initiator"); - m_responder = jingleElement.attribute("responder"); - m_sid = jingleElement.attribute("sid"); + d->initiator = jingleElement.attribute("initiator"); + d->responder = jingleElement.attribute("responder"); + d->sid = jingleElement.attribute("sid"); // content QDomElement contentElement = jingleElement.firstChildElement("content"); - m_content.parse(contentElement); + d->content.parse(contentElement); QDomElement reasonElement = jingleElement.firstChildElement("reason"); - m_reason.parse(reasonElement); + d->reason.parse(reasonElement); // ringing QDomElement ringingElement = jingleElement.firstChildElement("ringing"); - m_ringing = (ringingElement.namespaceURI() == ns_jingle_rtp_info); + d->ringing = (ringingElement.namespaceURI() == ns_jingle_rtp_info); } void QXmppJingleIq::toXmlElementFromChild(QXmlStreamWriter *writer) const { writer->writeStartElement("jingle"); writer->writeAttribute("xmlns", ns_jingle); - helperToXmlAddAttribute(writer, "action", jingle_actions[m_action]); - helperToXmlAddAttribute(writer, "initiator", m_initiator); - helperToXmlAddAttribute(writer, "responder", m_responder); - helperToXmlAddAttribute(writer, "sid", m_sid); - m_content.toXml(writer); - m_reason.toXml(writer); + helperToXmlAddAttribute(writer, "action", jingle_actions[d->action]); + helperToXmlAddAttribute(writer, "initiator", d->initiator); + helperToXmlAddAttribute(writer, "responder", d->responder); + helperToXmlAddAttribute(writer, "sid", d->sid); + d->content.toXml(writer); + d->reason.toXml(writer); // ringing - if (m_ringing) - { + if (d->ringing) { writer->writeStartElement("ringing"); writer->writeAttribute("xmlns", ns_jingle_rtp_info); writer->writeEndElement(); diff --git a/src/base/QXmppJingleIq.h b/src/base/QXmppJingleIq.h index 046b7c96..5156646d 100644 --- a/src/base/QXmppJingleIq.h +++ b/src/base/QXmppJingleIq.h @@ -29,6 +29,7 @@ #include "QXmppIq.h" class QXmppJingleIqContentPrivate; +class QXmppJingleIqPrivate; /// \brief The QXmppJinglePayloadType class represents a payload type /// as specified by XEP-0167: Jingle RTP Sessions and RFC 5245. @@ -289,35 +290,31 @@ public: }; QXmppJingleIq(); + QXmppJingleIq(const QXmppJingleIq &other); + ~QXmppJingleIq(); Action action() const; void setAction(Action action); + Content& content(); + const Content& content() const; + QString initiator() const; void setInitiator(const QString &initiator); + Reason& reason(); + const Reason& reason() const; + QString responder() const; void setResponder(const QString &responder); - QString sid() const; - void setSid(const QString &sid); - - /// Returns a reference to the IQ's content element. - Content& content() { return m_content; }; - - /// Returns a const reference to the IQ's content element. - const Content& content() const { return m_content; }; - - /// Returns a reference to the IQ's reason element. - Reason& reason() { return m_reason; }; - - /// Returns a const reference to the IQ's reason element. - const Reason& reason() const { return m_reason; }; - // 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 @@ -329,14 +326,7 @@ protected: /// \endcond private: - Action m_action; - QString m_initiator; - QString m_responder; - QString m_sid; - - Content m_content; - Reason m_reason; - bool m_ringing; + QSharedDataPointer d; }; #endif -- cgit v1.2.3 From 596e81b331da8d24ca3d9f2604752f692e1ad3fe Mon Sep 17 00:00:00 2001 From: Jeremy Lainé Date: Mon, 31 Aug 2015 14:33:25 +0200 Subject: allow QXmppJingleIq to have multiple contents --- src/base/QXmppJingleIq.cpp | 38 +++++++-- src/base/QXmppJingleIq.h | 6 +- src/client/QXmppCallManager.cpp | 136 ++++++++++++++++-------------- tests/qxmppjingleiq/tst_qxmppjingleiq.cpp | 5 +- 4 files changed, 108 insertions(+), 77 deletions(-) (limited to 'src/base') diff --git a/src/base/QXmppJingleIq.cpp b/src/base/QXmppJingleIq.cpp index 6d2304f1..fa958490 100644 --- a/src/base/QXmppJingleIq.cpp +++ b/src/base/QXmppJingleIq.cpp @@ -267,6 +267,12 @@ QList QXmppJingleIq::Content::transportCandidates() const return d->transportCandidates; } +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; @@ -662,7 +668,7 @@ public: QString responder; QString sid; - QXmppJingleIq::Content content; + QList contents; QXmppJingleIq::Reason reason; bool ringing; }; @@ -705,18 +711,25 @@ void QXmppJingleIq::setAction(QXmppJingleIq::Action action) d->action = action; } -/// Returns a reference to the IQ's content element. +// Adds an element to the IQ's content elements. + +void QXmppJingleIq::addContent(const QXmppJingleIq::Content &content) +{ + d->contents << content; +} + +/// Returns the IQ's content elements. -QXmppJingleIq::Content& QXmppJingleIq::content() +QList QXmppJingleIq::contents() const { - return d->content; + return d->contents; } -/// Returns a const reference to the IQ's content element. +/// Sets the IQ's content elements. -const QXmppJingleIq::Content& QXmppJingleIq::content() const +void QXmppJingleIq::setContents(const QList &contents) { - return d->content; + d->contents = contents; } /// Returns the session initiator. @@ -819,8 +832,14 @@ void QXmppJingleIq::parseElementFromChild(const QDomElement &element) d->sid = jingleElement.attribute("sid"); // content + d->contents.clear(); QDomElement contentElement = jingleElement.firstChildElement("content"); - d->content.parse(contentElement); + while (!contentElement.isNull()) { + QXmppJingleIq::Content content; + content.parse(contentElement); + addContent(content); + contentElement = contentElement.nextSiblingElement("content"); + } QDomElement reasonElement = jingleElement.firstChildElement("reason"); d->reason.parse(reasonElement); @@ -837,7 +856,8 @@ void QXmppJingleIq::toXmlElementFromChild(QXmlStreamWriter *writer) const helperToXmlAddAttribute(writer, "initiator", d->initiator); helperToXmlAddAttribute(writer, "responder", d->responder); helperToXmlAddAttribute(writer, "sid", d->sid); - d->content.toXml(writer); + foreach (const QXmppJingleIq::Content &content, d->contents) + content.toXml(writer); d->reason.toXml(writer); // ringing diff --git a/src/base/QXmppJingleIq.h b/src/base/QXmppJingleIq.h index 5156646d..7e21b054 100644 --- a/src/base/QXmppJingleIq.h +++ b/src/base/QXmppJingleIq.h @@ -213,6 +213,7 @@ public: void addTransportCandidate(const QXmppJingleCandidate &candidate); QList transportCandidates() const; + void setTransportCandidates(const QList &candidates); QString transportUser() const; void setTransportUser(const QString &user); @@ -296,8 +297,9 @@ public: Action action() const; void setAction(Action action); - Content& content(); - const Content& content() const; + void addContent(const Content &content); + QList contents() const; + void setContents(const QList &contents); QString initiator() const; void setInitiator(const QString &initiator); diff --git a/src/client/QXmppCallManager.cpp b/src/client/QXmppCallManager.cpp index a278eef6..7792dad8 100644 --- a/src/client/QXmppCallManager.cpp +++ b/src/client/QXmppCallManager.cpp @@ -171,6 +171,8 @@ bool QXmppCallPrivate::handleTransport(QXmppCallPrivate::Stream *stream, const Q 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) { @@ -182,10 +184,10 @@ void QXmppCallPrivate::handleRequest(const QXmppJingleIq &iq) sendAck(iq); // check content description and transport - QXmppCallPrivate::Stream *stream = findStreamByName(iq.content().name()); + QXmppCallPrivate::Stream *stream = findStreamByName(content.name()); if (!stream || - !handleDescription(stream, iq.content()) || - !handleTransport(stream, iq.content())) { + !handleDescription(stream, content) || + !handleTransport(stream, content)) { // terminate call terminate(QXmppJingleIq::Reason::FailedApplication); @@ -215,10 +217,10 @@ void QXmppCallPrivate::handleRequest(const QXmppJingleIq &iq) sendAck(iq); // check content description and transport - QXmppCallPrivate::Stream *stream = findStreamByName(iq.content().name()); + QXmppCallPrivate::Stream *stream = findStreamByName(content.name()); if (!stream || - !handleDescription(stream, iq.content()) || - !handleTransport(stream, iq.content())) { + !handleDescription(stream, content) || + !handleTransport(stream, content)) { // FIXME: what action? return; @@ -230,20 +232,20 @@ void QXmppCallPrivate::handleRequest(const QXmppJingleIq &iq) sendAck(iq); // check media stream does not exist yet - QXmppCallPrivate::Stream *stream = findStreamByName(iq.content().name()); + QXmppCallPrivate::Stream *stream = findStreamByName(content.name()); if (stream) return; // create media stream - stream = createStream(iq.content().descriptionMedia()); + stream = createStream(content.descriptionMedia()); if (!stream) return; - stream->creator = iq.content().creator(); - stream->name = iq.content().name(); + stream->creator = content.creator(); + stream->name = content.name(); // check content description - if (!handleDescription(stream, iq.content()) || - !handleTransport(stream, iq.content())) { + if (!handleDescription(stream, content) || + !handleTransport(stream, content)) { QXmppJingleIq iq; iq.setTo(q->jid()); @@ -263,21 +265,22 @@ void QXmppCallPrivate::handleRequest(const QXmppJingleIq &iq) iq.setType(QXmppIq::Set); iq.setAction(QXmppJingleIq::ContentAccept); iq.setSid(q->sid()); - iq.content().setCreator(stream->creator); - iq.content().setName(stream->name); + + QXmppJingleIq::Content responseContent; + responseContent.setCreator(stream->creator); + responseContent.setName(stream->name); // description - iq.content().setDescriptionMedia(stream->media); - iq.content().setDescriptionSsrc(stream->channel->localSsrc()); - foreach (const QXmppJinglePayloadType &payload, stream->channel->localPayloadTypes()) - iq.content().addPayloadType(payload); + responseContent.setDescriptionMedia(stream->media); + responseContent.setDescriptionSsrc(stream->channel->localSsrc()); + responseContent.setPayloadTypes(stream->channel->localPayloadTypes()); // transport - iq.content().setTransportUser(stream->connection->localUser()); - iq.content().setTransportPassword(stream->connection->localPassword()); - foreach (const QXmppJingleCandidate &candidate, stream->connection->localCandidates()) - iq.content().addTransportCandidate(candidate); + responseContent.setTransportUser(stream->connection->localUser()); + responseContent.setTransportPassword(stream->connection->localPassword()); + responseContent.setTransportCandidates(stream->connection->localCandidates()); + iq.addContent(responseContent); sendRequest(iq); } else if (iq.action() == QXmppJingleIq::TransportInfo) { @@ -286,9 +289,9 @@ void QXmppCallPrivate::handleRequest(const QXmppJingleIq &iq) sendAck(iq); // check content transport - QXmppCallPrivate::Stream *stream = findStreamByName(iq.content().name()); + QXmppCallPrivate::Stream *stream = findStreamByName(content.name()); if (!stream || - !handleTransport(stream, iq.content())) { + !handleTransport(stream, content)) { // FIXME: what action? return; } @@ -387,21 +390,22 @@ bool QXmppCallPrivate::sendInvite() // create audio stream QXmppCallPrivate::Stream *stream = findStreamByMedia(AUDIO_MEDIA); Q_ASSERT(stream); - iq.content().setCreator(stream->creator); - iq.content().setName(stream->name); - iq.content().setSenders("both"); + + QXmppJingleIq::Content content; + content.setCreator(stream->creator); + content.setName(stream->name); + content.setSenders("both"); // description - iq.content().setDescriptionMedia(stream->media); - foreach (const QXmppJinglePayloadType &payload, stream->channel->localPayloadTypes()) - iq.content().addPayloadType(payload); + content.setDescriptionMedia(stream->media); + content.setPayloadTypes(stream->channel->localPayloadTypes()); // transport - iq.content().setTransportUser(stream->connection->localUser()); - iq.content().setTransportPassword(stream->connection->localPassword()); - foreach (const QXmppJingleCandidate &candidate, stream->connection->localCandidates()) - iq.content().addTransportCandidate(candidate); + content.setTransportUser(stream->connection->localUser()); + content.setTransportPassword(stream->connection->localPassword()); + content.setTransportCandidates(stream->connection->localCandidates()); + iq.addContent(content); return sendRequest(iq); } @@ -490,20 +494,21 @@ void QXmppCall::accept() iq.setAction(QXmppJingleIq::SessionAccept); iq.setResponder(d->ownJid); iq.setSid(d->sid); - iq.content().setCreator(stream->creator); - iq.content().setName(stream->name); + + QXmppJingleIq::Content content; + content.setCreator(stream->creator); + content.setName(stream->name); // description - iq.content().setDescriptionMedia(stream->media); - foreach (const QXmppJinglePayloadType &payload, stream->channel->localPayloadTypes()) - iq.content().addPayloadType(payload); + content.setDescriptionMedia(stream->media); + content.setPayloadTypes(stream->channel->localPayloadTypes()); // transport - iq.content().setTransportUser(stream->connection->localUser()); - iq.content().setTransportPassword(stream->connection->localPassword()); - foreach (const QXmppJingleCandidate &candidate, stream->connection->localCandidates()) - iq.content().addTransportCandidate(candidate); + content.setTransportUser(stream->connection->localUser()); + content.setTransportPassword(stream->connection->localPassword()); + content.setTransportCandidates(stream->connection->localCandidates()); + iq.addContent(content); d->sendRequest(iq); // notify user @@ -606,15 +611,16 @@ void QXmppCall::localCandidatesChanged() iq.setAction(QXmppJingleIq::TransportInfo); iq.setSid(d->sid); - iq.content().setCreator(stream->creator); - iq.content().setName(stream->name); + QXmppJingleIq::Content content; + content.setCreator(stream->creator); + content.setName(stream->name); // transport - iq.content().setTransportUser(stream->connection->localUser()); - iq.content().setTransportPassword(stream->connection->localPassword()); - foreach (const QXmppJingleCandidate &candidate, stream->connection->localCandidates()) - iq.content().addTransportCandidate(candidate); + content.setTransportUser(stream->connection->localUser()); + content.setTransportPassword(stream->connection->localPassword()); + content.setTransportCandidates(stream->connection->localCandidates()); + iq.addContent(content); d->sendRequest(iq); } @@ -700,21 +706,22 @@ void QXmppCall::startVideo() iq.setType(QXmppIq::Set); iq.setAction(QXmppJingleIq::ContentAdd); iq.setSid(d->sid); - iq.content().setCreator(stream->creator); - iq.content().setName(stream->name); - iq.content().setSenders("both"); + + QXmppJingleIq::Content content; + content.setCreator(stream->creator); + content.setName(stream->name); + content.setSenders("both"); // description - iq.content().setDescriptionMedia(stream->media); - foreach (const QXmppJinglePayloadType &payload, stream->channel->localPayloadTypes()) - iq.content().addPayloadType(payload); + content.setDescriptionMedia(stream->media); + content.setPayloadTypes(stream->channel->localPayloadTypes()); // transport - iq.content().setTransportUser(stream->connection->localUser()); - iq.content().setTransportPassword(stream->connection->localPassword()); - foreach (const QXmppJingleCandidate &candidate, stream->connection->localCandidates()) - iq.content().addTransportCandidate(candidate); + content.setTransportUser(stream->connection->localUser()); + content.setTransportPassword(stream->connection->localPassword()); + content.setTransportCandidates(stream->connection->localCandidates()); + iq.addContent(content); d->sendRequest(iq); } @@ -939,18 +946,19 @@ void QXmppCallManager::_q_jingleIqReceived(const QXmppJingleIq &iq) QXmppCall *call = new QXmppCall(iq.from(), QXmppCall::IncomingDirection, this); call->d->sid = iq.sid(); - QXmppCallPrivate::Stream *stream = call->d->findStreamByMedia(iq.content().descriptionMedia()); + const QXmppJingleIq::Content content = iq.contents().isEmpty() ? QXmppJingleIq::Content() : iq.contents().first(); + QXmppCallPrivate::Stream *stream = call->d->findStreamByMedia(content.descriptionMedia()); if (!stream) return; - stream->creator = iq.content().creator(); - stream->name = iq.content().name(); + stream->creator = content.creator(); + stream->name = content.name(); // send ack call->d->sendAck(iq); // check content description and transport - if (!call->d->handleDescription(stream, iq.content()) || - !call->d->handleTransport(stream, iq.content())) { + if (!call->d->handleDescription(stream, content) || + !call->d->handleTransport(stream, content)) { // terminate call call->d->terminate(QXmppJingleIq::Reason::FailedApplication); diff --git a/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp b/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp index b7c197be..28beed98 100644 --- a/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp +++ b/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp @@ -344,8 +344,9 @@ void tst_QXmppJingleIq::testSession() QCOMPARE(session.action(), QXmppJingleIq::SessionInitiate); QCOMPARE(session.initiator(), QLatin1String("romeo@montague.lit/orchard")); QCOMPARE(session.sid(), QLatin1String("a73sjjvkla37jfea")); - QCOMPARE(session.content().creator(), QLatin1String("initiator")); - QCOMPARE(session.content().name(), QLatin1String("this-is-a-stub")); + 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); -- cgit v1.2.3 From c1b3a7421db8d5e3a0bed5fd761e918f99b90459 Mon Sep 17 00:00:00 2001 From: Jeremy Lainé Date: Mon, 31 Aug 2015 17:29:51 +0200 Subject: add assignment operators for QXmppJingleIq and QXmppJingleIq::Content --- src/base/QXmppJingleIq.cpp | 26 ++++++++++++++++++++++++++ src/base/QXmppJingleIq.h | 4 ++++ 2 files changed, 30 insertions(+) (limited to 'src/base') diff --git a/src/base/QXmppJingleIq.cpp b/src/base/QXmppJingleIq.cpp index fa958490..d24b8f73 100644 --- a/src/base/QXmppJingleIq.cpp +++ b/src/base/QXmppJingleIq.cpp @@ -175,16 +175,32 @@ QXmppJingleIqContentPrivate::QXmppJingleIqContentPrivate() { } +/// 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() { } @@ -695,6 +711,16 @@ 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 diff --git a/src/base/QXmppJingleIq.h b/src/base/QXmppJingleIq.h index 7e21b054..3dcdcd6d 100644 --- a/src/base/QXmppJingleIq.h +++ b/src/base/QXmppJingleIq.h @@ -191,6 +191,8 @@ public: Content(const QXmppJingleIq::Content &other); ~Content(); + Content& operator=(const Content &other); + QString creator() const; void setCreator(const QString &creator); @@ -294,6 +296,8 @@ public: QXmppJingleIq(const QXmppJingleIq &other); ~QXmppJingleIq(); + QXmppJingleIq& operator=(const QXmppJingleIq &other); + Action action() const; void setAction(Action action); -- cgit v1.2.3 From 9c4435ed1705ae3a3653b2c913f096f5ae0c568c Mon Sep 17 00:00:00 2001 From: Jeremy Lainé Date: Mon, 31 Aug 2015 17:49:07 +0200 Subject: hide QXmppJingleCandidate internals --- src/base/QXmppJingleIq.cpp | 141 ++++++++++++++++++++++++++++++--------------- src/base/QXmppJingleIq.h | 16 ++--- 2 files changed, 100 insertions(+), 57 deletions(-) (limited to 'src/base') diff --git a/src/base/QXmppJingleIq.cpp b/src/base/QXmppJingleIq.cpp index d24b8f73..89750a42 100644 --- a/src/base/QXmppJingleIq.cpp +++ b/src/base/QXmppJingleIq.cpp @@ -897,21 +897,68 @@ void QXmppJingleIq::toXmlElementFromChild(QXmlStreamWriter *writer) const } /// \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() - : m_component(0), - m_generation(0), - m_network(0), - m_port(0), - m_priority(0), - m_type(HostType) + : 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 m_component; + return d->component; } /// Sets the candidates's component ID. @@ -920,14 +967,14 @@ int QXmppJingleCandidate::component() const void QXmppJingleCandidate::setComponent(int component) { - m_component = component; + d->component = component; } /// Returns the candidate's foundation. QString QXmppJingleCandidate::foundation() const { - return m_foundation; + return d->foundation; } /// Sets the candidate's foundation. @@ -936,14 +983,14 @@ QString QXmppJingleCandidate::foundation() const void QXmppJingleCandidate::setFoundation(const QString &foundation) { - m_foundation = foundation; + d->foundation = foundation; } /// Returns the candidate's generation. int QXmppJingleCandidate::generation() const { - return m_generation; + return d->generation; } /// Sets the candidate's generation. @@ -952,7 +999,7 @@ int QXmppJingleCandidate::generation() const void QXmppJingleCandidate::setGeneration(int generation) { - m_generation = generation; + d->generation = generation; } /// Returns the candidate's host address. @@ -960,7 +1007,7 @@ void QXmppJingleCandidate::setGeneration(int generation) QHostAddress QXmppJingleCandidate::host() const { - return m_host; + return d->host; } /// Sets the candidate's host address. @@ -969,7 +1016,7 @@ QHostAddress QXmppJingleCandidate::host() const void QXmppJingleCandidate::setHost(const QHostAddress &host) { - m_host = host; + d->host = host; } /// Returns the candidate's unique identifier. @@ -977,7 +1024,7 @@ void QXmppJingleCandidate::setHost(const QHostAddress &host) QString QXmppJingleCandidate::id() const { - return m_id; + return d->id; } /// Sets the candidate's unique identifier. @@ -986,7 +1033,7 @@ QString QXmppJingleCandidate::id() const void QXmppJingleCandidate::setId(const QString &id) { - m_id = id; + d->id = id; } /// Returns the network index (starting at 0) the candidate is on. @@ -994,7 +1041,7 @@ void QXmppJingleCandidate::setId(const QString &id) int QXmppJingleCandidate::network() const { - return m_network; + return d->network; } /// Sets the network index (starting at 0) the candidate is on. @@ -1003,7 +1050,7 @@ int QXmppJingleCandidate::network() const void QXmppJingleCandidate::setNetwork(int network) { - m_network = network; + d->network = network; } /// Returns the candidate's port number. @@ -1011,7 +1058,7 @@ void QXmppJingleCandidate::setNetwork(int network) quint16 QXmppJingleCandidate::port() const { - return m_port; + return d->port; } /// Sets the candidate's port number. @@ -1020,7 +1067,7 @@ quint16 QXmppJingleCandidate::port() const void QXmppJingleCandidate::setPort(quint16 port) { - m_port = port; + d->port = port; } /// Returns the candidate's priority. @@ -1028,7 +1075,7 @@ void QXmppJingleCandidate::setPort(quint16 port) int QXmppJingleCandidate::priority() const { - return m_priority; + return d->priority; } /// Sets the candidate's priority. @@ -1037,7 +1084,7 @@ int QXmppJingleCandidate::priority() const void QXmppJingleCandidate::setPriority(int priority) { - m_priority = priority; + d->priority = priority; } /// Returns the candidate's protocol (e.g. "udp"). @@ -1045,7 +1092,7 @@ void QXmppJingleCandidate::setPriority(int priority) QString QXmppJingleCandidate::protocol() const { - return m_protocol; + return d->protocol; } /// Sets the candidate's protocol (e.g. "udp"). @@ -1054,7 +1101,7 @@ QString QXmppJingleCandidate::protocol() const void QXmppJingleCandidate::setProtocol(const QString &protocol) { - m_protocol = protocol; + d->protocol = protocol; } /// Returns the candidate type (e.g. "host"). @@ -1062,7 +1109,7 @@ void QXmppJingleCandidate::setProtocol(const QString &protocol) QXmppJingleCandidate::Type QXmppJingleCandidate::type() const { - return m_type; + return d->type; } /// Sets the candidate type (e.g. "host"). @@ -1071,7 +1118,7 @@ QXmppJingleCandidate::Type QXmppJingleCandidate::type() const void QXmppJingleCandidate::setType(QXmppJingleCandidate::Type type) { - m_type = type; + d->type = type; } /// Returns true if the host address or port are empty. @@ -1079,37 +1126,37 @@ void QXmppJingleCandidate::setType(QXmppJingleCandidate::Type type) bool QXmppJingleCandidate::isNull() const { - return m_host.isNull() || !m_port; + return d->host.isNull() || !d->port; } /// \cond void QXmppJingleCandidate::parse(const QDomElement &element) { - m_component = element.attribute("component").toInt(); - m_foundation = element.attribute("foundation"); - m_generation = element.attribute("generation").toInt(); - m_host = QHostAddress(element.attribute("ip")); - m_id = element.attribute("id"); - m_network = element.attribute("network").toInt(); - m_port = element.attribute("port").toInt(); - m_priority = element.attribute("priority").toInt(); - m_protocol = element.attribute("protocol"); - m_type = typeFromString(element.attribute("type")); + d->component = element.attribute("component").toInt(); + d->foundation = element.attribute("foundation"); + d->generation = element.attribute("generation").toInt(); + d->host = QHostAddress(element.attribute("ip")); + d->id = element.attribute("id"); + d->network = element.attribute("network").toInt(); + d->port = element.attribute("port").toInt(); + d->priority = element.attribute("priority").toInt(); + d->protocol = element.attribute("protocol"); + d->type = typeFromString(element.attribute("type")); } void QXmppJingleCandidate::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement("candidate"); - helperToXmlAddAttribute(writer, "component", QString::number(m_component)); - helperToXmlAddAttribute(writer, "foundation", m_foundation); - helperToXmlAddAttribute(writer, "generation", QString::number(m_generation)); - helperToXmlAddAttribute(writer, "id", m_id); - helperToXmlAddAttribute(writer, "ip", m_host.toString()); - helperToXmlAddAttribute(writer, "network", QString::number(m_network)); - helperToXmlAddAttribute(writer, "port", QString::number(m_port)); - helperToXmlAddAttribute(writer, "priority", QString::number(m_priority)); - helperToXmlAddAttribute(writer, "protocol", m_protocol); - helperToXmlAddAttribute(writer, "type", typeToString(m_type)); + helperToXmlAddAttribute(writer, "component", QString::number(d->component)); + helperToXmlAddAttribute(writer, "foundation", d->foundation); + helperToXmlAddAttribute(writer, "generation", QString::number(d->generation)); + helperToXmlAddAttribute(writer, "id", d->id); + helperToXmlAddAttribute(writer, "ip", d->host.toString()); + helperToXmlAddAttribute(writer, "network", QString::number(d->network)); + helperToXmlAddAttribute(writer, "port", QString::number(d->port)); + helperToXmlAddAttribute(writer, "priority", QString::number(d->priority)); + helperToXmlAddAttribute(writer, "protocol", d->protocol); + helperToXmlAddAttribute(writer, "type", typeToString(d->type)); writer->writeEndElement(); } diff --git a/src/base/QXmppJingleIq.h b/src/base/QXmppJingleIq.h index 3dcdcd6d..4c14a8a9 100644 --- a/src/base/QXmppJingleIq.h +++ b/src/base/QXmppJingleIq.h @@ -28,6 +28,7 @@ #include "QXmppIq.h" +class QXmppJingleCandidatePrivate; class QXmppJingleIqContentPrivate; class QXmppJingleIqPrivate; @@ -98,6 +99,10 @@ public: }; QXmppJingleCandidate(); + QXmppJingleCandidate(const QXmppJingleCandidate &other); + ~QXmppJingleCandidate(); + + QXmppJingleCandidate& operator=(const QXmppJingleCandidate &other); int component() const; void setComponent(int component); @@ -140,16 +145,7 @@ public: /// \endcond private: - int m_component; - QString m_foundation; - int m_generation; - QHostAddress m_host; - QString m_id; - int m_network; - quint16 m_port; - QString m_protocol; - int m_priority; - QXmppJingleCandidate::Type m_type; + QSharedDataPointer d; }; /// \brief The QXmppJingleIq class represents an IQ used for initiating media -- cgit v1.2.3 From 4392a229eb8a53b5903b0df8779ed8f1a6ee0f06 Mon Sep 17 00:00:00 2001 From: Jeremy Lainé Date: Mon, 31 Aug 2015 17:53:42 +0200 Subject: add some code docs --- src/base/QXmppJingleIq.cpp | 9 ++++++++- src/base/QXmppJingleIq.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'src/base') diff --git a/src/base/QXmppJingleIq.cpp b/src/base/QXmppJingleIq.cpp index 89750a42..94207a38 100644 --- a/src/base/QXmppJingleIq.cpp +++ b/src/base/QXmppJingleIq.cpp @@ -619,7 +619,6 @@ QString QXmppJingleIq::Content::toSdp() const return sdp.join("\r\n") + "\r\n"; } - /// \endcond QXmppJingleIq::Reason::Reason() @@ -627,21 +626,29 @@ QXmppJingleIq::Reason::Reason() { } +/// 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; diff --git a/src/base/QXmppJingleIq.h b/src/base/QXmppJingleIq.h index 4c14a8a9..e997bef4 100644 --- a/src/base/QXmppJingleIq.h +++ b/src/base/QXmppJingleIq.h @@ -249,6 +249,7 @@ public: class QXMPP_EXPORT Reason { public: + /// This enum is used to describe a reason's type. enum Type { None, AlternativeSession, -- cgit v1.2.3 From ca0937c6fcc2e4fc476437cd9d94e177e52227ff Mon Sep 17 00:00:00 2001 From: Jeremy Lainé Date: Mon, 31 Aug 2015 18:17:28 +0200 Subject: hide QXmppJinglePayloadType internals --- src/base/QXmppJingleIq.cpp | 140 ++++++++++++++++++++++++++++++--------------- src/base/QXmppJingleIq.h | 12 ++-- 2 files changed, 98 insertions(+), 54 deletions(-) (limited to 'src/base') diff --git a/src/base/QXmppJingleIq.cpp b/src/base/QXmppJingleIq.cpp index 94207a38..26967283 100644 --- a/src/base/QXmppJingleIq.cpp +++ b/src/base/QXmppJingleIq.cpp @@ -708,6 +708,10 @@ QXmppJingleIq::QXmppJingleIq() { } +/// Constructs a copy of other. +/// +/// \param other + QXmppJingleIq::QXmppJingleIq(const QXmppJingleIq &other) : QXmppIq(other) , d(other.d) @@ -744,7 +748,7 @@ void QXmppJingleIq::setAction(QXmppJingleIq::Action action) d->action = action; } -// Adds an element to the IQ's content elements. +/// Adds an element to the IQ's content elements. void QXmppJingleIq::addContent(const QXmppJingleIq::Content &content) { @@ -1211,12 +1215,44 @@ QString QXmppJingleCandidate::typeToString(QXmppJingleCandidate::Type type) } /// \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() - : m_channels(1), - m_clockrate(0), - m_id(0), - m_maxptime(0), - m_ptime(0) + : d(new QXmppJinglePayloadTypePrivate()) +{ +} + +/// Constructs a copy of other. +/// +/// \param other + +QXmppJinglePayloadType::QXmppJinglePayloadType(const QXmppJinglePayloadType &other) + : d(other.d) +{ +} + +QXmppJinglePayloadType::~QXmppJinglePayloadType() { } @@ -1225,7 +1261,7 @@ QXmppJinglePayloadType::QXmppJinglePayloadType() unsigned char QXmppJinglePayloadType::channels() const { - return m_channels; + return d->channels; } /// Sets the number of channels (e.g. 1 for mono, 2 for stereo). @@ -1234,7 +1270,7 @@ unsigned char QXmppJinglePayloadType::channels() const void QXmppJinglePayloadType::setChannels(unsigned char channels) { - m_channels = channels; + d->channels = channels; } /// Returns the clockrate in Hz, i.e. the number of samples per second. @@ -1242,7 +1278,7 @@ void QXmppJinglePayloadType::setChannels(unsigned char channels) unsigned int QXmppJinglePayloadType::clockrate() const { - return m_clockrate; + return d->clockrate; } /// Sets the clockrate in Hz, i.e. the number of samples per second. @@ -1251,7 +1287,7 @@ unsigned int QXmppJinglePayloadType::clockrate() const void QXmppJinglePayloadType::setClockrate(unsigned int clockrate) { - m_clockrate = clockrate; + d->clockrate = clockrate; } /// Returns the payload type identifier. @@ -1259,7 +1295,7 @@ void QXmppJinglePayloadType::setClockrate(unsigned int clockrate) unsigned char QXmppJinglePayloadType::id() const { - return m_id; + return d->id; } /// Sets the payload type identifier. @@ -1268,7 +1304,7 @@ unsigned char QXmppJinglePayloadType::id() const void QXmppJinglePayloadType::setId(unsigned char id) { Q_ASSERT(id <= 127); - m_id = id; + d->id = id; } /// Returns the maximum packet time in milliseconds. @@ -1276,7 +1312,7 @@ void QXmppJinglePayloadType::setId(unsigned char id) unsigned int QXmppJinglePayloadType::maxptime() const { - return m_maxptime; + return d->maxptime; } /// Sets the maximum packet type in milliseconds. @@ -1285,7 +1321,7 @@ unsigned int QXmppJinglePayloadType::maxptime() const void QXmppJinglePayloadType::setMaxptime(unsigned int maxptime) { - m_maxptime = maxptime; + d->maxptime = maxptime; } /// Returns the payload type name. @@ -1293,7 +1329,7 @@ void QXmppJinglePayloadType::setMaxptime(unsigned int maxptime) QString QXmppJinglePayloadType::name() const { - return m_name; + return d->name; } /// Sets the payload type name. @@ -1302,21 +1338,21 @@ QString QXmppJinglePayloadType::name() const void QXmppJinglePayloadType::setName(const QString &name) { - m_name = name; + d->name = name; } /// Returns the payload parameters. QMap QXmppJinglePayloadType::parameters() const { - return m_parameters; + return d->parameters; } /// Sets the payload parameters. void QXmppJinglePayloadType::setParameters(const QMap ¶meters) { - m_parameters = parameters; + d->parameters = parameters; } /// Returns the packet time in milliseconds (20 by default). @@ -1324,7 +1360,7 @@ void QXmppJinglePayloadType::setParameters(const QMap ¶met unsigned int QXmppJinglePayloadType::ptime() const { - return m_ptime ? m_ptime : 20; + return d->ptime ? d->ptime : 20; } /// Sets the packet time in milliseconds (20 by default). @@ -1333,24 +1369,24 @@ unsigned int QXmppJinglePayloadType::ptime() const void QXmppJinglePayloadType::setPtime(unsigned int ptime) { - m_ptime = ptime; + d->ptime = ptime; } /// \cond void QXmppJinglePayloadType::parse(const QDomElement &element) { - m_id = element.attribute("id").toInt(); - m_name = element.attribute("name"); - m_channels = element.attribute("channels").toInt(); - if (!m_channels) - m_channels = 1; - m_clockrate = element.attribute("clockrate").toInt(); - m_maxptime = element.attribute("maxptime").toInt(); - m_ptime = element.attribute("ptime").toInt(); + d->id = element.attribute("id").toInt(); + d->name = element.attribute("name"); + d->channels = element.attribute("channels").toInt(); + if (!d->channels) + d->channels = 1; + d->clockrate = element.attribute("clockrate").toInt(); + d->maxptime = element.attribute("maxptime").toInt(); + d->ptime = element.attribute("ptime").toInt(); QDomElement child = element.firstChildElement("parameter"); while (!child.isNull()) { - m_parameters.insert(child.attribute("name"), child.attribute("value")); + d->parameters.insert(child.attribute("name"), child.attribute("value")); child = child.nextSiblingElement("parameter"); } } @@ -1358,27 +1394,37 @@ void QXmppJinglePayloadType::parse(const QDomElement &element) void QXmppJinglePayloadType::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement("payload-type"); - helperToXmlAddAttribute(writer, "id", QString::number(m_id)); - helperToXmlAddAttribute(writer, "name", m_name); - if (m_channels > 1) - helperToXmlAddAttribute(writer, "channels", QString::number(m_channels)); - if (m_clockrate > 0) - helperToXmlAddAttribute(writer, "clockrate", QString::number(m_clockrate)); - if (m_maxptime > 0) - helperToXmlAddAttribute(writer, "maxptime", QString::number(m_maxptime)); - if (m_ptime > 0) - helperToXmlAddAttribute(writer, "ptime", QString::number(m_ptime)); - - foreach (const QString &key, m_parameters.keys()) { + helperToXmlAddAttribute(writer, "id", QString::number(d->id)); + helperToXmlAddAttribute(writer, "name", d->name); + if (d->channels > 1) + helperToXmlAddAttribute(writer, "channels", QString::number(d->channels)); + if (d->clockrate > 0) + helperToXmlAddAttribute(writer, "clockrate", QString::number(d->clockrate)); + if (d->maxptime > 0) + helperToXmlAddAttribute(writer, "maxptime", QString::number(d->maxptime)); + if (d->ptime > 0) + helperToXmlAddAttribute(writer, "ptime", QString::number(d->ptime)); + + foreach (const QString &key, d->parameters.keys()) { writer->writeStartElement("parameter"); writer->writeAttribute("name", key); - writer->writeAttribute("value", m_parameters.value(key)); + writer->writeAttribute("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 @@ -1386,10 +1432,10 @@ void QXmppJinglePayloadType::toXml(QXmlStreamWriter *writer) const bool QXmppJinglePayloadType::operator==(const QXmppJinglePayloadType &other) const { // FIXME : what to do with m_ptime and m_maxptime? - if (m_id <= 95) - return other.m_id == m_id && other.m_clockrate == m_clockrate; + if (d->id <= 95) + return other.d->id == d->id && other.d->clockrate == d->clockrate; else - return other.m_channels == m_channels && - other.m_clockrate == m_clockrate && - other.m_name.toLower() == m_name.toLower(); + return other.d->channels == d->channels && + other.d->clockrate == d->clockrate && + other.d->name.toLower() == d->name.toLower(); } diff --git a/src/base/QXmppJingleIq.h b/src/base/QXmppJingleIq.h index e997bef4..ba41dc8f 100644 --- a/src/base/QXmppJingleIq.h +++ b/src/base/QXmppJingleIq.h @@ -31,6 +31,7 @@ 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. @@ -40,6 +41,8 @@ class QXMPP_EXPORT QXmppJinglePayloadType { public: QXmppJinglePayloadType(); + QXmppJinglePayloadType(const QXmppJinglePayloadType &other); + ~QXmppJinglePayloadType(); unsigned char channels() const; void setChannels(unsigned char channels); @@ -67,16 +70,11 @@ public: void toXml(QXmlStreamWriter *writer) const; /// \endcond + QXmppJinglePayloadType& operator=(const QXmppJinglePayloadType &other); bool operator==(const QXmppJinglePayloadType &other) const; private: - unsigned char m_channels; - unsigned int m_clockrate; - unsigned char m_id; - unsigned int m_maxptime; - QString m_name; - QMap m_parameters; - unsigned int m_ptime; + QSharedDataPointer d; }; /// \brief The QXmppJingleCandidate class represents a transport candidate -- cgit v1.2.3 From 73d9aa8badbf4cabf55fcc740cc79ec0d4b528f9 Mon Sep 17 00:00:00 2001 From: Jeremy Lainé Date: Wed, 2 Sep 2015 16:45:37 +0200 Subject: bump version to 0.9.2 --- CHANGELOG | 6 ++++++ qxmpp.pri | 2 +- src/base/QXmppGlobal.h | 2 +- tests/qxmpputils/tst_qxmpputils.cpp | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) (limited to 'src/base') diff --git a/CHANGELOG b/CHANGELOG index 6d5f4da8..01ec1075 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,9 @@ +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) -------------------------- diff --git a/qxmpp.pri b/qxmpp.pri index fc4438bd..e3c651ba 100644 --- a/qxmpp.pri +++ b/qxmpp.pri @@ -1,7 +1,7 @@ # Common definitions QT += network xml -QXMPP_VERSION = 0.9.1 +QXMPP_VERSION = 0.9.2 QXMPP_INCLUDEPATH = $$PWD/src/base $$PWD/src/client $$PWD/src/server # Determine library name diff --git a/src/base/QXmppGlobal.h b/src/base/QXmppGlobal.h index f6373f08..01826e44 100644 --- a/src/base/QXmppGlobal.h +++ b/src/base/QXmppGlobal.h @@ -52,7 +52,7 @@ /// available. /// -#define QXMPP_VERSION 0x000901 +#define QXMPP_VERSION 0x000902 QXMPP_EXPORT QString QXmppVersion(); diff --git a/tests/qxmpputils/tst_qxmpputils.cpp b/tests/qxmpputils/tst_qxmpputils.cpp index f14eacc8..55314823 100644 --- a/tests/qxmpputils/tst_qxmpputils.cpp +++ b/tests/qxmpputils/tst_qxmpputils.cpp @@ -114,7 +114,7 @@ void tst_QXmppUtils::testMime() void tst_QXmppUtils::testLibVersion() { - QCOMPARE(QXmppVersion(), QString("0.9.1")); + QCOMPARE(QXmppVersion(), QString("0.9.2")); } void tst_QXmppUtils::testTimezoneOffset() -- cgit v1.2.3 From 1d828207bbab3b53ec251ac311d761a99e8977e5 Mon Sep 17 00:00:00 2001 From: Jeremy Lainé Date: Thu, 3 Sep 2015 11:03:18 +0200 Subject: Add QXmppIceConnection::gatheringState property --- CHANGELOG | 4 ++ src/base/QXmppStun.cpp | 77 +++++++++++++++++++++- src/base/QXmppStun.h | 19 ++++++ .../qxmppiceconnection/tst_qxmppiceconnection.cpp | 10 ++- 4 files changed, 107 insertions(+), 3 deletions(-) (limited to 'src/base') diff --git a/CHANGELOG b/CHANGELOG index 01ec1075..382c3974 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +QXmpp 0.9.3 (UNRELEASED) + + - Add QXmppIceConnection::gatheringState property. + QXmpp 0.9.2 (Sep 2, 2015) ------------------------- diff --git a/src/base/QXmppStun.cpp b/src/base/QXmppStun.cpp index 18a3342d..86d4004a 100644 --- a/src/base/QXmppStun.cpp +++ b/src/base/QXmppStun.cpp @@ -42,6 +42,12 @@ 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", @@ -1761,6 +1767,8 @@ public: const QXmppIcePrivate* const config; CandidatePair *fallbackPair; + QXmppIceConnection::GatheringState gatheringState; + QList localCandidates; quint32 peerReflexivePriority; @@ -1786,6 +1794,7 @@ QXmppIceComponentPrivate::QXmppIceComponentPrivate(int component_, QXmppIcePriva , component(component_) , config(config_) , fallbackPair(0) + , gatheringState(QXmppIceConnection::NewGatheringState) , peerReflexivePriority(0) , timer(0) , turnAllocation(0) @@ -1909,6 +1918,8 @@ void QXmppIceComponentPrivate::setSockets(QList sockets) transports << turnAllocation; turnAllocation->connectToHost(); } + + q->updateGatheringState(); } void QXmppIceComponentPrivate::setTurnServer(const QHostAddress &host, quint16 port) @@ -1965,6 +1976,9 @@ QXmppIceComponent::QXmppIceComponent(int component, QXmppIcePrivate *config, QOb check = connect(d->turnAllocation, SIGNAL(datagramReceived(QByteArray,QHostAddress,quint16)), this, SLOT(handleDatagram(QByteArray,QHostAddress,quint16))); Q_ASSERT(check); + check = connect(d->turnAllocation, SIGNAL(disconnected()), + this, SLOT(updateGatheringState())); + Q_ASSERT(check); // calculate peer-reflexive candidate priority // see RFC 5245 - 7.1.2.1. PRIORITY and USE-CANDIDATE @@ -2313,6 +2327,7 @@ void QXmppIceComponent::transactionFinished() transaction->response().errorPhrase)); } d->stunTransactions.remove(transaction); + updateGatheringState(); return; } } @@ -2328,6 +2343,7 @@ void QXmppIceComponent::turnConnected() d->localCandidates << candidate; emit localCandidatesChanged(); + updateGatheringState(); } static QList reservePort(const QList &addresses, quint16 port, QObject *parent) @@ -2443,6 +2459,23 @@ qint64 QXmppIceComponent::sendDatagram(const QByteArray &datagram) 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) { QXmppStunTransaction *transaction = qobject_cast(sender()); @@ -2476,6 +2509,8 @@ public: QMap components; QTimer *connectTimer; + QXmppIceConnection::GatheringState gatheringState; + QHostAddress turnHost; quint16 turnPort; QString turnUser; @@ -2483,7 +2518,8 @@ public: }; QXmppIceConnectionPrivate::QXmppIceConnectionPrivate() - : turnPort(0) + : gatheringState(QXmppIceConnection::NewGatheringState) + , turnPort(0) { } @@ -2549,6 +2585,10 @@ void QXmppIceConnection::addComponent(int component) this, SLOT(slotConnected())); Q_ASSERT(check); + check = connect(socket, SIGNAL(gatheringStateChanged()), + this, SLOT(slotGatheringStateChanged())); + Q_ASSERT(check); + d->components[component] = socket; } @@ -2622,6 +2662,14 @@ bool QXmppIceConnection::isConnected() const return true; } +/// Returns the ICE gathering state, that is the discovery of +/// local candidates. + +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 @@ -2740,6 +2788,33 @@ void QXmppIceConnection::slotConnected() emit connected(); } +void QXmppIceConnection::slotGatheringStateChanged() +{ + GatheringState newGatheringState; + bool allComplete = true; + bool allNew = true; + foreach (QXmppIceComponent *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(QString("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(QString("ICE negotiation timed out")); diff --git a/src/base/QXmppStun.h b/src/base/QXmppStun.h index cc8b01c1..bd0868c0 100644 --- a/src/base/QXmppStun.h +++ b/src/base/QXmppStun.h @@ -186,6 +186,7 @@ private slots: void handleDatagram(const QByteArray &datagram, const QHostAddress &host, quint16 port); void turnConnected(); void transactionFinished(); + void updateGatheringState(); void writeStun(const QXmppStunMessage &request); signals: @@ -195,6 +196,9 @@ signals: /// \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(); @@ -237,8 +241,17 @@ private: class QXMPP_EXPORT QXmppIceConnection : public QXmppLoggable { Q_OBJECT + Q_ENUMS(GatheringState) + Q_PROPERTY(QXmppIceConnection::GatheringState gatheringState READ gatheringState NOTIFY gatheringStateChanged) public: + enum GatheringState + { + NewGatheringState, + BusyGatheringState, + CompleteGatheringState + }; + QXmppIceConnection(QObject *parent = 0); ~QXmppIceConnection(); @@ -262,6 +275,8 @@ public: bool bind(const QList &addresses); bool isConnected() const; + GatheringState gatheringState() const; + signals: /// \brief This signal is emitted once ICE negotiation succeeds. void connected(); @@ -269,6 +284,9 @@ signals: /// \brief This signal is emitted when ICE negotiation fails. void disconnected(); + /// \brief 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(); @@ -278,6 +296,7 @@ public slots: private slots: void slotConnected(); + void slotGatheringStateChanged(); void slotTimeout(); private: diff --git a/tests/qxmppiceconnection/tst_qxmppiceconnection.cpp b/tests/qxmppiceconnection/tst_qxmppiceconnection.cpp index 39a2359f..dd449726 100644 --- a/tests/qxmppiceconnection/tst_qxmppiceconnection.cpp +++ b/tests/qxmppiceconnection/tst_qxmppiceconnection.cpp @@ -51,7 +51,9 @@ void tst_QXmppIceConnection::testBind() 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()); foreach (const QXmppJingleCandidate &c, client.localCandidates()) { @@ -80,13 +82,17 @@ void tst_QXmppIceConnection::testBindStun() 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, SIGNAL(localCandidatesChanged()), + connect(&client, SIGNAL(gatheringStateChanged()), &loop, SLOT(quit())); - client.bind(QXmppIceComponent::discoverAddresses()); loop.exec(); bool foundReflexive = false; + QCOMPARE(client.gatheringState(), QXmppIceConnection::CompleteGatheringState); QCOMPARE(client.localCandidates().size(), component->localCandidates().size()); QVERIFY(!client.localCandidates().isEmpty()); foreach (const QXmppJingleCandidate &c, client.localCandidates()) { -- cgit v1.2.3 From 88b4688afe762bf155225b8b8016ae81c24afa3b Mon Sep 17 00:00:00 2001 From: Jeremy Lainé Date: Thu, 3 Sep 2015 13:20:04 +0200 Subject: improve default candidate selection during SDP generation --- src/base/QXmppJingleIq.cpp | 12 +++++++- tests/qxmppjingleiq/tst_qxmppjingleiq.cpp | 50 +++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) (limited to 'src/base') diff --git a/src/base/QXmppJingleIq.cpp b/src/base/QXmppJingleIq.cpp index 26967283..b837a4de 100644 --- a/src/base/QXmppJingleIq.cpp +++ b/src/base/QXmppJingleIq.cpp @@ -559,12 +559,22 @@ bool QXmppJingleIq::Content::parseSdp(const QString &sdp) 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; - foreach (const QXmppJingleCandidate &candidate, d->transportCandidates) { + QList sortedCandidates = d->transportCandidates; + qSort(sortedCandidates.begin(), sortedCandidates.end(), candidateLessThan); + foreach (const QXmppJingleCandidate &candidate, sortedCandidates) { if (candidate.component() == RTP_COMPONENT) { localRtpAddress = candidate.host(); localRtpPort = candidate.port(); diff --git a/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp b/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp index 28beed98..b5e9c094 100644 --- a/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp +++ b/tests/qxmppjingleiq/tst_qxmppjingleiq.cpp @@ -34,6 +34,7 @@ private slots: void testContent(); void testContentFingerprint(); void testContentSdp(); + void testContentSdpReflexive(); void testContentSdpFingerprint(); void testContentSdpParameters(); void testSession(); @@ -209,6 +210,55 @@ void tst_QXmppJingleIq::testContentSdp() "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"); -- cgit v1.2.3 From a159e4c4afc62628ec5b753829e1a023b6ae5dea Mon Sep 17 00:00:00 2001 From: Jeremy Lainé Date: Thu, 3 Sep 2015 13:47:38 +0200 Subject: make SDP parser more tolerant : accept both LF and CRLF --- src/base/QXmppJingleIq.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/base') diff --git a/src/base/QXmppJingleIq.cpp b/src/base/QXmppJingleIq.cpp index b837a4de..899509e7 100644 --- a/src/base/QXmppJingleIq.cpp +++ b/src/base/QXmppJingleIq.cpp @@ -458,7 +458,10 @@ void QXmppJingleIq::Content::toXml(QXmlStreamWriter *writer) const bool QXmppJingleIq::Content::parseSdp(const QString &sdp) { QList payloads; - foreach (const QString &line, sdp.split("\r\n")) { + QString line; + foreach (line, sdp.split('\n')) { + if (line.endsWith('\r')) + line.resize(line.size() - 1); if (line.startsWith("a=")) { int idx = line.indexOf(':'); const QString attrName = idx != -1 ? line.mid(2, idx - 2) : line.mid(2); -- cgit v1.2.3